Some checks failed
deploy / deploy (push) Failing after 40s
Proof: targeted pair-native strategy, preflight, outcome, dashboard, and ops tests pass; full npm test passes 237/237; operator dashboard production bundle builds; ops watcher Python test passes. Assumptions: DB asset, pair, strategy config, and price route rows remain canonical; legacy EURe fields stay only for old-row/API compatibility; local shell has no Kubernetes context for direct live namespace recheck. Still fake: venue-native terminal fill ids and realized fee/PnL attribution remain unavailable; live deployment verification must happen through the repo workflow because manual cluster repair is out of scope.
127 lines
6.3 KiB
JavaScript
127 lines
6.3 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
|
|
const strategySource = readFileSync(new URL('../src/operator-dashboard/static/pages/StrategyPage.jsx', import.meta.url), 'utf8');
|
|
const fundsSource = readFileSync(new URL('../src/operator-dashboard/static/pages/FundsPage.jsx', import.meta.url), 'utf8');
|
|
const stylesSource = readFileSync(new URL('../src/operator-dashboard/static/styles.css', import.meta.url), 'utf8');
|
|
const serviceCardSource = readFileSync(new URL('../src/operator-dashboard/static/components/ServiceCard.jsx', import.meta.url), 'utf8');
|
|
const statusBarSource = readFileSync(new URL('../src/operator-dashboard/static/components/StatusBar.jsx', import.meta.url), 'utf8');
|
|
const systemSource = readFileSync(new URL('../src/operator-dashboard/static/pages/SystemPage.jsx', import.meta.url), 'utf8');
|
|
const appSource = readFileSync(new URL('../src/operator-dashboard/static/App.jsx', import.meta.url), 'utf8');
|
|
|
|
test('strategy page owns consolidated quote lifecycle and successful trade tables', () => {
|
|
assert.match(strategySource, /Quote lifecycle/);
|
|
assert.match(strategySource, /Incoming quotes and what happened next/);
|
|
assert.match(strategySource, /Responded\?/);
|
|
assert.match(strategySource, /Successful trades only/);
|
|
assert.match(strategySource, /Gross edge est\./);
|
|
assert.match(strategySource, /successful_trade_gross_edge_estimate_eure/);
|
|
assert.match(strategySource, /before fees/);
|
|
assert.match(strategySource, /Show lifecycle/);
|
|
assert.match(strategySource, /formatAgeFromTimestamp/);
|
|
assert.match(strategySource, /quote-row-flash/);
|
|
assert.match(strategySource, /Submitted means the relay accepted the response; it does not prove a trade\./);
|
|
assert.doesNotMatch(strategySource, /Actionable|actionable/);
|
|
});
|
|
|
|
test('funds page no longer renders duplicate quote and submission tables', () => {
|
|
assert.doesNotMatch(fundsSource, /Recent incoming quotes/);
|
|
assert.doesNotMatch(fundsSource, /Recent submitted quote terms/);
|
|
assert.doesNotMatch(fundsSource, /Submitted quote responses/);
|
|
assert.doesNotMatch(fundsSource, /Durable ledger/);
|
|
});
|
|
|
|
test('request UI uses pair-native source amount fields and copy', () => {
|
|
assert.match(fundsSource, /name="source_amount"/);
|
|
assert.match(fundsSource, /pair_id: form\.pair_id/);
|
|
assert.match(fundsSource, /Pair request creation/);
|
|
assert.match(fundsSource, /Quoted \$\{item\.quoted_destination_amount\} \$\{destinationSymbol\}/);
|
|
assert.doesNotMatch(fundsSource, /EURe to BTC request creation/);
|
|
assert.doesNotMatch(fundsSource, /EURe-to-BTC/);
|
|
assert.doesNotMatch(fundsSource, /Quoted \$\{item\.quoted_destination_amount\} BTC/);
|
|
assert.doesNotMatch(fundsSource, /source_symbol \|\| 'EURe'/);
|
|
assert.doesNotMatch(fundsSource, /destination_symbol \|\| 'BTC'/);
|
|
});
|
|
|
|
test('funds balance rows expose unvalued valuation reasons', () => {
|
|
assert.match(fundsSource, /valuation_status === 'unvalued'/);
|
|
assert.match(fundsSource, /valuation_route_missing/);
|
|
});
|
|
|
|
test('status bar labels reference routes instead of a single BTC\\/EUR tile', () => {
|
|
assert.match(statusBarSource, /Reference Route/);
|
|
assert.match(statusBarSource, /active_pairs/);
|
|
assert.doesNotMatch(statusBarSource, /Reference BTC\/EUR/);
|
|
});
|
|
|
|
test('dashboard freshness surfaces show age and exact timestamp evidence', () => {
|
|
assert.match(serviceCardSource, /formatAgeFromTimestamp\(service\.freshness_at, now\)/);
|
|
assert.match(serviceCardSource, /formatTimestamp\(service\.freshness_at\)/);
|
|
assert.match(serviceCardSource, /Freshness at/);
|
|
assert.match(stylesSource, /\.quote-row-flash td/);
|
|
assert.match(stylesSource, /@keyframes quote-row-flash-cell/);
|
|
});
|
|
|
|
test('mobile status bar uses normal document flow instead of sticky viewport positioning', () => {
|
|
assert.match(
|
|
stylesSource,
|
|
/@media \(max-width: 720px\)[\s\S]*?\.status-bar \{[\s\S]*?position: static;[\s\S]*?top: auto;[\s\S]*?z-index: auto;[\s\S]*?\}/,
|
|
);
|
|
});
|
|
|
|
|
|
test('dashboard UI exposes official NEAR upstream status separately from local freshness', () => {
|
|
assert.match(statusBarSource, /NEAR Intents/);
|
|
assert.match(statusBarSource, /near_intents_upstream_label/);
|
|
assert.match(statusBarSource, /near_intents_upstream_reason/);
|
|
assert.match(statusBarSource, /Official status at/);
|
|
assert.match(serviceCardSource, /upstream_status/);
|
|
assert.match(serviceCardSource, /Upstream at/);
|
|
assert.match(serviceCardSource, /decisive_reason/);
|
|
});
|
|
|
|
test('asset registry table renders the loaded catalog without a hidden 20 row cap', () => {
|
|
assert.match(strategySource, /items\.map\(\(asset\)/);
|
|
assert.doesNotMatch(strategySource, /items\.slice\(0,\s*20\)\.map/);
|
|
});
|
|
|
|
test('strategy page exposes pair activation, pause, edge, and deposit address controls', () => {
|
|
assert.match(strategySource, /set-pair-mode/);
|
|
assert.match(strategySource, /pause-pair/);
|
|
assert.match(strategySource, /Add, pause, and tune directed pairs/);
|
|
assert.match(strategySource, /Add \/ activate pair/);
|
|
assert.match(strategySource, /pair-edge-bps/);
|
|
assert.match(strategySource, /pair-max-notional/);
|
|
assert.match(strategySource, /Edge bps for/);
|
|
assert.match(strategySource, /Max notional for/);
|
|
assert.match(strategySource, /Init/);
|
|
assert.match(strategySource, /deposit_address/);
|
|
assert.match(strategySource, /Copy/);
|
|
});
|
|
|
|
test('pair controls are rendered before the long asset catalog table', () => {
|
|
assert.ok(
|
|
strategySource.indexOf('<PairConfigSection') < strategySource.indexOf('<AssetCatalogSection'),
|
|
);
|
|
});
|
|
|
|
|
|
test('system page exposes deduped environmental conditions history', () => {
|
|
assert.match(systemSource, /Environmental conditions/);
|
|
assert.match(systemSource, /Stored only when the normalized official status changes/);
|
|
assert.match(systemSource, /NEAR Intents upstream status changes/);
|
|
assert.match(systemSource, /status_fingerprint/);
|
|
});
|
|
|
|
test('dashboard loading state exposes API failures and retry action', () => {
|
|
assert.match(appSource, /Dashboard unavailable/);
|
|
assert.match(appSource, /<LoadingPanel[\s\S]*error=\{state\.error\}/);
|
|
assert.match(appSource, /Retry/);
|
|
assert.match(appSource, /bootDashboard\(\)\.catch/);
|
|
});
|
|
|
|
test('dashboard bootstrap uses an explicit long timeout for slow live state aggregation', () => {
|
|
assert.match(appSource, /const BOOTSTRAP_TIMEOUT_MS = 45_000/);
|
|
assert.match(appSource, /timeoutMs: BOOTSTRAP_TIMEOUT_MS/);
|
|
});
|