@@ -5,6 +5,10 @@ import { makeFakeBoard } from '@agoric/vats/tools/board-utils.js';
55import { planDepositTransfers } from '../tools/portfolio-actors.js' ;
66import { makeIssuerKit } from '@agoric/ertp' ;
77import type { TargetAllocation } from '../src/type-guards.js' ;
8+ import { handleDeposit } from '@aglocal/portfolio-planner/src/plan-deposit.ts' ;
9+ import type { VstorageKit } from '@agoric/client-utils' ;
10+ import { SpectrumClient } from '@aglocal/portfolio-planner/src/spectrum-client.ts' ;
11+ import { CosmosRestClient } from '@aglocal/portfolio-planner/src/cosmos-rest-client.ts' ;
812
913const { brand } = makeIssuerKit ( 'USDC' ) ;
1014
@@ -39,7 +43,11 @@ test('planDepositTransfers works in a handful of cases', t => {
3943 Aave_Arbitrum : make ( 100n ) ,
4044 Compound_Arbitrum : make ( 0n ) ,
4145 } ;
42- const targetAllocation2 = { USDN : 40 , Aave_Arbitrum : 40 , Compound_Arbitrum : 20 } ;
46+ const targetAllocation2 : TargetAllocation = {
47+ USDN : 40n ,
48+ Aave_Arbitrum : 40n ,
49+ Compound_Arbitrum : 20n ,
50+ } ;
4351
4452 const result2 = planDepositTransfers (
4553 deposit2 ,
@@ -63,7 +71,11 @@ test('planDepositTransfers works in a handful of cases', t => {
6371 Aave_Arbitrum : make ( 100n ) ,
6472 Compound_Arbitrum : make ( 50n ) ,
6573 } ;
66- const targetAllocation3 = { USDN : 50 , Aave_Arbitrum : 30 , Compound_Arbitrum : 20 } ;
74+ const targetAllocation3 : TargetAllocation = {
75+ USDN : 50n ,
76+ Aave_Arbitrum : 30n ,
77+ Compound_Arbitrum : 20n ,
78+ } ;
6779
6880 const result3 = planDepositTransfers (
6981 deposit3 ,
@@ -89,7 +101,11 @@ test('planDepositTransfers works in a handful of cases', t => {
89101 Aave_Arbitrum : make ( 0n ) ,
90102 Compound_Arbitrum : make ( 0n ) ,
91103 } ;
92- const targetAllocation4 = { USDN : 60 , Aave_Arbitrum : 30 , Compound_Arbitrum : 10 } ;
104+ const targetAllocation4 : TargetAllocation = {
105+ USDN : 60n ,
106+ Aave_Arbitrum : 30n ,
107+ Compound_Arbitrum : 10n ,
108+ } ;
93109
94110 const result4 = planDepositTransfers (
95111 deposit4 ,
@@ -107,7 +123,7 @@ test('planDepositTransfers works in a handful of cases', t => {
107123 // Test case 5: Single position target
108124 const deposit5 = make ( 1000n ) ;
109125 const currentBalances5 = { USDN : make ( 500n ) } ;
110- const targetAllocation5 = { USDN : 100 } ;
126+ const targetAllocation5 : TargetAllocation = { USDN : 100n } ;
111127
112128 const result5 = planDepositTransfers (
113129 deposit5 ,
@@ -120,3 +136,237 @@ test('planDepositTransfers works in a handful of cases', t => {
120136 USDN : make ( 1000n ) ,
121137 } ) ;
122138} ) ;
139+
140+ test ( 'handleDeposit works with mocked dependencies' , async t => {
141+ const make = value => AmountMath . make ( brand , value ) ;
142+ const deposit = make ( 1000n ) ;
143+ const portfolioKey = 'test.portfolios.portfolio1' as const ;
144+
145+ // Mock VstorageKit readPublished
146+ const mockReadPublished = async ( path : string ) => {
147+ if ( path === portfolioKey ) {
148+ return {
149+ positionKeys : [ 'USDN' , 'Aave_Arbitrum' , 'Compound_Arbitrum' ] ,
150+ flowCount : 0 ,
151+ accountIdByChain : {
152+ noble : 'noble:test:addr1' ,
153+ Arbitrum : 'arbitrum:test:addr2' ,
154+ } ,
155+ targetAllocation : {
156+ USDN : 50n ,
157+ Aave_Arbitrum : 30n ,
158+ Compound_Arbitrum : 20n ,
159+ } ,
160+ } ;
161+ }
162+ throw new Error ( `Unexpected path: ${ path } ` ) ;
163+ } ;
164+
165+ // Mock SpectrumClient
166+ class MockSpectrumClient extends SpectrumClient {
167+ async getPoolBalance ( chain : any , pool : any , addr : any ) {
168+ // Return different balances for different pools
169+ if ( pool === 'aave' && chain === 'arbitrum' ) {
170+ return {
171+ pool,
172+ chain,
173+ address : addr ,
174+ balance : { supplyBalance : 100 , borrowAmount : 0 } ,
175+ } ;
176+ }
177+ if ( pool === 'compound' && chain === 'arbitrum' ) {
178+ return {
179+ pool,
180+ chain,
181+ address : addr ,
182+ balance : { supplyBalance : 50 , borrowAmount : 0 } ,
183+ } ;
184+ }
185+ return {
186+ pool,
187+ chain,
188+ address : addr ,
189+ balance : { supplyBalance : 0 , borrowAmount : 0 } ,
190+ } ;
191+ }
192+ }
193+ const mockSpectrumClient = new MockSpectrumClient ( ) ;
194+
195+ // Mock CosmosRestClient
196+ class MockCosmosRestClient extends CosmosRestClient {
197+ async getAccountBalance ( chainName : string , addr : string , denom : string ) {
198+ if ( chainName === 'noble' && denom === 'usdn' ) {
199+ return { denom, amount : '200' } ;
200+ }
201+ return { denom, amount : '0' } ;
202+ }
203+ }
204+ const mockCosmosRestClient = new MockCosmosRestClient ( ) ;
205+
206+ // Mock VstorageKit
207+ const mockVstorageKit : VstorageKit = {
208+ readPublished : mockReadPublished ,
209+ } as VstorageKit ;
210+
211+ try {
212+ await handleDeposit (
213+ deposit ,
214+ portfolioKey ,
215+ mockVstorageKit . readPublished ,
216+ mockSpectrumClient ,
217+ mockCosmosRestClient ,
218+ ) ;
219+ t . fail ( 'Expected handleDeposit to throw Error with "moar TODO"' ) ;
220+ } catch ( error ) {
221+ t . is ( error . message , 'moar TODO' ) ;
222+ }
223+ } ) ;
224+
225+ test ( 'handleDeposit handles missing targetAllocation gracefully' , async t => {
226+ const make = value => AmountMath . make ( brand , value ) ;
227+ const deposit = make ( 1000n ) ;
228+ const portfolioKey = 'test.portfolios.portfolio1' as const ;
229+
230+ // Mock VstorageKit readPublished with no targetAllocation
231+ const mockReadPublished = async ( path : string ) => {
232+ if ( path === portfolioKey ) {
233+ return {
234+ positionKeys : [ 'USDN' , 'Aave_Arbitrum' ] ,
235+ flowCount : 0 ,
236+ accountIdByChain : {
237+ noble : 'noble:test:addr1' ,
238+ Arbitrum : 'arbitrum:test:addr2' ,
239+ } ,
240+ // No targetAllocation
241+ } ;
242+ }
243+ throw new Error ( `Unexpected path: ${ path } ` ) ;
244+ } ;
245+
246+ // Mock SpectrumClient
247+ class MockSpectrumClient2 extends SpectrumClient {
248+ async getPoolBalance ( chain : any , pool : any , addr : any ) {
249+ return {
250+ pool,
251+ chain,
252+ address : addr ,
253+ balance : { supplyBalance : 0 , borrowAmount : 0 } ,
254+ } ;
255+ }
256+ }
257+ const mockSpectrumClient = new MockSpectrumClient2 ( ) ;
258+
259+ // Mock CosmosRestClient
260+ class MockCosmosRestClient2 extends CosmosRestClient {
261+ async getAccountBalance ( chainName : string , addr : string , denom : string ) {
262+ return { denom, amount : '0' } ;
263+ }
264+ }
265+ const mockCosmosRestClient = new MockCosmosRestClient2 ( ) ;
266+
267+ // Mock VstorageKit
268+ const mockVstorageKit : VstorageKit = {
269+ readPublished : mockReadPublished ,
270+ } as VstorageKit ;
271+
272+ const result = await handleDeposit (
273+ deposit ,
274+ portfolioKey ,
275+ mockVstorageKit . readPublished ,
276+ mockSpectrumClient ,
277+ mockCosmosRestClient ,
278+ ) ;
279+
280+ t . is ( result , undefined ) ;
281+ } ) ;
282+
283+ test ( 'handleDeposit handles different position types correctly' , async t => {
284+ const make = value => AmountMath . make ( brand , value ) ;
285+ const deposit = make ( 1000n ) ;
286+ const portfolioKey = 'test.portfolios.portfolio1' as const ;
287+
288+ // Mock VstorageKit readPublished with various position types
289+ const mockReadPublished = async ( path : string ) => {
290+ if ( path === portfolioKey ) {
291+ return {
292+ positionKeys : [
293+ 'USDN' ,
294+ 'USDNVault' ,
295+ 'Aave_Avalanche' ,
296+ 'Compound_Polygon' ,
297+ ] ,
298+ flowCount : 0 ,
299+ accountIdByChain : {
300+ noble : 'noble:test:addr1' ,
301+ Avalanche : 'avalanche:test:addr2' ,
302+ Polygon : 'polygon:test:addr3' ,
303+ } ,
304+ targetAllocation : {
305+ USDN : 40n ,
306+ USDNVault : 20n ,
307+ Aave_Avalanche : 25n ,
308+ Compound_Polygon : 15n ,
309+ } ,
310+ } ;
311+ }
312+ throw new Error ( `Unexpected path: ${ path } ` ) ;
313+ } ;
314+
315+ // Mock SpectrumClient with different chain responses
316+ class MockSpectrumClient3 extends SpectrumClient {
317+ async getPoolBalance ( chain : any , pool : any , addr : any ) {
318+ if ( chain === 'avalanche' && pool === 'aave' ) {
319+ return {
320+ pool,
321+ chain,
322+ address : addr ,
323+ balance : { supplyBalance : 150 , borrowAmount : 0 } ,
324+ } ;
325+ }
326+ if ( chain === 'polygon' && pool === 'compound' ) {
327+ return {
328+ pool,
329+ chain,
330+ address : addr ,
331+ balance : { supplyBalance : 75 , borrowAmount : 0 } ,
332+ } ;
333+ }
334+ return {
335+ pool,
336+ chain,
337+ address : addr ,
338+ balance : { supplyBalance : 0 , borrowAmount : 0 } ,
339+ } ;
340+ }
341+ }
342+ const mockSpectrumClient = new MockSpectrumClient3 ( ) ;
343+
344+ // Mock CosmosRestClient
345+ class MockCosmosRestClient3 extends CosmosRestClient {
346+ async getAccountBalance ( chainName : string , addr : string , denom : string ) {
347+ if ( chainName === 'noble' && denom === 'usdn' ) {
348+ return { denom, amount : '300' } ;
349+ }
350+ return { denom, amount : '0' } ;
351+ }
352+ }
353+ const mockCosmosRestClient = new MockCosmosRestClient3 ( ) ;
354+
355+ // Mock VstorageKit
356+ const mockVstorageKit : VstorageKit = {
357+ readPublished : mockReadPublished ,
358+ } as VstorageKit ;
359+
360+ try {
361+ await handleDeposit (
362+ deposit ,
363+ portfolioKey ,
364+ mockVstorageKit . readPublished ,
365+ mockSpectrumClient ,
366+ mockCosmosRestClient ,
367+ ) ;
368+ t . fail ( 'Expected handleDeposit to throw Error with "moar TODO"' ) ;
369+ } catch ( error ) {
370+ t . is ( error . message , 'moar TODO' ) ;
371+ }
372+ } ) ;
0 commit comments