Skip to content

Commit 3d3afaf

Browse files
authored
Merge pull request #703 from Jagadeeshftw/test/683-auth-lifecycle
test: cover full wallet auth lifecycle
2 parents a075af9 + 88ef8c2 commit 3d3afaf

2 files changed

Lines changed: 67 additions & 2 deletions

File tree

docs/auth/session.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ runtime behavior, tests, and this doc aligned — see
88
[src/auth/session.test.ts](src/auth/session.test.ts) for the tests that pin
99
down every rule below.
1010

11+
The cross-module wallet flow is covered by
12+
[`src/routes/auth.test.ts`](../../src/routes/auth.test.ts): it drives the real
13+
Express app through challenge, signed verification, bearer logout, replay,
14+
expiry, and post-revocation rejection. The test uses a deterministic signer
15+
and replaces only the external RPC/database boundaries.
16+
1117
## Session authorization contract & security boundaries
1218

1319
### Caller roles & authorization requirements

src/routes/auth.test.ts

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
22
import request from "supertest";
33
import express from "express";
44
import crypto from "node:crypto";
5+
import { Account, type TypedData } from "starknet";
56

67
const { dbMock, schemaMock, mockState, eqMock, orMock, ltMock, isNotNullMock, mockProvider } = vi.hoisted(() => {
78
const mockState = {
@@ -96,7 +97,14 @@ const { dbMock, schemaMock, mockState, eqMock, orMock, ltMock, isNotNullMock, mo
9697
return { dbMock: db, schemaMock: schema, mockState, eqMock, orMock, ltMock, isNotNullMock, mockProvider };
9798
});
9899

99-
vi.mock("../db/index.js", () => ({ db: dbMock, schema: schemaMock }));
100+
vi.mock("../db/index.js", () => ({
101+
db: dbMock,
102+
schema: schemaMock,
103+
checkDbHealth: vi.fn().mockResolvedValue(true),
104+
getPoolStats: vi.fn().mockReturnValue({ total: 0, idle: 0, active: 0, waiting: 0 }),
105+
closePool: vi.fn().mockResolvedValue(undefined),
106+
waitForDbReadiness: vi.fn().mockResolvedValue(undefined),
107+
}));
100108
vi.mock("../db/schema.js", () => ({ sessions: schemaMock.sessions }));
101109
vi.mock("drizzle-orm", () => ({
102110
eq: eqMock,
@@ -123,6 +131,11 @@ vi.mock("../config.js", () => ({
123131
vi.mock("../starknet/client.js", () => ({
124132
provider: mockProvider,
125133
getCachedNetworkInfo: vi.fn().mockResolvedValue({ chainId: "0x534e5f5345504f4c4941" }),
134+
getEscrowAbi: vi.fn().mockReturnValue([]),
135+
getAgreementAbi: vi.fn().mockReturnValue([]),
136+
getCircuitBreakerSnapshots: vi.fn().mockReturnValue([]),
137+
agreementContract: vi.fn(),
138+
escrowContract: vi.fn(),
126139
}));
127140

128141
import { authRouter, rebuildAdminSet } from "./auth.js";
@@ -133,6 +146,7 @@ import {
133146
} from "./auth-metrics.js";
134147
import { lockouts } from "../auth/lockout.js";
135148
import { clearChallengesForTesting } from "../auth/challenge.js";
149+
import { app } from "../index.js";
136150

137151
/**
138152
* Issue #193: locks the authorization contract for every route on this
@@ -344,6 +358,52 @@ describe("Auth Routes Integration", () => {
344358
expect(logoutPostLogoutRes.status).toBe(401);
345359
});
346360

361+
it("covers the wallet lifecycle through the real app with signed and invalidated requests", async () => {
362+
const address = "0x123456789abcdef";
363+
const signer = new Account({ provider: mockProvider as any, address, signer: "0x1" });
364+
365+
const challenge = await request(app)
366+
.post("/api/v1/auth/challenge")
367+
.send({ address })
368+
.expect(200);
369+
const signature = await signer.signMessage(challenge.body.typed_data as TypedData);
370+
mockProvider.verifyMessageInStarknet.mockResolvedValue(true);
371+
372+
const verified = await request(app)
373+
.post("/api/v1/auth/verify")
374+
.send({ address, signature: [signature.r.toString(), signature.s.toString()] })
375+
.expect(200);
376+
const token = verified.body.session_token as string;
377+
378+
await request(app)
379+
.post("/api/v1/auth/logout")
380+
.set("x-user-address", address)
381+
.set("authorization", `Bearer ${token}`)
382+
.expect(200);
383+
384+
await request(app)
385+
.post("/api/v1/auth/logout")
386+
.set("x-user-address", address)
387+
.set("authorization", `Bearer ${token}`)
388+
.expect(401);
389+
390+
await request(app)
391+
.post("/api/v1/auth/verify")
392+
.send({ address, signature: [signature.r.toString(), signature.s.toString()] })
393+
.expect(400);
394+
395+
const expiredAddress = "0x123456789abcdf0";
396+
await request(app)
397+
.post("/api/v1/auth/challenge")
398+
.send({ address: expiredAddress })
399+
.expect(200);
400+
vi.advanceTimersByTime(300_001);
401+
await request(app)
402+
.post("/api/v1/auth/verify")
403+
.send({ address: expiredAddress, signature: ["0x1", "0x2"] })
404+
.expect(400);
405+
});
406+
347407
it("rejects verify once the challenge TTL has elapsed", async () => {
348408
const address = "0xeeee000000000001";
349409
const appInstance = makeApp();
@@ -997,7 +1057,6 @@ describe("Auth Routes Integration", () => {
9971057
});
9981058
});
9991059
});
1000-
});
10011060

10021061
// ---------------------------------------------------------------------------
10031062
// rebuildAdminSet / isAdminAddress — pre-built Set contract

0 commit comments

Comments
 (0)