agent conformation
This commit is contained in:
@@ -10,6 +10,8 @@ import {
|
||||
isAwaitingReview,
|
||||
isDismissed,
|
||||
isSettled,
|
||||
currentStage,
|
||||
isStuckOnMissingRunner,
|
||||
productsOf,
|
||||
releasedRunId,
|
||||
summarise,
|
||||
@@ -203,3 +205,132 @@ test('only our own file contributes products', () => {
|
||||
// and every caller that prices products must make it.
|
||||
assert.equal(productsOf(run).length, 2);
|
||||
});
|
||||
|
||||
/*
|
||||
The stage timeline, and the runner that silently isn't there.
|
||||
|
||||
Both arrived with the ingest team's 31 Aug documentation update. The timeline is
|
||||
what lets the console draw the real eleven stages instead of a file-count bar;
|
||||
the runner is a trap, and the more important of the two.
|
||||
*/
|
||||
|
||||
const runningFile = {
|
||||
index: 0,
|
||||
filename: 'catalog.csv',
|
||||
status: 'running' as const,
|
||||
stage_index: 6,
|
||||
stage_name: 'Image Search & Contamination Filtering',
|
||||
total_stages: 11,
|
||||
rows_done: 120,
|
||||
rows_total: 400,
|
||||
stages: [
|
||||
{
|
||||
index: 1,
|
||||
name: 'Brand Resolution & FSSAI Licence Mapping',
|
||||
rows_done: 400,
|
||||
rows_total: 400,
|
||||
started_at: 1756612800.1,
|
||||
finished_at: 1756612801.4,
|
||||
},
|
||||
{
|
||||
index: 6,
|
||||
name: 'Image Search & Contamination Filtering',
|
||||
rows_done: 120,
|
||||
rows_total: 400,
|
||||
started_at: 1756612809.7,
|
||||
finished_at: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// `finished_at: null` is the marker, not the last array entry and not
|
||||
// `stage_index`. Reading the position any other way breaks the moment a stage
|
||||
// completes out of order or the array carries a trailing finished entry.
|
||||
test('the running stage is the one with no finish time', () => {
|
||||
const stage = currentStage(runningFile);
|
||||
assert.equal(stage?.index, 6);
|
||||
assert.equal(stage?.rows_done, 120);
|
||||
});
|
||||
|
||||
// A finished file keeps its history, which is the whole reason the timeline
|
||||
// exists — the scalars only ever describe the present moment, and for a
|
||||
// finished file that moment is over.
|
||||
test('a finished file still reports its last stage', () => {
|
||||
const done = {
|
||||
...runningFile,
|
||||
status: 'done' as const,
|
||||
stages: runningFile.stages.map((s) => ({ ...s, finished_at: s.finished_at ?? 1756612900.0 })),
|
||||
};
|
||||
assert.equal(currentStage(done)?.index, 6);
|
||||
});
|
||||
|
||||
// A service build that predates the timeline still has to render. The scalars
|
||||
// are the fallback, not the source of truth.
|
||||
test('a response without a timeline falls back to the scalars', () => {
|
||||
const { stages: _stages, ...noTimeline } = runningFile;
|
||||
const stage = currentStage(noTimeline);
|
||||
assert.equal(stage?.index, 6);
|
||||
assert.equal(stage?.name, 'Image Search & Contamination Filtering');
|
||||
});
|
||||
|
||||
test('a file that has not started reports no stage at all', () => {
|
||||
assert.equal(currentStage({ index: 0, filename: 'a.csv', status: 'queued' }), null);
|
||||
});
|
||||
|
||||
/*
|
||||
`runner: "dagster"` never runs in production — Dagster is a development tool,
|
||||
absent from the deployed image — so the batch waits for a worker that will never
|
||||
claim it. Every visible signal is identical to a batch merely waiting its turn,
|
||||
which is exactly why it has to be named rather than rendered as progress.
|
||||
*/
|
||||
test('a batch staged for the absent orchestrator is called out', () => {
|
||||
assert.equal(isStuckOnMissingRunner({ ...held, status: 'queued', runner: 'dagster' }), true);
|
||||
});
|
||||
|
||||
test('the in-process runner is not a stall', () => {
|
||||
assert.equal(isStuckOnMissingRunner({ ...held, status: 'queued', runner: 'inprocess' }), false);
|
||||
});
|
||||
|
||||
// A batch that reached `running` plainly found an executor, whatever it was
|
||||
// staged for. Warning then would contradict the progress on screen.
|
||||
test('a batch already running is not stuck, whatever it was staged for', () => {
|
||||
assert.equal(isStuckOnMissingRunner({ ...held, status: 'running', runner: 'dagster' }), false);
|
||||
});
|
||||
|
||||
/*
|
||||
A drop is not a run, and the difference is easy to lose.
|
||||
|
||||
`released_to` lives on the DROP's files. Once an admin releases it, following
|
||||
that pointer lands on the run — and the run carries no `released_to` of its own,
|
||||
because nothing released it. So a caller who resolves first and asks for the run
|
||||
id second gets null, and the only pointer from the id they hold to the id with
|
||||
the results is never recorded.
|
||||
|
||||
This cost a real bug in both directions: the Uploads page never saved a run id,
|
||||
and the import panel keyed the shelving write on `batch.batch_id` — which by
|
||||
then was the run — updating a receipt row that does not exist, silently.
|
||||
*/
|
||||
test('the run id is on the drop, and gone from the run it points to', () => {
|
||||
const drop = {
|
||||
...held,
|
||||
batch_id: 'drop-1',
|
||||
status: 'retired' as const,
|
||||
files: [
|
||||
{ index: 0, filename: 'catalog.csv', status: 'released' as const, released_to: 'run-1' },
|
||||
],
|
||||
};
|
||||
assert.equal(releasedRunId(drop), 'run-1');
|
||||
|
||||
// The same question asked of the run answers null. Read the drop first.
|
||||
const run = {
|
||||
...held,
|
||||
batch_id: 'run-1',
|
||||
status: 'done' as const,
|
||||
files: [{ index: 0, filename: 'catalog.csv', status: 'done' as const }],
|
||||
};
|
||||
assert.equal(releasedRunId(run), null);
|
||||
|
||||
// And the two ids differ, which is exactly why a receipt keyed on the drop
|
||||
// cannot be written using the run's.
|
||||
assert.notEqual(drop.batch_id, run.batch_id);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user