200 lines
8.8 KiB
JavaScript
200 lines
8.8 KiB
JavaScript
// Generated, do not edit. sha256:dd655ee8d8d39adcd4b3b77241ca06552db9cdf6cc3583a38396eaddd1252282
|
|||
|
|
import { describe, it, beforeEach, mock } from 'node:test';
|
||
|
|
import assert from 'node:assert/strict';
|
||
|
|
|
||
|
|
import { createDbHarness } from '../helpers/db-node.mjs';
|
||
|
|
|
||
|
|
const harness = createDbHarness();
|
||
|
|
mock.module('../../server/db.js', { namedExports: harness.exports });
|
||
|
|
|
||
|
|
const { createCommand, claimNextCommand, resolveCommand, getCommand, pruneCommands } =
|
||
|
|
await import('../../server/data/commands.js');
|
||
|
|
const { takeChanges } = await import('../../server/data/_events.js');
|
||
|
|
const { CONFIG } = await import('../../shared/config.js');
|
||
|
|
|
||
|
|
const straks = (seconden = 60) => new Date(Date.now() + seconden * 1000).toISOString();
|
||
|
|
const zoëven = (seconden = 60) => new Date(Date.now() - seconden * 1000).toISOString();
|
||
|
|
const doel = { route: 'system', tab: 'ai' };
|
||
|
|
|
||
|
|
beforeEach(() => { harness.recreate(); takeChanges(); });
|
||
|
|
|
||
|
|
describe('commands: klaarzetten', () => {
|
||
|
|
it('legt een opdracht neer als open, met zijn lading', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
const c = getCommand(id);
|
||
|
|
assert.equal(c.state, 'open');
|
||
|
|
assert.deepEqual(c.payload, doel, 'de lading komt als object terug, niet als JSON-tekst');
|
||
|
|
assert.equal(c.claimedBy, null);
|
||
|
|
assert.equal(c.resolvedAt, null);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('meldt op de bus dát er iets klaarstaat, zonder te zeggen wát', async () => {
|
||
|
|
await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
assert.deepEqual(takeChanges(), ['commands-changed']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('weigert een verzonnen soort, en legt dan niets neer', async () => {
|
||
|
|
await assert.rejects(() => createCommand({ kind: 'zweef', payload: doel }), /Invalid kind/);
|
||
|
|
assert.equal(await claimNextCommand('tab-1'), null, 'er staat niets klaar');
|
||
|
|
assert.deepEqual(takeChanges(), [], 'en er gaat ook geen signaal uit');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('commands: het claim-model', () => {
|
||
|
|
it('geeft de opdracht aan het tabblad dat hem als eerste pakt', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
const eerste = await claimNextCommand('tab-1');
|
||
|
|
assert.equal(eerste.id, id);
|
||
|
|
assert.equal(eerste.state, 'claimed');
|
||
|
|
assert.equal(eerste.claimedBy, 'tab-1');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('GEEFT HEM NIET AAN HET TWEEDE TABBLAD, en dat is de kern van deze fase', async () => {
|
||
|
|
await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
const eerste = await claimNextCommand('tab-1');
|
||
|
|
const tweede = await claimNextCommand('tab-2');
|
||
|
|
assert.ok(eerste);
|
||
|
|
assert.equal(tweede, null, 'de tweede krijgt niets en weet dus dat een ander hem heeft');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('geeft null als er niets klaarstaat', async () => {
|
||
|
|
assert.equal(await claimNextCommand('tab-1'), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('pakt de oudste eerst', async () => {
|
||
|
|
const een = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
assert.equal((await claimNextCommand('tab-1')).id, een);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('weigert een claim zonder tabblad in plaats van hem aan niemand toe te wijzen', async () => {
|
||
|
|
await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await assert.rejects(() => claimNextCommand(''), /without a tab/);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('commands: claimen op soort', () => {
|
||
|
|
it('LAAT LIGGEN WAT DIT TABBLAD NIET KAN, en dat is de reden dat de filter bestaat', async () => {
|
||
|
|
const id = await createCommand({ kind: 'locate' });
|
||
|
|
assert.equal(await claimNextCommand('tab-1', ['navigate']), null, 'niet opgepakt');
|
||
|
|
assert.equal(getCommand(id).state, 'open', 'en ook niet afgewezen: hij blijft gewoon staan');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('slaat een soort over en pakt de eerstvolgende die hij wél kent', async () => {
|
||
|
|
await createCommand({ kind: 'locate' });
|
||
|
|
const kan = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
assert.equal((await claimNextCommand('tab-1', ['navigate'])).id, kan);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('pakt zonder filter nog steeds alles', async () => {
|
||
|
|
const id = await createCommand({ kind: 'locate' });
|
||
|
|
assert.equal((await claimNextCommand('tab-1')).id, id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('weigert een verzonnen soort en een lege lijst', async () => {
|
||
|
|
await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await assert.rejects(() => claimNextCommand('tab-1', ['zweef']), /Invalid kind/);
|
||
|
|
await assert.rejects(() => claimNextCommand('tab-1', []), /zero kinds/);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('commands: verlopen', () => {
|
||
|
|
it('pakt een verlopen opdracht niet op, maar sluit hem af', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: zoëven() });
|
||
|
|
assert.equal(await claimNextCommand('tab-1'), null);
|
||
|
|
const c = getCommand(id);
|
||
|
|
assert.equal(c.state, 'expired');
|
||
|
|
assert.ok(c.resolvedAt, 'en hij is afgehandeld, niet blijven hangen');
|
||
|
|
assert.match(c.detail, /op tijd/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('laat een opdracht zonder verlooptijd staan', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel });
|
||
|
|
const c = await claimNextCommand('tab-1');
|
||
|
|
assert.equal(c.id, id, 'hij is gewoon op te pakken');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('commands: afsluiten', () => {
|
||
|
|
it('rondt af als uitgevoerd', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await resolveCommand(id, 'tab-1', 'done');
|
||
|
|
const c = getCommand(id);
|
||
|
|
assert.equal(c.state, 'done');
|
||
|
|
assert.ok(c.resolvedAt);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('rondt af als tegengehouden, met de reden erbij', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await resolveCommand(id, 'tab-1', 'rejected', 'Gestopt door de gebruiker.');
|
||
|
|
const c = getCommand(id);
|
||
|
|
assert.equal(c.state, 'rejected');
|
||
|
|
assert.equal(c.detail, 'Gestopt door de gebruiker.');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('LAAT EEN ANDER TABBLAD HEM NIET AFSLUITEN', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await resolveCommand(id, 'tab-2', 'done');
|
||
|
|
assert.equal(getCommand(id).state, 'claimed', 'onaangeroerd');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('sluit een al afgesloten opdracht niet nog eens af', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await resolveCommand(id, 'tab-1', 'done');
|
||
|
|
await resolveCommand(id, 'tab-1', 'rejected', 'te laat');
|
||
|
|
assert.equal(getCommand(id).state, 'done', 'de eerste uitkomst blijft staan');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('weigert een uitkomst die geen afronding is', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await assert.rejects(() => resolveCommand(id, 'tab-1', 'open'), /not a conclusion/);
|
||
|
|
await assert.rejects(() => resolveCommand(id, 'tab-1', 'claimed'), /not a conclusion/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('weigert een verzonnen uitkomst', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel, expiresAt: straks() });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await assert.rejects(() => resolveCommand(id, 'tab-1', 'weggewaaid'), /Invalid state/);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('commands: opruimen', () => {
|
||
|
|
const verouder = (id) => harness.exports.run(`UPDATE Command SET resolvedAt = ? WHERE id = ?`,
|
||
|
|
[new Date(Date.now() - (CONFIG.COMMANDS.RETENTION_HOURS + 1) * 3600_000).toISOString(), id]);
|
||
|
|
|
||
|
|
it('gooit alleen afgehandelde opdrachten weg', async () => {
|
||
|
|
const oud = await createCommand({ kind: 'navigate', payload: doel });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await resolveCommand(oud, 'tab-1', 'done');
|
||
|
|
const open = await createCommand({ kind: 'navigate', payload: doel });
|
||
|
|
verouder(oud);
|
||
|
|
|
||
|
|
assert.equal(pruneCommands(), 1);
|
||
|
|
assert.equal(getCommand(oud), null);
|
||
|
|
assert.ok(getCommand(open), 'wat nog openstaat blijft, hoe oud ook');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('ruimt op zodra er een nieuwe opdracht wordt klaargezet', async () => {
|
||
|
|
const oud = await createCommand({ kind: 'navigate', payload: doel });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await resolveCommand(oud, 'tab-1', 'done');
|
||
|
|
verouder(oud);
|
||
|
|
|
||
|
|
const nieuw = await createCommand({ kind: 'navigate', payload: doel });
|
||
|
|
assert.equal(getCommand(oud), null, 'de oude is weg zonder dat iemand erom vroeg');
|
||
|
|
assert.ok(getCommand(nieuw), 'en de nieuwe staat er gewoon');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('laat een recent afgehandelde opdracht staan', async () => {
|
||
|
|
const id = await createCommand({ kind: 'navigate', payload: doel });
|
||
|
|
await claimNextCommand('tab-1');
|
||
|
|
await resolveCommand(id, 'tab-1', 'done');
|
||
|
|
assert.equal(pruneCommands(), 0);
|
||
|
|
assert.ok(getCommand(id));
|
||
|
|
});
|
||
|
|
});
|