[grade=B] test: sync auth and version contracts
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled

This commit is contained in:
Hermes
2026-07-24 22:39:29 -07:00
parent 140ef8726b
commit 0d46225efc
2 changed files with 17 additions and 19 deletions
@@ -4,8 +4,8 @@
* Covers the BACKLOG.md DC-006 acceptance criteria:
* - no code → 400 (ValidationError)
* - wrong code → 401 (AuthenticationError)
* - valid TOTP → 200 + session cookie + CSRF token
* - check-session with valid session → 200 { authenticated: true }
* - valid TOTP → 200 + session cookie + CSRF token + SSO handoff token
* - check-session with valid session → 200 { success: true, authenticated: true }
* - check-session without session → 401 (AuthenticationError)
*
* Uses real otplib for code generation (so we exercise the actual TOTP math)
@@ -79,6 +79,7 @@ function createApp(depsOverride = {}) {
sessionStore.delete(ip);
}),
clearCookie: jest.fn(),
createHandoffToken: jest.fn(() => 'mock-sso-handoff-token'),
isValid: jest.fn((req) => {
const ip = session.getClientIP(req);
const entry = sessionStore.get(ip);
@@ -303,8 +304,10 @@ describe('TOTP Auth Routes — DC-006 Integration Test', () => {
expect(res.body.success).toBe(true);
expect(res.body.message).toMatch(/Authenticated successfully/);
expect(res.body.csrfToken).toBe('mock-csrf-token');
expect(res.body.ssoToken).toBe('mock-sso-handoff-token');
expect(deps.session.create).toHaveBeenCalled();
expect(deps.session.setCookie).toHaveBeenCalled();
expect(deps.session.createHandoffToken).toHaveBeenCalledTimes(1);
expect(deps.renewCSRFToken).toHaveBeenCalled();
});
});
@@ -350,7 +353,7 @@ describe('TOTP Auth Routes — DC-006 Integration Test', () => {
deps.session._grantSession('127.0.0.1');
const res = await request(app).get('/api/totp/check-session').set('X-Forwarded-For', '127.0.0.1');
expect(res.status).toBe(200);
expect(res.body).toEqual({ authenticated: true });
expect(res.body).toEqual({ success: true, authenticated: true });
});
});
@@ -450,24 +453,23 @@ describe('TOTP Auth Routes — DC-006 Integration Test', () => {
const loginRes = await request(app).post('/api/totp/verify').send({ code: loginCode });
expect(loginRes.status).toBe(200);
expect(loginRes.body.csrfToken).toBeDefined();
expect(loginRes.body.ssoToken).toBe('mock-sso-handoff-token');
expect(deps.session.createHandoffToken).toHaveBeenCalledTimes(1);
// 5. Check-session — should now be authenticated (the BACKLOG "→ endpoint succeeds" step)
const checkRes = await request(app).get('/api/totp/check-session');
expect(checkRes.status).toBe(200);
expect(checkRes.body).toEqual({ authenticated: true });
expect(checkRes.body).toEqual({ success: true, authenticated: true });
// 6. Logout / disable
const disableCode = authenticator.generate(secret);
const disableRes = await request(app).post('/api/totp/disable').send({ code: disableCode });
expect(disableRes.status).toBe(200);
// 7. After disable, check-session should be 401 (bypass removed for security)
// unless the user still holds a valid session, in which case it's 200.
// The login step (4) may or may not have granted one depending on test order.
// 7. After disable, check-session deterministically rejects before
// checking session validity because TOTP protection is disabled.
const afterRes = await request(app).get('/api/totp/check-session');
// After disable, TOTP is off AND we may or may not have an active session.
// The new contract: bypass is gone, but a valid session still authenticates.
expect([200, 401]).toContain(afterRes.status);
expect(afterRes.status).toBe(401);
});
it('proves otplib is real (not stubbed) by using a totally bogus code', async () => {
@@ -78,23 +78,19 @@ describe('SelfUpdater.getLocalVersion() — DC-033 regression', () => {
}
});
test('commit is a git SHA (7-40 hex chars), not null', () => {
test('commit contains a git SHA and is not null', () => {
expect(result.commit).not.toBeNull();
expect(result.commit).toMatch(/^[0-9a-f]{7,40}$/);
expect(result.commit).toMatch(/(?:^|-)[0-9a-f]{7,40}$/);
});
});
describe('candidate-path resolution survives missing sibling files', () => {
// If we shadow __dirname by requiring the module through a different
// require() chain, the function should still find package.json via its
// candidate-list fallback. This catches the case where someone refactors
// the file to a deeper subdirectory and forgets to update the candidates.
test('getLocalVersion works regardless of how the module is required', () => {
describe('repeat construction uses the same resolved metadata', () => {
test('a second instance resolves the same non-fallback version metadata', () => {
const mod = require(path.join(API_ROOT, 'src', 'docker', 'self-updater.js'));
const Cls = mod.SelfUpdater || mod.default || mod;
const result = new Cls({}).getLocalVersion();
expect(result.version).not.toBe('0.0.0');
expect(result.commit).toMatch(/^[0-9a-f]{7,40}$/);
expect(result.commit).toMatch(/(?:^|-)[0-9a-f]{7,40}$/);
});
});
});