mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-17 05:43:36 +00:00
refactor(tests): move backend tests into src/backend/tests mirror tree
Backend *.test.ts files (and the test-support harness) no longer sit next to source files; they live under src/backend/tests/ mirroring the source layout. Imports rewritten accordingly; CLAUDE.md convention updated.
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
sqlite: null as {
|
||||
exec: (sql: string) => void;
|
||||
prepare: (sql: string) => {
|
||||
all: () => Array<Record<string, unknown>>;
|
||||
run: (...values: unknown[]) => unknown;
|
||||
};
|
||||
} | null,
|
||||
saveDatabase: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
vi.mock("../../database/db/index.js", async () => {
|
||||
const { default: Database } = await import("better-sqlite3");
|
||||
const { drizzle } = await import("drizzle-orm/better-sqlite3");
|
||||
const sqlite = new Database(":memory:");
|
||||
mocks.sqlite = sqlite;
|
||||
const db = drizzle(sqlite);
|
||||
return {
|
||||
db,
|
||||
getDb: () => db,
|
||||
saveMemoryDatabaseToFile: mocks.saveDatabase,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../utils/user-keys.js", () => ({
|
||||
UserKeyManager: {
|
||||
getInstance: () => ({
|
||||
invalidate: vi.fn(),
|
||||
tryGetUserDEK: vi.fn(() => null),
|
||||
hasUserDEK: vi.fn(() => true),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("../../utils/system-crypto.js", () => ({
|
||||
SystemCrypto: { getInstance: () => ({}) },
|
||||
}));
|
||||
|
||||
vi.mock("../../utils/data-crypto.js", () => ({
|
||||
DataCrypto: { getInstance: () => ({}) },
|
||||
}));
|
||||
|
||||
vi.mock("../../utils/logger.js", () => ({
|
||||
authLogger: {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
success: vi.fn(),
|
||||
},
|
||||
databaseLogger: {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
success: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const { AuthManager } = await import("../../utils/auth-manager.js");
|
||||
const authManager = AuthManager.getInstance();
|
||||
|
||||
type SessionInput = {
|
||||
id: string;
|
||||
userId: string;
|
||||
sub: string;
|
||||
sid: string | null;
|
||||
providerId: number | null;
|
||||
};
|
||||
|
||||
function insertSession({ id, userId, sub, sid, providerId }: SessionInput) {
|
||||
mocks
|
||||
.sqlite!.prepare(
|
||||
`INSERT INTO sessions (
|
||||
id, user_id, jwt_token, device_type, device_info,
|
||||
oidc_sub, oidc_sid, sso_provider_id,
|
||||
created_at, expires_at, last_active_at
|
||||
) VALUES (?, ?, ?, 'browser', 'test', ?, ?, ?, ?, ?, ?)`,
|
||||
)
|
||||
.run(
|
||||
id,
|
||||
userId,
|
||||
`token-${id}`,
|
||||
sub,
|
||||
sid,
|
||||
providerId,
|
||||
"2026-07-10T00:00:00.000Z",
|
||||
"2026-07-11T00:00:00.000Z",
|
||||
"2026-07-10T00:00:00.000Z",
|
||||
);
|
||||
}
|
||||
|
||||
function sessionIds(): string[] {
|
||||
return mocks
|
||||
.sqlite!.prepare("SELECT id FROM sessions ORDER BY id")
|
||||
.all()
|
||||
.map((row) => String(row.id));
|
||||
}
|
||||
|
||||
describe("AuthManager.revokeSessionsByOidc", () => {
|
||||
beforeAll(() => {
|
||||
mocks.sqlite!.exec(`
|
||||
CREATE TABLE sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
jwt_token TEXT NOT NULL,
|
||||
device_type TEXT NOT NULL,
|
||||
device_info TEXT NOT NULL,
|
||||
oidc_sub TEXT,
|
||||
oidc_sid TEXT,
|
||||
sso_provider_id INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
expires_at TEXT NOT NULL,
|
||||
last_active_at TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.sqlite!.exec("DELETE FROM sessions");
|
||||
mocks.saveDatabase.mockReset().mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("revokes only the matching provider session when sid is present", async () => {
|
||||
insertSession({
|
||||
id: "matching",
|
||||
userId: "user-1",
|
||||
sub: "subject-1",
|
||||
sid: "session-1",
|
||||
providerId: 7,
|
||||
});
|
||||
insertSession({
|
||||
id: "other-provider",
|
||||
userId: "user-1",
|
||||
sub: "subject-1",
|
||||
sid: "session-1",
|
||||
providerId: 8,
|
||||
});
|
||||
insertSession({
|
||||
id: "other-session",
|
||||
userId: "user-1",
|
||||
sub: "subject-1",
|
||||
sid: "session-2",
|
||||
providerId: 7,
|
||||
});
|
||||
|
||||
await expect(
|
||||
authManager.revokeSessionsByOidc({
|
||||
ssoProviderId: 7,
|
||||
sub: "subject-1",
|
||||
sid: "session-1",
|
||||
}),
|
||||
).resolves.toBe(1);
|
||||
|
||||
expect(sessionIds()).toEqual(["other-provider", "other-session"]);
|
||||
});
|
||||
|
||||
it("revokes all provider sessions for a subject when sid is absent", async () => {
|
||||
insertSession({
|
||||
id: "first",
|
||||
userId: "user-1",
|
||||
sub: "subject-1",
|
||||
sid: "session-1",
|
||||
providerId: 7,
|
||||
});
|
||||
insertSession({
|
||||
id: "second",
|
||||
userId: "user-1",
|
||||
sub: "subject-1",
|
||||
sid: "session-2",
|
||||
providerId: 7,
|
||||
});
|
||||
insertSession({
|
||||
id: "other-subject",
|
||||
userId: "user-2",
|
||||
sub: "subject-2",
|
||||
sid: null,
|
||||
providerId: 7,
|
||||
});
|
||||
|
||||
await expect(
|
||||
authManager.revokeSessionsByOidc({
|
||||
ssoProviderId: 7,
|
||||
sub: "subject-1",
|
||||
}),
|
||||
).resolves.toBe(2);
|
||||
|
||||
expect(sessionIds()).toEqual(["other-subject"]);
|
||||
});
|
||||
|
||||
it("does not persist when no session matches", async () => {
|
||||
await expect(
|
||||
authManager.revokeSessionsByOidc({
|
||||
ssoProviderId: 7,
|
||||
sid: "missing",
|
||||
}),
|
||||
).resolves.toBe(0);
|
||||
|
||||
expect(mocks.saveDatabase).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("propagates persistence failures so the provider can retry", async () => {
|
||||
insertSession({
|
||||
id: "matching",
|
||||
userId: "user-1",
|
||||
sub: "subject-1",
|
||||
sid: "session-1",
|
||||
providerId: 7,
|
||||
});
|
||||
mocks.saveDatabase.mockRejectedValueOnce(new Error("disk full"));
|
||||
|
||||
await expect(
|
||||
authManager.revokeSessionsByOidc({
|
||||
ssoProviderId: 7,
|
||||
sid: "session-1",
|
||||
}),
|
||||
).rejects.toThrow("disk full");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user