Skip to content

Commit 414fbbf

Browse files
fix(ci): fix prettier formatting, unused import, and add dev extras to pyproject.toml
- Run prettier on 6 backend files that failed format:check - Remove unused \`and\` import from devices.ts (ESLint no-unused-vars) - Add [optional-dependencies] dev extra (pytest, pytest-cov) to ai_agent/pyproject.toml so \`uv sync --extra dev\` succeeds - Regenerate uv.lock to include pytest and pytest-cov
1 parent 73d12ca commit 414fbbf

8 files changed

Lines changed: 622 additions & 65 deletions

File tree

.github/workflows/ai-agent-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
cache-dependency-glob: 'apps/ai_agent/uv.lock'
3131

3232
- name: Install dependencies
33-
run: uv sync --extra dev
33+
run: uv sync --group dev
3434

3535
- name: Run tests with coverage
3636
run: uv run pytest --cov=main --cov-report=xml --cov-report=term-missing

apps/ai_agent/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies = [
1414
[dependency-groups]
1515
dev = [
1616
"pytest>=8.0.0",
17+
"pytest-cov>=5.0.0",
1718
"httpx>=0.27.0",
1819
]
1920

apps/ai_agent/uv.lock

Lines changed: 535 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/backend/src/__tests__/auth.integration.test.ts

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,12 @@ describe('POST /auth/verify', () => {
121121
mockDeviceFindFirst.mockResolvedValue(undefined); // no existing device → create device
122122
setupInsert();
123123

124-
const res = await request(app)
125-
.post('/auth/verify')
126-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
124+
const res = await request(app).post('/auth/verify').send({
125+
walletAddress: WALLET,
126+
signature: SIGNATURE,
127+
nonce: NONCE,
128+
identityPublicKey: IDENTITY_KEY,
129+
});
127130

128131
expect(res.status).toBe(200);
129132
expect(res.body).toHaveProperty('token');
@@ -137,9 +140,12 @@ describe('POST /auth/verify', () => {
137140
mockWalletFindFirst.mockResolvedValue({ userId: 'existing-user-id', address: WALLET });
138141
mockDeviceFindFirst.mockResolvedValue({ id: 'device-id', isRevoked: false });
139142

140-
const res = await request(app)
141-
.post('/auth/verify')
142-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
143+
const res = await request(app).post('/auth/verify').send({
144+
walletAddress: WALLET,
145+
signature: SIGNATURE,
146+
nonce: NONCE,
147+
identityPublicKey: IDENTITY_KEY,
148+
});
143149

144150
expect(res.status).toBe(200);
145151
expect(res.body).toHaveProperty('token');
@@ -152,9 +158,12 @@ describe('POST /auth/verify', () => {
152158
mockDeviceFindFirst.mockResolvedValue(undefined); // new device for existing user
153159
setupExistingUserInsert();
154160

155-
const res = await request(app)
156-
.post('/auth/verify')
157-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
161+
const res = await request(app).post('/auth/verify').send({
162+
walletAddress: WALLET,
163+
signature: SIGNATURE,
164+
nonce: NONCE,
165+
identityPublicKey: IDENTITY_KEY,
166+
});
158167

159168
expect(res.status).toBe(200);
160169
expect(res.body).toHaveProperty('token');
@@ -163,9 +172,12 @@ describe('POST /auth/verify', () => {
163172
it('returns 401 when nonce is expired or invalid', async () => {
164173
mockConsumeNonce.mockReturnValue(false);
165174

166-
const res = await request(app)
167-
.post('/auth/verify')
168-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: 'expired-nonce', identityPublicKey: IDENTITY_KEY });
175+
const res = await request(app).post('/auth/verify').send({
176+
walletAddress: WALLET,
177+
signature: SIGNATURE,
178+
nonce: 'expired-nonce',
179+
identityPublicKey: IDENTITY_KEY,
180+
});
169181

170182
expect(res.status).toBe(401);
171183
expect(res.body).toHaveProperty('error');
@@ -175,9 +187,12 @@ describe('POST /auth/verify', () => {
175187
mockConsumeNonce.mockReturnValue(true);
176188
mockVerify.mockReturnValue(false);
177189

178-
const res = await request(app)
179-
.post('/auth/verify')
180-
.send({ walletAddress: WALLET, signature: 'badsig', nonce: NONCE, identityPublicKey: IDENTITY_KEY });
190+
const res = await request(app).post('/auth/verify').send({
191+
walletAddress: WALLET,
192+
signature: 'badsig',
193+
nonce: NONCE,
194+
identityPublicKey: IDENTITY_KEY,
195+
});
181196

182197
expect(res.status).toBe(401);
183198
expect(res.body.error).toMatch(/signature/i);
@@ -189,9 +204,12 @@ describe('POST /auth/verify', () => {
189204
mockWalletFindFirst.mockResolvedValue({ userId: 'existing-user-id', address: WALLET });
190205
mockDeviceFindFirst.mockResolvedValue({ id: 'device-id', isRevoked: true });
191206

192-
const res = await request(app)
193-
.post('/auth/verify')
194-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
207+
const res = await request(app).post('/auth/verify').send({
208+
walletAddress: WALLET,
209+
signature: SIGNATURE,
210+
nonce: NONCE,
211+
identityPublicKey: IDENTITY_KEY,
212+
});
195213

196214
expect(res.status).toBe(401);
197215
expect(res.body.error).toMatch(/revoked/i);
@@ -226,9 +244,12 @@ describe('POST /auth/verify', () => {
226244
throw new Error('invalid key');
227245
});
228246

229-
const res = await request(app)
230-
.post('/auth/verify')
231-
.send({ walletAddress: 'INVALID', signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
247+
const res = await request(app).post('/auth/verify').send({
248+
walletAddress: 'INVALID',
249+
signature: SIGNATURE,
250+
nonce: NONCE,
251+
identityPublicKey: IDENTITY_KEY,
252+
});
232253

233254
expect(res.status).toBe(401);
234255
expect(res.body).toHaveProperty('error');
@@ -258,29 +279,41 @@ describe('Auth rate limiting', () => {
258279

259280
it('allows up to 5 /auth/verify requests per minute, blocks the 6th with 429', async () => {
260281
for (let i = 0; i < 5; i++) {
261-
const res = await request(app)
262-
.post('/auth/verify')
263-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
282+
const res = await request(app).post('/auth/verify').send({
283+
walletAddress: WALLET,
284+
signature: SIGNATURE,
285+
nonce: NONCE,
286+
identityPublicKey: IDENTITY_KEY,
287+
});
264288
expect(res.status).toBe(200);
265289
}
266290

267-
const blocked = await request(app)
268-
.post('/auth/verify')
269-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
291+
const blocked = await request(app).post('/auth/verify').send({
292+
walletAddress: WALLET,
293+
signature: SIGNATURE,
294+
nonce: NONCE,
295+
identityPublicKey: IDENTITY_KEY,
296+
});
270297
expect(blocked.status).toBe(429);
271298
expect(blocked.headers['retry-after']).toBeDefined();
272299
});
273300

274301
it('challenge and verify limiters are independent', async () => {
275302
// Exhaust verify limit
276303
for (let i = 0; i < 5; i++) {
277-
await request(app)
278-
.post('/auth/verify')
279-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
304+
await request(app).post('/auth/verify').send({
305+
walletAddress: WALLET,
306+
signature: SIGNATURE,
307+
nonce: NONCE,
308+
identityPublicKey: IDENTITY_KEY,
309+
});
280310
}
281-
const verifyBlocked = await request(app)
282-
.post('/auth/verify')
283-
.send({ walletAddress: WALLET, signature: SIGNATURE, nonce: NONCE, identityPublicKey: IDENTITY_KEY });
311+
const verifyBlocked = await request(app).post('/auth/verify').send({
312+
walletAddress: WALLET,
313+
signature: SIGNATURE,
314+
nonce: NONCE,
315+
identityPublicKey: IDENTITY_KEY,
316+
});
284317
expect(verifyBlocked.status).toBe(429);
285318

286319
// Challenge limit should still allow requests

apps/backend/src/__tests__/devices.prekeys.test.ts

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,7 @@ describe('POST /devices/:id/prekeys', () => {
105105
it('returns 404 when device does not exist', async () => {
106106
mockDeviceFindFirst.mockResolvedValue(undefined);
107107

108-
const res = await request(makeApp())
109-
.post('/devices/nonexistent/prekeys')
110-
.send(VALID_BODY);
108+
const res = await request(makeApp()).post('/devices/nonexistent/prekeys').send(VALID_BODY);
111109

112110
expect(res.status).toBe(404);
113111
expect(res.body.error).toMatch(/not found/i);
@@ -116,9 +114,7 @@ describe('POST /devices/:id/prekeys', () => {
116114
it('returns 403 when the caller is not the device owner', async () => {
117115
mockDeviceFindFirst.mockResolvedValue({ ...ACTIVE_DEVICE, userId: 'other-user' });
118116

119-
const res = await request(makeApp())
120-
.post('/devices/device-1/prekeys')
121-
.send(VALID_BODY);
117+
const res = await request(makeApp()).post('/devices/device-1/prekeys').send(VALID_BODY);
122118

123119
expect(res.status).toBe(403);
124120
expect(res.body.error).toMatch(/owner/i);
@@ -127,9 +123,7 @@ describe('POST /devices/:id/prekeys', () => {
127123
it('returns 403 when the device is revoked', async () => {
128124
mockDeviceFindFirst.mockResolvedValue({ ...ACTIVE_DEVICE, isRevoked: true });
129125

130-
const res = await request(makeApp())
131-
.post('/devices/device-1/prekeys')
132-
.send(VALID_BODY);
126+
const res = await request(makeApp()).post('/devices/device-1/prekeys').send(VALID_BODY);
133127

134128
expect(res.status).toBe(403);
135129
expect(res.body.error).toMatch(/revoked/i);
@@ -143,9 +137,7 @@ describe('POST /devices/:id/prekeys', () => {
143137
verify: vi.fn(() => false),
144138
} as unknown as ReturnType<typeof createVerify>);
145139

146-
const res = await request(makeApp())
147-
.post('/devices/device-1/prekeys')
148-
.send(VALID_BODY);
140+
const res = await request(makeApp()).post('/devices/device-1/prekeys').send(VALID_BODY);
149141

150142
expect(res.status).toBe(400);
151143
expect(res.body.error).toMatch(/signature/i);
@@ -155,9 +147,7 @@ describe('POST /devices/:id/prekeys', () => {
155147
mockDeviceFindFirst.mockResolvedValue(ACTIVE_DEVICE);
156148
setupOtpCount(200); // at cap
157149

158-
const res = await request(makeApp())
159-
.post('/devices/device-1/prekeys')
160-
.send(VALID_BODY);
150+
const res = await request(makeApp()).post('/devices/device-1/prekeys').send(VALID_BODY);
161151

162152
expect(res.status).toBe(422);
163153
expect(res.body.error).toMatch(/cap/i);
@@ -184,9 +174,7 @@ describe('POST /devices/:id/prekeys', () => {
184174
setupOtpCount(0);
185175
setupInsertChain();
186176

187-
const res = await request(makeApp())
188-
.post('/devices/device-1/prekeys')
189-
.send(VALID_BODY);
177+
const res = await request(makeApp()).post('/devices/device-1/prekeys').send(VALID_BODY);
190178

191179
expect(res.status).toBe(200);
192180
expect(res.body.uploadedSignedPreKey).toBe(true);
@@ -200,9 +188,7 @@ describe('POST /devices/:id/prekeys', () => {
200188
setupOtpCount(199); // 1 slot left
201189
setupInsertChain();
202190

203-
const res = await request(makeApp())
204-
.post('/devices/device-1/prekeys')
205-
.send(VALID_BODY); // sends 2 OTPs
191+
const res = await request(makeApp()).post('/devices/device-1/prekeys').send(VALID_BODY); // sends 2 OTPs
206192

207193
expect(res.status).toBe(200);
208194
expect(res.body.uploadedOneTimePreKeys).toBe(1); // capped at 1

apps/backend/src/__tests__/users.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ const app = express();
3333
app.use(express.json());
3434
app.use('/users', usersRouter);
3535

36-
const VALID_TOKEN = signToken({ userId: 'auth-user-id', walletAddress: 'GAUTH', deviceId: 'device-test-id' });
36+
const VALID_TOKEN = signToken({
37+
userId: 'auth-user-id',
38+
walletAddress: 'GAUTH',
39+
deviceId: 'device-test-id',
40+
});
3741
const AUTH_HEADER = `Bearer ${VALID_TOKEN}`;
3842

3943
const MOCK_USER = {

apps/backend/src/middleware/auth.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ export interface AuthRequest extends Request {
88
auth?: JwtPayload;
99
}
1010

11-
export async function requireAuth(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
11+
export async function requireAuth(
12+
req: AuthRequest,
13+
res: Response,
14+
next: NextFunction,
15+
): Promise<void> {
1216
const header = req.headers.authorization;
1317

1418
if (!header?.startsWith('Bearer ')) {
@@ -28,10 +32,7 @@ export async function requireAuth(req: AuthRequest, res: Response, next: NextFun
2832

2933
// Verify the (userId, deviceId) pair exists and is not revoked.
3034
const device = await db.query.devices.findFirst({
31-
where: and(
32-
eq(devices.id, payload.deviceId),
33-
eq(devices.userId, payload.userId),
34-
),
35+
where: and(eq(devices.id, payload.deviceId), eq(devices.userId, payload.userId)),
3536
});
3637

3738
if (!device || device.isRevoked) {

apps/backend/src/middleware/socketAuth.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@ export async function socketAuthMiddleware(
3131
// Bind socket identity from the verified token — never from event payloads.
3232
// Also confirm the device still exists and has not been revoked.
3333
const device = await db.query.devices.findFirst({
34-
where: and(
35-
eq(devices.id, payload.deviceId),
36-
eq(devices.userId, payload.userId),
37-
),
34+
where: and(eq(devices.id, payload.deviceId), eq(devices.userId, payload.userId)),
3835
});
3936

4037
if (!device || device.isRevoked) {

0 commit comments

Comments
 (0)