import { describe, it, expect } from 'vitest';
import * as jose from 'jose';
import { createSession, verifySession, refreshSession, InvalidSessionPayloadError, } from '../../auth/session.js';
const TEST_SECRET = 'a-secret-key-that-is-at-least-32-chars-long!!';
const TEST_PAYLOAD = {
    userId: 'user_001',
    role: 'admin',
    sessionId: 'sess_abc123',
};
describe('createSession', () => {
    it('creates a valid JWT string', async () => {
        const token = await createSession(TEST_PAYLOAD, { secret: TEST_SECRET });
        expect(typeof token).toBe('string');
        const parts = token.split('.');
        expect(parts).toHaveLength(3);
    });
    it('creates tokens that can be verified', async () => {
        const token = await createSession(TEST_PAYLOAD, { secret: TEST_SECRET });
        const decoded = await verifySession(token, { secret: TEST_SECRET });
        expect(decoded.userId).toBe(TEST_PAYLOAD.userId);
        expect(decoded.role).toBe(TEST_PAYLOAD.role);
        expect(decoded.sessionId).toBe(TEST_PAYLOAD.sessionId);
    });
});
describe('verifySession', () => {
    it('decodes the payload correctly', async () => {
        const token = await createSession(TEST_PAYLOAD, { secret: TEST_SECRET });
        const decoded = await verifySession(token, { secret: TEST_SECRET });
        expect(decoded.userId).toBe('user_001');
        expect(decoded.role).toBe('admin');
        expect(decoded.sessionId).toBe('sess_abc123');
    });
    it('throws on expired token', async () => {
        const token = await createSession(TEST_PAYLOAD, {
            secret: TEST_SECRET,
            maxAge: 1,
        });
        await new Promise((resolve) => setTimeout(resolve, 1500));
        await expect(verifySession(token, { secret: TEST_SECRET })).rejects.toThrow();
    });
    it('throws on invalid secret', async () => {
        const token = await createSession(TEST_PAYLOAD, { secret: TEST_SECRET });
        await expect(verifySession(token, { secret: 'wrong-secret-that-is-also-32-chars!!' })).rejects.toThrow();
    });
    it('throws on tampered token', async () => {
        const token = await createSession(TEST_PAYLOAD, { secret: TEST_SECRET });
        const tampered = token.slice(0, -5) + 'XXXXX';
        await expect(verifySession(tampered, { secret: TEST_SECRET })).rejects.toThrow();
    });
});
describe('verifySession payload validation', () => {
    // Forge a JWT signed with the right secret but with a malformed payload.
    // jose.jwtVerify accepts these; assertSessionPayload is the line of defense.
    async function forgeJWT(payload) {
        const secret = new TextEncoder().encode(TEST_SECRET);
        return new jose.SignJWT(payload)
            .setProtectedHeader({ alg: 'HS256' })
            .setIssuedAt()
            .setExpirationTime('1h')
            .setIssuer('actuate-cms')
            .setAudience('actuate-cms')
            .sign(secret);
    }
    it('rejects payload missing userId', async () => {
        const token = await forgeJWT({ role: 'admin', sessionId: 'sess1' });
        await expect(verifySession(token, { secret: TEST_SECRET })).rejects.toBeInstanceOf(InvalidSessionPayloadError);
    });
    it('rejects payload missing role', async () => {
        const token = await forgeJWT({ userId: 'u1', sessionId: 'sess1' });
        await expect(verifySession(token, { secret: TEST_SECRET })).rejects.toBeInstanceOf(InvalidSessionPayloadError);
    });
    it('rejects payload missing sessionId', async () => {
        const token = await forgeJWT({ userId: 'u1', role: 'admin' });
        await expect(verifySession(token, { secret: TEST_SECRET })).rejects.toBeInstanceOf(InvalidSessionPayloadError);
    });
    it('rejects empty-string userId', async () => {
        const token = await forgeJWT({ userId: '', role: 'admin', sessionId: 'sess1' });
        await expect(verifySession(token, { secret: TEST_SECRET })).rejects.toBeInstanceOf(InvalidSessionPayloadError);
    });
    it('rejects non-string fingerprint when present', async () => {
        const token = await forgeJWT({
            userId: 'u1',
            role: 'admin',
            sessionId: 'sess1',
            fingerprint: 12345,
        });
        await expect(verifySession(token, { secret: TEST_SECRET })).rejects.toBeInstanceOf(InvalidSessionPayloadError);
    });
    it('strips extra attacker-supplied claims from the returned payload', async () => {
        const token = await forgeJWT({
            userId: 'u1',
            role: 'CLIENT',
            sessionId: 'sess1',
            isSuperUser: true,
            injectedField: 'pwn',
        });
        const decoded = await verifySession(token, { secret: TEST_SECRET });
        expect(Object.keys(decoded).sort()).toEqual(['role', 'sessionId', 'userId']);
        expect(decoded.isSuperUser).toBeUndefined();
        expect(decoded.injectedField).toBeUndefined();
    });
});
describe('refreshSession', () => {
    it('returns a new valid token with the same payload', async () => {
        const original = await createSession(TEST_PAYLOAD, {
            secret: TEST_SECRET,
            maxAge: 10,
        });
        await new Promise((resolve) => setTimeout(resolve, 1100));
        const refreshed = await refreshSession(original, { secret: TEST_SECRET });
        expect(typeof refreshed).toBe('string');
        expect(refreshed).not.toBe(original);
        const decoded = await verifySession(refreshed, { secret: TEST_SECRET });
        expect(decoded.userId).toBe(TEST_PAYLOAD.userId);
        expect(decoded.role).toBe(TEST_PAYLOAD.role);
        expect(decoded.sessionId).toBe(TEST_PAYLOAD.sessionId);
    });
});
//# sourceMappingURL=session.test.js.map