64 lines
2.7 KiB
TypeScript
64 lines
2.7 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const storeMock = vi.hoisted(() => ({
|
|
createUserSession: vi.fn(),
|
|
revokeSessionByToken: vi.fn(),
|
|
}));
|
|
vi.mock("./sessionStore.server", () => ({
|
|
createUserSession: (...args: unknown[]) => storeMock.createUserSession(...args),
|
|
revokeSessionByToken: (...args: unknown[]) => storeMock.revokeSessionByToken(...args),
|
|
}));
|
|
|
|
const geoMock = vi.hoisted(() => ({ lookupCountry: vi.fn() }));
|
|
vi.mock("~/modules/ipAddress/services/geoip.server", () => ({
|
|
// Realistischer Mock: der Hash enthaelt die Roh-IP NICHT (wie der echte hashIp).
|
|
hashIp: () => "HASHED",
|
|
lookupCountry: (...args: unknown[]) => geoMock.lookupCountry(...args),
|
|
}));
|
|
|
|
import { buildSessionDeviceContext, endServerSession, startServerSession } from "./sessionLifecycle.server";
|
|
|
|
function requestWith(headers: Record<string, string>) {
|
|
return new Request("https://example.test/login", { headers });
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
geoMock.lookupCountry.mockResolvedValue({ countryCode: "DE", countryName: "Germany" });
|
|
});
|
|
|
|
describe("buildSessionDeviceContext (AD-22)", () => {
|
|
it("hashes the IP and resolves the country, never exposing the raw IP", async () => {
|
|
const ctx = await buildSessionDeviceContext(requestWith({ "user-agent": "Safari", "x-forwarded-for": "9.9.9.9" }));
|
|
expect(ctx.ipHash).toBe("HASHED");
|
|
expect(ctx.userAgent).toBe("Safari");
|
|
expect(ctx.approxLocation).toBe("Germany");
|
|
expect(JSON.stringify(ctx)).not.toContain("9.9.9.9");
|
|
});
|
|
|
|
it("yields nulls and skips the geo lookup when no IP is present", async () => {
|
|
const ctx = await buildSessionDeviceContext(requestWith({}));
|
|
expect(ctx.ipHash).toBeNull();
|
|
expect(ctx.userAgent).toBeNull();
|
|
expect(ctx.approxLocation).toBeNull();
|
|
expect(geoMock.lookupCountry).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("startServerSession", () => {
|
|
it("creates a session row with the pseudonymized device context and returns the raw token", async () => {
|
|
storeMock.createUserSession.mockResolvedValue({ session: { id: "s1" }, rawToken: "tok-123" });
|
|
const token = await startServerSession({ request: requestWith({ "user-agent": "Safari", "x-forwarded-for": "1.2.3.4" }), userId: "u1" });
|
|
expect(token).toBe("tok-123");
|
|
expect(storeMock.createUserSession).toHaveBeenCalledWith({ userId: "u1", ipHash: "HASHED", userAgent: "Safari", approxLocation: "Germany" });
|
|
});
|
|
});
|
|
|
|
describe("endServerSession", () => {
|
|
it("delegates revocation to the store", async () => {
|
|
storeMock.revokeSessionByToken.mockResolvedValue(true);
|
|
await endServerSession("tok-123");
|
|
expect(storeMock.revokeSessionByToken).toHaveBeenCalledWith("tok-123");
|
|
});
|
|
});
|