From 79439ff8270006d7fe61a2a7deba2fbecf45b52d Mon Sep 17 00:00:00 2001
From: "coderabbitai[bot]"
<136622811+coderabbitai[bot]@users.noreply.github.com>
Date: Sun, 15 Mar 2026 23:03:24 +0000
Subject: [PATCH] CodeRabbit Generated Unit Tests: Add unit tests for PR
changes
---
.../OrderGrid/__tests__/OrderGrid.test.tsx | 140 ++++++-
.../__tests__/order-grid.navigation.test.ts | 67 ++++
.../__tests__/order-grid.query.test.ts | 67 ++++
lib/orders/__tests__/orders.service.test.ts | 63 +++-
lib/orders/__tests__/orders.sort.test.ts | 341 ++++++++++++++++++
5 files changed, 676 insertions(+), 2 deletions(-)
create mode 100644 components/OrderGrid/__tests__/order-grid.navigation.test.ts
create mode 100644 components/OrderGrid/__tests__/order-grid.query.test.ts
create mode 100644 lib/orders/__tests__/orders.sort.test.ts
diff --git a/components/OrderGrid/__tests__/OrderGrid.test.tsx b/components/OrderGrid/__tests__/OrderGrid.test.tsx
index 8ffe3e8..0264668 100644
--- a/components/OrderGrid/__tests__/OrderGrid.test.tsx
+++ b/components/OrderGrid/__tests__/OrderGrid.test.tsx
@@ -76,4 +76,142 @@ describe('OrderGrid', () => {
expect(OrdersGrid.Body).toBeDefined();
expect(OrdersGrid.defaultColumns).toBe(defaultColumns);
});
-});
+
+ it('renders sortable header links with active indicator', () => {
+ render(
+ ,
+ );
+
+ const priceSortLink = screen.getByRole('link', { name: 'Ordenar por Preço' });
+ expect(priceSortLink).toBeInTheDocument();
+ expect(priceSortLink).toHaveTextContent('▼');
+ });
+
+ it('renders plain text headers when no sortState is provided', () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByText('Instrumento')).toBeInTheDocument();
+ expect(screen.queryByRole('link', { name: /Ordenar/ })).not.toBeInTheDocument();
+ });
+
+ it('shows ascending indicator (▲) for active ascending sort column', () => {
+ render(
+ ,
+ );
+
+ const priceSortLink = screen.getByRole('link', { name: 'Ordenar por Preço' });
+ expect(priceSortLink).toHaveTextContent('▲');
+ });
+
+ it('shows neutral indicator (↕) for inactive sortable column', () => {
+ render(
+ ,
+ );
+
+ const instrumentLink = screen.getByRole('link', { name: 'Ordenar por Instrumento' });
+ expect(instrumentLink).toHaveTextContent('↕');
+ });
+
+ it('sets aria-sort descending on active descending column', () => {
+ const { container } = render(
+ ,
+ );
+
+ const priceHeader = container.querySelector('th[aria-sort="descending"]');
+ expect(priceHeader).toBeInTheDocument();
+ });
+
+ it('sets aria-sort ascending on active ascending column', () => {
+ const { container } = render(
+ ,
+ );
+
+ const instrumentHeader = container.querySelector('th[aria-sort="ascending"]');
+ expect(instrumentHeader).toBeInTheDocument();
+ });
+
+ it('sets aria-sort none on all headers when no sortState is provided', () => {
+ const { container } = render(
+ ,
+ );
+
+ const headers = container.querySelectorAll('th');
+ headers.forEach((th) => {
+ expect(th).toHaveAttribute('aria-sort', 'none');
+ });
+ });
+
+ it('applies active CSS class only to the active sort link', () => {
+ const { container } = render(
+ ,
+ );
+
+ const activeLink = container.querySelector('.orders-grid__sort-link--active');
+ expect(activeLink).not.toBeNull();
+ expect(activeLink).toHaveAttribute('aria-label', 'Ordenar por Preço');
+
+ const allLinks = container.querySelectorAll('.orders-grid__sort-link');
+ const inactiveLinks = container.querySelectorAll(
+ '.orders-grid__sort-link:not(.orders-grid__sort-link--active)',
+ );
+ expect(inactiveLinks.length).toBe(allLinks.length - 1);
+ });
+});
\ No newline at end of file
diff --git a/components/OrderGrid/__tests__/order-grid.navigation.test.ts b/components/OrderGrid/__tests__/order-grid.navigation.test.ts
new file mode 100644
index 0000000..3a90fce
--- /dev/null
+++ b/components/OrderGrid/__tests__/order-grid.navigation.test.ts
@@ -0,0 +1,67 @@
+jest.mock('@/lib/orders/orders.sort', () => ({
+ getDefaultSortDirForField: (field: string) =>
+ field === 'timestamp' ? 'desc' : 'asc',
+}));
+
+import {
+ buildOrderGridHref,
+ buildOrderGridSortLinks,
+ getNextSortDir,
+} from '../order-grid.navigation';
+
+describe('order-grid.navigation', () => {
+ it('builds grid href with page and sorting params', () => {
+ const href = buildOrderGridHref(3, 'price', 'asc');
+
+ expect(href).toBe('/?page=3&sortBy=price&sortDir=asc');
+ });
+
+ it('toggles direction when sorting same field', () => {
+ expect(getNextSortDir('price', 'asc', 'price')).toBe('desc');
+ expect(getNextSortDir('price', 'desc', 'price')).toBe('asc');
+ });
+
+ it('uses field default direction when sorting a different field', () => {
+ expect(getNextSortDir('timestamp', 'desc', 'price')).toBe('asc');
+ expect(getNextSortDir('price', 'asc', 'timestamp')).toBe('desc');
+ });
+
+ it('builds sort links for all sortable fields', () => {
+ const links = buildOrderGridSortLinks('price', 'asc');
+
+ expect(links.instrument).toBe('/?page=1&sortBy=instrument&sortDir=asc');
+ expect(links.side).toBe('/?page=1&sortBy=side&sortDir=asc');
+ expect(links.price).toBe('/?page=1&sortBy=price&sortDir=desc');
+ expect(links.quantity).toBe('/?page=1&sortBy=quantity&sortDir=asc');
+ expect(links.remainingQuantity).toBe(
+ '/?page=1&sortBy=remainingQuantity&sortDir=asc',
+ );
+ expect(links.status).toBe('/?page=1&sortBy=status&sortDir=asc');
+ expect(links.timestamp).toBe('/?page=1&sortBy=timestamp&sortDir=desc');
+ });
+
+ it('always resets to page 1 in sort links regardless of current page', () => {
+ const links = buildOrderGridSortLinks('instrument', 'asc');
+
+ // All links should point to page=1
+ Object.values(links).forEach((href) => {
+ expect(href).toMatch(/page=1/);
+ });
+ });
+
+ it('builds sort links when timestamp is the active sort field', () => {
+ const links = buildOrderGridSortLinks('timestamp', 'desc');
+
+ // timestamp is active with desc → next click should go asc
+ expect(links.timestamp).toBe('/?page=1&sortBy=timestamp&sortDir=asc');
+ // other fields use their default direction (asc)
+ expect(links.price).toBe('/?page=1&sortBy=price&sortDir=asc');
+ expect(links.instrument).toBe('/?page=1&sortBy=instrument&sortDir=asc');
+ });
+
+ it('builds href with page 1', () => {
+ const href = buildOrderGridHref(1, 'instrument', 'desc');
+
+ expect(href).toBe('/?page=1&sortBy=instrument&sortDir=desc');
+ });
+});
\ No newline at end of file
diff --git a/components/OrderGrid/__tests__/order-grid.query.test.ts b/components/OrderGrid/__tests__/order-grid.query.test.ts
new file mode 100644
index 0000000..85107e6
--- /dev/null
+++ b/components/OrderGrid/__tests__/order-grid.query.test.ts
@@ -0,0 +1,67 @@
+jest.mock('@/lib/orders/orders.sort', () => ({
+ resolveOrdersSortBy: (value?: string) =>
+ value === 'price' || value === 'timestamp' ? value : 'timestamp',
+ resolveOrdersSortDir: (value?: string) =>
+ value === 'asc' || value === 'desc' ? value : 'desc',
+}));
+
+import { resolveOrderGridQuery } from '../order-grid.query';
+
+describe('order-grid.query', () => {
+ it('returns defaults when query params are missing', () => {
+ const result = resolveOrderGridQuery({});
+
+ expect(result).toEqual({
+ requestedPage: 1,
+ sortBy: 'timestamp',
+ sortDir: 'desc',
+ });
+ });
+
+ it('clamps page to at least 1', () => {
+ expect(resolveOrderGridQuery({ page: '0' }).requestedPage).toBe(1);
+ expect(resolveOrderGridQuery({ page: '-10' }).requestedPage).toBe(1);
+ expect(resolveOrderGridQuery({ page: '5' }).requestedPage).toBe(5);
+ });
+
+ it('falls back to defaults for invalid sorting params', () => {
+ const result = resolveOrderGridQuery({
+ sortBy: 'invalid-field',
+ sortDir: 'invalid-dir',
+ });
+
+ expect(result.sortBy).toBe('timestamp');
+ expect(result.sortDir).toBe('desc');
+ });
+
+ it('resolves valid sorting params', () => {
+ const result = resolveOrderGridQuery({
+ sortBy: 'price',
+ sortDir: 'asc',
+ });
+
+ expect(result.sortBy).toBe('price');
+ expect(result.sortDir).toBe('asc');
+ });
+
+ it('falls back page to 1 when page is NaN', () => {
+ expect(resolveOrderGridQuery({ page: 'abc' }).requestedPage).toBe(1);
+ expect(resolveOrderGridQuery({ page: 'NaN' }).requestedPage).toBe(1);
+ });
+
+ it('returns page 1 when page param is absent', () => {
+ const result = resolveOrderGridQuery({ sortBy: 'price' });
+
+ expect(result.requestedPage).toBe(1);
+ });
+
+ it('returns all three resolved fields together', () => {
+ const result = resolveOrderGridQuery({ page: '3', sortBy: 'timestamp', sortDir: 'desc' });
+
+ expect(result).toEqual({
+ requestedPage: 3,
+ sortBy: 'timestamp',
+ sortDir: 'desc',
+ });
+ });
+});
\ No newline at end of file
diff --git a/lib/orders/__tests__/orders.service.test.ts b/lib/orders/__tests__/orders.service.test.ts
index 4b6efc3..58e1b95 100644
--- a/lib/orders/__tests__/orders.service.test.ts
+++ b/lib/orders/__tests__/orders.service.test.ts
@@ -65,4 +65,65 @@ describe('orders.service', () => {
expect(result.prevPage).toBeUndefined();
expect(result.nextPage).toBeUndefined();
});
-});
+
+ it('sorts by price ascending before pagination', async () => {
+ mockedFindAllOrders.mockResolvedValue([
+ makeOrder({ id: '1', price: 15.2 }),
+ makeOrder({ id: '2', price: 9.5 }),
+ makeOrder({ id: '3', price: 11.0 }),
+ ]);
+
+ const result = await getPaginatedOrdersForGrid(1, 15, 'price', 'asc');
+
+ expect(result.items.map((order) => order.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('sorts by status descending using market priority', async () => {
+ mockedFindAllOrders.mockResolvedValue([
+ makeOrder({ id: '1', status: 'Executada' }),
+ makeOrder({ id: '2', status: 'Aberta' }),
+ makeOrder({ id: '3', status: 'Cancelada' }),
+ makeOrder({ id: '4', status: 'Parcial' }),
+ ]);
+
+ const result = await getPaginatedOrdersForGrid(1, 15, 'status', 'desc');
+
+ expect(result.items.map((order) => order.id)).toEqual(['2', '4', '1', '3']);
+ });
+
+ it('sorts by instrument ascending before pagination', async () => {
+ mockedFindAllOrders.mockResolvedValue([
+ makeOrder({ id: '1', instrument: 'VALE3' }),
+ makeOrder({ id: '2', instrument: 'ABEV3' }),
+ makeOrder({ id: '3', instrument: 'PETR4' }),
+ ]);
+
+ const result = await getPaginatedOrdersForGrid(1, 15, 'instrument', 'asc');
+
+ expect(result.items.map((order) => order.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('sorts by quantity descending before pagination', async () => {
+ mockedFindAllOrders.mockResolvedValue([
+ makeOrder({ id: '1', quantity: 100 }),
+ makeOrder({ id: '2', quantity: 500 }),
+ makeOrder({ id: '3', quantity: 300 }),
+ ]);
+
+ const result = await getPaginatedOrdersForGrid(1, 15, 'quantity', 'desc');
+
+ expect(result.items.map((order) => order.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('uses default sort (timestamp desc) when sort params are omitted', async () => {
+ mockedFindAllOrders.mockResolvedValue([
+ makeOrder({ id: '1', timestamp: '2026-03-15T10:00:00Z' }),
+ makeOrder({ id: '2', timestamp: '2026-03-15T12:00:00Z' }),
+ makeOrder({ id: '3', timestamp: '2026-03-15T11:00:00Z' }),
+ ]);
+
+ const result = await getPaginatedOrdersForGrid(1, 15);
+
+ expect(result.items.map((order) => order.id)).toEqual(['2', '3', '1']);
+ });
+});
\ No newline at end of file
diff --git a/lib/orders/__tests__/orders.sort.test.ts b/lib/orders/__tests__/orders.sort.test.ts
new file mode 100644
index 0000000..edb08e1
--- /dev/null
+++ b/lib/orders/__tests__/orders.sort.test.ts
@@ -0,0 +1,341 @@
+import { makeOrder } from './fixtures/orders.fixture';
+import {
+ getDefaultSortDirForField,
+ getSortedOrders,
+ isOrdersSortBy,
+ resolveOrdersSortBy,
+ resolveOrdersSortDir,
+} from '../orders.sort';
+
+describe('orders.sort', () => {
+ describe('isOrdersSortBy', () => {
+ it('returns true for all valid sort fields', () => {
+ expect(isOrdersSortBy('timestamp')).toBe(true);
+ expect(isOrdersSortBy('instrument')).toBe(true);
+ expect(isOrdersSortBy('side')).toBe(true);
+ expect(isOrdersSortBy('price')).toBe(true);
+ expect(isOrdersSortBy('quantity')).toBe(true);
+ expect(isOrdersSortBy('remainingQuantity')).toBe(true);
+ expect(isOrdersSortBy('status')).toBe(true);
+ });
+
+ it('returns false for unknown field names', () => {
+ expect(isOrdersSortBy('unknown')).toBe(false);
+ expect(isOrdersSortBy('PRICE')).toBe(false);
+ expect(isOrdersSortBy('')).toBe(false);
+ expect(isOrdersSortBy('id')).toBe(false);
+ });
+ });
+
+ describe('resolveOrdersSortBy', () => {
+ it('returns default (timestamp) when value is undefined', () => {
+ expect(resolveOrdersSortBy(undefined)).toBe('timestamp');
+ });
+
+ it('returns default when value is empty string', () => {
+ expect(resolveOrdersSortBy('')).toBe('timestamp');
+ });
+
+ it('returns default when value is not a valid sort field', () => {
+ expect(resolveOrdersSortBy('invalid')).toBe('timestamp');
+ expect(resolveOrdersSortBy('PRICE')).toBe('timestamp');
+ });
+
+ it('returns the value when it is a valid sort field', () => {
+ expect(resolveOrdersSortBy('price')).toBe('price');
+ expect(resolveOrdersSortBy('instrument')).toBe('instrument');
+ expect(resolveOrdersSortBy('side')).toBe('side');
+ expect(resolveOrdersSortBy('quantity')).toBe('quantity');
+ expect(resolveOrdersSortBy('remainingQuantity')).toBe('remainingQuantity');
+ expect(resolveOrdersSortBy('status')).toBe('status');
+ expect(resolveOrdersSortBy('timestamp')).toBe('timestamp');
+ });
+ });
+
+ describe('resolveOrdersSortDir', () => {
+ it('returns default (desc) when value is undefined', () => {
+ expect(resolveOrdersSortDir(undefined)).toBe('desc');
+ });
+
+ it('returns default when value is not asc or desc', () => {
+ expect(resolveOrdersSortDir('invalid')).toBe('desc');
+ expect(resolveOrdersSortDir('')).toBe('desc');
+ expect(resolveOrdersSortDir('ASC')).toBe('desc');
+ });
+
+ it('returns asc when value is asc', () => {
+ expect(resolveOrdersSortDir('asc')).toBe('asc');
+ });
+
+ it('returns desc when value is desc', () => {
+ expect(resolveOrdersSortDir('desc')).toBe('desc');
+ });
+ });
+
+ describe('getDefaultSortDirForField', () => {
+ it('returns desc for timestamp', () => {
+ expect(getDefaultSortDirForField('timestamp')).toBe('desc');
+ });
+
+ it('returns asc for all other sort fields', () => {
+ expect(getDefaultSortDirForField('instrument')).toBe('asc');
+ expect(getDefaultSortDirForField('side')).toBe('asc');
+ expect(getDefaultSortDirForField('price')).toBe('asc');
+ expect(getDefaultSortDirForField('quantity')).toBe('asc');
+ expect(getDefaultSortDirForField('remainingQuantity')).toBe('asc');
+ expect(getDefaultSortDirForField('status')).toBe('asc');
+ });
+ });
+
+ describe('getSortedOrders', () => {
+ it('does not mutate the original array', () => {
+ const orders = [
+ makeOrder({ id: '1', price: 20 }),
+ makeOrder({ id: '2', price: 10 }),
+ ];
+ const original = [...orders];
+
+ getSortedOrders(orders, 'price', 'asc');
+
+ expect(orders).toEqual(original);
+ });
+
+ describe('sort by timestamp', () => {
+ it('sorts ascending by timestamp', () => {
+ const orders = [
+ makeOrder({ id: '1', timestamp: '2026-03-15T12:00:00Z' }),
+ makeOrder({ id: '2', timestamp: '2026-03-15T10:00:00Z' }),
+ makeOrder({ id: '3', timestamp: '2026-03-15T11:00:00Z' }),
+ ];
+
+ const result = getSortedOrders(orders, 'timestamp', 'asc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('sorts descending by timestamp', () => {
+ const orders = [
+ makeOrder({ id: '1', timestamp: '2026-03-15T10:00:00Z' }),
+ makeOrder({ id: '2', timestamp: '2026-03-15T12:00:00Z' }),
+ makeOrder({ id: '3', timestamp: '2026-03-15T11:00:00Z' }),
+ ];
+
+ const result = getSortedOrders(orders, 'timestamp', 'desc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+ });
+
+ describe('sort by instrument', () => {
+ it('sorts ascending alphabetically by instrument', () => {
+ const orders = [
+ makeOrder({ id: '1', instrument: 'VALE3' }),
+ makeOrder({ id: '2', instrument: 'ABEV3' }),
+ makeOrder({ id: '3', instrument: 'PETR4' }),
+ ];
+
+ const result = getSortedOrders(orders, 'instrument', 'asc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('sorts descending alphabetically by instrument', () => {
+ const orders = [
+ makeOrder({ id: '1', instrument: 'ABEV3' }),
+ makeOrder({ id: '2', instrument: 'VALE3' }),
+ makeOrder({ id: '3', instrument: 'PETR4' }),
+ ];
+
+ const result = getSortedOrders(orders, 'instrument', 'desc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+ });
+
+ describe('sort by side', () => {
+ it('sorts ascending: Compra before Venda', () => {
+ const orders = [
+ makeOrder({ id: '1', side: 'Venda' }),
+ makeOrder({ id: '2', side: 'Compra' }),
+ ];
+
+ const result = getSortedOrders(orders, 'side', 'asc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '1']);
+ });
+
+ it('sorts descending: Venda before Compra', () => {
+ const orders = [
+ makeOrder({ id: '1', side: 'Compra' }),
+ makeOrder({ id: '2', side: 'Venda' }),
+ ];
+
+ const result = getSortedOrders(orders, 'side', 'desc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '1']);
+ });
+ });
+
+ describe('sort by price', () => {
+ it('sorts ascending by price', () => {
+ const orders = [
+ makeOrder({ id: '1', price: 50.0 }),
+ makeOrder({ id: '2', price: 10.0 }),
+ makeOrder({ id: '3', price: 30.0 }),
+ ];
+
+ const result = getSortedOrders(orders, 'price', 'asc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('sorts descending by price', () => {
+ const orders = [
+ makeOrder({ id: '1', price: 10.0 }),
+ makeOrder({ id: '2', price: 50.0 }),
+ makeOrder({ id: '3', price: 30.0 }),
+ ];
+
+ const result = getSortedOrders(orders, 'price', 'desc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+ });
+
+ describe('sort by quantity', () => {
+ it('sorts ascending by quantity', () => {
+ const orders = [
+ makeOrder({ id: '1', quantity: 300 }),
+ makeOrder({ id: '2', quantity: 100 }),
+ makeOrder({ id: '3', quantity: 200 }),
+ ];
+
+ const result = getSortedOrders(orders, 'quantity', 'asc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('sorts descending by quantity', () => {
+ const orders = [
+ makeOrder({ id: '1', quantity: 100 }),
+ makeOrder({ id: '2', quantity: 300 }),
+ makeOrder({ id: '3', quantity: 200 }),
+ ];
+
+ const result = getSortedOrders(orders, 'quantity', 'desc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+ });
+
+ describe('sort by remainingQuantity', () => {
+ it('sorts ascending by remainingQuantity', () => {
+ const orders = [
+ makeOrder({ id: '1', remainingQuantity: 300 }),
+ makeOrder({ id: '2', remainingQuantity: 0 }),
+ makeOrder({ id: '3', remainingQuantity: 150 }),
+ ];
+
+ const result = getSortedOrders(orders, 'remainingQuantity', 'asc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('sorts descending by remainingQuantity', () => {
+ const orders = [
+ makeOrder({ id: '1', remainingQuantity: 0 }),
+ makeOrder({ id: '2', remainingQuantity: 300 }),
+ makeOrder({ id: '3', remainingQuantity: 150 }),
+ ];
+
+ const result = getSortedOrders(orders, 'remainingQuantity', 'desc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+ });
+
+ describe('sort by status', () => {
+ it('sorts ascending by status priority (Cancelada < Executada < Parcial < Aberta)', () => {
+ const orders = [
+ makeOrder({ id: '1', status: 'Aberta' }),
+ makeOrder({ id: '2', status: 'Cancelada' }),
+ makeOrder({ id: '3', status: 'Parcial' }),
+ makeOrder({ id: '4', status: 'Executada' }),
+ ];
+
+ const result = getSortedOrders(orders, 'status', 'asc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '4', '3', '1']);
+ });
+
+ it('sorts descending by status priority (Aberta > Parcial > Executada > Cancelada)', () => {
+ const orders = [
+ makeOrder({ id: '1', status: 'Executada' }),
+ makeOrder({ id: '2', status: 'Aberta' }),
+ makeOrder({ id: '3', status: 'Cancelada' }),
+ makeOrder({ id: '4', status: 'Parcial' }),
+ ];
+
+ const result = getSortedOrders(orders, 'status', 'desc');
+
+ expect(result.map((o) => o.id)).toEqual(['2', '4', '1', '3']);
+ });
+ });
+
+ describe('tie-breaking', () => {
+ it('breaks ties by timestamp descending when primary sort values are equal', () => {
+ const orders = [
+ makeOrder({
+ id: '1',
+ price: 10,
+ timestamp: '2026-03-15T10:00:00Z',
+ }),
+ makeOrder({
+ id: '2',
+ price: 10,
+ timestamp: '2026-03-15T12:00:00Z',
+ }),
+ makeOrder({
+ id: '3',
+ price: 10,
+ timestamp: '2026-03-15T11:00:00Z',
+ }),
+ ];
+
+ const result = getSortedOrders(orders, 'price', 'asc');
+
+ // All prices equal → tie-break by timestamp desc
+ expect(result.map((o) => o.id)).toEqual(['2', '3', '1']);
+ });
+
+ it('breaks ties by id descending (numeric) when timestamp is also equal', () => {
+ const sameTimestamp = '2026-03-15T10:00:00Z';
+ const orders = [
+ makeOrder({ id: '1', price: 10, timestamp: sameTimestamp }),
+ makeOrder({ id: '3', price: 10, timestamp: sameTimestamp }),
+ makeOrder({ id: '2', price: 10, timestamp: sameTimestamp }),
+ ];
+
+ const result = getSortedOrders(orders, 'price', 'asc');
+
+ // All equal → tie-break by id desc numerically
+ expect(result.map((o) => o.id)).toEqual(['3', '2', '1']);
+ });
+ });
+
+ it('returns empty array when given empty input', () => {
+ const result = getSortedOrders([], 'price', 'asc');
+
+ expect(result).toEqual([]);
+ });
+
+ it('returns single-element array unchanged', () => {
+ const order = makeOrder({ id: '1', price: 42 });
+
+ const result = getSortedOrders([order], 'price', 'asc');
+
+ expect(result).toHaveLength(1);
+ expect(result[0].id).toBe('1');
+ });
+ });
+});
\ No newline at end of file