From cfd158aef0025b9d5960f8627add51086da479fe Mon Sep 17 00:00:00 2001 From: "Ikenga Ifeanyi .M." Date: Wed, 1 Jul 2026 05:58:48 +0100 Subject: [PATCH] Reject top-ups for paused or inactive streams --- backend/src/controllers/stream.controller.ts | 14 ++ backend/tests/integration/top-up.test.ts | 16 +++ package-lock.json | 129 ++++++------------- 3 files changed, 67 insertions(+), 92 deletions(-) diff --git a/backend/src/controllers/stream.controller.ts b/backend/src/controllers/stream.controller.ts index 5476fb6e..a5bb3a6d 100644 --- a/backend/src/controllers/stream.controller.ts +++ b/backend/src/controllers/stream.controller.ts @@ -552,6 +552,20 @@ export const topUpStreamHandler = async (req: Request, res: Response) => { return res.status(403).json({ error: 'Only the stream sender may top up this stream' }); } + if (stream.isPaused) { + return res.status(409).json({ + error: 'Conflict', + message: 'Cannot top up a paused stream', + }); + } + + if (!stream.isActive) { + return res.status(409).json({ + error: 'Conflict', + message: 'Cannot top up an inactive stream', + }); + } + const txHash = await topUpStream(streamId, amount, callerAddress); const newDeposited = (BigInt(stream.depositedAmount) + amount).toString(); diff --git a/backend/tests/integration/top-up.test.ts b/backend/tests/integration/top-up.test.ts index e6549e8f..0c1f503a 100644 --- a/backend/tests/integration/top-up.test.ts +++ b/backend/tests/integration/top-up.test.ts @@ -148,6 +148,22 @@ describe('POST /v1/streams/:streamId/top-up', () => { expect(res.status).toBe(403); }); + it('returns 409 when stream is inactive', async () => { + vi.mocked(mockPrisma.stream.findUnique).mockResolvedValue({ + ...mockStream, + isActive: false, + isPaused: false, + } as any); + + const res = await request(app) + .post('/v1/streams/42/top-up') + .set('Authorization', 'Bearer dummy') + .send({ amount: '1000' }); + + expect(res.status).toBe(409); + expect(topUpStream).not.toHaveBeenCalled(); + }); + it('updates depositedAmount in DB on success', async () => { await request(app) .post('/v1/streams/42/top-up') diff --git a/package-lock.json b/package-lock.json index 8437f8e7..ea26efbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -148,7 +148,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -316,6 +315,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -417,6 +417,7 @@ "version": "7.29.7", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -526,7 +527,7 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -534,7 +535,7 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -562,7 +563,7 @@ }, "node_modules/@babel/parser": { "version": "7.29.7", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -614,7 +615,7 @@ }, "node_modules/@babel/types": { "version": "7.29.7", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -736,6 +737,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -780,6 +782,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -798,7 +801,8 @@ "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.4.1.tgz", "integrity": "sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q==", "devOptional": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/@electric-sql/pglite-socket": { "version": "0.1.1", @@ -823,31 +827,6 @@ "@electric-sql/pglite": "0.4.1" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -855,7 +834,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -1645,7 +1623,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@tybys/wasm-util": "^0.10.1" }, @@ -1879,7 +1856,6 @@ "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/Boshen" } @@ -2295,7 +2271,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2313,7 +2288,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2331,7 +2305,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2349,7 +2322,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2367,7 +2339,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2385,7 +2356,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2403,7 +2373,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2421,7 +2390,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2439,7 +2407,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2457,7 +2424,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2475,7 +2441,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2493,7 +2458,6 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2508,7 +2472,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", @@ -2531,7 +2494,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2549,7 +2511,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -3218,27 +3179,6 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "dev": true, @@ -3362,6 +3302,7 @@ "version": "10.4.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -3464,7 +3405,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -3559,6 +3499,7 @@ "node_modules/@types/node": { "version": "20.19.33", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3594,14 +3535,16 @@ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { "version": "19.2.3", - "devOptional": true, + "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3713,6 +3656,7 @@ "version": "8.60.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/types": "8.60.1", @@ -4104,6 +4048,7 @@ "version": "8.16.0", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4624,6 +4569,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -5164,7 +5110,8 @@ }, "node_modules/csstype": { "version": "3.2.3", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -5690,6 +5637,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -6143,6 +6091,7 @@ "version": "9.39.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6314,6 +6263,7 @@ "version": "2.32.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6570,6 +6520,7 @@ "node_modules/express": { "version": "5.2.1", "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -7407,6 +7358,7 @@ "integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==", "devOptional": true, "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -8774,7 +8726,7 @@ }, "node_modules/magicast": { "version": "0.3.5", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.25.4", @@ -9573,6 +9525,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.13.0", "pg-pool": "^3.14.0", @@ -9811,6 +9764,7 @@ "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@prisma/config": "7.8.0", "@prisma/dev": "0.24.3", @@ -9999,6 +9953,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10008,6 +9963,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -10244,7 +10200,6 @@ "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" @@ -10278,8 +10233,7 @@ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/rollup": { "version": "4.62.0", @@ -11445,6 +11399,7 @@ "version": "4.0.4", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11648,6 +11603,7 @@ "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.28.0" }, @@ -11761,6 +11717,7 @@ "version": "5.9.3", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11937,6 +11894,7 @@ "version": "5.4.21", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -12058,6 +12016,7 @@ "version": "2.1.9", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "2.1.9", "@vitest/mocker": "2.1.9", @@ -12481,21 +12440,6 @@ "dev": true, "license": "ISC" }, - "node_modules/yaml": { - "version": "2.9.0", - "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/yn": { "version": "3.1.1", "dev": true, @@ -12531,6 +12475,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }