Skip to content

Commit f00ce46

Browse files
authored
test(express): added supertest for coin signtx
2 parents 68c0250 + 91c5d0b commit f00ce46

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed

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

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,273 @@ import {
1212
PostCoinSignTx,
1313
} from '../../../src/typedRoutes/api/v2/coinSignTx';
1414
import { assertDecode } from './common';
15+
import 'should';
16+
import 'should-http';
17+
import 'should-sinon';
18+
import * as sinon from 'sinon';
19+
import { BitGo } from 'bitgo';
20+
import { setupAgent } from '../../lib/testutil';
1521

1622
describe('CoinSignTx codec tests', function () {
23+
describe('coinSignTx', function () {
24+
const agent = setupAgent();
25+
const coin = 'tbtc';
26+
27+
const mockFullySignedResponse = {
28+
txHex:
29+
'0100000001c7dad3d9607a23c45a6c1c5ad7bce02acff71a0f21eb4a72a59d0c0e19402d0f0000000000ffffffff0180a21900000000001976a914c918e1b36f2c72b1aaef94dbb7f578a4b68b542788ac00000000',
30+
};
31+
32+
afterEach(function () {
33+
sinon.restore();
34+
});
35+
36+
it('should successfully sign a transaction', async function () {
37+
const requestBody = {
38+
txPrebuild: {
39+
txHex:
40+
'0100000001c7dad3d9607a23c45a6c1c5ad7bce02acff71a0f21eb4a72a59d0c0e19402d0f0000000000ffffffff0180a21900000000001976a914c918e1b36f2c72b1aaef94dbb7f578a4b68b542788ac00000000',
41+
walletId: '5a1341e7c8421dc90710673b3166bbd5',
42+
},
43+
prv: 'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2',
44+
isLastSignature: true,
45+
};
46+
47+
// Create mock coin with signTransaction method
48+
const mockCoin = {
49+
signTransaction: sinon.stub().resolves(mockFullySignedResponse),
50+
};
51+
52+
// Stub BitGo.prototype.coin to return our mock coin
53+
const coinStub = sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any);
54+
55+
// Make the request to Express
56+
const result = await agent
57+
.post(`/api/v2/${coin}/signtx`)
58+
.set('Authorization', 'Bearer test_access_token_12345')
59+
.set('Content-Type', 'application/json')
60+
.send(requestBody);
61+
62+
// Verify the response
63+
assert.strictEqual(result.status, 200);
64+
result.body.should.have.property('txHex');
65+
assert.strictEqual(result.body.txHex, mockFullySignedResponse.txHex);
66+
67+
// This ensures the response structure matches the typed definition
68+
const decodedResponse = assertDecode(FullySignedTransactionResponse, result.body);
69+
assert.strictEqual(decodedResponse.txHex, mockFullySignedResponse.txHex);
70+
71+
// Verify that the correct BitGoJS methods were called
72+
assert.strictEqual(coinStub.calledOnceWith(coin), true);
73+
assert.strictEqual(
74+
mockCoin.signTransaction.calledOnceWith(
75+
sinon.match({
76+
coin: coin,
77+
txPrebuild: sinon.match.object,
78+
prv: requestBody.prv,
79+
isLastSignature: true,
80+
})
81+
),
82+
true
83+
);
84+
});
85+
86+
it('should successfully sign a half-signed transaction', async function () {
87+
const requestBody = {
88+
txPrebuild: {
89+
txBase64:
90+
'AQAAAAFz2JT3Xvjk8jKcYcMrKR8tPMRm5+/Q6J2sMgtz7QDpAAAAAAD+////AoCWmAAAAAAAGXapFJA29QPQaHHwR3Uriuhw2A6tHkPgiKwAAAAAAAEBH9cQ2QAAAAAAAXapFCf/zr8zPrMftHGIRsOt0Cf+wdOyiKwA',
91+
},
92+
prv: 'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2',
93+
};
94+
95+
const mockHalfSignedResponse = {
96+
halfSigned: {
97+
txBase64:
98+
'AQAAAAFz2JT3Xvjk8jKcYcMrKR8tPMRm5+/Q6J2sMgtz7QDpAAAAAAD+////AoCWmAAAAAAAGXapFJA29QPQaHHwR3Uriuhw2A6tHkPgiKwAAAAAAAEBH9cQ2QAAAAAAAXapFCf/zr8zPrMftHGIRsOt0Cf+wdOyiKwA',
99+
},
100+
};
101+
102+
// Create mock coin with signTransaction method
103+
const mockCoin = {
104+
signTransaction: sinon.stub().resolves(mockHalfSignedResponse),
105+
};
106+
107+
// Stub BitGo.prototype.coin to return our mock coin
108+
const coinStub = sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any);
109+
110+
// Make the request to Express
111+
const result = await agent
112+
.post(`/api/v2/${coin}/signtx`)
113+
.set('Authorization', 'Bearer test_access_token_12345')
114+
.set('Content-Type', 'application/json')
115+
.send(requestBody);
116+
117+
// Verify the response
118+
assert.strictEqual(result.status, 200);
119+
result.body.should.have.property('halfSigned');
120+
result.body.halfSigned.should.have.property('txBase64');
121+
assert.strictEqual(result.body.halfSigned.txBase64, mockHalfSignedResponse.halfSigned.txBase64);
122+
123+
// This ensures the response structure matches the typed definition
124+
const decodedResponse = assertDecode(HalfSignedAccountTransactionResponse, result.body);
125+
assert.strictEqual(decodedResponse.halfSigned.txBase64, mockHalfSignedResponse.halfSigned.txBase64);
126+
127+
// Verify that the correct BitGoJS methods were called
128+
assert.strictEqual(coinStub.calledOnceWith(coin), true);
129+
assert.strictEqual(mockCoin.signTransaction.calledOnce, true);
130+
});
131+
132+
it('should successfully return a transaction request ID', async function () {
133+
const requestBody = {
134+
txPrebuild: {
135+
txHex:
136+
'0100000001c7dad3d9607a23c45a6c1c5ad7bce02acff71a0f21eb4a72a59d0c0e19402d0f0000000000ffffffff0180a21900000000001976a914c918e1b36f2c72b1aaef94dbb7f578a4b68b542788ac00000000',
137+
},
138+
prv: 'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2',
139+
};
140+
141+
const mockTxRequestResponse = {
142+
txRequestId: '5a1341e7c8421dc90710673b3166bbd5',
143+
};
144+
145+
// Create mock coin with signTransaction method
146+
const mockCoin = {
147+
signTransaction: sinon.stub().resolves(mockTxRequestResponse),
148+
};
149+
150+
// Stub BitGo.prototype.coin to return our mock coin
151+
const coinStub = sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any);
152+
153+
// Make the request to Express
154+
const result = await agent
155+
.post(`/api/v2/${coin}/signtx`)
156+
.set('Authorization', 'Bearer test_access_token_12345')
157+
.set('Content-Type', 'application/json')
158+
.send(requestBody);
159+
160+
// Verify the response
161+
assert.strictEqual(result.status, 200);
162+
result.body.should.have.property('txRequestId');
163+
assert.strictEqual(result.body.txRequestId, mockTxRequestResponse.txRequestId);
164+
165+
// This ensures the response structure matches the typed definition
166+
const decodedResponse = assertDecode(SignedTransactionRequestResponse, result.body);
167+
assert.strictEqual(decodedResponse.txRequestId, mockTxRequestResponse.txRequestId);
168+
169+
// Verify that the correct BitGoJS methods were called
170+
assert.strictEqual(coinStub.calledOnceWith(coin), true);
171+
assert.strictEqual(mockCoin.signTransaction.calledOnce, true);
172+
});
173+
174+
it('should successfully return a TSS transaction request (Full TxRequestResponse)', async function () {
175+
const requestBody = {
176+
txPrebuild: {
177+
txHex:
178+
'0100000001c7dad3d9607a23c45a6c1c5ad7bce02acff71a0f21eb4a72a59d0c0e19402d0f0000000000ffffffff0180a21900000000001976a914c918e1b36f2c72b1aaef94dbb7f578a4b68b542788ac00000000',
179+
},
180+
prv: 'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2',
181+
};
182+
183+
const mockTxRequestFullResponse = {
184+
txRequestId: '5a1341e7c8421dc90710673b3166bbd5',
185+
walletId: '5a1341e7c8421dc90710673b3166bbd5',
186+
walletType: 'hot',
187+
version: 1,
188+
state: 'pendingApproval',
189+
date: '2023-01-01T00:00:00.000Z',
190+
createdDate: '2023-01-01T00:00:00.000Z',
191+
userId: '5a1341e7c8421dc90710673b3166bbd5',
192+
initiatedBy: '5a1341e7c8421dc90710673b3166bbd5',
193+
updatedBy: '5a1341e7c8421dc90710673b3166bbd5',
194+
intents: [],
195+
enterpriseId: '5a1341e7c8421dc90710673b3166bbd5',
196+
intent: {},
197+
pendingApprovalId: '5a1341e7c8421dc90710673b3166bbd5',
198+
policiesChecked: true,
199+
signatureShares: [
200+
{
201+
from: 'user',
202+
to: 'bitgo',
203+
share: 'abc123',
204+
},
205+
],
206+
commitmentShares: [
207+
{
208+
from: 'user',
209+
to: 'bitgo',
210+
share: 'abc123',
211+
type: 'commitment',
212+
},
213+
],
214+
txHashes: ['hash1', 'hash2'],
215+
unsignedTxs: [
216+
{
217+
serializedTxHex:
218+
'0100000001c7dad3d9607a23c45a6c1c5ad7bce02acff71a0f21eb4a72a59d0c0e19402d0f0000000000ffffffff0180a21900000000001976a914c918e1b36f2c72b1aaef94dbb7f578a4b68b542788ac00000000',
219+
signableHex:
220+
'0100000001c7dad3d9607a23c45a6c1c5ad7bce02acff71a0f21eb4a72a59d0c0e19402d0f0000000000ffffffff0180a21900000000001976a914c918e1b36f2c72b1aaef94dbb7f578a4b68b542788ac00000000',
221+
derivationPath: "m/44'/0'/0'/0/0",
222+
},
223+
],
224+
apiVersion: 'lite',
225+
latest: true,
226+
};
227+
228+
// Create mock coin with signTransaction method
229+
const mockCoin = {
230+
signTransaction: sinon.stub().resolves(mockTxRequestFullResponse),
231+
};
232+
233+
// Stub BitGo.prototype.coin to return our mock coin
234+
const coinStub = sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any);
235+
236+
// Make the request to Express
237+
const result = await agent
238+
.post(`/api/v2/${coin}/signtx`)
239+
.set('Authorization', 'Bearer test_access_token_12345')
240+
.set('Content-Type', 'application/json')
241+
.send(requestBody);
242+
243+
// Verify the response
244+
assert.strictEqual(result.status, 200);
245+
result.body.should.have.property('txRequestId');
246+
result.body.should.have.property('walletId');
247+
result.body.should.have.property('version');
248+
result.body.should.have.property('state');
249+
result.body.should.have.property('intents');
250+
result.body.should.have.property('latest');
251+
assert.strictEqual(result.body.txRequestId, mockTxRequestFullResponse.txRequestId);
252+
assert.strictEqual(result.body.walletId, mockTxRequestFullResponse.walletId);
253+
assert.strictEqual(result.body.version, mockTxRequestFullResponse.version);
254+
assert.strictEqual(result.body.state, mockTxRequestFullResponse.state);
255+
assert.strictEqual(result.body.latest, mockTxRequestFullResponse.latest);
256+
257+
// Verify TSS-specific fields
258+
result.body.should.have.property('signatureShares');
259+
result.body.should.have.property('commitmentShares');
260+
result.body.should.have.property('unsignedTxs');
261+
result.body.signatureShares.should.be.Array();
262+
result.body.signatureShares.should.have.length(1);
263+
result.body.commitmentShares.should.be.Array();
264+
result.body.commitmentShares.should.have.length(1);
265+
result.body.unsignedTxs.should.be.Array();
266+
result.body.unsignedTxs.should.have.length(1);
267+
268+
// This ensures the TSS transaction request response structure matches the typed definition
269+
const decodedResponse = assertDecode(TxRequestResponse, result.body);
270+
assert.strictEqual(decodedResponse.txRequestId, mockTxRequestFullResponse.txRequestId);
271+
assert.strictEqual(decodedResponse.walletId, mockTxRequestFullResponse.walletId);
272+
assert.strictEqual(decodedResponse.version, mockTxRequestFullResponse.version);
273+
assert.strictEqual(decodedResponse.state, mockTxRequestFullResponse.state);
274+
assert.strictEqual(decodedResponse.latest, mockTxRequestFullResponse.latest);
275+
276+
// Verify that the correct BitGoJS methods were called
277+
assert.strictEqual(coinStub.calledOnceWith(coin), true);
278+
assert.strictEqual(mockCoin.signTransaction.calledOnce, true);
279+
});
280+
});
281+
17282
describe('CoinSignTxParams', function () {
18283
it('should validate params with required coin', function () {
19284
const validParams = {

0 commit comments

Comments
 (0)