@@ -5,6 +5,19 @@ jest.mock('../src/utils/wallet', () => ({
55 getSelectedNetwork : jest . fn ( ( ) => 'bitcoin' ) ,
66} ) ) ;
77
8+ jest . mock ( '../src/store/helpers' , ( ) => ( {
9+ getFeesStore : jest . fn ( ( ) => ( {
10+ onchain : {
11+ fast : 50 ,
12+ normal : 40 ,
13+ slow : 30 ,
14+ minimum : 20 ,
15+ timestamp : Date . now ( ) - 30 * 60 * 1000 , // 30 minutes ago
16+ } ,
17+ override : false ,
18+ } ) ) ,
19+ } ) ) ;
20+
821describe ( 'getFees' , ( ) => {
922 const MEMPOOL_URL = 'https://mempool.space/api/v1/fees/recommended' ;
1023 const BLOCKTANK_URL = 'https://api1.blocktank.to/api/info' ;
@@ -45,134 +58,146 @@ describe('getFees', () => {
4558 } ) ;
4659 } ) ;
4760
48- it ( 'should use mempool.space when both APIs succeed' , async ( ) => {
61+ it ( 'should use blocktank when both APIs succeed' , async ( ) => {
4962 const result = await getFees ( ) ;
5063
5164 expect ( result ) . toEqual ( {
52- maxAllowedNonAnchorChannelRemoteFee : Math . max ( 25 , 111 * 10 ) ,
53- minAllowedAnchorChannelRemoteFee : 108 ,
54- minAllowedNonAnchorChannelRemoteFee : 107 ,
55- anchorChannelFee : 109 ,
56- nonAnchorChannelFee : 110 ,
57- channelCloseMinimum : 108 ,
58- outputSpendingFee : 111 ,
59- maximumFeeEstimate : 111 * 10 ,
60- urgentOnChainSweep : 111 ,
65+ maxAllowedNonAnchorChannelRemoteFee : Math . max ( 25 , 999 * 10 ) ,
66+ minAllowedAnchorChannelRemoteFee : 997 ,
67+ minAllowedNonAnchorChannelRemoteFee : 996 ,
68+ anchorChannelFee : 997 ,
69+ nonAnchorChannelFee : 998 ,
70+ channelCloseMinimum : 997 ,
71+ outputSpendingFee : 999 ,
72+ maximumFeeEstimate : 999 * 10 ,
73+ urgentOnChainSweep : 999 ,
6174 } ) ;
6275 expect ( fetch ) . toHaveBeenCalledTimes ( 2 ) ;
6376 expect ( fetch ) . toHaveBeenCalledWith ( MEMPOOL_URL ) ;
6477 expect ( fetch ) . toHaveBeenCalledWith ( BLOCKTANK_URL ) ;
6578 } ) ;
6679
67- it ( 'should use blocktank when mempool.space fails' , async ( ) => {
80+ it ( 'should use mempool.space when blocktank fails' , async ( ) => {
6881 ( global . fetch as jest . Mock ) = jest . fn ( url => {
69- if ( url === MEMPOOL_URL ) {
70- return Promise . reject ( 'Mempool failed' ) ;
71- }
7282 if ( url === BLOCKTANK_URL ) {
83+ return Promise . reject ( 'Blocktank failed' ) ;
84+ }
85+ if ( url === MEMPOOL_URL ) {
7386 return Promise . resolve ( {
7487 ok : true ,
75- json : ( ) => Promise . resolve ( mockBlocktankResponse ) ,
88+ json : ( ) => Promise . resolve ( mockMempoolResponse ) ,
7689 } ) ;
7790 }
7891 return Promise . reject ( new Error ( `Unexpected URL: ${ url } ` ) ) ;
7992 } ) ;
8093
8194 const result = await getFees ( ) ;
8295 expect ( result ) . toEqual ( {
83- maxAllowedNonAnchorChannelRemoteFee : Math . max ( 25 , 999 * 10 ) ,
84- minAllowedAnchorChannelRemoteFee : 997 ,
85- minAllowedNonAnchorChannelRemoteFee : 996 ,
86- anchorChannelFee : 997 ,
87- nonAnchorChannelFee : 998 ,
88- channelCloseMinimum : 997 ,
89- outputSpendingFee : 999 ,
90- maximumFeeEstimate : 999 * 10 ,
91- urgentOnChainSweep : 999 ,
96+ maxAllowedNonAnchorChannelRemoteFee : Math . max ( 25 , 111 * 10 ) ,
97+ minAllowedAnchorChannelRemoteFee : 108 ,
98+ minAllowedNonAnchorChannelRemoteFee : 107 ,
99+ anchorChannelFee : 109 ,
100+ nonAnchorChannelFee : 110 ,
101+ channelCloseMinimum : 108 ,
102+ outputSpendingFee : 111 ,
103+ maximumFeeEstimate : 111 * 10 ,
104+ urgentOnChainSweep : 111 ,
92105 } ) ;
93106 expect ( fetch ) . toHaveBeenCalledTimes ( 3 ) ;
94107 } ) ;
95108
96- it ( 'should retry mempool once and succeed even if blocktank fails' , async ( ) => {
97- let mempoolAttempts = 0 ;
109+ it ( 'should retry blocktank once and succeed even if mempool fails' , async ( ) => {
110+ let blocktankAttempts = 0 ;
98111 ( global . fetch as jest . Mock ) = jest . fn ( url => {
99- if ( url === MEMPOOL_URL ) {
100- mempoolAttempts ++ ;
101- return mempoolAttempts === 1
102- ? Promise . reject ( 'First mempool try failed' )
112+ if ( url === BLOCKTANK_URL ) {
113+ blocktankAttempts ++ ;
114+ return blocktankAttempts === 1
115+ ? Promise . reject ( 'First blocktank try failed' )
103116 : Promise . resolve ( {
104117 ok : true ,
105- json : ( ) => Promise . resolve ( mockMempoolResponse ) ,
118+ json : ( ) => Promise . resolve ( mockBlocktankResponse ) ,
106119 } ) ;
107120 }
108- if ( url === BLOCKTANK_URL ) {
109- return Promise . reject ( 'Blocktank failed' ) ;
121+ if ( url === MEMPOOL_URL ) {
122+ return Promise . reject ( 'Mempool failed' ) ;
110123 }
111124 return Promise . reject ( new Error ( `Unexpected URL: ${ url } ` ) ) ;
112125 } ) ;
113126
114127 const result = await getFees ( ) ;
115- expect ( result . urgentOnChainSweep ) . toBe ( 111 ) ;
128+ expect ( result . urgentOnChainSweep ) . toBe ( 999 ) ;
116129 expect ( fetch ) . toHaveBeenCalledTimes ( 4 ) ;
117130 expect ( fetch ) . toHaveBeenCalledWith ( MEMPOOL_URL ) ;
118131 expect ( fetch ) . toHaveBeenCalledWith ( BLOCKTANK_URL ) ;
119132 } ) ;
120133
121- it ( 'should throw error when all fetches fail' , async ( ) => {
134+ it ( 'should use cached fees when all fetches fail' , async ( ) => {
122135 ( global . fetch as jest . Mock ) = jest . fn ( url => {
123136 if ( url === MEMPOOL_URL || url === BLOCKTANK_URL ) {
124137 return Promise . reject ( 'API failed' ) ;
125138 }
126139 return Promise . reject ( new Error ( `Unexpected URL: ${ url } ` ) ) ;
127140 } ) ;
128141
129- await expect ( getFees ( ) ) . rejects . toThrow ( ) ;
142+ const result = await getFees ( ) ;
143+ // Should use cached fees from mock (30 minutes old)
144+ expect ( result ) . toEqual ( {
145+ maxAllowedNonAnchorChannelRemoteFee : Math . max ( 25 , 50 * 10 ) ,
146+ minAllowedAnchorChannelRemoteFee : 20 ,
147+ minAllowedNonAnchorChannelRemoteFee : 19 ,
148+ anchorChannelFee : 30 ,
149+ nonAnchorChannelFee : 40 ,
150+ channelCloseMinimum : 20 ,
151+ outputSpendingFee : 50 ,
152+ maximumFeeEstimate : 50 * 10 ,
153+ urgentOnChainSweep : 50 ,
154+ } ) ;
130155 expect ( fetch ) . toHaveBeenCalledTimes ( 4 ) ;
131156 } ) ;
132157
133- it ( 'should handle invalid mempool response' , async ( ) => {
158+ it ( 'should handle invalid blocktank response and use mempool ' , async ( ) => {
134159 ( global . fetch as jest . Mock ) = jest . fn ( url => {
135- if ( url === MEMPOOL_URL ) {
160+ if ( url === BLOCKTANK_URL ) {
136161 return Promise . resolve ( {
137162 ok : true ,
138- json : ( ) => Promise . resolve ( { fastestFee : 0 } ) ,
163+ json : ( ) => Promise . resolve ( { onchain : { feeRates : { fast : 0 } } } ) ,
139164 } ) ;
140165 }
141- if ( url === BLOCKTANK_URL ) {
166+ if ( url === MEMPOOL_URL ) {
142167 return Promise . resolve ( {
143168 ok : true ,
144- json : ( ) => Promise . resolve ( mockBlocktankResponse ) ,
169+ json : ( ) => Promise . resolve ( mockMempoolResponse ) ,
145170 } ) ;
146171 }
147172 return Promise . reject ( new Error ( `Unexpected URL: ${ url } ` ) ) ;
148173 } ) ;
149174
150175 const result = await getFees ( ) ;
151- expect ( result . urgentOnChainSweep ) . toBe ( 999 ) ;
176+ expect ( result . urgentOnChainSweep ) . toBe ( 111 ) ;
152177 } ) ;
153178
154- it ( 'should handle invalid blocktank response' , async ( ) => {
179+ it ( 'should handle invalid mempool response and use blocktank ' , async ( ) => {
155180 ( global . fetch as jest . Mock ) = jest . fn ( url => {
156181 if ( url === MEMPOOL_URL ) {
157182 return Promise . resolve ( {
158183 ok : true ,
159- json : ( ) => Promise . resolve ( mockMempoolResponse ) ,
184+ json : ( ) => Promise . resolve ( { fastestFee : 0 } ) ,
160185 } ) ;
161186 }
162187 if ( url === BLOCKTANK_URL ) {
163188 return Promise . resolve ( {
164189 ok : true ,
165- json : ( ) => Promise . resolve ( { onchain : { feeRates : { fast : 0 } } } ) ,
190+ json : ( ) => Promise . resolve ( mockBlocktankResponse ) ,
166191 } ) ;
167192 }
168193 return Promise . reject ( new Error ( `Unexpected URL: ${ url } ` ) ) ;
169194 } ) ;
170195
171196 const result = await getFees ( ) ;
172- expect ( result . urgentOnChainSweep ) . toBe ( 111 ) ;
197+ expect ( result . urgentOnChainSweep ) . toBe ( 999 ) ;
173198 } ) ;
174199
175- it ( 'should handle timeout errors gracefully ' , async ( ) => {
200+ it ( 'should use cached fees when all requests timeout ' , async ( ) => {
176201 jest . useFakeTimers ( ) ;
177202
178203 ( global . fetch as jest . Mock ) = jest . fn ( url => {
@@ -199,7 +224,9 @@ describe('getFees', () => {
199224
200225 jest . advanceTimersByTime ( 11000 ) ;
201226
202- await expect ( feesPromise ) . rejects . toThrow ( ) ;
227+ const result = await feesPromise ;
228+ // Should use cached fees when all requests timeout
229+ expect ( result . urgentOnChainSweep ) . toBe ( 50 ) ;
203230 expect ( fetch ) . toHaveBeenCalledTimes ( 2 ) ;
204231
205232 jest . useRealTimers ( ) ;
0 commit comments