fix: harden auth and investment synchronization
Backend security / security (push) Successful in 8s
Backend verify / verify (push) Successful in 11m9s
Conventional commits / commits (pull_request) Successful in 28s
Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 10m38s
Backend security / security (push) Successful in 8s
Backend verify / verify (push) Successful in 11m9s
Conventional commits / commits (pull_request) Successful in 28s
Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 10m38s
Resolve A01-A10 and C01-C07 from the backend audit. Preserve auth failures, forced candle reconciliation and forecast freshness. Validate MOEX responses and nested HTTP errors; remove unused legacy paths. Verified: 209 unit tests, 262 SQLite e2e tests on Linux, OpenAPI and durability. Record the remaining macOS native SQLite abort in the audit.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
jest.mock('../infrastructure/candle.repository', () => ({
|
||||
CandleRepository: class {},
|
||||
}));
|
||||
jest.mock('./catalog-enrichment.service', () => ({
|
||||
CatalogEnrichmentService: class {},
|
||||
}));
|
||||
jest.mock('../../../../integrations/tbank/tbank.api', () => ({
|
||||
TbankAccessService: class {},
|
||||
TbankClient: class {},
|
||||
}));
|
||||
|
||||
import { CandleService } from './candle.service';
|
||||
|
||||
describe('CandleService forced demand', () => {
|
||||
it.each([false, true])(
|
||||
'reconciles the full range through the latest forced timestamp while force=%s is loading',
|
||||
async (initialForce) => {
|
||||
let entered = () => {};
|
||||
let release = () => {};
|
||||
let complete = () => {};
|
||||
const started = new Promise<void>((resolve) => (entered = resolve));
|
||||
const blocked = new Promise<void>((resolve) => (release = resolve));
|
||||
const finished = new Promise<void>((resolve) => (complete = resolve));
|
||||
let completed = 0;
|
||||
const work = { id: 'work', attemptId: 'attempt' };
|
||||
const repository = {
|
||||
reserve: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ work, launch: true })
|
||||
.mockResolvedValueOnce({
|
||||
work: { id: 'other', attemptId: 'other-attempt' },
|
||||
launch: true,
|
||||
})
|
||||
.mockResolvedValue({ work, launch: false }),
|
||||
read: jest.fn().mockResolvedValue({
|
||||
coverage: [
|
||||
{
|
||||
from: new Date('2026-01-01Z'),
|
||||
to: new Date('2026-02-01Z'),
|
||||
isComplete: true,
|
||||
fetchedAt: new Date('2026-02-01Z'),
|
||||
},
|
||||
],
|
||||
}),
|
||||
stage: jest.fn().mockResolvedValue(true),
|
||||
publish: jest.fn((_attempt, _intent, done = true) => {
|
||||
if (done && ++completed === 2) complete();
|
||||
return Promise.resolve(true);
|
||||
}),
|
||||
fail: jest.fn(),
|
||||
};
|
||||
const sources = {
|
||||
ensureDetail: jest.fn().mockResolvedValue({
|
||||
first_1dayCandleDate: {
|
||||
seconds: String(Date.parse('2026-01-01Z') / 1000),
|
||||
},
|
||||
}),
|
||||
};
|
||||
const upstream = { getCandles: jest.fn().mockResolvedValue([]) };
|
||||
upstream.getCandles.mockImplementationOnce(async () => {
|
||||
entered();
|
||||
await blocked;
|
||||
return [];
|
||||
});
|
||||
const service = new CandleService(
|
||||
repository as never,
|
||||
sources as never,
|
||||
{ tokenForAttempt: jest.fn().mockResolvedValue('token') } as never,
|
||||
upstream as never,
|
||||
{ event: jest.fn() } as never,
|
||||
);
|
||||
const input = {
|
||||
uid: 'uid',
|
||||
instrumentType: 'share' as const,
|
||||
period: 'all' as const,
|
||||
};
|
||||
await service.request(
|
||||
'owner',
|
||||
'revision',
|
||||
input,
|
||||
new Date('2026-02-02T12:00:00Z'),
|
||||
initialForce,
|
||||
2,
|
||||
);
|
||||
await started;
|
||||
const latest = new Date('2026-02-02T13:00:00Z');
|
||||
await service.request(
|
||||
'owner',
|
||||
'revision',
|
||||
{ ...input, uid: 'other' },
|
||||
latest,
|
||||
false,
|
||||
1,
|
||||
);
|
||||
expect(
|
||||
await service.request('owner', 'revision', input, latest, true, 0),
|
||||
).toBe('work');
|
||||
release();
|
||||
await finished;
|
||||
|
||||
expect(upstream.getCandles).toHaveBeenCalledWith(
|
||||
'token',
|
||||
'uid',
|
||||
new Date('2026-01-01Z'),
|
||||
latest,
|
||||
);
|
||||
expect(repository.fail).not.toHaveBeenCalled();
|
||||
expect(
|
||||
upstream.getCandles.mock.calls.map((call: unknown[]) => call[1]),
|
||||
).toEqual(['uid', 'uid', 'other']);
|
||||
},
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user