42 lines
2.1 KiB
TypeScript
42 lines
2.1 KiB
TypeScript
// @vitest-environment node
|
|
import { describe, expect, it } from "vitest";
|
|
import { deferredHrFieldCrypto, getHrFieldCrypto, HrCryptoNotProvisionedError, type HrFieldCrypto } from "./hrFieldCrypto.server";
|
|
|
|
// AK-P17a — the AD-8 per-tenant envelope (R-P5 `getTenantFieldCrypto`) is wired:
|
|
// `getHrFieldCrypto()` returns a real provider. The deferred binding is retained
|
|
// as a fail-closed scaffold (refuse to touch a sensitive value with a Klartext
|
|
// M002 message rather than silently store plaintext).
|
|
|
|
describe("hrFieldCrypto seam (bound to the R-P5 per-tenant envelope)", () => {
|
|
it("the deferred fail-closed scaffold rejects encrypt with a Klartext error", async () => {
|
|
await expect(deferredHrFieldCrypto.encrypt("tenant_1", "secret")).rejects.toBeInstanceOf(HrCryptoNotProvisionedError);
|
|
});
|
|
|
|
it("the deferred fail-closed scaffold rejects decrypt with a Klartext error", async () => {
|
|
await expect(deferredHrFieldCrypto.decrypt("tenant_1", "cipher")).rejects.toBeInstanceOf(HrCryptoNotProvisionedError);
|
|
});
|
|
|
|
it("the error carries an i18n message key, not a bare string", async () => {
|
|
const error = await deferredHrFieldCrypto.encrypt("tenant_1", "x").catch((caught) => caught);
|
|
expect(error).toBeInstanceOf(HrCryptoNotProvisionedError);
|
|
expect((error as HrCryptoNotProvisionedError).messageKey).toBe("hr.error.cryptoNotProvisioned");
|
|
});
|
|
|
|
it("getHrFieldCrypto returns a real provider, not the deferred scaffold", () => {
|
|
const crypto = getHrFieldCrypto();
|
|
expect(crypto).not.toBe(deferredHrFieldCrypto);
|
|
expect(typeof crypto.encrypt).toBe("function");
|
|
expect(typeof crypto.decrypt).toBe("function");
|
|
});
|
|
|
|
it("a working provider can be injected (proves the seam is swap-ready)", async () => {
|
|
const reversible: HrFieldCrypto = {
|
|
encrypt: (_tenantId, plaintext) => Promise.resolve(`enc:${plaintext}`),
|
|
decrypt: (_tenantId, ciphertext) => Promise.resolve(ciphertext.replace(/^enc:/, "")),
|
|
};
|
|
const cipher = await reversible.encrypt("tenant_1", "hunter2");
|
|
expect(cipher).toBe("enc:hunter2");
|
|
expect(await reversible.decrypt("tenant_1", cipher)).toBe("hunter2");
|
|
});
|
|
});
|