upload sheet according to the tenant

This commit is contained in:
2026-08-29 12:08:38 +05:30
parent 32c3bef3ad
commit 29613e25ee
8 changed files with 1227 additions and 75 deletions

163
src/api/ingest.test.ts Normal file
View 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);
});