Skip to content

Commit bbd170c

Browse files
authored
Merge pull request #7482 from BitGo/WP-6717-accept-share-v1-type-validation
fix(express): acceptShare type codec
2 parents e136ac5 + 8749ead commit bbd170c

File tree

2 files changed

+88
-3
lines changed

2 files changed

+88
-3
lines changed

modules/express/src/typedRoutes/api/v1/acceptShare.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,18 @@ export const AcceptShareRequestBody = {
1212
userPassword: optional(t.string),
1313
/** New passphrase to encrypt the shared wallet's keys */
1414
newWalletPassphrase: optional(t.string),
15-
/** Optional encrypted private key to use instead of generating a new one */
15+
/** Optional encrypted xprv to use instead of generating a new one */
1616
overrideEncryptedXprv: optional(t.string),
1717
};
1818

19+
/** Response from accepting a wallet share */
20+
export const AcceptShareResponse = t.type({
21+
/** Indicates whether the share state was changed by this operation */
22+
changed: t.boolean,
23+
/** Current state of the wallet share */
24+
state: t.string,
25+
});
26+
1927
/**
2028
* Accept a Wallet Share
2129
* Allows users to accept a wallet share invitation from another user.
@@ -34,7 +42,7 @@ export const PostAcceptShare = httpRoute({
3442
}),
3543
response: {
3644
/** Successfully accepted wallet share */
37-
200: t.UnknownRecord,
45+
200: AcceptShareResponse,
3846
/** Error response */
3947
400: BitgoExpressError,
4048
},

modules/express/test/unit/typedRoutes/acceptShare.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as t from 'io-ts';
33
import {
44
AcceptShareRequestParams,
55
AcceptShareRequestBody,
6+
AcceptShareResponse,
67
PostAcceptShare,
78
} from '../../../src/typedRoutes/api/v1/acceptShare';
89
import { assertDecode } from './common';
@@ -126,14 +127,79 @@ describe('AcceptShare codec tests', function () {
126127
});
127128
});
128129

130+
describe('AcceptShareResponse', function () {
131+
it('should validate valid response with all fields', function () {
132+
const validResponse = {
133+
changed: true,
134+
state: 'accepted',
135+
};
136+
137+
const decoded = assertDecode(AcceptShareResponse, validResponse);
138+
assert.strictEqual(decoded.changed, validResponse.changed);
139+
assert.strictEqual(decoded.state, validResponse.state);
140+
});
141+
142+
it('should validate response with changed=false', function () {
143+
const validResponse = {
144+
changed: false,
145+
state: 'pending',
146+
};
147+
148+
const decoded = assertDecode(AcceptShareResponse, validResponse);
149+
assert.strictEqual(decoded.changed, false);
150+
assert.strictEqual(decoded.state, 'pending');
151+
});
152+
153+
it('should reject response without changed field', function () {
154+
const invalidResponse = {
155+
state: 'accepted',
156+
};
157+
158+
assert.throws(() => {
159+
assertDecode(AcceptShareResponse, invalidResponse);
160+
});
161+
});
162+
163+
it('should reject response without state field', function () {
164+
const invalidResponse = {
165+
changed: true,
166+
};
167+
168+
assert.throws(() => {
169+
assertDecode(AcceptShareResponse, invalidResponse);
170+
});
171+
});
172+
173+
it('should reject response with non-boolean changed field', function () {
174+
const invalidResponse = {
175+
changed: 'true',
176+
state: 'accepted',
177+
};
178+
179+
assert.throws(() => {
180+
assertDecode(AcceptShareResponse, invalidResponse);
181+
});
182+
});
183+
184+
it('should reject response with non-string state field', function () {
185+
const invalidResponse = {
186+
changed: true,
187+
state: 123,
188+
};
189+
190+
assert.throws(() => {
191+
assertDecode(AcceptShareResponse, invalidResponse);
192+
});
193+
});
194+
});
195+
129196
describe('Supertest Integration Tests', function () {
130197
const agent = setupAgent();
131198
const shareId = 'share123456789abcdef';
132199

133200
const mockAcceptShareResponse = {
134201
state: 'accepted',
135202
changed: true,
136-
walletId: 'wallet123',
137203
};
138204

139205
afterEach(function () {
@@ -163,6 +229,11 @@ describe('AcceptShare codec tests', function () {
163229
assert.strictEqual(result.status, 200);
164230
assert.ok(result.body);
165231

232+
// Validate response structure
233+
const decodedResponse = assertDecode(AcceptShareResponse, result.body);
234+
assert.strictEqual(typeof decodedResponse.changed, 'boolean');
235+
assert.strictEqual(typeof decodedResponse.state, 'string');
236+
166237
// Verify the method was called with correct params
167238
sinon.assert.calledOnce(acceptShareStub);
168239
const callArgs = acceptShareStub.firstCall.args[0];
@@ -191,6 +262,9 @@ describe('AcceptShare codec tests', function () {
191262
assert.strictEqual(result.status, 200);
192263
assert.ok(result.body);
193264

265+
// Validate response structure
266+
assertDecode(AcceptShareResponse, result.body);
267+
194268
sinon.assert.calledOnce(acceptShareStub);
195269
const callArgs = acceptShareStub.firstCall.args[0];
196270
assert.strictEqual(callArgs.walletShareId, shareId);
@@ -217,6 +291,9 @@ describe('AcceptShare codec tests', function () {
217291
assert.strictEqual(result.status, 200);
218292
assert.ok(result.body);
219293

294+
// Validate response structure
295+
assertDecode(AcceptShareResponse, result.body);
296+
220297
sinon.assert.calledOnce(acceptShareStub);
221298
const callArgs = acceptShareStub.firstCall.args[0];
222299
assert.strictEqual(callArgs.walletShareId, shareId);

0 commit comments

Comments
 (0)