upload sheet according to the tenant
This commit is contained in:
163
src/api/ingest.test.ts
Normal file
163
src/api/ingest.test.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* The review inbox, and the status that nearly slipped through as success.
|
||||
*
|
||||
* The fixture is the live response to an anonymous upload on 28 Aug 2026 —
|
||||
* `status: "pending"`, every total zero, the file still queued.
|
||||
*/
|
||||
import assert from 'node:assert/strict';
|
||||
import { test } from 'node:test';
|
||||
import {
|
||||
isAwaitingReview,
|
||||
isDismissed,
|
||||
isSettled,
|
||||
productsOf,
|
||||
releasedRunId,
|
||||
summarise,
|
||||
type IngestBatch,
|
||||
} from './ingest';
|
||||
|
||||
const held = {
|
||||
batch_id: '2ee38d06b583454ea0278a7f6de2c87f',
|
||||
status: 'pending',
|
||||
detail: 'Waiting for review. Nothing runs until an admin starts it.',
|
||||
submitted_by: 'anonymous',
|
||||
files_total: 1,
|
||||
files_done: 0,
|
||||
files_failed: 0,
|
||||
totals: {
|
||||
rows_total: 0,
|
||||
products_built: 0,
|
||||
inserted: 0,
|
||||
backfilled: 0,
|
||||
skipped_existing: 0,
|
||||
rejected: 0,
|
||||
},
|
||||
brands: [],
|
||||
files: [{ index: 0, filename: 'qa.csv', status: 'queued' as const }],
|
||||
} satisfies IngestBatch;
|
||||
|
||||
// The bug this guards: `isSettled` used to mean "not queued and not running",
|
||||
// so `pending` counted as finished and the panel rendered a completed import of
|
||||
// zero products for a batch that had not started.
|
||||
test('a batch held for review is not treated as finished', () => {
|
||||
assert.equal(isSettled(held), false, 'a held batch must not read as settled');
|
||||
assert.equal(isAwaitingReview(held), true);
|
||||
});
|
||||
|
||||
test('the summary says it is waiting, not that nothing imported', () => {
|
||||
const line = summarise(held);
|
||||
assert.match(line, /review/i);
|
||||
assert.doesNotMatch(line, /0 added/, 'must not report an import that never ran');
|
||||
});
|
||||
|
||||
test('a real result is still settled', () => {
|
||||
for (const status of ['done', 'partial', 'failed', 'interrupted', 'cancelled'] as const) {
|
||||
assert.equal(isSettled({ ...held, status }), true, `${status} should be settled`);
|
||||
}
|
||||
});
|
||||
|
||||
test('queued and running are still in flight', () => {
|
||||
for (const status of ['queued', 'running'] as const) {
|
||||
assert.equal(isSettled({ ...held, status }), false, `${status} should not be settled`);
|
||||
}
|
||||
});
|
||||
|
||||
// isSettled is written as a positive list precisely so a status nobody
|
||||
// anticipated stalls a spinner rather than fabricating a completed import.
|
||||
test('an unknown future status does not read as finished', () => {
|
||||
const unknown = { ...held, status: 'quarantined' as unknown as IngestBatch['status'] };
|
||||
assert.equal(isSettled(unknown), false);
|
||||
});
|
||||
|
||||
/* ── The drop lifecycle ───────────────────────────────────────────────────── */
|
||||
|
||||
const released = {
|
||||
...held,
|
||||
batch_id: '9f088d949aa9',
|
||||
status: 'pending' as const,
|
||||
files: [{ index: 0, filename: 'qa.csv', status: 'released' as const, released_to: '8dcef8a2ad94' }],
|
||||
} satisfies IngestBatch;
|
||||
|
||||
const dismissed = {
|
||||
...held,
|
||||
files: [{ index: 0, filename: 'qa.csv', status: 'dismissed' as const, released_to: null }],
|
||||
} satisfies IngestBatch;
|
||||
|
||||
// A released drop is not still waiting — the run is one hop away, and treating
|
||||
// it as held would leave the screen saying "queued for review" forever.
|
||||
test('a released drop is no longer awaiting review', () => {
|
||||
assert.equal(releasedRunId(released), '8dcef8a2ad94');
|
||||
assert.equal(isAwaitingReview(released), false);
|
||||
assert.equal(isAwaitingReview(held), true, 'an unreleased drop is still waiting');
|
||||
});
|
||||
|
||||
// Declined is terminal. Polling on is waiting for something that cannot happen.
|
||||
test('a dismissed drop is recognised and reported as declined', () => {
|
||||
assert.equal(isDismissed(dismissed), true);
|
||||
assert.equal(isDismissed(held), false);
|
||||
assert.match(summarise(dismissed), /declined/i);
|
||||
assert.doesNotMatch(summarise(dismissed), /0 added/);
|
||||
});
|
||||
|
||||
test('the manifest is collected across files', () => {
|
||||
const done = {
|
||||
...held,
|
||||
status: 'done' as const,
|
||||
files: [
|
||||
{
|
||||
index: 0,
|
||||
filename: 'a.csv',
|
||||
status: 'done' as const,
|
||||
result: {
|
||||
products: [
|
||||
{
|
||||
image_id: 'amul_amul_butter_100g',
|
||||
brand: 'amul',
|
||||
product_name: 'Amul Butter 100g',
|
||||
product_sku: 'ACME-BUT-100',
|
||||
sku_source: 'sheet',
|
||||
disposition: 'inserted' as const,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
} satisfies IngestBatch;
|
||||
|
||||
const products = productsOf(done);
|
||||
assert.equal(products.length, 1);
|
||||
// image_id is the join key; matching on name creates duplicates instead of
|
||||
// updating, which is why it is asserted rather than the name.
|
||||
assert.equal(products[0]?.image_id, 'amul_amul_butter_100g');
|
||||
assert.equal(products[0]?.disposition, 'inserted');
|
||||
});
|
||||
|
||||
/* ── retired: the drop is spent, the answer is on the files ───────────────── */
|
||||
|
||||
// A drop released into a run reads `retired`, and the run is elsewhere. Calling
|
||||
// it finished would report an import that is running right now as a completed
|
||||
// import of zero products.
|
||||
test('a retired drop that was released is not finished', () => {
|
||||
const retired = {
|
||||
...held,
|
||||
status: 'retired' as const,
|
||||
files: [
|
||||
{ index: 0, filename: 'qa.csv', status: 'released' as const, released_to: '8dcef8a2ad94' },
|
||||
],
|
||||
} satisfies IngestBatch;
|
||||
|
||||
assert.equal(isSettled(retired), false, 'the run still has to be followed');
|
||||
assert.equal(releasedRunId(retired), '8dcef8a2ad94');
|
||||
});
|
||||
|
||||
// Retired with nothing to follow is genuinely over — otherwise the panel spins
|
||||
// on a drop that no longer exists.
|
||||
test('a retired drop with nowhere to follow is finished', () => {
|
||||
const retired = {
|
||||
...held,
|
||||
status: 'retired' as const,
|
||||
files: [{ index: 0, filename: 'qa.csv', status: 'dismissed' as const, released_to: null }],
|
||||
} satisfies IngestBatch;
|
||||
|
||||
assert.equal(isSettled(retired), true);
|
||||
});
|
||||
Reference in New Issue
Block a user