Files
hedena_gold/apps/api/src/modules/customers/order-items-include.spec.ts
T
s.esmaeiliandCursor afb068c643 feat: paginate customer orders and polish the dashboard hero
Add paged orders API and infinite scroll, short order numbers, and align the guest invite card with the logged-in hero shell.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-17 13:11:42 +03:30

110 lines
4.1 KiB
TypeScript

import { CustomersService } from './customers.service';
/**
* `GET customer/me/orders` must carry each item's product and variant.
*
* The apps used to label order rows by downloading the ENTIRE catalogue and
* joining it in the browser (composables/useOrderItemProduct.ts). With the real
* catalogue at ~16k products that is the wrong trade for a few row labels, and
* it was also broken for the case order history exists for: a product that has
* since left the storefront is absent from the catalogue, so the row rendered
* blank.
*
* Both apps now read these fields straight off the item, so the include is part
* of the contract — hence a test, which this method did not have.
*/
describe('CustomersService.getOrders — items carry their product', () => {
function setup() {
const findMany = jest.fn().mockResolvedValue([]);
const count = jest.fn().mockResolvedValue(0);
const prisma = { order: { findMany, count } };
// getOrders touches neither the accounting sync nor anything on it.
const service = new CustomersService(prisma as never, {} as never);
return { service, findMany, count };
}
it('includes the variant, its product and the first image', async () => {
const { service, findMany } = setup();
await service.getOrders('c1');
const include = findMany.mock.calls[0][0].include;
const variant = include.items.include.productVariant;
expect(variant.select.title).toBe(true);
expect(variant.select.product.select.title).toBe(true);
// images[0] is the product's main photo, so it must be ordered and limited
// to one — the apps read `images[0].url` and nothing else.
expect(variant.select.product.select.images).toMatchObject({
orderBy: { sortOrder: 'asc' },
take: 1,
});
});
it('includes status events oldest-first for the detail timeline', async () => {
const { service, findMany } = setup();
await service.getOrders('c1');
expect(findMany.mock.calls[0][0].include.statusEvents).toMatchObject({
orderBy: { createdAt: 'asc' },
select: {
id: true,
fromStatus: true,
toStatus: true,
note: true,
createdAt: true,
},
});
});
it('is scoped to the caller and newest first', async () => {
// Guards the two properties that were already here: one customer must never
// receive another's orders, and the list is reverse-chronological.
const { service, findMany } = setup();
await service.getOrders('c1');
expect(findMany.mock.calls[0][0]).toMatchObject({
where: { customerId: 'c1' },
orderBy: { createdAt: 'desc' },
});
});
it('does not filter the product by storefront visibility', async () => {
// Deliberate: an order for a product that has since been deactivated, or
// had its photo removed, must still show what was bought. The storefront
// rules apply to the catalogue, not to history.
const { service, findMany } = setup();
await service.getOrders('c1');
const product = findMany.mock.calls[0][0].include.items.include.productVariant.select.product;
expect(product.where).toBeUndefined();
});
it('pages with skip/take and returns count', async () => {
const { service, findMany, count } = setup();
findMany.mockResolvedValue([{ id: 'o1' }]);
count.mockResolvedValue(41);
await expect(service.getOrders('c1', { page: 2, limit: 20 })).resolves.toEqual({
result: [{ id: 'o1' }],
count: 41,
page: 2,
limit: 20,
});
expect(findMany.mock.calls[0][0]).toMatchObject({ skip: 20, take: 20 });
});
it('filters by status and orderNumber on the server', async () => {
const { service, findMany } = setup();
await service.getOrders('c1', { status: 'PLACED', q: '12' });
expect(findMany.mock.calls[0][0].where).toEqual({
customerId: 'c1',
status: 'PLACED',
orderNumber: { contains: '12', mode: 'insensitive' },
});
});
it('ignores an unknown status instead of throwing', async () => {
const { service, findMany } = setup();
await service.getOrders('c1', { status: 'NOT_A_STATUS' });
expect(findMany.mock.calls[0][0].where).toEqual({ customerId: 'c1' });
});
});