Add like/dislike surveys with majority alerts for manufacturing decisions, clearer accounting mapping copy, and related cart/catalog admin-app improvements. Co-authored-by: Cursor <cursoragent@cursor.com>
125 lines
4.5 KiB
TypeScript
125 lines
4.5 KiB
TypeScript
import { CartExpiryWorkerService } from './cart-expiry.worker';
|
||
import { tehranWallToUtc } from './cart-schedule';
|
||
|
||
describe('CartExpiryWorkerService', () => {
|
||
function build(opts: {
|
||
cfg?: Record<string, unknown>;
|
||
carts?: Array<Record<string, unknown>>;
|
||
}) {
|
||
const carts = opts.carts ?? [];
|
||
const updates: Array<{ customerId: string; data: Record<string, unknown> }> = [];
|
||
const sms = { send: jest.fn(async () => ({ ok: true })) };
|
||
const prisma = {
|
||
customerCart: {
|
||
findMany: jest.fn(async () => carts),
|
||
update: jest.fn(async ({ where, data }: { where: { customerId: string }; data: Record<string, unknown> }) => {
|
||
updates.push({ customerId: where.customerId, data });
|
||
return {};
|
||
}),
|
||
},
|
||
$executeRaw: jest.fn(async () => 1),
|
||
};
|
||
const cart = {
|
||
getConfig: jest.fn(async () => ({
|
||
enabled: true,
|
||
clearHour: 13,
|
||
clearMinute: 0,
|
||
sameDayDeferFromHour: 8,
|
||
sameDayDeferFromMinute: 0,
|
||
reminderEnabled: true,
|
||
reminderHoursBefore: 2,
|
||
reminderBody: 'تا %count ساعت',
|
||
worker: 'on' as const,
|
||
...opts.cfg,
|
||
})),
|
||
};
|
||
const worker = new CartExpiryWorkerService(prisma as never, cart as never, sms as never);
|
||
return { worker, sms, updates, prisma, cart };
|
||
}
|
||
|
||
it('no-ops when disabled', async () => {
|
||
const { worker, sms, prisma } = build({ cfg: { enabled: false } });
|
||
await worker.tick(tehranWallToUtc(2026, 11, 12, 12, 0, 0));
|
||
expect(sms.send).not.toHaveBeenCalled();
|
||
expect(prisma.customerCart.findMany).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it('reminds carts due today (touched before 08:00), not deferred ones', async () => {
|
||
const todayClear = tehranWallToUtc(2026, 11, 12, 13, 0, 0);
|
||
const { worker, sms, updates } = build({
|
||
carts: [
|
||
{
|
||
customerId: 'early',
|
||
items: [{ pid: 'p1', vid: 'v1', qty: 1 }],
|
||
reminderSentForClearAt: null,
|
||
updatedAt: tehranWallToUtc(2026, 11, 12, 7, 0, 0),
|
||
customer: { mobile: '09120000000', isActive: true },
|
||
},
|
||
{
|
||
customerId: 'deferred',
|
||
items: [{ pid: 'p2', vid: 'v2', qty: 1 }],
|
||
reminderSentForClearAt: null,
|
||
// 10:00 → deadline tomorrow 13 — no reminder yet at 11:30 today
|
||
updatedAt: tehranWallToUtc(2026, 11, 12, 10, 0, 0),
|
||
customer: { mobile: '09121111111', isActive: true },
|
||
},
|
||
],
|
||
});
|
||
await worker.tick(tehranWallToUtc(2026, 11, 12, 11, 30, 0));
|
||
expect(sms.send).toHaveBeenCalledTimes(1);
|
||
expect(sms.send).toHaveBeenCalledWith('09120000000', expect.any(String));
|
||
expect(updates[0]?.customerId).toBe('early');
|
||
expect(updates[0]?.data.reminderSentForClearAt).toEqual(todayClear);
|
||
});
|
||
|
||
it('does not clear 08–13 carts at today 13; clears pre-08 carts', async () => {
|
||
const { worker, updates, sms } = build({
|
||
carts: [
|
||
{
|
||
customerId: 'early',
|
||
items: [{ pid: 'p1', vid: 'v1', qty: 1 }],
|
||
reminderSentForClearAt: null,
|
||
updatedAt: tehranWallToUtc(2026, 11, 12, 7, 0, 0),
|
||
customer: { mobile: '09120000000', isActive: true },
|
||
},
|
||
{
|
||
customerId: 'deferred',
|
||
items: [{ pid: 'p2', vid: 'v2', qty: 1 }],
|
||
reminderSentForClearAt: null,
|
||
updatedAt: tehranWallToUtc(2026, 11, 12, 10, 0, 0),
|
||
customer: { mobile: '09121111111', isActive: true },
|
||
},
|
||
{
|
||
customerId: 'afternoon',
|
||
items: [{ pid: 'p3', vid: 'v3', qty: 1 }],
|
||
reminderSentForClearAt: null,
|
||
updatedAt: tehranWallToUtc(2026, 11, 12, 14, 0, 0),
|
||
customer: { mobile: '09122222222', isActive: true },
|
||
},
|
||
],
|
||
});
|
||
await worker.tick(tehranWallToUtc(2026, 11, 12, 13, 5, 0));
|
||
expect(sms.send).not.toHaveBeenCalled();
|
||
expect(updates).toHaveLength(1);
|
||
expect(updates[0].customerId).toBe('early');
|
||
expect(updates[0].data.items).toEqual([]);
|
||
});
|
||
|
||
it('clears deferred (08–13) carts at tomorrow 13', async () => {
|
||
const { worker, updates } = build({
|
||
carts: [
|
||
{
|
||
customerId: 'deferred',
|
||
items: [{ pid: 'p2', vid: 'v2', qty: 1 }],
|
||
reminderSentForClearAt: null,
|
||
updatedAt: tehranWallToUtc(2026, 11, 12, 10, 0, 0),
|
||
customer: { mobile: '09121111111', isActive: true },
|
||
},
|
||
],
|
||
});
|
||
await worker.tick(tehranWallToUtc(2026, 11, 13, 13, 5, 0));
|
||
expect(updates).toHaveLength(1);
|
||
expect(updates[0].customerId).toBe('deferred');
|
||
});
|
||
});
|