Refactor API to use nested sessions/devices routers and fix test infrastructure
- Update API contract to use nested router structure for sessions and devices (me.sessions.list, me.devices.getInfo, etc.) - Update frontend Svelte components to use new nested API paths - Fix test assertion patterns for consistency (remove async () => wrappers) - Fix test-db.ts findRepoRoot to use existsSync for directory checking (Bun.file().exists() returns false for directories) - Add ESLint config override for test files to handle expect().rejects patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,9 +31,9 @@ import {
|
||||
} from "bun:test";
|
||||
import { call } from "@orpc/server";
|
||||
import { router } from "../../router.js";
|
||||
import { hashPassword } from "../../utils/password.js";
|
||||
import { hashToken } from "../../utils/crypto.js";
|
||||
import { COOKIE_NAMES } from "../../utils/cookies.js";
|
||||
import { hashToken } from "../../utils/crypto.js";
|
||||
import { hashPassword } from "../../utils/password.js";
|
||||
import { TEST_RP } from "../helpers/test-constants.js";
|
||||
import {
|
||||
createTestDb,
|
||||
@@ -76,7 +76,9 @@ function createAPIContext(options?: {
|
||||
cookies.push(`${COOKIE_NAMES.SESSION_TOKEN}=${options.sessionToken}`);
|
||||
}
|
||||
if (options?.deviceFingerprint) {
|
||||
cookies.push(`${COOKIE_NAMES.DEVICE_FINGERPRINT}=${options.deviceFingerprint}`);
|
||||
cookies.push(
|
||||
`${COOKIE_NAMES.DEVICE_FINGERPRINT}=${options.deviceFingerprint}`,
|
||||
);
|
||||
}
|
||||
if (cookies.length > 0) {
|
||||
reqHeaders.set("cookie", cookies.join("; "));
|
||||
@@ -102,7 +104,7 @@ async function createSession(
|
||||
userId: number,
|
||||
options?: { ipAddress?: string; userAgent?: string },
|
||||
): Promise<{ token: string; sessionId: number }> {
|
||||
const token = "test-session-" + String(Date.now()) + String(Math.random());
|
||||
const token = `test-session-${String(Date.now())}${String(Math.random())}`;
|
||||
const tokenHashValue = await hashToken(token);
|
||||
const expiresAt = new Date(Date.now() + SESSION_EXPIRY_MS);
|
||||
|
||||
@@ -137,6 +139,9 @@ async function createUserAPIContext(
|
||||
return { context, token };
|
||||
}
|
||||
|
||||
// Export to suppress unused warning - helper available for future tests
|
||||
void createUserAPIContext;
|
||||
|
||||
/**
|
||||
* Create a device in the database and return the fingerprint
|
||||
*/
|
||||
@@ -151,7 +156,7 @@ async function createDevice(
|
||||
): Promise<{ fingerprint: string; deviceId: number }> {
|
||||
const fingerprint =
|
||||
options?.fingerprint ??
|
||||
"test-fp-" + String(Date.now()) + String(Math.random());
|
||||
`test-fp-${String(Date.now())}${String(Math.random())}`;
|
||||
|
||||
const result = await getDb()
|
||||
.insertInto("user_devices")
|
||||
@@ -176,8 +181,7 @@ async function createDevice(
|
||||
async function createApiToken(
|
||||
userId: number,
|
||||
): Promise<{ token: string; name: string }> {
|
||||
const token =
|
||||
"test-api-token-" + String(Date.now()) + String(Math.random());
|
||||
const token = `test-api-token-${String(Date.now())}${String(Math.random())}`;
|
||||
const tokenHashValue = await hashToken(token);
|
||||
const expiresAt = new Date(Date.now() + API_TOKEN_EXPIRY_MS);
|
||||
|
||||
@@ -633,6 +637,7 @@ describe("me.setPassword", () => {
|
||||
// Password must be at least 8 chars to pass schema validation
|
||||
// "password" passes length check but fails zxcvbn strength check
|
||||
// zxcvbn provides feedback like "This is a top-10 common password"
|
||||
|
||||
await expect(
|
||||
call(
|
||||
router.me.setPassword,
|
||||
@@ -741,10 +746,10 @@ describe("me.sessions.list", () => {
|
||||
});
|
||||
|
||||
// Create multiple sessions
|
||||
const { token: sessionToken1, sessionId: id1 } = await createSession(
|
||||
user.id,
|
||||
{ ipAddress: "192.168.1.1", userAgent: "Chrome/1.0" },
|
||||
);
|
||||
const { token: sessionToken1 } = await createSession(user.id, {
|
||||
ipAddress: "192.168.1.1",
|
||||
userAgent: "Chrome/1.0",
|
||||
});
|
||||
await createSession(user.id, {
|
||||
ipAddress: "192.168.1.2",
|
||||
userAgent: "Firefox/1.0",
|
||||
@@ -838,7 +843,11 @@ describe("me.sessions.revoke", () => {
|
||||
const { sessionId: sessionId2 } = await createSession(user.id);
|
||||
|
||||
const context = createAPIContext({ sessionToken: sessionToken1 });
|
||||
await call(router.me.sessions.revoke, { sessionId: sessionId2 }, { context });
|
||||
await call(
|
||||
router.me.sessions.revoke,
|
||||
{ sessionId: sessionId2 },
|
||||
{ context },
|
||||
);
|
||||
|
||||
// Verify session is revoked
|
||||
const session = await getDb()
|
||||
@@ -1265,9 +1274,9 @@ describe("me.devices.revokeAll", () => {
|
||||
email: "revokealldevices@example.com",
|
||||
});
|
||||
|
||||
const { deviceId: id1 } = await createDevice(user.id, { isTrusted: true });
|
||||
const { deviceId: id2 } = await createDevice(user.id, { isTrusted: true });
|
||||
const { deviceId: id3 } = await createDevice(user.id, { isTrusted: false });
|
||||
await createDevice(user.id, { isTrusted: true });
|
||||
await createDevice(user.id, { isTrusted: true });
|
||||
await createDevice(user.id, { isTrusted: false });
|
||||
|
||||
const { token: sessionToken } = await createSession(user.id);
|
||||
const context = createAPIContext({ sessionToken });
|
||||
@@ -1282,7 +1291,7 @@ describe("me.devices.revokeAll", () => {
|
||||
.execute();
|
||||
|
||||
expect(devices).toHaveLength(3);
|
||||
expect(devices.every((d) => d.is_trusted === false)).toBe(true);
|
||||
expect(devices.every((d) => !d.is_trusted)).toBe(true);
|
||||
});
|
||||
|
||||
test("works when no devices exist", async () => {
|
||||
|
||||
Reference in New Issue
Block a user