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(); }); });