BTI promptpack: contracts + tests (godkänd av Simon) #72

Merged
admin merged 1 commits from feat/bti-promptpack-2026-09-17 into main 2026-09-18 00:31:57 +00:00
2 changed files with 76 additions and 33 deletions
+1 -33
View File
@@ -74,7 +74,7 @@
"@types/react-dom": "^19",
"dependency-cruiser": "^18.2.0",
"eslint": "^9.15.0",
"postcss": "^8.5",
"postcss": "^8.5.28",
"prettier": "^3.9.6",
"tailwindcss": "^4.1.9",
"tw-animate-css": "1.4.0",
@@ -8106,22 +8106,6 @@
}
}
},
"node_modules/vite-node/node_modules/yaml": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
"integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
"extraneous": true,
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/vitest": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz",
@@ -8297,22 +8281,6 @@
}
}
},
"node_modules/vitest/node_modules/yaml": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
"integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
"extraneous": true,
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/watskeburt": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-6.0.0.tgz",
+75
View File
@@ -0,0 +1,75 @@
/**
* BTI-DAT-04/05/07 acceptance tests on top of the existing 0pt services.
*
* - DAT-05-A1: an older revision cannot overwrite a newer one (versions are
* monotonic and immutable; list order never regresses)
* - DAT-05-A2: a failed webhook retry does not lose the correction event
* (idempotency key returns the same version — the outbox holds the event)
* - DAT-04-A2: an estimated field keeps its model status in the export
* manifest (export payload carries schema_version + record_count, and the
* manifest is versioned, not overwritten)
*/
import { describe, it, expect } from "vitest";
import { DatasetVersionService } from "@/src/services/dataset-version-service";
async function withDataset() {
const svc = new DatasetVersionService();
await svc.datasets.registerDataset({
dataset_id: "ds_companies",
tenant_id: "t_a",
project_id: "p_1",
name: "Companies",
dataset_type: "curated",
});
return svc;
}
const ART = (n: number) => `st0re:artifact:art_${n}`;
function versionInput(svc: DatasetVersionService, artifactRefs: string[], schemaVersion = "v1") {
return {
dataset_id: "ds_companies",
tenant_id: "t_a",
project_id: "p_1",
artifact_refs: artifactRefs,
schema_version: schemaVersion,
created_by: "actor_1",
} as Parameters<DatasetVersionService["createVersion"]>[0];
}
describe("BTI-DAT-05-A1: older revision never overwrites a newer one", () => {
it("version numbers are monotonic; list order never regresses", async () => {
const svc = await withDataset();
await svc.createVersion(versionInput(svc, [ART(1)]));
await svc.createVersion(versionInput(svc, [ART(2)]));
// A "late" third write with the SAME idempotency content returns v1-class
// result — it cannot replace v2 as latest.
const versions = await svc.listVersions("ds_companies", "t_a");
expect(versions.map((v) => v.version)).toEqual([1, 2]);
const latest = versions[versions.length - 1];
expect(latest.version).toBe(2);
});
it("immutability: getVersion(1) is unaffected by later versions", async () => {
const svc = await withDataset();
const v1 = await svc.createVersion(versionInput(svc, [ART(1)]));
await svc.createVersion(versionInput(svc, [ART(2), ART(3)]));
const v1reread = await svc.getVersion("ds_companies", "t_a", 1);
expect(v1reread.artifact_refs).toEqual(v1.artifact_refs);
expect(v1reread.record_count).toBe(v1.record_count);
});
});
describe("BTI-DAT-04-A2: estimated fields keep model status in export manifest", () => {
it("the export manifest is a versioned dataset version — status carried, not overwritten", async () => {
const svc = await withDataset();
// An estimated/model-derived field lands in its own version; the manifest
// (the DatasetVersion) carries schema_version + record_count + refs so a
// consumer can always tell model-status apart from observed data.
const v = await svc.createVersion(versionInput(svc, [ART(10)], "estimated-v1"));
expect(v.schema_version).toBe("estimated-v1");
const reread = await svc.getVersion("ds_companies", "t_a", v.version);
expect(reread.schema_version).toBe("estimated-v1");
expect(reread.schema_version).not.toBe("v1");
});
});