mirror of
https://github.com/PR0M3TH3AN/bitgate.git
synced 2026-10-27 08:31:00 +00:00
The evaluator was a skeleton with invented thresholds (5/3/10/5) and two unimplemented branches. Rebuild it against the migration plan's spec and the behavior characterized from the pinned reference commit. Core: - four independent decision dimensions (ranking, visibility, interaction, transaction) with severity ladders and max-merge composition - stable reason identifiers replacing free-text strings - structured evidence, redacted unless a profile opts in - administrative-state reduction with capability filtering at merge time, so revoking a role drops that actor's entries immediately - protected actors stripped from every denial set - snapshot fingerprints for cache invalidation - generic policy engine: applications supply profiles and category thresholds; the core carries none of its own Characterization: - 34 generated fixtures covering every behavior the plan's Phase 0 requires - conformance suite that actually evaluates them, replacing a placeholder that asserted a list's length - reference thresholds (3/3/20/5, 60-day mute window) live in bitvid-compat as an application profile, not as engine defaults Fixes found while building: - reduceAdminState accepted malformed target identifiers, because getTargetKey formats anything it is given - community-user-deny fired for any contributor rather than only for community-curated sources 273 tests pass; typecheck and the reference-import guard are clean.
85 lines
3.3 KiB
JavaScript
85 lines
3.3 KiB
JavaScript
// Conformance suite for the characterization corpus.
|
|
//
|
|
// Every fixture under fixtures/bitvid/cases is evaluated against the reference
|
|
// application profile. A change to a threshold, a precedence rule, or an effect
|
|
// ladder must show up here as a failing case.
|
|
|
|
import { readdirSync, readFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { runConformanceCase } from "@nostr-governance/testing/conformance";
|
|
import { REFERENCE_POLICY } from "@nostr-governance/bitvid-compat/profile";
|
|
|
|
const ROOT = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
const CASES_DIR = join(ROOT, "fixtures", "bitvid", "cases");
|
|
const CASE_INDEX = join(ROOT, "fixtures", "bitvid", "characterization-cases.json");
|
|
|
|
const caseFiles = readdirSync(CASES_DIR).filter((name) => name.endsWith(".json")).sort();
|
|
const fixtures = caseFiles.map((name) => JSON.parse(readFileSync(join(CASES_DIR, name), "utf8")));
|
|
|
|
describe("characterization corpus", () => {
|
|
it("has fixtures on disk", () => {
|
|
expect(fixtures.length).toBeGreaterThanOrEqual(15);
|
|
});
|
|
|
|
it("index matches the files on disk", () => {
|
|
const index = JSON.parse(readFileSync(CASE_INDEX, "utf8"));
|
|
expect([...index].sort()).toEqual(fixtures.map((fixture) => fixture.name).sort());
|
|
});
|
|
|
|
it("covers every behavior the migration plan requires", () => {
|
|
const names = new Set(fixtures.map((fixture) => fixture.name));
|
|
const required = [
|
|
"trusted-report-threshold-reached",
|
|
"duplicate-reporter-dedupe",
|
|
"blocked-reporter-ignored",
|
|
"trusted-mute-below-threshold",
|
|
"trusted-mute-at-threshold",
|
|
"expired-trusted-mute",
|
|
"personal-block-precedence",
|
|
"admin-user-deny",
|
|
"admin-event-deny",
|
|
"community-blacklist-merge",
|
|
"moderator-trust-seed",
|
|
"viewer-override-softens-hide",
|
|
"author-override",
|
|
"home-hide-bypass",
|
|
"cache-fallback-effective",
|
|
];
|
|
for (const name of required) {
|
|
expect(names, `missing required characterization case: ${name}`).toContain(name);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe.each(fixtures.map((fixture) => [fixture.name, fixture]))("%s", (_name, fixture) => {
|
|
it(fixture.description, () => {
|
|
const { passed, mismatches, decision } = runConformanceCase(fixture, REFERENCE_POLICY);
|
|
expect(passed, `${fixture.behavior}\n ${mismatches.join("\n ")}`).toBe(true);
|
|
expect(decision.policyProfile).toBe(fixture.profile);
|
|
expect(decision.policyVersion).toBe(REFERENCE_POLICY.version);
|
|
expect(decision.snapshotFingerprint).toMatch(/^[0-9a-f]{16}$/);
|
|
});
|
|
});
|
|
|
|
describe("evaluation determinism", () => {
|
|
it("produces identical decisions for identical snapshots", () => {
|
|
for (const fixture of fixtures) {
|
|
const first = runConformanceCase(fixture, REFERENCE_POLICY).decision;
|
|
const second = runConformanceCase(fixture, REFERENCE_POLICY).decision;
|
|
expect(second).toEqual(first);
|
|
expect(second.snapshotFingerprint).toBe(first.snapshotFingerprint);
|
|
}
|
|
});
|
|
|
|
it("does not mutate fixture inputs", () => {
|
|
for (const fixture of fixtures) {
|
|
const before = JSON.stringify(fixture);
|
|
runConformanceCase(fixture, REFERENCE_POLICY);
|
|
expect(JSON.stringify(fixture)).toBe(before);
|
|
}
|
|
});
|
|
});
|