Files
investor-flow/app/server/src/onboarding/__tests__/starter.test.ts
T
Investor Flow Build ac94acf9e3
CI / Test & Type-Check (push) Canceled after 0s
feat: dealer flow, mirror portfolio (M21), options convexity, FINRA short interest, alert producers, vendor gate
Snapshot of in-progress module work across multiple slices:

- Dealer Flow: dealerExposureEngine, dealerMapService, dealerMapExplain,
  dealerMapIntegrity, dealerMapReplay, dealerStudyEngine, hanStyleLevels
- Mirror Portfolio (M21): fundRepository, captureIngest, mirrorAlertProducers,
  fund holdings strip, live book, position capture ingest
- Options: BSM, NormalizedOptionSurface types, OptionsChainRouter,
  ConvexityGate, option legs panel
- Alert producers: vixLevel, rotation, thesis, unlock, portfolioRisk,
  mirror (fund_capture, fund_13f, mirror_diff)
- FINRA short interest adapter + queue integration
- SEC company tickers adapter + ingest (symbol search index seed)
- Vendor gate (rate-limit-first data plane, ADR-0009)
- CUSIP registry, reverse 13F refresh, stock float service
- LRU cache, portfolio backtest engine
- Frontend: dealer-flow, funds, journal, lab, monitor, plan, portfolio,
  reports, screener, strategies, theses, guided-start, exits, more pages
- Volume profile, workspace profile, visibility-aware poll
- ADRs 0010 (mirror math not advice), 0011 (symbol search index)
- VENDOR_INTEGRATIONS.md, END_USER_TEST.md
- .gitignore: exclude DBs, .DS_Store, local config, agent scratch
2026-08-10 13:36:26 -04:00

138 lines
4.8 KiB
TypeScript

import { describe, it } from "node:test";
import * as assert from "node:assert/strict";
import {
STARTER_WATCHLIST,
STARTER_PACK_FOCUSED,
STARTER_PACK_STANDARD,
ONBOARDING_DISCLAIMER,
defaultDrawdownTolerancePct,
defaultRiskTolerance,
starterPackFor,
resolveWorkspaceDefaults,
complexityFromExperience,
defaultDensity,
type StarterSymbol,
} from "../starter.ts";
const FORBIDDEN = [
/buy\b/i,
/sell\b/i,
/you should\b/i,
/add to your\b/i,
/rotate into\b/i,
/action needed\b/i,
];
function assertNoTradeVerbs(items: StarterSymbol[], label: string) {
for (const item of items) {
const hit = FORBIDDEN.find((rx) => rx.test(item.reason));
assert.equal(hit, undefined, `${label}: forbidden verb in reason for ${item.symbol}`);
assert.ok(item.reason.length > 0, `${label}: empty reason for ${item.symbol}`);
}
}
describe("onboarding/starter", () => {
describe("STARTER_PACK_FOCUSED / STARTER_WATCHLIST", () => {
it("focused pack has liquid exemplars (no thin microcaps as default)", () => {
const symbols = STARTER_PACK_FOCUSED.map((s) => s.symbol);
assert.ok(symbols.includes("VTI"));
assert.ok(symbols.includes("AAPL"));
assert.ok(!symbols.includes("IREN"));
assert.ok(!symbols.includes("CIFR"));
assert.equal(STARTER_WATCHLIST.length, STARTER_PACK_FOCUSED.length);
});
it("every focused entry has tickerKind and reason without trade verbs", () => {
assertNoTradeVerbs(STARTER_PACK_FOCUSED, "focused");
});
});
describe("starterPackFor", () => {
it("standard includes focused symbols plus benchmarks", () => {
const pack = starterPackFor("standard");
const symbols = pack.map((s) => s.symbol);
assert.ok(symbols.includes("VTI"));
assert.ok(symbols.includes("SPY"));
assert.ok(symbols.includes("NVDA"));
assertNoTradeVerbs(pack, "standard");
});
it("full adds optional higher-vol research examples", () => {
const pack = starterPackFor("full");
const symbols = pack.map((s) => s.symbol);
assert.ok(symbols.includes("SPY"));
assert.ok(symbols.includes("IREN") || symbols.includes("BTC"));
assertNoTradeVerbs(pack, "full");
});
});
describe("resolveWorkspaceDefaults", () => {
it("maps never_invested → beginner + focused + focused pack", () => {
const d = resolveWorkspaceDefaults({ experienceStage: "never_invested" });
assert.equal(d.complexity, "beginner");
assert.equal(d.density, "focused");
assert.equal(d.drawdownTolerancePct, -20);
assert.deepEqual(
d.starterPack.map((s) => s.symbol),
STARTER_PACK_FOCUSED.map((s) => s.symbol),
);
});
it("maps active_self_directed → advanced + full", () => {
const d = resolveWorkspaceDefaults({ experienceStage: "active_self_directed" });
assert.equal(d.complexity, "advanced");
assert.equal(d.density, "full");
assert.equal(d.riskTolerance, "aggressive");
});
it("respects explicit density override", () => {
const d = resolveWorkspaceDefaults({
experienceStage: "never_invested",
density: "standard",
goal: "protect",
horizon: "short",
});
assert.equal(d.density, "standard");
assert.equal(d.goal, "protect");
assert.equal(d.horizon, "short");
assert.ok(d.starterPack.length >= STARTER_PACK_STANDARD.length - 1);
});
});
describe("complexityFromExperience / defaultDensity", () => {
it("maps stages", () => {
assert.equal(complexityFromExperience("never_invested"), "beginner");
assert.equal(complexityFromExperience("some_stocks"), "intermediate");
assert.equal(complexityFromExperience("active_self_directed"), "advanced");
assert.equal(defaultDensity("never_invested"), "focused");
assert.equal(defaultDensity("some_stocks"), "standard");
assert.equal(defaultDensity("active_self_directed"), "full");
});
});
describe("defaultDrawdownTolerancePct", () => {
it("returns expected levels", () => {
assert.equal(defaultDrawdownTolerancePct("beginner"), -20);
assert.equal(defaultDrawdownTolerancePct("intermediate"), -30);
assert.equal(defaultDrawdownTolerancePct("advanced"), -40);
});
});
describe("defaultRiskTolerance", () => {
it("returns expected levels", () => {
assert.equal(defaultRiskTolerance("beginner"), "moderate");
assert.equal(defaultRiskTolerance("intermediate"), "moderate");
assert.equal(defaultRiskTolerance("advanced"), "aggressive");
});
});
describe("ONBOARDING_DISCLAIMER", () => {
it("is short operational setup copy", () => {
assert.ok(ONBOARDING_DISCLAIMER.length > 0);
assert.ok(ONBOARDING_DISCLAIMER.toLowerCase().includes("watchlist"));
assert.ok(!ONBOARDING_DISCLAIMER.toLowerCase().includes("educational"));
});
});
});