101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
/**
|
|
* The receipt, and the two things it has to get right.
|
|
*
|
|
* A receipt exists because the ingest service's batch id is the only credential
|
|
* for reading a result back, it is handed out once, and an unreviewed drop is
|
|
* deleted after seven days. So the tests that matter are about not losing that
|
|
* window, and about the label their admin reads when deciding whether to
|
|
* approve the file.
|
|
*/
|
|
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { buildSender, daysUntilExpiry, DROP_RETENTION_DAYS, type UploadReceipt } from './uploads';
|
|
|
|
function receipt(overrides: Partial<UploadReceipt> = {}): UploadReceipt {
|
|
return {
|
|
uploadid: 1,
|
|
tenantid: 1141,
|
|
locationid: 1180,
|
|
categoryid: 2,
|
|
batchid: '49a82536866a483a9189954d3c749243',
|
|
runid: '',
|
|
filename: 'kmart-opening.xlsx',
|
|
sender: 'Kmart · Peelamedu · abhishek',
|
|
uploadedby: 1475,
|
|
uploadedname: 'abhishek',
|
|
rowcount: 20,
|
|
laststatus: 'pending',
|
|
inserted: 0,
|
|
backfilled: 0,
|
|
skipped: 0,
|
|
rejected: 0,
|
|
shelvedcount: 0,
|
|
skippedcount: 0,
|
|
shelvedat: null,
|
|
created: new Date().toISOString(),
|
|
updated: new Date().toISOString(),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
const daysAgo = (n: number) => new Date(Date.now() - n * 86_400_000).toISOString();
|
|
|
|
test('a drop uploaded today has the full window left', () => {
|
|
assert.equal(daysUntilExpiry(receipt()), DROP_RETENTION_DAYS);
|
|
});
|
|
|
|
test('the window closes as the drop sits unreviewed', () => {
|
|
assert.equal(daysUntilExpiry(receipt({ created: daysAgo(5) })), 2);
|
|
});
|
|
|
|
// Never negative. A drop past the deadline is gone, and "-3 days left" would
|
|
// read as a countdown that is still running.
|
|
test('an expired drop reports zero, not a negative', () => {
|
|
assert.equal(daysUntilExpiry(receipt({ created: daysAgo(30) })), 0);
|
|
});
|
|
|
|
/*
|
|
Retention applies to a drop nobody acted on. Once an admin releases it the run
|
|
is the record and the drop's own expiry is irrelevant — showing a countdown on
|
|
a released upload would push someone to chase a deadline that has already been
|
|
met.
|
|
*/
|
|
test('a released drop has no expiry to report', () => {
|
|
assert.equal(daysUntilExpiry(receipt({ runid: '8dcef8a2ad94', laststatus: 'retired' })), null);
|
|
assert.equal(daysUntilExpiry(receipt({ laststatus: 'done' })), null);
|
|
});
|
|
|
|
test('a receipt with an unreadable date reports nothing rather than guessing', () => {
|
|
assert.equal(daysUntilExpiry(receipt({ created: 'not a date' })), null);
|
|
});
|
|
|
|
/*
|
|
The sender label. Free text on their side, capped at 60 characters, and shown to
|
|
the admin who decides whether to run the file — so it has to identify the shop,
|
|
not the console.
|
|
*/
|
|
test('the sender names the merchant, the branch and the person', () => {
|
|
assert.equal(
|
|
buildSender({ tenantname: 'Kmart', locationname: 'Peelamedu', username: 'abhishek' }),
|
|
'Kmart · Peelamedu · abhishek',
|
|
);
|
|
});
|
|
|
|
// The tenant name is truncated, not the whole label. Cutting the tail would
|
|
// drop the branch and the person — the two parts that say WHICH shelf and WHO —
|
|
// and leave only a long restaurant name that identifies neither.
|
|
test('a long merchant name is trimmed so the branch and person survive', () => {
|
|
const sender = buildSender({
|
|
tenantname: 'Ninhao The New Age Chinese Restaurant',
|
|
locationname: 'Race Course',
|
|
username: 'abhishek',
|
|
});
|
|
assert.ok(sender.length <= 60, `sender was ${sender.length} chars: ${sender}`);
|
|
assert.ok(sender.includes('Race Course'), sender);
|
|
assert.ok(sender.includes('abhishek'), sender);
|
|
});
|
|
|
|
test('a sender with nothing to say still labels the console', () => {
|
|
assert.equal(buildSender({}), 'nearle-console');
|
|
});
|