emailpassword.test.ts55 lines · main
1import { describe, expect, test } from 'bun:test';
2
3import {
4 hashPassword,
5 verifyPassword,
6 verifyPasswordFlexible,
7} from './emailpassword.js';
8
9describe('briven-engine password hash (Phase 2)', () => {
10 test('hashes and verifies correct password', () => {
11 const { hash } = hashPassword('Step2Test!Pass99');
12 expect(hash.includes(':')).toBe(true);
13 expect(verifyPassword('Step2Test!Pass99', hash)).toBe(true);
14 });
15
16 test('rejects wrong password', () => {
17 const { hash } = hashPassword('correct-horse');
18 expect(verifyPassword('wrong-battery', hash)).toBe(false);
19 });
20
21 test('same salt is deterministic', () => {
22 const salt = 'a'.repeat(32);
23 const a = hashPassword('same', salt);
24 const b = hashPassword('same', salt);
25 expect(a.hash).toBe(b.hash);
26 });
27
28 test('malformed stored hash fails closed', () => {
29 expect(verifyPassword('x', 'not-a-hash')).toBe(false);
30 expect(verifyPassword('x', '')).toBe(false);
31 });
32
33 test('import:bcrypt foreign hash verifies and flags upgrade', async () => {
34 const raw = await Bun.password.hash('MigrateMe!99', { algorithm: 'bcrypt', cost: 4 });
35 const stored = `import:bcrypt:${raw}`;
36 expect(verifyPassword('MigrateMe!99', stored)).toBe(false); // sync path rejects foreign
37 const ok = await verifyPasswordFlexible('MigrateMe!99', stored);
38 expect(ok.ok).toBe(true);
39 expect(ok.upgradeToBriven).toBe(true);
40 const bad = await verifyPasswordFlexible('wrong', stored);
41 expect(bad.ok).toBe(false);
42 });
43
44 test('import:argon2id foreign hash verifies and flags upgrade', async () => {
45 const raw = await Bun.password.hash('ArgonMigrate!42', {
46 algorithm: 'argon2id',
47 });
48 const stored = `import:argon2:${raw}`;
49 const ok = await verifyPasswordFlexible('ArgonMigrate!42', stored);
50 expect(ok.ok).toBe(true);
51 expect(ok.upgradeToBriven).toBe(true);
52 const bad = await verifyPasswordFlexible('nope', stored);
53 expect(bad.ok).toBe(false);
54 });
55});