@@ -16,16 +16,12 @@ jest.mock("../services/profileQueue", () => ({
1616 enqueueProfileUpdate : jest . fn ( ) . mockResolvedValue ( undefined ) ,
1717} ) ) ;
1818
19- jest . mock ( "../services/pushQueue" , ( ) => ( {
20- enqueuePushNotification : jest . fn ( ) . mockResolvedValue ( undefined ) ,
21- } ) ) ;
22-
2319const { server } = require ( "../services/stellar" ) ;
2420const pool = require ( "../db/pool" ) ;
2521const { computeBadges } = require ( "../services/store" ) ;
2622const { enqueueProfileUpdate } = require ( "../services/profileQueue" ) ;
27- const { recordDonation } = require ( "./donations" ) ;
2823const { AppError } = require ( "../errors" ) ;
24+ const { recordDonation } = require ( "./donations" ) ;
2925
3026function makePublicKey ( char = "A" ) {
3127 return `G${ char . repeat ( 55 ) } ` ;
@@ -73,17 +69,20 @@ function createMockResponse() {
7369 } ;
7470}
7571
76- async function invokeRecordDonation ( body , headers = { } ) {
77- const req = { body, headers } ;
72+ const STATUS_FALLBACK_CODE = { 400 : "VALIDATION_ERROR" , 404 : "NOT_FOUND" , 409 : "DUPLICATE_DONATION" , 413 : "FILE_TOO_LARGE" , 422 : "SCHEMA_VALIDATION_ERROR" , 429 : "RATE_LIMITED" } ;
73+
74+ async function invokeRecordDonation ( body ) {
75+ const req = { body } ;
7876 const res = createMockResponse ( ) ;
7977 const next = jest . fn ( ( err ) => {
8078 if ( err ) {
8179 if ( err instanceof AppError ) {
8280 res . status ( err . status ) . json ( err . toJSON ( ) ) ;
81+ } else if ( err . status && err . status < 500 ) {
82+ const code = STATUS_FALLBACK_CODE [ err . status ] || "VALIDATION_ERROR" ;
83+ res . status ( err . status ) . json ( { error : { code, message : err . message } } ) ;
8384 } else {
84- res
85- . status ( err . status || 500 )
86- . json ( { error : err . message || "Internal server error" } ) ;
85+ res . status ( 500 ) . json ( { error : { code : "INTERNAL_ERROR" , message : "Internal server error" } } ) ;
8786 }
8887 }
8988 } ) ;
@@ -221,7 +220,7 @@ describe("POST /api/donations", () => {
221220
222221 expect ( next ) . toHaveBeenCalledTimes ( 1 ) ;
223222 expect ( res . statusCode ) . toBe ( 404 ) ;
224- expect ( res . body . error . code ) . toBe ( "PROJECT_NOT_FOUND" ) ;
223+ expect ( res . body . error ) . toEqual ( { code : "PROJECT_NOT_FOUND" , message : "Project not found" } ) ;
225224 expect ( client . release ) . toHaveBeenCalledTimes ( 1 ) ;
226225 } ) ;
227226
@@ -235,7 +234,7 @@ describe("POST /api/donations", () => {
235234
236235 expect ( next ) . toHaveBeenCalledTimes ( 1 ) ;
237236 expect ( res . statusCode ) . toBe ( 400 ) ;
238- expect ( res . body . error . code ) . toBe ( "INVALID_ADDRESS" ) ;
237+ expect ( res . body . error ) . toEqual ( { code : "INVALID_ADDRESS" , message : "Invalid Stellar address" } ) ;
239238 expect ( pool . connect ) . not . toHaveBeenCalled ( ) ;
240239 } ) ;
241240
@@ -249,7 +248,7 @@ describe("POST /api/donations", () => {
249248
250249 expect ( next ) . toHaveBeenCalledTimes ( 1 ) ;
251250 expect ( res . statusCode ) . toBe ( 400 ) ;
252- expect ( res . body . error . code ) . toBe ( "INVALID_TX_HASH" ) ;
251+ expect ( res . body . error ) . toEqual ( { code : "INVALID_TX_HASH" , message : "Invalid transaction hash" } ) ;
253252 expect ( pool . connect ) . not . toHaveBeenCalled ( ) ;
254253 } ) ;
255254
@@ -332,7 +331,7 @@ describe("POST /api/donations", () => {
332331
333332 test ( "calculates badges from cumulative donations across multiple requests" , async ( ) => {
334333 const donorAddress = makePublicKey ( "F" ) ;
335- createMockClient (
334+ void createMockClient (
336335 queryResult ( [ { id : "project-3" } ] ) , // SELECT project
337336 queryResult ( [ ] ) , // dedup check
338337 queryResult ( ) , // BEGIN
@@ -382,7 +381,7 @@ describe("POST /api/donations", () => {
382381
383382 expect ( next ) . toHaveBeenCalledTimes ( 1 ) ;
384383 expect ( res . statusCode ) . toBe ( 400 ) ;
385- expect ( res . body . error . code ) . toBe ( "TX_FAILED" ) ;
384+ expect ( res . body . error ) . toEqual ( { code : "TX_FAILED" , message : "Transaction failed on Stellar" } ) ;
386385 // No DB write transaction should have been opened.
387386 expect ( client . query ) . not . toHaveBeenCalledWith ( "BEGIN" ) ;
388387 expect ( client . release ) . toHaveBeenCalledTimes ( 1 ) ;
@@ -404,7 +403,7 @@ describe("POST /api/donations", () => {
404403
405404 expect ( next ) . toHaveBeenCalledTimes ( 1 ) ;
406405 expect ( res . statusCode ) . toBe ( 400 ) ;
407- expect ( res . body . error . code ) . toBe ( "TX_NOT_FOUND" ) ;
406+ expect ( res . body . error ) . toEqual ( { code : "TX_NOT_FOUND" , message : "Transaction not found on Stellar" } ) ;
408407 expect ( client . query ) . not . toHaveBeenCalledWith ( "BEGIN" ) ;
409408 expect ( client . release ) . toHaveBeenCalledTimes ( 1 ) ;
410409 } ) ;
@@ -510,7 +509,7 @@ describe("profile upsert on first donation", () => {
510509 created_at : "2026-03-29T10:00:00.000Z" ,
511510 } ;
512511
513- createMockClient (
512+ void createMockClient (
514513 queryResult ( [ { id : "project-p" } ] ) ,
515514 queryResult ( [ ] ) ,
516515 queryResult ( ) ,
@@ -549,7 +548,7 @@ describe("profile upsert on first donation", () => {
549548 created_at : "2026-03-29T10:00:00.000Z" ,
550549 } ;
551550
552- createMockClient (
551+ void createMockClient (
553552 queryResult ( [ { id : "project-q" } ] ) ,
554553 queryResult ( [ ] ) ,
555554 queryResult ( ) ,
@@ -596,7 +595,7 @@ describe("profile upsert on first donation", () => {
596595 created_at : "2026-03-29T10:00:00.000Z" ,
597596 } ;
598597
599- createMockClient (
598+ void createMockClient (
600599 queryResult ( [ { id : "project-r" } ] ) ,
601600 queryResult ( [ ] ) ,
602601 queryResult ( ) ,
0 commit comments