mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
2 changed files with 112 additions and 2 deletions
@ -0,0 +1,106 @@ |
|||||
|
import { HttpClient } from '@angular/common/http'; |
||||
|
import { TestBed } from '@angular/core/testing'; |
||||
|
import { of } from 'rxjs'; |
||||
|
|
||||
|
import { ImportActivitiesService } from './import-activities.service'; |
||||
|
|
||||
|
jest.mock('uuid', () => ({ |
||||
|
v4: () => '00000000-0000-4000-8000-000000000001' |
||||
|
})); |
||||
|
|
||||
|
describe('ImportActivitiesService', () => { |
||||
|
let service: ImportActivitiesService; |
||||
|
let post: jest.Mock; |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
post = jest.fn().mockImplementation((_url, { activities }) => { |
||||
|
return of({ activities }); |
||||
|
}); |
||||
|
|
||||
|
TestBed.configureTestingModule({ |
||||
|
providers: [ |
||||
|
ImportActivitiesService, |
||||
|
{ provide: HttpClient, useValue: { post } } |
||||
|
] |
||||
|
}); |
||||
|
|
||||
|
service = TestBed.inject(ImportActivitiesService); |
||||
|
}); |
||||
|
|
||||
|
describe('importCsv', () => { |
||||
|
it.each(['Symbol', 'CODE', 'ticker'])( |
||||
|
'preserves leading zeroes in the %s column', |
||||
|
async (symbolColumn) => { |
||||
|
await service.importCsv({ |
||||
|
fileContent: `${symbolColumn},currency,date,type,quantity,unitPrice,fee\n"0005",HKD,2026-01-02,BUY,10,3.12,-0.5`, |
||||
|
isDryRun: true, |
||||
|
userAccounts: [] |
||||
|
}); |
||||
|
|
||||
|
expect(post).toHaveBeenCalledWith( |
||||
|
'/api/v1/import?dryRun=true', |
||||
|
expect.objectContaining({ |
||||
|
activities: [ |
||||
|
expect.objectContaining({ |
||||
|
currency: 'HKD', |
||||
|
date: new Date(2026, 0, 2).toISOString(), |
||||
|
fee: 0.5, |
||||
|
quantity: 10, |
||||
|
symbol: '0005', |
||||
|
type: 'BUY', |
||||
|
unitPrice: 3.12 |
||||
|
}) |
||||
|
] |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
it.each(['639', '000000', '1E3', 'TRUE', 'FALSE', 'AAPL', '0639.HK'])( |
||||
|
'preserves the symbol %s without CSV type coercion', |
||||
|
async (symbol) => { |
||||
|
await service.importCsv({ |
||||
|
fileContent: `symbol,currency,date,type,quantity,unitPrice,fee\n${symbol},USD,2026-01-02,BUY,1,10,0`, |
||||
|
userAccounts: [] |
||||
|
}); |
||||
|
|
||||
|
expect(post.mock.calls[0][1].activities[0].symbol).toBe(symbol); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
it.each(['', '""'])( |
||||
|
'rejects an empty symbol (%s) before posting the import', |
||||
|
async (symbol) => { |
||||
|
await expect( |
||||
|
service.importCsv({ |
||||
|
fileContent: `symbol,currency,date,type,quantity,unitPrice,fee\n${symbol},USD,2026-01-02,BUY,1,10,0`, |
||||
|
userAccounts: [] |
||||
|
}) |
||||
|
).rejects.toMatchObject({ |
||||
|
message: 'activities.0.symbol is not valid' |
||||
|
}); |
||||
|
|
||||
|
expect(post).not.toHaveBeenCalled(); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
it('preserves numeric names when creating a manual asset profile', async () => { |
||||
|
const result = await service.importCsv({ |
||||
|
fileContent: |
||||
|
'symbol,dataSource,currency,date,type,quantity,unitPrice,fee\n0005,MANUAL,HKD,2026-01-02,BUY,10,3.12,0', |
||||
|
userAccounts: [] |
||||
|
}); |
||||
|
|
||||
|
expect(result.assetProfiles).toEqual([ |
||||
|
expect.objectContaining({ |
||||
|
dataSource: 'MANUAL', |
||||
|
name: '0005', |
||||
|
symbol: '00000000-0000-4000-8000-000000000001' |
||||
|
}) |
||||
|
]); |
||||
|
expect(post.mock.calls[0][1].activities[0].symbol).toBe( |
||||
|
result.assetProfiles[0].symbol |
||||
|
); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue