196 lines
7.4 KiB
TypeScript
196 lines
7.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { attachIntegrity, buildCanonicalReportPayload, canonicalizeReportForIntegrity, createExportIntegrity, verifyEntryIntegrity } from "./integrity";
|
|
import type { Report } from "./types";
|
|
|
|
const report: Report = {
|
|
id: "rapport-1",
|
|
report_time: "08:00:00",
|
|
report_date: "2026-05-27",
|
|
created_at: "2026-05-27T06:00:00.000Z",
|
|
type: "work_report",
|
|
work_ability: 4,
|
|
energy_level: 3,
|
|
mental_clarity: 4,
|
|
symptom_burden: 2,
|
|
effort_strain: 3,
|
|
status: "kan_fortsette",
|
|
physical_energy: 3,
|
|
mental_energy: 3,
|
|
task_types: [],
|
|
main_limitations: [],
|
|
helpful_accommodations: []
|
|
};
|
|
|
|
describe("integritetskontroll", () => {
|
|
it("legger til og validerer integritetsmetadata", () => {
|
|
const signed = attachIntegrity(report);
|
|
const verified = verifyEntryIntegrity(signed);
|
|
|
|
expect(signed.integrity?.payload_hash_sha256).toBeTruthy();
|
|
expect(signed.integrity?.payload_hash_sha256).toHaveLength(64);
|
|
expect(signed.integrity?.algorithm).toBe("sha256");
|
|
expect(verified.integrity?.verification_status).toBe("valid");
|
|
});
|
|
|
|
it("markerer endret rapport som mulig endret", () => {
|
|
const signed = attachIntegrity(report);
|
|
const tampered = { ...signed, energy_level: 1 as const };
|
|
const verified = verifyEntryIntegrity(tampered);
|
|
|
|
expect(verified.integrity?.verification_status).toBe("modified");
|
|
});
|
|
|
|
it("validerer uendret eksport og import uten falsk modified", () => {
|
|
const signed = attachIntegrity({ ...report, total_score_percent: 72 });
|
|
const exported = JSON.stringify({ reports: [signed] });
|
|
const imported = JSON.parse(exported).reports[0] as Report;
|
|
const verified = verifyEntryIntegrity(imported);
|
|
|
|
expect(verified.integrity?.verification_status).toBe("valid");
|
|
});
|
|
|
|
it("påvirkes ikke av nøkkelrekkefølge eller appgenerert verifiseringsstatus", () => {
|
|
const signed = attachIntegrity(report);
|
|
const reordered = {
|
|
type: signed.type,
|
|
created_at: signed.created_at,
|
|
id: signed.id,
|
|
report_date: signed.report_date,
|
|
report_time: signed.report_time,
|
|
status: signed.status,
|
|
effort_strain: signed.effort_strain,
|
|
symptom_burden: signed.symptom_burden,
|
|
mental_clarity: signed.mental_clarity,
|
|
energy_level: signed.energy_level,
|
|
work_ability: signed.work_ability,
|
|
physical_energy: signed.physical_energy,
|
|
mental_energy: signed.mental_energy,
|
|
helpful_accommodations: signed.helpful_accommodations,
|
|
main_limitations: signed.main_limitations,
|
|
task_types: signed.task_types,
|
|
integrity: { ...signed.integrity!, verification_status: "modified" as const }
|
|
};
|
|
|
|
expect(canonicalizeReportForIntegrity(reordered)).toBe(canonicalizeReportForIntegrity(signed));
|
|
expect(verifyEntryIntegrity(reordered).integrity?.verification_status).toBe("valid");
|
|
});
|
|
|
|
it("fjerner hele integrity-objektet fra canonical payload", () => {
|
|
const signed = attachIntegrity(report);
|
|
const withChangedIntegrity = {
|
|
...signed,
|
|
integrity: {
|
|
...signed.integrity!,
|
|
verification_status: "modified" as const,
|
|
verification_reason: "Endret lokal forklaring",
|
|
signed_at: "2099-01-01T00:00:00.000Z"
|
|
}
|
|
};
|
|
|
|
expect(buildCanonicalReportPayload(withChangedIntegrity)).toBe(buildCanonicalReportPayload(report));
|
|
expect(verifyEntryIntegrity(withChangedIntegrity).integrity?.verification_status).toBe("valid");
|
|
});
|
|
|
|
it("ignorerer rekalkulert totalscore og veiledernotater i integritetskontrollen", () => {
|
|
const signed = attachIntegrity(report);
|
|
const withLocalFields = {
|
|
...signed,
|
|
total_score_percent: 12,
|
|
evaluator_notes: [{ id: "note-1", text: "Lokalt notat" }],
|
|
report_review_note: "Lokalt notat",
|
|
normalized_period_score: 55
|
|
} as Report;
|
|
|
|
expect(verifyEntryIntegrity(withLocalFields).integrity?.verification_status).toBe("valid");
|
|
});
|
|
|
|
it("validerer rapport med arbeidssted og legacy energidetaljer uten false modified", () => {
|
|
const raw = {
|
|
id: "aac87f66-2d39-4a6f-8169-7d39aa5fc0e6",
|
|
created_at: "2026-05-27T18:36:17.482Z",
|
|
report_date: "2026-05-27",
|
|
report_time: "20:36:17",
|
|
type: "work_report",
|
|
workplace: "Hjemme",
|
|
work_start_time: "12:30",
|
|
work_end_time: "20:00",
|
|
work_ability: 4,
|
|
energy_level: 3,
|
|
mental_clarity: 4,
|
|
symptom_burden: 1,
|
|
effort_strain: 2,
|
|
status: "kan_fortsette",
|
|
physical_energy: 3,
|
|
mental_energy: 4,
|
|
physical_energy_detail: 3,
|
|
mental_energy_detail: 4,
|
|
perceived_productivity: 4,
|
|
task_completion: "som_forventet",
|
|
task_types: ["sitting", "computer_work", "concentration"],
|
|
main_limitations: ["stress_anxiety"],
|
|
helpful_accommodations: [],
|
|
total_score_percent: 81
|
|
} as Report;
|
|
|
|
const signed = attachIntegrity(raw);
|
|
const imported = JSON.parse(JSON.stringify(signed)) as Report;
|
|
|
|
expect(buildCanonicalReportPayload(imported)).not.toContain("physical_energy_detail");
|
|
expect(buildCanonicalReportPayload(imported)).not.toContain("mental_energy_detail");
|
|
expect(verifyEntryIntegrity(imported).integrity?.verification_status).toBe("valid");
|
|
});
|
|
|
|
it("bruker legacy energidetaljer som fallback når foretrukket felt mangler", () => {
|
|
const preferred = attachIntegrity({ ...report, physical_energy: 2, mental_energy: 4 });
|
|
const aliasOnly = {
|
|
...report,
|
|
physical_energy: undefined,
|
|
mental_energy: undefined,
|
|
physical_energy_detail: 2,
|
|
mental_energy_detail: 4
|
|
} as unknown as Report;
|
|
|
|
expect(buildCanonicalReportPayload(aliasOnly)).toBe(buildCanonicalReportPayload(preferred));
|
|
expect(verifyEntryIntegrity({ ...aliasOnly, integrity: preferred.integrity }).integrity?.verification_status).toBe("valid");
|
|
});
|
|
|
|
it("oppdager faktisk endring av rapporterte felt", () => {
|
|
const signed = attachIntegrity({ ...report, note: "Opprinnelig kommentar" });
|
|
|
|
expect(verifyEntryIntegrity({ ...signed, work_ability: 1 }).integrity?.verification_status).toBe("modified");
|
|
expect(verifyEntryIntegrity({ ...signed, energy_level: 1 }).integrity?.verification_status).toBe("modified");
|
|
expect(verifyEntryIntegrity({ ...signed, symptom_burden: 5 }).integrity?.verification_status).toBe("modified");
|
|
expect(verifyEntryIntegrity({ ...signed, workplace: "Annet sted" }).integrity?.verification_status).toBe("modified");
|
|
expect(verifyEntryIntegrity({ ...signed, note: "Endret kommentar" }).integrity?.verification_status).toBe("modified");
|
|
});
|
|
|
|
it("markerer eldre local-fnv1a-64 metadata som unsupported, ikke modified", () => {
|
|
const legacy = {
|
|
...report,
|
|
integrity: {
|
|
report_id: report.id,
|
|
canonicalization: "stable-json-v2" as const,
|
|
algorithm: "local-fnv1a-64" as const,
|
|
payload_hash_sha256: "1234",
|
|
signed_at: "2026-05-27T06:00:00.000Z"
|
|
}
|
|
} as Report;
|
|
|
|
expect(verifyEntryIntegrity(legacy).integrity?.verification_status).toBe("unsupported");
|
|
});
|
|
|
|
it("markerer rapport uten metadata som ikke verifisert", () => {
|
|
const verified = verifyEntryIntegrity(report);
|
|
|
|
expect(verified.integrity?.verification_status).toBe("missing");
|
|
});
|
|
|
|
it("lager eksportintegritet for manifest", () => {
|
|
const signed = attachIntegrity(report);
|
|
const integrity = createExportIntegrity([signed]);
|
|
|
|
expect(integrity.schema).toBe("arbeidsevne-export-integrity-v1");
|
|
expect(integrity.manifest_hash_sha256).toBeTruthy();
|
|
});
|
|
});
|