@@ -190,6 +190,72 @@ func TestCurrenciesService(t *testing.T) {
190190 })
191191 })
192192
193+ t .Run ("ListWithCostBasis" , func (t * testing.T ) {
194+ // given:
195+ // - a custom currency with an active USD cost basis
196+ testCases := []struct {
197+ name string
198+ currencyType * currencies.CurrencyType
199+ expectedTotal int
200+ expectFiatResult bool
201+ }{
202+ {
203+ name : "custom currencies" ,
204+ currencyType : lo .ToPtr (currencies .CurrencyTypeCustom ),
205+ expectedTotal : 1 ,
206+ },
207+ {
208+ name : "custom and fiat currencies" ,
209+ expectedTotal : 2 ,
210+ expectFiatResult : true ,
211+ },
212+ }
213+
214+ for _ , testCase := range testCases {
215+ t .Run (testCase .name , func (t * testing.T ) {
216+ // when:
217+ // - currencies are listed with cost-basis data expanded
218+ result , err := env .Service .ListCurrencies (t .Context (), currencies.ListCurrenciesInput {
219+ Page : pagination .NewPage (1 , 10 ),
220+ Namespace : namespace ,
221+ CurrencyType : testCase .currencyType ,
222+ Code : & filter.FilterString {
223+ In : lo .ToPtr ([]string {"TOKENS" , "USD" }),
224+ },
225+ CurrencyExpandOptions : currencies.CurrencyExpandOptions {
226+ CostBasis : true ,
227+ },
228+ })
229+
230+ // then:
231+ // - custom currencies include active cost-basis data while fiat currencies do not
232+ require .NoError (t , err )
233+ require .Equal (t , testCase .expectedTotal , result .TotalCount )
234+ require .Len (t , result .Items , testCase .expectedTotal )
235+
236+ customCurrencies := lo .Filter (result .Items , func (item currencies.Currency , _ int ) bool {
237+ return item .Type () == currencyx .CurrencyTypeCustom
238+ })
239+ require .Len (t , customCurrencies , 1 )
240+ assert .Equal (t , createdCurrency .ID , customCurrencies [0 ].ID )
241+ require .NotNil (t , customCurrencies [0 ].CostBasis )
242+ require .Len (t , * customCurrencies [0 ].CostBasis , 1 )
243+ assert .Equal (t , usd .ID , (* customCurrencies [0 ].CostBasis )[0 ].ID )
244+
245+ fiatCurrencies := lo .Filter (result .Items , func (item currencies.Currency , _ int ) bool {
246+ return item .Type () == currencyx .CurrencyTypeFiat
247+ })
248+ if testCase .expectFiatResult {
249+ require .Len (t , fiatCurrencies , 1 )
250+ assert .Equal (t , currencyx .Code ("USD" ), fiatCurrencies [0 ].Details ().Code )
251+ assert .Nil (t , fiatCurrencies [0 ].CostBasis )
252+ } else {
253+ assert .Empty (t , fiatCurrencies )
254+ }
255+ })
256+ }
257+ })
258+
193259 t .Run ("Multiple" , func (t * testing.T ) {
194260 // given:
195261 // - a custom currency with an active USD cost basis
@@ -318,6 +384,94 @@ func TestCurrenciesService(t *testing.T) {
318384 })
319385 })
320386
387+ t .Run ("ListDeletedCurrencyWithCostBasis" , func (t * testing.T ) {
388+ // given:
389+ // - a deleted custom currency whose cost-basis history contains entries that were expired, active, and future at deletion
390+ deletedNamespace := currenciestestutils .NewTestNamespace (t )
391+ deletedCurrency , err := env .Service .CreateCurrency (t .Context (), currencies.CreateCurrencyInput {
392+ Namespace : deletedNamespace ,
393+ CurrencyDetails : currencyx.CurrencyDetails {
394+ Code : "CREDITS" ,
395+ Name : "Credits" ,
396+ Symbol : "C" ,
397+ Precision : 2 ,
398+ DecimalMark : "." ,
399+ ThousandsSeparator : "," ,
400+ },
401+ })
402+ require .NoError (t , err )
403+
404+ deletedAt := now .Add (- 24 * time .Hour )
405+ costBasisFixtures := []struct {
406+ fiatCode currencyx.Code
407+ effectiveFrom time.Time
408+ effectiveTo * time.Time
409+ active bool
410+ }{
411+ {
412+ fiatCode : "EUR" ,
413+ effectiveFrom : deletedAt .Add (- 48 * time .Hour ),
414+ effectiveTo : lo .ToPtr (deletedAt .Add (- time .Hour )),
415+ },
416+ {
417+ fiatCode : "USD" ,
418+ effectiveFrom : deletedAt .Add (- time .Hour ),
419+ effectiveTo : lo .ToPtr (deletedAt .Add (time .Hour )),
420+ active : true ,
421+ },
422+ {
423+ fiatCode : "GBP" ,
424+ effectiveFrom : deletedAt .Add (time .Hour ),
425+ },
426+ }
427+
428+ var activeCostBasisID string
429+ for _ , fixture := range costBasisFixtures {
430+ costBasis , err := env .Client .CurrencyCostBasis .Create ().
431+ SetNamespace (deletedNamespace ).
432+ SetCurrencyID (deletedCurrency .ID ).
433+ SetFiatCode (fixture .fiatCode ).
434+ SetRate (alpacadecimal .RequireFromString ("1" )).
435+ SetEffectiveFrom (fixture .effectiveFrom ).
436+ SetNillableEffectiveTo (fixture .effectiveTo ).
437+ SetDeletedAt (deletedAt ).
438+ Save (t .Context ())
439+ require .NoError (t , err )
440+
441+ if fixture .active {
442+ activeCostBasisID = costBasis .ID
443+ }
444+ }
445+ require .NotEmpty (t , activeCostBasisID )
446+
447+ _ , err = env .Client .CustomCurrency .UpdateOneID (deletedCurrency .ID ).
448+ SetDeletedAt (deletedAt ).
449+ Save (t .Context ())
450+ require .NoError (t , err )
451+
452+ // when:
453+ // - the deleted currency is listed with cost-basis data expanded
454+ result , err := env .Service .ListCurrencies (t .Context (), currencies.ListCurrenciesInput {
455+ Page : pagination .NewPage (1 , 10 ),
456+ Namespace : deletedNamespace ,
457+ CurrencyType : lo .ToPtr (currencies .CurrencyTypeCustom ),
458+ Code : & filter.FilterString {
459+ In : lo .ToPtr ([]string {"CREDITS" }),
460+ },
461+ CurrencyExpandOptions : currencies.CurrencyExpandOptions {
462+ CostBasis : true ,
463+ },
464+ })
465+
466+ // then:
467+ // - only the cost basis effective at the currency's deletion time is returned
468+ require .NoError (t , err )
469+ require .Len (t , result .Items , 1 )
470+ require .NotNil (t , result .Items [0 ].CostBasis )
471+ require .Len (t , * result .Items [0 ].CostBasis , 1 )
472+ assert .Equal (t , activeCostBasisID , (* result .Items [0 ].CostBasis )[0 ].ID )
473+ })
474+
321475 t .Run ("List" , func (t * testing.T ) {
322476 // given:
323477 // - independently persisted custom currencies
0 commit comments