Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/longbridge-tools/src/normalizer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, expect, it } from 'bun:test';
import { existsSync } from 'node:fs';
import {
classifyPortfolioFailure,
computeUnrealizedPnLPercent,
Expand All @@ -7,7 +8,7 @@ import {
} from './normalizer.ts';
import { parsePortfolioResponse } from './parser.ts';
import { LongBridgeError } from './errors.ts';
import { loadFixture, loadFixtureText } from './testing/load-fixture.ts';
import { fixturePath, loadFixture, loadFixtureText } from './testing/load-fixture.ts';
import type { Holding, PortfolioSnapshot } from '@finagent/core';

// ── UI-visible invariants (spec §15, §17) ─────────────────────────────────
Expand Down Expand Up @@ -161,7 +162,9 @@ describe('normalizePortfolioSnapshot fixture matrix (spec §59)', () => {
});

describe('real CLI fixture', () => {
it('maps the real `longbridge portfolio --format json` body (totals + holdings + base USD)', () => {
// The `portfolio` fixture is captured from a real authenticated account and
// never committed; skip when absent (see .gitignore + capture.sh).
it.skipIf(!existsSync(fixturePath('portfolio')))('maps the real `longbridge portfolio --format json` body (totals + holdings + base USD)', () => {
const raw = JSON.parse(loadFixtureText('portfolio'));
const snapshot = normalizePortfolioSnapshot(raw, 1000);
expect(snapshot.baseCurrency).toBe('USD');
Expand Down
22 changes: 15 additions & 7 deletions packages/longbridge-tools/src/phase-two.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { beforeEach, describe, expect, it, mock } from 'bun:test';
import { loadFixture } from './testing/load-fixture.ts';
import { existsSync } from 'node:fs';
import { fixturePath, loadFixture } from './testing/load-fixture.ts';
import { LongBridgeError } from './errors.ts';

// Account-scoped fixtures (positions/assets/cash-flow/portfolio) are captured
// from a real authenticated account and never committed (see .gitignore +
// capture.sh). Skip the tests that need them when they are absent, so a fresh
// clone keeps a green suite; maintainers with a captured account still get the
// coverage.
const hasAccountFixture = (name: string): boolean => existsSync(fixturePath(name));

type ExecaResult = { stdout: string };
type ExecaHandler = (
command: string,
Expand Down Expand Up @@ -135,22 +143,22 @@ describe('phase-2 parser normalization', () => {
expect(events).toEqual([]);
});

it('parses positions fixture', () => {
it.skipIf(!hasAccountFixture('positions'))('parses positions fixture', () => {
const positions = parsePositionsResponse(JSON.stringify(loadFixture('positions')));
expect(positions.length).toBeGreaterThan(0);
expect(typeof positions[0].quantity).toBe('number');
expect(typeof positions[0].costPrice).toBe('number');
expect(positions[0].symbol).toBeTruthy();
});

it('parses assets fixture', () => {
it.skipIf(!hasAccountFixture('assets'))('parses assets fixture', () => {
const assets = parseAssetsResponse(JSON.stringify(loadFixture('assets')));
expect(assets.length).toBeGreaterThan(0);
expect(typeof assets[0].netAssets).toBe('number');
expect(assets[0].cashInfos.length).toBeGreaterThan(0);
});

it('parses cash-flow fixture', () => {
it.skipIf(!hasAccountFixture('cash-flow'))('parses cash-flow fixture', () => {
const flows = parseCashFlowResponse(JSON.stringify(loadFixture('cash-flow')));
expect(flows.length).toBeGreaterThan(0);
expect(typeof flows[0].amount).toBe('number');
Expand Down Expand Up @@ -227,13 +235,13 @@ describe('phase-2 argv construction', () => {
);
});

it('getAccountPositions takes no symbol', async () => {
it.skipIf(!hasAccountFixture('positions'))('getAccountPositions takes no symbol', async () => {
execaHandler = async () => ({ stdout: JSON.stringify(loadFixture('positions')) });
await getAccountPositions();
expect(execaMock).toHaveBeenCalledWith('longbridge', ['positions', '--format', 'json'], expect.any(Object));
});

it('getAssets forwards optional currency', async () => {
it.skipIf(!hasAccountFixture('assets'))('getAssets forwards optional currency', async () => {
execaHandler = async () => ({ stdout: JSON.stringify(loadFixture('assets')) });
await getAssets('HKD');
expect(execaMock).toHaveBeenCalledWith(
Expand All @@ -243,7 +251,7 @@ describe('phase-2 argv construction', () => {
);
});

it('getCashFlow forwards date range', async () => {
it.skipIf(!hasAccountFixture('cash-flow'))('getCashFlow forwards date range', async () => {
execaHandler = async () => ({ stdout: JSON.stringify(loadFixture('cash-flow')) });
await getCashFlow({ start: '2026-01-01', end: '2026-03-31' });
expect(execaMock).toHaveBeenCalledWith(
Expand Down
Loading