Surface audit generations on dashboard audits
This commit is contained in:
@@ -30,7 +30,7 @@ test("audits dashboard page uses a dedicated board component", async () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("audits board renders compact list with convex list query and core columns", async () => {
|
||||
test("audits board renders compact list with dashboard rows query and core columns", async () => {
|
||||
const boardSource = await source("components/audits/audits-board.tsx");
|
||||
|
||||
assert.match(
|
||||
@@ -40,13 +40,13 @@ test("audits board renders compact list with convex list query and core columns"
|
||||
);
|
||||
assert.match(
|
||||
boardSource,
|
||||
/useQuery\s*\(\s*api\.audits\.list,\s*\{\s*limit:\s*100\s*\}\s*\)/,
|
||||
"AuditsBoard should call api.audits.list with { limit: 100 }.",
|
||||
/useQuery\s*\(\s*api\.audits\.listDashboardRows,\s*\{\s*limit:\s*100\s*\}\s*\)/,
|
||||
"AuditsBoard should call api.audits.listDashboardRows with { limit: 100 }.",
|
||||
);
|
||||
assert.match(
|
||||
boardSource,
|
||||
/sort\(\(\s*a,\s*b\s*\)\s*=>\s*b\.createdAt\s*-\s*a\.createdAt\)/,
|
||||
"Audits should be sorted newest first.",
|
||||
/sort\(\(\s*a,\s*b\s*\)\s*=>\s*b\.updatedAt\s*-\s*a\.updatedAt\)/,
|
||||
"Dashboard rows should be sorted newest first.",
|
||||
);
|
||||
assert.match(boardSource, /Loading|lädt|Lade/i);
|
||||
assert.match(boardSource, /Keine Audits|keine Audits/i);
|
||||
@@ -54,10 +54,20 @@ test("audits board renders compact list with convex list query and core columns"
|
||||
assert.match(boardSource, /Domain/);
|
||||
assert.match(boardSource, /Status/);
|
||||
assert.match(boardSource, /Seiten/);
|
||||
assert.match(boardSource, /Generierung läuft|Generierung laeuft/);
|
||||
assert.match(boardSource, /Fehlgeschlagen/);
|
||||
assert.match(boardSource, /Wartet auf finales Audit/);
|
||||
assert.match(boardSource, /Wartet auf Start/);
|
||||
assert.match(boardSource, /Abgebrochen/);
|
||||
assert.match(
|
||||
boardSource,
|
||||
/href=\{`\/dashboard\/audits\/\$\{audit\._id\}`\}/,
|
||||
"Each audit row should link to /dashboard/audits/{id}.",
|
||||
/href=\{row\.detailHref\}/,
|
||||
"Final audit rows should link through their detailHref.",
|
||||
);
|
||||
assert.match(
|
||||
boardSource,
|
||||
/row\.kind\s*===\s*"audit"/,
|
||||
"Board should branch between final audit rows and generation rows.",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
124
tests/audits-dashboard-query-source.test.ts
Normal file
124
tests/audits-dashboard-query-source.test.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
const source = async (relativePath: string) => {
|
||||
return await readFile(
|
||||
join(process.cwd(), ...relativePath.split("/")),
|
||||
"utf8",
|
||||
);
|
||||
};
|
||||
|
||||
function extractExportSource(sourceText: string, name: string) {
|
||||
const marker = `export const ${name} = `;
|
||||
const declarationIndex = sourceText.indexOf(marker);
|
||||
assert.notEqual(declarationIndex, -1, `Expected declaration for ${name}.`);
|
||||
|
||||
const openBraceIndex = sourceText.indexOf("{", declarationIndex);
|
||||
let depth = 0;
|
||||
let end = -1;
|
||||
|
||||
for (let index = openBraceIndex; index < sourceText.length; index += 1) {
|
||||
const char = sourceText[index];
|
||||
if (char === "{") {
|
||||
depth += 1;
|
||||
} else if (char === "}") {
|
||||
depth -= 1;
|
||||
if (depth === 0) {
|
||||
end = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.notEqual(end, -1, `Expected balanced braces for ${name}.`);
|
||||
return sourceText.slice(openBraceIndex, end + 1);
|
||||
}
|
||||
|
||||
test("audits dashboard query combines final audits with generation runs", async () => {
|
||||
const auditsSource = await source("convex/audits.ts");
|
||||
const querySource = extractExportSource(auditsSource, "listDashboardRows");
|
||||
|
||||
assert.match(
|
||||
auditsSource,
|
||||
/export const listDashboardRows = query/,
|
||||
"Dashboard rows should be exposed as a public Convex query.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/requireOperator\(ctx\)/,
|
||||
"Dashboard rows should require an authenticated operator.",
|
||||
);
|
||||
assert.match(
|
||||
auditsSource,
|
||||
/ctx\.auth\.getUserIdentity\(\)/,
|
||||
"Operator checks should derive identity from Convex auth.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/kind:\s*"audit"/,
|
||||
"Final audit documents should be returned as audit rows.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/kind:\s*"generation"/,
|
||||
"Audit generation runs should be returned as generation rows.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/\.query\("audits"\)/,
|
||||
"Dashboard rows should read finalized audits.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/\.query\("agentRuns"\)[\s\S]*\.withIndex\("by_type"/,
|
||||
"Dashboard rows should read audit_generation runs through the type index.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/\.query\("auditGenerations"\)[\s\S]*\.withIndex\("by_runId"/,
|
||||
"Dashboard rows should load generation stages by run id.",
|
||||
);
|
||||
});
|
||||
|
||||
test("audits dashboard query suppresses generation rows once a final audit exists", async () => {
|
||||
const auditsSource = await source("convex/audits.ts");
|
||||
const querySource = extractExportSource(auditsSource, "listDashboardRows");
|
||||
|
||||
assert.match(
|
||||
querySource,
|
||||
/finalAuditRunIds/,
|
||||
"Query should track run ids that already have finalized audits.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/finalAuditLeadIds/,
|
||||
"Query should track lead ids that already have finalized audits.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/\.query\("audits"\)[\s\S]*\.withIndex\("by_leadId"/,
|
||||
"Query should suppress generation rows even when the finalized audit is outside the fetched dashboard page.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/ctx\.db\.get\(run\.auditId\)/,
|
||||
"Query should suppress generation rows that directly reference an existing audit.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/continue/,
|
||||
"Query should skip duplicate generation rows instead of returning them.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/latestStage/,
|
||||
"Generation rows should surface the latest generation stage.",
|
||||
);
|
||||
assert.match(
|
||||
querySource,
|
||||
/errorSummary/,
|
||||
"Generation rows should surface run or stage errors.",
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user