Files
notification-wakeup-service/test/util/smsVerificationCode.test.ts
T

71 lines
2.5 KiB
TypeScript

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
VERIFICATION_CODE_LENGTH,
hashPhoneNumber,
hashVerificationCode,
mintVerificationCode,
verificationCodeMatches,
} from "../../src/util/smsVerificationCode.js";
const SECRET = "test-sms-code-secret";
describe("mintVerificationCode", () => {
it("always produces six digits, leading zeros included", () => {
for (let i = 0; i < 2000; i += 1) {
const code = mintVerificationCode();
assert.equal(code.length, VERIFICATION_CODE_LENGTH);
assert.match(code, /^\d{6}$/);
}
});
it("does not return the same code every call", () => {
const seen = new Set<string>();
for (let i = 0; i < 200; i += 1) seen.add(mintVerificationCode());
assert.ok(seen.size > 100, `expected varied codes, got ${seen.size}`);
});
});
describe("hashVerificationCode", () => {
it("is stable for one secret and different across secrets", () => {
assert.equal(
hashVerificationCode("483920", SECRET),
hashVerificationCode("483920", SECRET)
);
assert.notEqual(
hashVerificationCode("483920", SECRET),
hashVerificationCode("483920", "other-secret")
);
});
it("never contains the plaintext code", () => {
assert.equal(hashVerificationCode("483920", SECRET).includes("483920"), false);
});
});
describe("hashPhoneNumber", () => {
it("is stable per number and does not contain the number", () => {
const hash = hashPhoneNumber("+15555550123", SECRET);
assert.equal(hash, hashPhoneNumber("+15555550123", SECRET));
assert.notEqual(hash, hashPhoneNumber("+15555550124", SECRET));
assert.equal(hash.includes("5555550123"), false);
});
});
describe("verificationCodeMatches", () => {
it("accepts the matching code and rejects every near miss", () => {
const stored = hashVerificationCode("483920", SECRET);
assert.equal(verificationCodeMatches("483920", stored, SECRET), true);
assert.equal(verificationCodeMatches("483921", stored, SECRET), false);
assert.equal(verificationCodeMatches("48392", stored, SECRET), false);
assert.equal(verificationCodeMatches("", stored, SECRET), false);
assert.equal(verificationCodeMatches("483920", stored, "wrong"), false);
});
it("compares full hashes, so a shared prefix is not a partial match", () => {
const stored = hashVerificationCode("111111", SECRET);
const truncated = stored.slice(0, 10);
assert.equal(verificationCodeMatches("111111", truncated, SECRET), false);
});
});