@@ -5,11 +5,10 @@ import nock from 'nock';
55import { app as expressApp } from '../../../masterExpressApp' ;
66import { AppMode , MasterExpressConfig , TlsMode } from '../../../shared/types' ;
77import { Environments , Wallet } from '@bitgo/sdk-core' ;
8- import { Hteth } from '@bitgo/sdk-coin-eth' ;
98
10- describe ( 'POST /api/:coin/wallet/:walletId/consolidate ' , ( ) => {
9+ describe ( 'POST /api/:coin/wallet/:walletId/consolidateunspents ' , ( ) => {
1110 let agent : request . SuperAgentTest ;
12- const coin = 'hteth ' ;
11+ const coin = 'btc ' ;
1312 const walletId = 'test-wallet-id' ;
1413 const accessToken = 'test-access-token' ;
1514 const bitgoApiUrl = Environments . test . uri ;
@@ -21,7 +20,7 @@ describe('POST /api/:coin/wallet/:walletId/consolidate', () => {
2120
2221 const config : MasterExpressConfig = {
2322 appMode : AppMode . MASTER_EXPRESS ,
24- port : 0 , // Let OS assign a free port
23+ port : 0 ,
2524 bind : 'localhost' ,
2625 timeout : 30000 ,
2726 logFile : '' ,
@@ -44,8 +43,7 @@ describe('POST /api/:coin/wallet/:walletId/consolidate', () => {
4443 sinon . restore ( ) ;
4544 } ) ;
4645
47- it ( 'should consolidate account addresses by calling the enclaved express service' , async ( ) => {
48- // Mock wallet get request
46+ it ( 'should return transfer, txid, tx, and status on success' , async ( ) => {
4947 const walletGetNock = nock ( bitgoApiUrl )
5048 . get ( `/api/v2/${ coin } /wallet/${ walletId } ` )
5149 . matchHeader ( 'any' , ( ) => true )
@@ -56,7 +54,6 @@ describe('POST /api/:coin/wallet/:walletId/consolidate', () => {
5654 keys : [ 'user-key-id' , 'backup-key-id' , 'bitgo-key-id' ] ,
5755 } ) ;
5856
59- // Mock keychain get request
6057 const keychainGetNock = nock ( bitgoApiUrl )
6158 . get ( `/api/v2/${ coin } /key/user-key-id` )
6259 . matchHeader ( 'any' , ( ) => true )
@@ -65,99 +62,52 @@ describe('POST /api/:coin/wallet/:walletId/consolidate', () => {
6562 pub : 'xpub_user' ,
6663 } ) ;
6764
68- // Mock sendAccountConsolidations
69- const sendConsolidationsStub = sinon
70- . stub ( Wallet . prototype , 'sendAccountConsolidations' )
71- . resolves ( {
72- success : [
73- {
74- txid : 'consolidation-tx-1' ,
75- status : 'signed' ,
76- } ,
65+ const mockResult = {
66+ transfer : {
67+ entries : [
68+ { address : 'tb1qu...' , value : - 4000 } ,
69+ { address : 'tb1qle...' , value : - 4000 } ,
70+ { address : 'tb1qtw...' , value : 2714 , isChange : true } ,
7771 ] ,
78- failure : [ ] ,
79- } ) ;
80-
81- const response = await agent
82- . post ( `/api/${ coin } /wallet/${ walletId } /consolidate` )
83- . set ( 'Authorization' , `Bearer ${ accessToken } ` )
84- . send ( {
85- source : 'user' ,
86- pubkey : 'xpub_user' ,
87- consolidateAddresses : [ '0x1234567890abcdef' , '0xfedcba0987654321' ] ,
88- } ) ;
89-
90- response . status . should . equal ( 200 ) ;
91- response . body . should . have . property ( 'success' ) ;
92- response . body . success . should . have . length ( 1 ) ;
93- response . body . success [ 0 ] . should . have . property ( 'txid' , 'consolidation-tx-1' ) ;
94-
95- walletGetNock . done ( ) ;
96- keychainGetNock . done ( ) ;
97- sinon . assert . calledOnce ( sendConsolidationsStub ) ;
98- } ) ;
99-
100- it ( 'should handle partial consolidation failures' , async ( ) => {
101- // Mock wallet get request
102- const walletGetNock = nock ( bitgoApiUrl )
103- . get ( `/api/v2/${ coin } /wallet/${ walletId } ` )
104- . matchHeader ( 'any' , ( ) => true )
105- . reply ( 200 , {
106- id : walletId ,
107- type : 'cold' ,
108- subType : 'onPrem' ,
109- keys : [ 'user-key-id' , 'backup-key-id' , 'bitgo-key-id' ] ,
110- } ) ;
111-
112- // Mock keychain get request
113- const keychainGetNock = nock ( bitgoApiUrl )
114- . get ( `/api/v2/${ coin } /key/user-key-id` )
115- . matchHeader ( 'any' , ( ) => true )
116- . reply ( 200 , {
117- id : 'user-key-id' ,
118- pub : 'xpub_user' ,
119- } ) ;
72+ id : '685ac2f3c2f8a2a5d9cc18d3593f1751' ,
73+ coin : 'tbtc' ,
74+ wallet : '685abbf19ca95b79f88e0b41d9337109' ,
75+ txid : '239d143cdfc6d6c83a935da4f3d610b2364a956c7b6dcdc165eb706f62c4432a' ,
76+ status : 'signed' ,
77+ } ,
78+ txid : '239d143cdfc6d6c83a935da4f3d610b2364a956c7b6dcdc165eb706f62c4432a' ,
79+ tx : '01000000000102580b...' ,
80+ status : 'signed' ,
81+ } ;
12082
121- // Mock sendAccountConsolidations with partial failures
122- const sendConsolidationsStub = sinon
123- . stub ( Wallet . prototype , 'sendAccountConsolidations' )
124- . resolves ( {
125- success : [
126- {
127- txid : 'consolidation-tx-1' ,
128- status : 'signed' ,
129- } ,
130- ] ,
131- failure : [
132- {
133- error : 'Insufficient funds' ,
134- address : '0xfedcba0987654321' ,
135- } ,
136- ] ,
137- } ) ;
83+ const consolidateUnspentsStub = sinon
84+ . stub ( Wallet . prototype , 'consolidateUnspents' )
85+ . resolves ( mockResult ) ;
13886
13987 const response = await agent
140- . post ( `/api/${ coin } /wallet/${ walletId } /consolidate ` )
88+ . post ( `/api/${ coin } /wallet/${ walletId } /consolidateunspents ` )
14189 . set ( 'Authorization' , `Bearer ${ accessToken } ` )
14290 . send ( {
14391 source : 'user' ,
14492 pubkey : 'xpub_user' ,
145- consolidateAddresses : [ '0x1234567890abcdef' , '0xfedcba0987654321' ] ,
93+ feeRate : 1000 ,
14694 } ) ;
14795
148- response . status . should . equal ( 500 ) ;
149- response . body . should . have . property ( 'error' , 'Internal Server Error' ) ;
150- response . body . should . have
151- . property ( 'details' )
152- . which . match ( / C o n s o l i d a t i o n s f a i l e d : 1 a n d s u c c e e d e d : 1 / ) ;
96+ response . status . should . equal ( 200 ) ;
97+ response . body . should . have . property ( 'transfer' ) ;
98+ response . body . should . have . property ( 'txid' , mockResult . txid ) ;
99+ response . body . should . have . property ( 'tx' , mockResult . tx ) ;
100+ response . body . should . have . property ( 'status' , mockResult . status ) ;
101+ response . body . transfer . should . have . property ( 'txid' , mockResult . transfer . txid ) ;
102+ response . body . transfer . should . have . property ( 'status' , mockResult . transfer . status ) ;
103+ response . body . transfer . should . have . property ( 'entries' ) . which . is . Array ( ) ;
153104
154105 walletGetNock . done ( ) ;
155106 keychainGetNock . done ( ) ;
156- sinon . assert . calledOnce ( sendConsolidationsStub ) ;
107+ sinon . assert . calledOnce ( consolidateUnspentsStub ) ;
157108 } ) ;
158109
159- it ( 'should throw error when all consolidations fail' , async ( ) => {
160- // Mock wallet get request
110+ it ( 'should return error, name, and details on failure' , async ( ) => {
161111 const walletGetNock = nock ( bitgoApiUrl )
162112 . get ( `/api/v2/${ coin } /wallet/${ walletId } ` )
163113 . matchHeader ( 'any' , ( ) => true )
@@ -168,7 +118,6 @@ describe('POST /api/:coin/wallet/:walletId/consolidate', () => {
168118 keys : [ 'user-key-id' , 'backup-key-id' , 'bitgo-key-id' ] ,
169119 } ) ;
170120
171- // Mock keychain get request
172121 const keychainGetNock = nock ( bitgoApiUrl )
173122 . get ( `/api/v2/${ coin } /key/user-key-id` )
174123 . matchHeader ( 'any' , ( ) => true )
@@ -177,83 +126,37 @@ describe('POST /api/:coin/wallet/:walletId/consolidate', () => {
177126 pub : 'xpub_user' ,
178127 } ) ;
179128
180- // Mock sendAccountConsolidations with all failures
181- const sendConsolidationsStub = sinon
182- . stub ( Wallet . prototype , 'sendAccountConsolidations' )
183- . resolves ( {
184- success : [ ] ,
185- failure : [
186- {
187- error : 'All consolidations failed' ,
188- address : '0x1234567890abcdef' ,
189- } ,
190- {
191- error : 'All consolidations failed' ,
192- address : '0xfedcba0987654321' ,
193- } ,
194- ] ,
195- } ) ;
196-
197- const response = await agent
198- . post ( `/api/${ coin } /wallet/${ walletId } /consolidate` )
199- . set ( 'Authorization' , `Bearer ${ accessToken } ` )
200- . send ( {
201- source : 'user' ,
202- pubkey : 'xpub_user' ,
203- consolidateAddresses : [ '0x1234567890abcdef' , '0xfedcba0987654321' ] ,
204- } ) ;
205-
206- response . status . should . equal ( 500 ) ;
207- response . body . should . have . property ( 'error' ) ;
208- response . body . should . have . property ( 'details' ) . which . match ( / A l l c o n s o l i d a t i o n s f a i l e d / ) ;
209-
210- walletGetNock . done ( ) ;
211- keychainGetNock . done ( ) ;
212- sinon . assert . calledOnce ( sendConsolidationsStub ) ;
213- } ) ;
214-
215- it ( 'should throw error when coin does not support account consolidations' , async ( ) => {
216- // Mock wallet get request
217- const walletGetNock = nock ( bitgoApiUrl )
218- . get ( `/api/v2/${ coin } /wallet/${ walletId } ` )
219- . matchHeader ( 'any' , ( ) => true )
220- . reply ( 200 , {
221- id : walletId ,
222- type : 'cold' ,
223- subType : 'onPrem' ,
224- keys : [ 'user-key-id' , 'backup-key-id' , 'bitgo-key-id' ] ,
225- } ) ;
226- // Mock keychain get request
227- const keychainGetNock = nock ( bitgoApiUrl )
228- . get ( `/api/v2/${ coin } /key/user-key-id` )
229- . matchHeader ( 'any' , ( ) => true )
230- . reply ( 200 , {
231- id : 'user-key-id' ,
232- pub : 'xpub_user' ,
233- } ) ;
129+ const mockError = {
130+ error : 'Internal Server Error' ,
131+ name : 'ApiResponseError' ,
132+ details :
133+ 'There are too few unspents that meet the given parameters to consolidate (1 available).' ,
134+ } ;
234135
235- // Mock allowsAccountConsolidations to return false
236- const allowsConsolidationsStub = sinon
237- . stub ( Hteth . prototype , 'allowsAccountConsolidations' )
238- . returns ( false ) ;
136+ const consolidateUnspentsStub = sinon
137+ . stub ( Wallet . prototype , 'consolidateUnspents' )
138+ . throws ( Object . assign ( new Error ( mockError . details ) , mockError ) ) ;
239139
240140 const response = await agent
241- . post ( `/api/${ coin } /wallet/${ walletId } /consolidate ` )
141+ . post ( `/api/${ coin } /wallet/${ walletId } /consolidateunspents ` )
242142 . set ( 'Authorization' , `Bearer ${ accessToken } ` )
243143 . send ( {
244144 source : 'user' ,
245145 pubkey : 'xpub_user' ,
146+ feeRate : 1000 ,
246147 } ) ;
247148
248149 response . status . should . equal ( 500 ) ;
150+ response . body . should . have . property ( 'error' , mockError . error ) ;
151+ response . body . should . have . property ( 'name' , mockError . name ) ;
152+ response . body . should . have . property ( 'details' , mockError . details ) ;
249153
250154 walletGetNock . done ( ) ;
251155 keychainGetNock . done ( ) ;
252- sinon . assert . calledOnce ( allowsConsolidationsStub ) ;
156+ sinon . assert . calledOnce ( consolidateUnspentsStub ) ;
253157 } ) ;
254158
255159 it ( 'should throw error when provided pubkey does not match wallet keychain' , async ( ) => {
256- // Mock wallet get request
257160 const walletGetNock = nock ( bitgoApiUrl )
258161 . get ( `/api/v2/${ coin } /wallet/${ walletId } ` )
259162 . matchHeader ( 'any' , ( ) => true )
@@ -264,7 +167,6 @@ describe('POST /api/:coin/wallet/:walletId/consolidate', () => {
264167 keys : [ 'user-key-id' , 'backup-key-id' , 'bitgo-key-id' ] ,
265168 } ) ;
266169
267- // Mock keychain get request
268170 const keychainGetNock = nock ( bitgoApiUrl )
269171 . get ( `/api/v2/${ coin } /key/user-key-id` )
270172 . matchHeader ( 'any' , ( ) => true )
@@ -274,11 +176,12 @@ describe('POST /api/:coin/wallet/:walletId/consolidate', () => {
274176 } ) ;
275177
276178 const response = await agent
277- . post ( `/api/${ coin } /wallet/${ walletId } /consolidate ` )
179+ . post ( `/api/${ coin } /wallet/${ walletId } /consolidateunspents ` )
278180 . set ( 'Authorization' , `Bearer ${ accessToken } ` )
279181 . send ( {
280182 source : 'user' ,
281183 pubkey : 'wrong_pubkey' ,
184+ feeRate : 1000 ,
282185 } ) ;
283186
284187 response . status . should . equal ( 500 ) ;
0 commit comments