From 206ffe9e2e46769465aac7b56163f571c082de9d Mon Sep 17 00:00:00 2001 From: Vaibhav Chopra Date: Wed, 29 Apr 2026 13:23:05 +0530 Subject: [PATCH 1/5] test: add unit tests for utility functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tests for isNonNullObject (plain objects, null, arrays, primitives) - Add tests for isValidUrl (https, http, invalid URLs, non-string inputs) - Add tests for generateOnboardingSignature (hex output, different inputs, empty params) - Add edge case tests for validateWebhookSignature: - undefined params, Buffer body, wrong length - non-hex signature, empty signature, tampered valid-hex signature - Add error handling tests for validatePaymentVerification (missing secret, missing order_id) Coverage improvement: - razorpay-utils.js: 64% → 96.87% - Overall: 73.47% → 75.07% - Tests: 379 → 400 (+21) --- test/utils/razorpay-utils.spec.js | 184 +++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 2 deletions(-) diff --git a/test/utils/razorpay-utils.spec.js b/test/utils/razorpay-utils.spec.js index a5242a3f..f8ae428c 100644 --- a/test/utils/razorpay-utils.spec.js +++ b/test/utils/razorpay-utils.spec.js @@ -11,9 +11,12 @@ const { normalizeNotes, getDateInSecs, isDefined, + isNonNullObject, getTestError, validateWebhookSignature, - validatePaymentVerification + validatePaymentVerification, + isValidUrl, + generateOnboardingSignature } = require('../../dist/utils/razorpay-utils') describe('Razorpay Utils', () => { @@ -144,7 +147,7 @@ describe('Razorpay Utils', () => { }) it('Payment Verfication', () => { - + const respBody = { 'order_id':'order_IEIaMR65cu6nz3', 'payment_id':'pay_IH4NVgf4Dreq1l', @@ -159,4 +162,181 @@ describe('Razorpay Utils', () => { 'Validates payment' ); }) + + // New tests for isNonNullObject + describe('isNonNullObject', () => { + it('should return true for plain objects', () => { + assert.equal(isNonNullObject({}), true) + assert.equal(isNonNullObject({ key: 'value' }), true) + }) + + it('should return false for null', () => { + assert.equal(isNonNullObject(null), false) + }) + + it('should return false for arrays', () => { + assert.equal(isNonNullObject([]), false) + assert.equal(isNonNullObject([1, 2, 3]), false) + }) + + it('should return false for primitives', () => { + assert.equal(isNonNullObject(undefined), false) + assert.equal(isNonNullObject('string'), false) + assert.equal(isNonNullObject(123), false) + assert.equal(isNonNullObject(true), false) + }) + }) + + // New tests for isValidUrl + describe('isValidUrl', () => { + it('should return true for valid https URLs', () => { + assert.equal(isValidUrl('https://razorpay.com'), true) + assert.equal(isValidUrl('https://api.razorpay.com/v1/orders'), true) + }) + + it('should return true for valid http URLs', () => { + assert.equal(isValidUrl('http://localhost:3000'), true) + assert.equal(isValidUrl('http://example.com/path?query=1'), true) + }) + + it('should return false for invalid URLs', () => { + assert.equal(isValidUrl('not-a-url'), false) + assert.equal(isValidUrl(''), false) + assert.equal(isValidUrl('razorpay.com'), false) + }) + + it('should return false for non-string inputs', () => { + assert.equal(isValidUrl(null), false) + assert.equal(isValidUrl(undefined), false) + assert.equal(isValidUrl(123), false) + }) + }) + + // New tests for generateOnboardingSignature + describe('generateOnboardingSignature', () => { + it('should generate a hex string signature', () => { + const params = { key: 'value', nested: { a: 1 } } + const secret = '1234567890123456' // 16 chars for AES-128 + const result = generateOnboardingSignature(params, secret) + + assert.ok(typeof result === 'string', 'Result should be a string') + assert.ok(result.length > 0, 'Result should not be empty') + assert.ok(/^[0-9a-f]+$/.test(result), 'Result should be valid hex') + }) + + it('should produce different output for different inputs', () => { + const secret = '1234567890123456' + const sig1 = generateOnboardingSignature({ a: 1 }, secret) + const sig2 = generateOnboardingSignature({ a: 2 }, secret) + + assert.notEqual(sig1, sig2, 'Different inputs should produce different signatures') + }) + + it('should handle empty params', () => { + const secret = '1234567890123456' + const result = generateOnboardingSignature({}, secret) + + assert.ok(typeof result === 'string', 'Result should be a string') + assert.ok(result.length > 0, 'Result should not be empty') + }) + }) + + // New tests for validateWebhookSignature edge cases + describe('validateWebhookSignature - Edge Cases', () => { + it('should throw error when body is undefined', () => { + assert.throws(() => { + validateWebhookSignature(undefined, 'signature', 'secret') + }, /Invalid Parameters/) + }) + + it('should throw error when signature is undefined', () => { + assert.throws(() => { + validateWebhookSignature('body', undefined, 'secret') + }, /Invalid Parameters/) + }) + + it('should throw error when secret is undefined', () => { + assert.throws(() => { + validateWebhookSignature('body', 'signature', undefined) + }, /Invalid Parameters/) + }) + + it('should handle Buffer body input', () => { + const body = Buffer.from('{"test":"data"}') + const secret = 'test_secret' + const crypto = require('crypto') + const expectedSig = crypto.createHmac('sha256', secret) + .update(body.toString()) + .digest('hex') + + assert.equal( + validateWebhookSignature(body, expectedSig, secret), + true, + 'Should validate Buffer body correctly' + ) + }) + + it('should return false for wrong length signature', () => { + const body = '{"test":"data"}' + const secret = 'test_secret' + const shortSig = 'abc123' + + assert.equal( + validateWebhookSignature(body, shortSig, secret), + false, + 'Should reject wrong length signature' + ) + }) + + it('should return false for non-hex signature', () => { + const body = '{"test":"data"}' + const secret = 'test_secret' + const nonHexSig = 'not-valid-hex-chars!!@@##$$%%' + + assert.equal( + validateWebhookSignature(body, nonHexSig, secret), + false, + 'Should reject non-hex signature' + ) + }) + + it('should return false for empty signature', () => { + const body = '{"test":"data"}' + const secret = 'test_secret' + + assert.equal( + validateWebhookSignature(body, '', secret), + false, + 'Should reject empty signature' + ) + }) + + it('should return false for tampered but valid-hex signature', () => { + const body = '{"test":"data"}' + const secret = 'test_secret' + // Valid hex but wrong value + const tamperedSig = 'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899' + + assert.equal( + validateWebhookSignature(body, tamperedSig, secret), + false, + 'Should reject tampered signature' + ) + }) + }) + + // New tests for validatePaymentVerification error handling + describe('validatePaymentVerification - Error Handling', () => { + it('should throw error when secret is missing', () => { + assert.throws(() => { + validatePaymentVerification({ order_id: 'order_123', payment_id: 'pay_123' }, 'sig', '') + }, /secret is mandatory/) + }) + + it('should throw error when neither order_id nor subscription_id is provided', () => { + assert.throws(() => { + validatePaymentVerification({ payment_id: 'pay_123' }, 'sig', 'secret') + }, /Either order_id or subscription_id is mandatory/) + }) + }) }) From a46e1d7a103b889cc4aa4bfa19d7d67b1338561d Mon Sep 17 00:00:00 2001 From: Vaibhav Chopra Date: Wed, 29 Apr 2026 14:50:42 +0530 Subject: [PATCH 2/5] Add positive test case for validateWebhookSignature --- test/utils/razorpay-utils.spec.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/utils/razorpay-utils.spec.js b/test/utils/razorpay-utils.spec.js index f8ae428c..5d7706f6 100644 --- a/test/utils/razorpay-utils.spec.js +++ b/test/utils/razorpay-utils.spec.js @@ -243,6 +243,21 @@ describe('Razorpay Utils', () => { // New tests for validateWebhookSignature edge cases describe('validateWebhookSignature - Edge Cases', () => { + it('should return true for valid signature', () => { + const body = '{"test":"data"}' + const secret = 'test_secret' + const crypto = require('crypto') + const validSig = crypto.createHmac('sha256', secret) + .update(body) + .digest('hex') + + assert.equal( + validateWebhookSignature(body, validSig, secret), + true, + 'Should return true for valid signature' + ) + }) + it('should throw error when body is undefined', () => { assert.throws(() => { validateWebhookSignature(undefined, 'signature', 'secret') From 32d6b27bfdd01470dca6a8c3a88b8aafa3a3dad6 Mon Sep 17 00:00:00 2001 From: Vaibhav Chopra Date: Wed, 29 Apr 2026 14:56:56 +0530 Subject: [PATCH 3/5] Fix typo in CI workflow node version matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4232a6f2..91dddf32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [12.x, 14.x, 16.x, 18.x, 19,x] + node-version: [12.x, 14.x, 16.x, 18.x, 19.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v2 From 04bc72ea7801cc9e7640ff7d73928e8633e068d7 Mon Sep 17 00:00:00 2001 From: Vaibhav Chopra Date: Wed, 29 Apr 2026 15:07:43 +0530 Subject: [PATCH 4/5] Upgrade actions/checkout and setup-node to v4 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91dddf32..15e531ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,9 @@ jobs: node-version: [12.x, 14.x, 16.x, 18.x, 19.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'npm' @@ -40,9 +40,9 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - run: sudo apt-get install -y oathtool - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: node-version: 16 registry-url: https://registry.npmjs.org/ From 8f5e09991e8a7946e749401fbce3c4e40e3d60e7 Mon Sep 17 00:00:00 2001 From: Vaibhav Chopra Date: Wed, 29 Apr 2026 15:11:48 +0530 Subject: [PATCH 5/5] Upgrade actions in node.js.yml to v4 --- .github/workflows/node.js.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 3f165c16..a653bd10 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -20,9 +20,9 @@ jobs: # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'npm'