phase-8:server code enhancements.

This commit is contained in:
Sai Kiran Vella
2026-06-05 22:44:04 -04:00
parent a7108b448a
commit 1e2aac7164
15 changed files with 781 additions and 94 deletions
+166
View File
@@ -0,0 +1,166 @@
/**
* Unit tests for MarketCallRepository
* Each test gets its own temp file so tests are fully isolated.
*/
import { test, after } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { MarketCallRepository } from '../server/repositories/MarketCallRepository';
// ── Temp-file helpers ─────────────────────────────────────────────────────────
const tmpDirs: string[] = [];
function tempRepo(): MarketCallRepository {
const dir = mkdtempSync(join(tmpdir(), 'mkt-calls-test-'));
const path = join(dir, 'calls.json');
tmpDirs.push(dir);
return new MarketCallRepository(path);
}
after(() => {
for (const dir of tmpDirs) {
rmSync(dir, { recursive: true, force: true });
}
});
// ── Fixtures ──────────────────────────────────────────────────────────────────
const CALL_INPUT = {
title: 'Rate pivot play',
quarter: 'Q3 2025',
thesis: 'Fed cuts expected — rotate into duration and growth.',
tickers: ['TLT', 'QQQ'],
};
// ── Tests ─────────────────────────────────────────────────────────────────────
test('list() returns empty array when file does not exist', () => {
const repo = tempRepo();
assert.deepEqual(repo.list(), []);
});
test('create() returns call with id, createdAt, and correct fields', () => {
const repo = tempRepo();
const call = repo.create(CALL_INPUT);
assert.ok(call.id, 'id should be set');
assert.ok(call.createdAt, 'createdAt should be set');
assert.equal(call.title, CALL_INPUT.title);
assert.equal(call.quarter, CALL_INPUT.quarter);
assert.equal(call.thesis, CALL_INPUT.thesis);
assert.deepEqual(call.tickers, CALL_INPUT.tickers);
});
test('create() persists to disk — list() returns the created call', () => {
const repo = tempRepo();
repo.create(CALL_INPUT);
assert.equal(repo.list().length, 1);
assert.equal(repo.list()[0].title, CALL_INPUT.title);
});
test('list() returns calls newest-first', () => {
// Write two calls directly with distinct timestamps to guarantee stable ordering.
const dir = mkdtempSync(join(tmpdir(), 'mkt-order-'));
tmpDirs.push(dir);
const path = join(dir, 'calls.json');
const older = {
id: 'old-id',
title: 'First',
quarter: 'Q1',
date: '2025-01-01',
thesis: 'A',
tickers: [],
snapshot: {},
createdAt: '2025-01-01T00:00:00.000Z',
};
const newer = {
id: 'new-id',
title: 'Second',
quarter: 'Q1',
date: '2025-01-02',
thesis: 'B',
tickers: [],
snapshot: {},
createdAt: '2025-01-02T00:00:00.000Z',
};
writeFileSync(path, JSON.stringify({ calls: [older, newer] }), 'utf8');
const repo = new MarketCallRepository(path);
const list = repo.list();
assert.equal(list[0].id, 'new-id', 'newer call should be first');
assert.equal(list[1].id, 'old-id', 'older call should be second');
});
test('get() returns the call by id', () => {
const repo = tempRepo();
const call = repo.create(CALL_INPUT);
const found = repo.get(call.id);
assert.ok(found, 'should find by id');
assert.equal(found!.id, call.id);
});
test('get() returns null for unknown id', () => {
const repo = tempRepo();
assert.equal(repo.get('nonexistent-id'), null);
});
test('delete() removes the call and returns true', () => {
const repo = tempRepo();
const call = repo.create(CALL_INPUT);
const ok = repo.delete(call.id);
assert.equal(ok, true);
assert.equal(repo.list().length, 0);
assert.equal(repo.get(call.id), null);
});
test('delete() returns false for unknown id', () => {
const repo = tempRepo();
assert.equal(repo.delete('no-such-id'), false);
});
test('delete() only removes the targeted call, leaves others intact', () => {
const repo = tempRepo();
const a = repo.create({ ...CALL_INPUT, title: 'Keep me' });
const b = repo.create({ ...CALL_INPUT, title: 'Delete me' });
repo.delete(b.id);
const list = repo.list();
assert.equal(list.length, 1);
assert.equal(list[0].id, a.id);
});
test('create() stores snapshot when provided', () => {
const repo = tempRepo();
const snapshot = { TLT: { price: 95.5, signal: '✅ Strong Buy' } };
const call = repo.create({ ...CALL_INPUT, snapshot } as any);
const found = repo.get(call.id)!;
assert.deepEqual(found.snapshot, snapshot);
});
test('create() sets default date when not provided', () => {
const repo = tempRepo();
const call = repo.create(CALL_INPUT);
assert.match(call.date, /^\d{4}-\d{2}-\d{2}$/);
});
test('create() uses provided date', () => {
const repo = tempRepo();
const call = repo.create({ ...CALL_INPUT, date: '2025-03-15' });
assert.equal(call.date, '2025-03-15');
});
test('concurrent writes: two rapid creates do not lose data', async () => {
const repo = tempRepo();
// Both writes happen synchronously (writeFileSync), so the second
// always sees the first. This test documents the behaviour.
const a = repo.create({ ...CALL_INPUT, title: 'A' });
const b = repo.create({ ...CALL_INPUT, title: 'B' });
const list = repo.list();
assert.equal(list.length, 2, 'both calls should be persisted');
const ids = new Set(list.map((c) => c.id));
assert.ok(ids.has(a.id), 'call A should be present');
assert.ok(ids.has(b.id), 'call B should be present');
});
+9 -9
View File
@@ -12,7 +12,7 @@ const stubClient = {} as unknown as YahooFinanceClient;
// Cast to any to access private methods — tests exercise internal behaviour directly.
const advisor = new PortfolioAdvisor(stubClient) as any;
// Minimal holding shape used by _position and _advice (only costBasis/shares matter).
// Minimal holding shape used by position and advice (only costBasis/shares matter).
const holding = (costBasis: number, shares: number): PortfolioHolding => ({
ticker: 'TEST',
source: 'Test',
@@ -22,45 +22,45 @@ const holding = (costBasis: number, shares: number): PortfolioHolding => ({
});
test('_position: computes gain/loss correctly', () => {
const pos = advisor._position(holding(100, 10), 150);
const pos = advisor.position(holding(100, 10), 150);
assert.equal(pos.gainLossPct, '50.0');
assert.equal(pos.marketValue, '1500.00');
assert.equal(pos.totalCost, '1000.00');
});
test('_position: returns null gainLoss when price unavailable', () => {
const pos = advisor._position(holding(100, 10), null);
const pos = advisor.position(holding(100, 10), null);
assert.equal(pos.gainLossPct, null);
assert.equal(pos.marketValue, null);
});
test('_advice: Strong Buy → Hold & Add', () => {
const { action } = advisor._advice(SIGNAL.STRONG_BUY, holding(100, 10), 150);
const { action } = advisor.advice(SIGNAL.STRONG_BUY, holding(100, 10), 150);
assert.equal(action, '🟢 Hold & Add');
});
test('_advice: Avoid + loss → Sell (Cut Loss)', () => {
const { action } = advisor._advice(SIGNAL.AVOID, holding(150, 10), 100);
const { action } = advisor.advice(SIGNAL.AVOID, holding(150, 10), 100);
assert.equal(action, '🔴 Sell (Cut Loss)');
});
test('_advice: Avoid + profit → Sell (Take Profits)', () => {
const { action } = advisor._advice(SIGNAL.AVOID, holding(100, 10), 150);
const { action } = advisor.advice(SIGNAL.AVOID, holding(100, 10), 150);
assert.equal(action, '🔴 Sell (Take Profits)');
});
test('_advice: Speculation + >20% gain → Reduce Position', () => {
const { action } = advisor._advice(SIGNAL.SPECULATION, holding(100, 10), 125);
const { action } = advisor.advice(SIGNAL.SPECULATION, holding(100, 10), 125);
assert.equal(action, '🟠 Reduce Position');
});
test('_cryptoAdvice: no price → No price data', () => {
const { action } = advisor._cryptoAdvice(holding(100, 1), null);
const { action } = advisor.cryptoAdvice(holding(100, 1), null);
assert.equal(action, '⚪ No price data');
});
test('_cryptoAdvice: >100% gain → Consider taking profits', () => {
const { action } = advisor._cryptoAdvice(holding(10000, 1), 25000);
const { action } = advisor.cryptoAdvice(holding(10000, 1), 25000);
assert.equal(action, '🟠 Consider taking profits');
});
+214
View File
@@ -0,0 +1,214 @@
/**
* Integration tests for CallsController
* Uses Fastify inject() with an in-memory MarketCallRepository stub.
*/
import { test } from 'node:test';
import assert from 'node:assert/strict';
import Fastify from 'fastify';
import cors from '@fastify/cors';
import { CallsController } from '../server/controllers/calls.controller';
import type { ScreenerEngine } from '../server/services/ScreenerEngine';
import type { YahooFinanceClient } from '../server/clients/YahooFinanceClient';
import type { MarketCall, ScreenerResult, MarketContext, CreateCallInput } from '../server/types';
// ── Stubs ────────────────────────────────────────────────────────────────────
const MARKET_CTX: MarketContext = {
sp500Price: 5000,
riskFreeRate: 4.5,
vixLevel: 18,
rateRegime: 'NORMAL',
volatilityRegime: 'NORMAL',
benchmarks: { marketPE: 22, techPE: 30, reitYield: 3.5, igSpread: 1.0 },
};
const EMPTY_RESULT: ScreenerResult = {
STOCK: [],
ETF: [],
BOND: [],
ERROR: [],
marketContext: MARKET_CTX,
};
const stubEngine = {
screenTickers: async () => EMPTY_RESULT,
} as unknown as ScreenerEngine;
const stubYahoo = {
fetchCalendarEvents: async () => null,
} as unknown as YahooFinanceClient;
// In-memory MarketCallRepository stub
function makeRepoStub() {
const calls: (MarketCall & { createdAt: string })[] = [];
return {
list: () =>
[...calls].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()),
get: (id: string) => calls.find((c) => c.id === id) ?? null,
create: ({
title,
quarter,
date,
thesis,
tickers,
snapshot,
}: CreateCallInput & { snapshot: any }) => {
const call = {
id: `call-${calls.length + 1}`,
title,
quarter,
date: date ?? new Date().toISOString().slice(0, 10),
thesis,
tickers,
snapshot,
createdAt: new Date().toISOString(),
};
calls.push(call);
return call;
},
delete: (id: string) => {
const idx = calls.findIndex((c) => c.id === id);
if (idx === -1) return false;
calls.splice(idx, 1);
return true;
},
};
}
// ── App factory ──────────────────────────────────────────────────────────────
async function buildTestApp() {
const app = Fastify({ logger: false });
await app.register(cors, { origin: '*' });
new CallsController(makeRepoStub() as any, stubEngine, stubYahoo).register(app);
await app.ready();
return app;
}
// ── Tests ────────────────────────────────────────────────────────────────────
test('GET /api/calls → 200 with empty calls list', async () => {
const app = await buildTestApp();
const res = await app.inject({ method: 'GET', url: '/api/calls' });
assert.equal(res.statusCode, 200);
assert.deepEqual(res.json(), { calls: [] });
});
test('POST /api/calls → 201 and returns the created call', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/calls',
payload: {
title: 'Q3 rate pivot play',
quarter: 'Q3 2025',
thesis: 'Fed cuts incoming — rotate into duration and growth.',
tickers: ['TLT', 'QQQ'],
},
});
assert.equal(res.statusCode, 201);
const body = res.json();
assert.equal(body.title, 'Q3 rate pivot play');
assert.deepEqual(body.tickers, ['TLT', 'QQQ']);
assert.ok(body.id);
assert.ok(body.createdAt);
});
test('POST /api/calls → created call appears in GET /api/calls', async () => {
const app = await buildTestApp();
await app.inject({
method: 'POST',
url: '/api/calls',
payload: {
title: 'AI semiconductor cycle',
quarter: 'Q4 2025',
thesis: 'Capex cycle benefits chip designers more than hyperscalers.',
tickers: ['NVDA', 'AMD'],
},
});
const listRes = await app.inject({ method: 'GET', url: '/api/calls' });
assert.equal(listRes.json().calls.length, 1);
assert.equal(listRes.json().calls[0].title, 'AI semiconductor cycle');
});
test('POST /api/calls with missing required fields → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/calls',
payload: { title: 'incomplete' }, // missing quarter, thesis, tickers
});
assert.equal(res.statusCode, 400);
});
test('POST /api/calls with thesis too short → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/calls',
payload: { title: 'Test', quarter: 'Q1', thesis: 'short', tickers: ['AAPL'] },
});
assert.equal(res.statusCode, 400);
});
test('DELETE /api/calls/:id on non-existent id → 404', async () => {
const app = await buildTestApp();
const res = await app.inject({ method: 'DELETE', url: '/api/calls/nonexistent' });
assert.equal(res.statusCode, 404);
});
test('DELETE /api/calls/:id removes the call', async () => {
const app = await buildTestApp();
const created = await app.inject({
method: 'POST',
url: '/api/calls',
payload: {
title: 'Call to delete',
quarter: 'Q1 2025',
thesis: 'This call will be deleted in the test.',
tickers: ['SPY'],
},
});
const { id } = created.json();
const del = await app.inject({ method: 'DELETE', url: `/api/calls/${id}` });
assert.equal(del.statusCode, 200);
assert.deepEqual(del.json(), { ok: true });
const list = await app.inject({ method: 'GET', url: '/api/calls' });
assert.equal(list.json().calls.length, 0);
});
test('GET /api/calls/:id on non-existent id → 404', async () => {
const app = await buildTestApp();
const res = await app.inject({ method: 'GET', url: '/api/calls/no-such-id' });
assert.equal(res.statusCode, 404);
});
test('GET /api/calls/:id returns call with current snapshot shape', async () => {
const app = await buildTestApp();
const created = await app.inject({
method: 'POST',
url: '/api/calls',
payload: {
title: 'Rate trade',
quarter: 'Q2 2025',
thesis: 'Long duration bonds when yield curve inverts.',
tickers: ['TLT'],
},
});
const { id } = created.json();
const res = await app.inject({ method: 'GET', url: `/api/calls/${id}` });
assert.equal(res.statusCode, 200);
const body = res.json();
assert.equal(body.id, id);
assert.ok('current' in body, 'response should include current snapshot');
});
test('GET /api/calls/calendar with no calls → 200 empty events', async () => {
const app = await buildTestApp();
const res = await app.inject({ method: 'GET', url: '/api/calls/calendar' });
assert.equal(res.statusCode, 200);
assert.deepEqual(res.json(), { events: [] });
});
+177
View File
@@ -0,0 +1,177 @@
/**
* Integration tests for FinanceController
* Uses Fastify inject() with stub engine, advisor, and in-memory portfolio repo.
*/
import { test } from 'node:test';
import assert from 'node:assert/strict';
import Fastify from 'fastify';
import cors from '@fastify/cors';
import { FinanceController } from '../server/controllers/finance.controller';
import type { ScreenerEngine } from '../server/services/ScreenerEngine';
import type { PortfolioAdvisor } from '../server/services/PortfolioAdvisor';
import type { PortfolioHolding, MarketContext, ScreenerResult } from '../server/types';
// ── Stubs ────────────────────────────────────────────────────────────────────
const MARKET_CTX: MarketContext = {
sp500Price: 5000,
riskFreeRate: 4.5,
vixLevel: 18,
rateRegime: 'NORMAL',
volatilityRegime: 'NORMAL',
benchmarks: { marketPE: 22, techPE: 30, reitYield: 3.5, igSpread: 1.0 },
};
const EMPTY_RESULT: ScreenerResult = {
STOCK: [],
ETF: [],
BOND: [],
ERROR: [],
marketContext: MARKET_CTX,
};
const stubEngine = {
screenTickers: async () => EMPTY_RESULT,
getMarketContext: async () => MARKET_CTX,
} as unknown as ScreenerEngine;
const stubAdvisor = {
advise: async () => [],
} as unknown as PortfolioAdvisor;
// In-memory PortfolioRepository stub
function makePortfolioRepo(seed: PortfolioHolding[] = []) {
const holdings: PortfolioHolding[] = [...seed];
return {
exists: () => true,
read: () => ({ holdings: [...holdings] }),
upsert: (entry: PortfolioHolding) => {
const idx = holdings.findIndex((h) => h.ticker === entry.ticker);
if (idx >= 0) holdings[idx] = entry;
else holdings.push(entry);
return entry;
},
remove: (ticker: string) => {
const idx = holdings.findIndex((h) => h.ticker === ticker);
if (idx === -1) return false;
holdings.splice(idx, 1);
return true;
},
};
}
function makeEmptyRepo() {
return {
exists: () => false,
read: () => ({ holdings: [] }),
upsert: () => {},
remove: () => false,
};
}
// ── App factory ──────────────────────────────────────────────────────────────
async function buildTestApp(repo = makePortfolioRepo()) {
const app = Fastify({ logger: false });
await app.register(cors, { origin: '*' });
new FinanceController(stubEngine, repo as any, stubAdvisor).register(app);
await app.ready();
return app;
}
// ── Tests ────────────────────────────────────────────────────────────────────
test('GET /api/finance/portfolio → 200 with advice and marketContext keys', async () => {
const app = await buildTestApp();
const res = await app.inject({ method: 'GET', url: '/api/finance/portfolio' });
assert.equal(res.statusCode, 200);
const body = res.json();
assert.ok(Array.isArray(body.advice), 'advice should be array');
assert.ok(body.marketContext, 'marketContext should be present');
});
test('GET /api/finance/portfolio with no portfolio.json → 404', async () => {
const app = await buildTestApp(makeEmptyRepo() as any);
const res = await app.inject({ method: 'GET', url: '/api/finance/portfolio' });
assert.equal(res.statusCode, 404);
});
test('GET /api/finance/market-context → 200 with benchmark fields', async () => {
const app = await buildTestApp();
const res = await app.inject({ method: 'GET', url: '/api/finance/market-context' });
assert.equal(res.statusCode, 200);
const body = res.json();
assert.ok(typeof body.riskFreeRate === 'number');
assert.ok(typeof body.sp500Price === 'number');
assert.ok(body.benchmarks);
});
test('POST /api/finance/holdings → 201 and returns the holding', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/finance/holdings',
payload: { ticker: 'AAPL', shares: 10, costBasis: 150, type: 'stock', source: 'Robinhood' },
});
assert.equal(res.statusCode, 201);
const body = res.json();
assert.equal(body.ticker, 'AAPL');
assert.equal(body.shares, 10);
});
test('POST /api/finance/holdings with missing shares → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/finance/holdings',
payload: { ticker: 'AAPL' },
});
assert.equal(res.statusCode, 400);
});
test('POST /api/finance/holdings with missing ticker → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/finance/holdings',
payload: { shares: 5 },
});
assert.equal(res.statusCode, 400);
});
test('POST /api/finance/holdings with zero shares → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/finance/holdings',
payload: { ticker: 'AAPL', shares: 0 },
});
assert.equal(res.statusCode, 400);
});
test('POST /api/finance/holdings with invalid type → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/finance/holdings',
payload: { ticker: 'AAPL', shares: 5, type: 'options' },
});
assert.equal(res.statusCode, 400);
});
test('DELETE /api/finance/holdings/:ticker removes existing holding → 200', async () => {
const repo = makePortfolioRepo([
{ ticker: 'MSFT', shares: 5, costBasis: 300, type: 'stock', source: 'Manual' },
]);
const app = await buildTestApp(repo);
const res = await app.inject({ method: 'DELETE', url: '/api/finance/holdings/MSFT' });
assert.equal(res.statusCode, 200);
assert.deepEqual(res.json(), { ok: true });
});
test('DELETE /api/finance/holdings/:ticker on missing ticker → 404', async () => {
const app = await buildTestApp();
const res = await app.inject({ method: 'DELETE', url: '/api/finance/holdings/NOTHERE' });
assert.equal(res.statusCode, 404);
});
+118
View File
@@ -0,0 +1,118 @@
/**
* Integration tests for ScreenerController + /health
* Uses Fastify inject() — no real Yahoo calls, no live server.
*/
import { test } from 'node:test';
import assert from 'node:assert/strict';
import Fastify from 'fastify';
import cors from '@fastify/cors';
import { ScreenerController } from '../server/controllers/screener.controller';
import type { ScreenerEngine } from '../server/services/ScreenerEngine';
import type { ScreenerResult, MarketContext } from '../server/types';
// ── Fixture data ────────────────────────────────────────────────────────────
const MARKET_CTX: MarketContext = {
sp500Price: 5000,
riskFreeRate: 4.5,
vixLevel: 18,
rateRegime: 'NORMAL',
volatilityRegime: 'NORMAL',
benchmarks: { marketPE: 22, techPE: 30, reitYield: 3.5, igSpread: 1.0 },
};
const EMPTY_RESULT: ScreenerResult = {
STOCK: [],
ETF: [],
BOND: [],
ERROR: [],
marketContext: MARKET_CTX,
};
// ── Stub ────────────────────────────────────────────────────────────────────
const stubEngine = {
screenTickers: async (_tickers: string[]) => EMPTY_RESULT,
getMarketContext: async () => MARKET_CTX,
} as unknown as ScreenerEngine;
// ── App factory ─────────────────────────────────────────────────────────────
async function buildTestApp() {
const app = Fastify({ logger: false });
await app.register(cors, { origin: '*' });
new ScreenerController(stubEngine).register(app);
app.get('/health', async () => ({ status: 'ok' }));
await app.ready();
return app;
}
// ── Tests ────────────────────────────────────────────────────────────────────
test('GET /health → 200 { status: ok }', async () => {
const app = await buildTestApp();
const res = await app.inject({ method: 'GET', url: '/health' });
assert.equal(res.statusCode, 200);
assert.deepEqual(res.json(), { status: 'ok' });
});
test('POST /api/screen → 200 with STOCK/ETF/BOND/ERROR/marketContext keys', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/screen',
payload: { tickers: ['AAPL'] },
});
assert.equal(res.statusCode, 200);
const body = res.json();
assert.ok(Array.isArray(body.STOCK), 'STOCK should be array');
assert.ok(Array.isArray(body.ETF), 'ETF should be array');
assert.ok(Array.isArray(body.BOND), 'BOND should be array');
assert.ok(Array.isArray(body.ERROR), 'ERROR should be array');
assert.ok(body.marketContext, 'marketContext should be present');
});
test('POST /api/screen → marketContext has expected shape', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/screen',
payload: { tickers: ['MSFT'] },
});
const { marketContext } = res.json();
assert.ok(typeof marketContext.riskFreeRate === 'number');
assert.ok(typeof marketContext.sp500Price === 'number');
assert.ok(typeof marketContext.vixLevel === 'number');
assert.ok(marketContext.benchmarks);
});
test('POST /api/screen with missing tickers → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/screen',
payload: {},
});
assert.equal(res.statusCode, 400);
});
test('POST /api/screen with empty tickers array → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/screen',
payload: { tickers: [] },
});
assert.equal(res.statusCode, 400);
});
test('POST /api/screen with too many tickers (>50) → 400', async () => {
const app = await buildTestApp();
const res = await app.inject({
method: 'POST',
url: '/api/screen',
payload: { tickers: Array.from({ length: 51 }, (_, i) => `T${i}`) },
});
assert.equal(res.statusCode, 400);
});