Skip to content

Commit cd7837d

Browse files
test(api): align error envelope assertions
1 parent 0b56eb0 commit cd7837d

6 files changed

Lines changed: 37 additions & 26 deletions

File tree

backend/swagger/flowfi.openapi.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,10 @@
829829
"properties": {
830830
"error": {
831831
"type": "object",
832-
"required": ["code", "message"],
832+
"required": [
833+
"code",
834+
"message"
835+
],
833836
"properties": {
834837
"code": {
835838
"type": "string",
@@ -849,7 +852,9 @@
849852
}
850853
}
851854
},
852-
"required": ["error"]
855+
"required": [
856+
"error"
857+
]
853858
}
854859
}
855860
},

backend/tests/integration/streams/cancel.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ describe('POST /v1/streams/:streamId/cancel', () => {
108108
.set('Authorization', 'Bearer dummy_token');
109109

110110
expect(res.status).toBe(400);
111-
expect(res.body.message).toContain('senderSecret');
111+
expect(res.body.error.message).toContain('senderSecret');
112112
expect(sorobanService.cancelStream).not.toHaveBeenCalled();
113113
});
114114

@@ -127,7 +127,10 @@ describe('POST /v1/streams/:streamId/cancel', () => {
127127
.set('Authorization', 'Bearer dummy_token');
128128

129129
expect(res.status).toBe(403);
130-
expect(res.body.error).toBe('Forbidden');
130+
expect(res.body.error).toMatchObject({
131+
code: 'FORBIDDEN',
132+
message: 'Only the sender can cancel the stream',
133+
});
131134
expect(sorobanService.cancelStream).not.toHaveBeenCalled();
132135
});
133136

@@ -158,7 +161,7 @@ describe('POST /v1/streams/:streamId/cancel', () => {
158161
.set('Authorization', 'Bearer dummy_token');
159162

160163
expect(res.status).toBe(409);
161-
expect(res.body.message).toContain('already cancelled');
164+
expect(res.body.error.message).toContain('already cancelled');
162165
});
163166

164167
it('handles concurrent cancel requests correctly', async () => {

backend/tests/integration/streams/withdraw.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ describe('POST /api/v1/streams/:streamId/withdraw', () => {
150150
.set('Authorization', `Bearer ${token}`);
151151

152152
expect(response.status).toBe(403);
153-
expect(response.body.error).toBe('Forbidden');
153+
expect(response.body.error).toMatchObject({
154+
code: 'FORBIDDEN',
155+
message: 'Only the stream recipient can withdraw from the stream',
156+
});
154157
});
155158

156159
it('returns 404 if stream not found', async () => {
@@ -164,7 +167,10 @@ describe('POST /api/v1/streams/:streamId/withdraw', () => {
164167
.set('Authorization', `Bearer ${token}`);
165168

166169
expect(response.status).toBe(404);
167-
expect(response.body.error).toBe('Stream not found');
170+
expect(response.body.error).toMatchObject({
171+
code: 'NOT_FOUND',
172+
message: 'Stream not found',
173+
});
168174
});
169175

170176
it('returns 409 if no claimable balance available', async () => {
@@ -192,7 +198,7 @@ describe('POST /api/v1/streams/:streamId/withdraw', () => {
192198
.set('Authorization', `Bearer ${token}`);
193199

194200
expect(response.status).toBe(409);
195-
expect(response.body.message).toBe('No claimable balance is currently available');
201+
expect(response.body.error.message).toBe('No claimable balance is currently available');
196202
});
197203

198204
it('does not double-count withdrawnAmount when the same claim window is withdrawn twice in a row', async () => {
@@ -291,7 +297,7 @@ describe('POST /api/v1/streams/:streamId/withdraw', () => {
291297
// lastUpdateTime, so the second call correctly finds nothing left to
292298
// claim in this window and is rejected.
293299
expect(second.status).toBe(409);
294-
expect(second.body.message).toBe('No claimable balance is currently available');
300+
expect(second.body.error.message).toBe('No claimable balance is currently available');
295301
}
296302

297303
// The critical assertion: withdrawnAmount reflects only the ONE

backend/tests/integration/top-up.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ describe('POST /v1/streams/:streamId/top-up', () => {
182182
.send({ amount: '1000' });
183183

184184
expect(res.status).toBe(409);
185-
expect(res.body.message).toMatch(/inactive stream/);
185+
expect(res.body.error.message).toMatch(/inactive stream/);
186186
});
187187

188188
it('returns 409 when stream is paused', async () => {
@@ -194,7 +194,7 @@ describe('POST /v1/streams/:streamId/top-up', () => {
194194
.send({ amount: '1000' });
195195

196196
expect(res.status).toBe(409);
197-
expect(res.body.message).toMatch(/paused stream/);
197+
expect(res.body.error.message).toMatch(/paused stream/);
198198
});
199199

200200
it('leaves DB unchanged when topUpStream fails on-chain', async () => {

backend/tests/stream.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,10 @@ describe('POST /v1/streams', () => {
159159
.set('Accept', 'application/json');
160160

161161
expect(response.status).toBe(403);
162-
expect(response.body).toHaveProperty('error', 'Forbidden');
162+
expect(response.body.error).toMatchObject({
163+
code: 'FORBIDDEN',
164+
message: 'sender must match the authenticated wallet',
165+
});
163166
expect(prisma.stream.upsert).not.toHaveBeenCalled();
164167
});
165168

frontend/src/lib/api-types.generated.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2801,20 +2801,14 @@ export interface components {
28012801
};
28022802
};
28032803
Error: {
2804-
/**
2805-
* @description Error message
2806-
* @example Resource not found
2807-
*/
2808-
error?: string;
2809-
/**
2810-
* @description Error code
2811-
* @example NOT_FOUND
2812-
*/
2813-
code?: string;
2814-
/** @description Human-readable detail (present on many error responses) */
2815-
message?: string | null;
2816-
/** @description Structured validation issues (zod) when the error is a 400 */
2817-
details?: Record<string, never>[] | null;
2804+
error: {
2805+
/** @example NOT_FOUND */
2806+
code: string;
2807+
/** @example Resource not found */
2808+
message: string;
2809+
/** @description Structured validation issues when applicable */
2810+
details?: Record<string, never>[];
2811+
};
28182812
};
28192813
};
28202814
responses: never;

0 commit comments

Comments
 (0)