@@ -6,6 +6,12 @@ import {
66 PostCreateAddress ,
77} from '../../../src/typedRoutes/api/v2/createAddress' ;
88import { assertDecode } from './common' ;
9+ import 'should' ;
10+ import 'should-http' ;
11+ import 'should-sinon' ;
12+ import * as sinon from 'sinon' ;
13+ import { BitGo } from 'bitgo' ;
14+ import { setupAgent } from '../../lib/testutil' ;
915
1016/**
1117 * Helper function to extract path parameter names from a route path
@@ -26,6 +32,133 @@ function getCodecParamNames(paramsCodec: Record<string, any>): string[] {
2632}
2733
2834describe ( 'CreateAddress codec tests' , function ( ) {
35+ describe ( 'createAddress' , function ( ) {
36+ const agent = setupAgent ( ) ;
37+
38+ const walletId = '68c02f96aa757d9212bd1a536f123456' ;
39+ const coin = 'tbtc' ;
40+
41+ const mockResponse = {
42+ id : '68ed4dbfe664aa98d171ac0f524ef111' ,
43+ address : '2N632etYgykMhGrScEN7RpAthg64ACsTL6v' ,
44+ chain : 0 ,
45+ index : 1 ,
46+ coin : 'tbtc' ,
47+ wallet : '68c02f96aa757d9212bd1a536f123456' ,
48+ label : 'My new address' ,
49+ coinSpecific : {
50+ redeemScript :
51+ '522103ebc10ae7ca49f55228fd29dd32eadfcd289fb43082fabb8e0f3bcee9ab09853721039b1a9b93a2e8114d6627753fc37f115e721609b35187d74dc32e5adfa97992402102a704615d8eedaf2e0ff37f7e503615fb6765b8b147a97ac1d930b37e709755dc53ae' ,
52+ } ,
53+ addressType : 'p2sh' ,
54+ keychains : [
55+ {
56+ id : '68c02f94aa757d9212bd19b783deb065' ,
57+ pub : 'xpub661MyMwAqRbcGCSB6BrtvtRzb4Pebi5RLaC8dqZ8Zt2sJsWZ8dj7pXrLRfiGjYAD5PMV23dAxNQww8kggwKZUZtEhcjH1vPDhu4dUihbv2T' ,
58+ ethAddress : '0x23f7b80a5f5dce2bfb9e09d40e37794cc8e2a3be' ,
59+ source : 'user' ,
60+ type : 'independent' ,
61+ encryptedPrv :
62+ '{"iv":"abc123xyz456==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"def789ghi012=","ct":"mockEncryptedPrivateKeyData1234567890"}' ,
63+ } ,
64+ {
65+ id : '68c02f94a36d5167fa49878f9553f40e' ,
66+ pub : 'xpub661MyMwAqRbcFUMUdma6ot9QPBcBDiyJSo3CsyE53hjcZ9RgAH2oxcNViRTv9bUJrHeEiDhL2sLDa9kXgjSwL33Bca3ApQaG7nBJJc5enLr' ,
67+ ethAddress : '0xfb1d696334a2632952ae25aead77be781df91e18' ,
68+ source : 'backup' ,
69+ type : 'independent' ,
70+ encryptedPrv :
71+ '{"iv":"uvw345qrs678==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"jkl901mno234=","ct":"mockEncryptedPrivateKeyDataBackup567890"}' ,
72+ } ,
73+ {
74+ id : '68c02f95aa757d9212bd19e7ee0744e4' ,
75+ pub : 'xpub661MyMwAqRbcFZ1e1eeAuFnu8gBAM1Yd7bJUSEL7TRX3QQ6vbY6B5Uugf8E93eTfvcKRHHZUiqq7WLLerZdFkp2guAVG1Mkuqp6ZZqbQ53u' ,
76+ ethAddress : '0xfad325e7f4b57549ff68395e0d42378046208a71' ,
77+ source : 'bitgo' ,
78+ type : 'independent' ,
79+ isBitGo : true ,
80+ isTrust : false ,
81+ hsmType : 'institutional' ,
82+ } ,
83+ ] ,
84+ } ;
85+
86+ afterEach ( function ( ) {
87+ sinon . restore ( ) ;
88+ } ) ;
89+
90+ it ( 'should successfully create a new wallet address' , async function ( ) {
91+ const requestBody = {
92+ label : 'My new address' ,
93+ chain : 0 ,
94+ allowSkipVerifyAddress : true ,
95+ } ;
96+
97+ // Create mock wallet with createAddress method
98+ const mockWallet = {
99+ createAddress : sinon . stub ( ) . resolves ( mockResponse ) ,
100+ } ;
101+
102+ // Stub the wallets().get() chain
103+ const walletsGetStub = sinon . stub ( ) . resolves ( mockWallet ) ;
104+
105+ const mockWallets = {
106+ get : walletsGetStub ,
107+ } ;
108+
109+ const mockCoin = {
110+ wallets : sinon . stub ( ) . returns ( mockWallets ) ,
111+ } ;
112+
113+ // Stub BitGo.prototype.coin to return our mock coin
114+ const coinStub = sinon . stub ( BitGo . prototype , 'coin' ) . returns ( mockCoin as any ) ;
115+
116+ // Make the request to Express
117+ const result = await agent
118+ . post ( `/api/v2/${ coin } /wallet/${ walletId } /address` )
119+ . set ( 'Authorization' , 'Bearer test_access_token_12345' )
120+ . set ( 'Content-Type' , 'application/json' )
121+ . send ( requestBody ) ;
122+
123+ // Verify the response
124+ assert . strictEqual ( result . status , 200 ) ;
125+ result . body . should . have . property ( 'id' , '68ed4dbfe664aa98d171ac0f524ef111' ) ;
126+ result . body . should . have . property ( 'address' , '2N632etYgykMhGrScEN7RpAthg64ACsTL6v' ) ;
127+ result . body . should . have . property ( 'chain' , 0 ) ;
128+ result . body . should . have . property ( 'index' , 1 ) ;
129+ result . body . should . have . property ( 'coin' , 'tbtc' ) ;
130+ result . body . should . have . property ( 'wallet' , walletId ) ;
131+ result . body . should . have . property ( 'label' , 'My new address' ) ;
132+ result . body . should . have . property ( 'addressType' , 'p2sh' ) ;
133+ result . body . should . have . property ( 'keychains' ) ;
134+ result . body . keychains . should . be . Array ( ) ;
135+ result . body . keychains . should . have . length ( 3 ) ;
136+
137+ // Verify the keychain structure
138+ result . body . keychains [ 0 ] . should . have . property ( 'source' , 'user' ) ;
139+ result . body . keychains [ 1 ] . should . have . property ( 'source' , 'backup' ) ;
140+ result . body . keychains [ 2 ] . should . have . property ( 'source' , 'bitgo' ) ;
141+ result . body . keychains [ 2 ] . should . have . property ( 'isBitGo' , true ) ;
142+
143+ // Verify that the correct BitGoJS methods were called
144+ assert . strictEqual ( coinStub . calledOnceWith ( coin ) , true ) ;
145+ assert . strictEqual ( mockCoin . wallets . calledOnce , true ) ;
146+ assert . strictEqual ( walletsGetStub . calledOnceWith ( { id : walletId } ) , true ) ;
147+ assert . strictEqual (
148+ mockWallet . createAddress . calledOnceWith (
149+ sinon . match ( {
150+ coin : coin ,
151+ id : walletId ,
152+ label : 'My new address' ,
153+ chain : 0 ,
154+ allowSkipVerifyAddress : true ,
155+ } )
156+ ) ,
157+ true
158+ ) ;
159+ } ) ;
160+ } ) ;
161+
29162 describe ( 'CreateAddressParams' , function ( ) {
30163 it ( 'should validate params with required coin and id' , function ( ) {
31164 const validParams = {
0 commit comments