diff --git a/modules/express/src/typedRoutes/api/v1/acceptShare.ts b/modules/express/src/typedRoutes/api/v1/acceptShare.ts index 27759027f9..1940261157 100644 --- a/modules/express/src/typedRoutes/api/v1/acceptShare.ts +++ b/modules/express/src/typedRoutes/api/v1/acceptShare.ts @@ -12,10 +12,18 @@ export const AcceptShareRequestBody = { userPassword: optional(t.string), /** New passphrase to encrypt the shared wallet's keys */ newWalletPassphrase: optional(t.string), - /** Optional encrypted private key to use instead of generating a new one */ + /** Optional encrypted xprv to use instead of generating a new one */ overrideEncryptedXprv: optional(t.string), }; +/** Response from accepting a wallet share */ +export const AcceptShareResponse = t.type({ + /** Indicates whether the share state was changed by this operation */ + changed: t.boolean, + /** Current state of the wallet share */ + state: t.string, +}); + /** * Accept a Wallet Share * Allows users to accept a wallet share invitation from another user. @@ -34,7 +42,7 @@ export const PostAcceptShare = httpRoute({ }), response: { /** Successfully accepted wallet share */ - 200: t.UnknownRecord, + 200: AcceptShareResponse, /** Error response */ 400: BitgoExpressError, }, diff --git a/modules/express/test/unit/typedRoutes/acceptShare.ts b/modules/express/test/unit/typedRoutes/acceptShare.ts index 99254d00c3..49efcd1c44 100644 --- a/modules/express/test/unit/typedRoutes/acceptShare.ts +++ b/modules/express/test/unit/typedRoutes/acceptShare.ts @@ -3,6 +3,7 @@ import * as t from 'io-ts'; import { AcceptShareRequestParams, AcceptShareRequestBody, + AcceptShareResponse, PostAcceptShare, } from '../../../src/typedRoutes/api/v1/acceptShare'; import { assertDecode } from './common'; @@ -126,6 +127,72 @@ describe('AcceptShare codec tests', function () { }); }); + describe('AcceptShareResponse', function () { + it('should validate valid response with all fields', function () { + const validResponse = { + changed: true, + state: 'accepted', + }; + + const decoded = assertDecode(AcceptShareResponse, validResponse); + assert.strictEqual(decoded.changed, validResponse.changed); + assert.strictEqual(decoded.state, validResponse.state); + }); + + it('should validate response with changed=false', function () { + const validResponse = { + changed: false, + state: 'pending', + }; + + const decoded = assertDecode(AcceptShareResponse, validResponse); + assert.strictEqual(decoded.changed, false); + assert.strictEqual(decoded.state, 'pending'); + }); + + it('should reject response without changed field', function () { + const invalidResponse = { + state: 'accepted', + }; + + assert.throws(() => { + assertDecode(AcceptShareResponse, invalidResponse); + }); + }); + + it('should reject response without state field', function () { + const invalidResponse = { + changed: true, + }; + + assert.throws(() => { + assertDecode(AcceptShareResponse, invalidResponse); + }); + }); + + it('should reject response with non-boolean changed field', function () { + const invalidResponse = { + changed: 'true', + state: 'accepted', + }; + + assert.throws(() => { + assertDecode(AcceptShareResponse, invalidResponse); + }); + }); + + it('should reject response with non-string state field', function () { + const invalidResponse = { + changed: true, + state: 123, + }; + + assert.throws(() => { + assertDecode(AcceptShareResponse, invalidResponse); + }); + }); + }); + describe('Supertest Integration Tests', function () { const agent = setupAgent(); const shareId = 'share123456789abcdef'; @@ -133,7 +200,6 @@ describe('AcceptShare codec tests', function () { const mockAcceptShareResponse = { state: 'accepted', changed: true, - walletId: 'wallet123', }; afterEach(function () { @@ -163,6 +229,11 @@ describe('AcceptShare codec tests', function () { assert.strictEqual(result.status, 200); assert.ok(result.body); + // Validate response structure + const decodedResponse = assertDecode(AcceptShareResponse, result.body); + assert.strictEqual(typeof decodedResponse.changed, 'boolean'); + assert.strictEqual(typeof decodedResponse.state, 'string'); + // Verify the method was called with correct params sinon.assert.calledOnce(acceptShareStub); const callArgs = acceptShareStub.firstCall.args[0]; @@ -191,6 +262,9 @@ describe('AcceptShare codec tests', function () { assert.strictEqual(result.status, 200); assert.ok(result.body); + // Validate response structure + assertDecode(AcceptShareResponse, result.body); + sinon.assert.calledOnce(acceptShareStub); const callArgs = acceptShareStub.firstCall.args[0]; assert.strictEqual(callArgs.walletShareId, shareId); @@ -217,6 +291,9 @@ describe('AcceptShare codec tests', function () { assert.strictEqual(result.status, 200); assert.ok(result.body); + // Validate response structure + assertDecode(AcceptShareResponse, result.body); + sinon.assert.calledOnce(acceptShareStub); const callArgs = acceptShareStub.firstCall.args[0]; assert.strictEqual(callArgs.walletShareId, shareId);