import { describe, it, expect, beforeEach } from 'vitest'; import bcrypt from 'bcrypt'; import { verifyAgainstDummy, _resetDummyHashForTests } from '../../src/services/dummy-hash.ts'; import { hashPassword, verifyPassword } from '../../src/services/auth.ts'; describe('dummy hash', () => { beforeEach(() => { _resetDummyHashForTests(); }); it('always returns false', async () => { expect(await verifyAgainstDummy('whatever')).toBe(false); expect(await verifyAgainstDummy('')).toBe(false); expect(await verifyAgainstDummy('admin')).toBe(false); }); it('takes comparable time to verifying a real bcrypt hash (within 5x)', async () => { // Warm dummy hash so the cache is hot. await verifyAgainstDummy('warmup'); const realHash = await hashPassword('actual-password'); const start1 = Date.now(); await verifyPassword('actual-password', realHash); const realMs = Date.now() - start1; const start2 = Date.now(); await verifyAgainstDummy('any-password'); const dummyMs = Date.now() - start2; // Both should be in the same ballpark — bcrypt cost factor is the same. // Generous bound to avoid flakes on slow CI. expect(dummyMs).toBeGreaterThan(realMs / 5); expect(dummyMs).toBeLessThan(realMs * 5); }, 10_000); it('memoizes the dummy hash across calls', async () => { // First call computes, subsequent calls reuse — covered by cache hit // being noticeably faster than a fresh hash. Just assert the function // is callable repeatedly without error. await verifyAgainstDummy('a'); await verifyAgainstDummy('b'); await verifyAgainstDummy('c'); expect(true).toBe(true); }); it('runs a real bcrypt comparison (does not short-circuit)', async () => { // Spy by counting bcrypt.compare calls would be nice, but bcrypt // is a compiled module. Indirect check: the call must actually take // bcrypt-comparison time after warmup. await verifyAgainstDummy('warmup'); const start = Date.now(); await verifyAgainstDummy('test'); const elapsed = Date.now() - start; // bcrypt 12 rounds takes >50ms on any modern CPU expect(elapsed).toBeGreaterThan(20); expect(bcrypt).toBeDefined(); }, 10_000); });