diff --git a/backend/verifyMarketDataEndpoints.ts b/backend/verifyMarketDataEndpoints.ts index 48b689c..9e0ac7b 100644 --- a/backend/verifyMarketDataEndpoints.ts +++ b/backend/verifyMarketDataEndpoints.ts @@ -64,7 +64,7 @@ function testChartBarsContract() { '/api/chart/bars must reject missing symbols', ); assertSourceMatches( - /this\.app\.get\('\/api\/chart\/bars'[\s\S]*Alpaca credentials not configured/, + /this\.app\.get\('\/api\/chart\/bars'[\s\S]*getUserMarketDataAlpacaCredentials\(authUserId\)[\s\S]*if \(error instanceof MissingServiceConfigError\)[\s\S]*status\(503\)\.json\(\{ error: error\.message \}\)/, '/api/chart/bars must fail closed when Alpaca credentials are absent', ); @@ -156,26 +156,18 @@ function testFmpProxyContracts() { "import { fetchFmpJson, FmpFetchError } from './fmpCache.js';", 'FMP routes must use the shared cache helper and typed upstream error', ); - assertConfigIncludes( - "FMP_API_KEY: process.env.FMP_API_KEY || '',", - 'FMP config must not silently default to the shared demo key', - ); assertSourceIncludes( - "apiKey.toLowerCase() === 'demo'", - 'FMP routes must reject the shared demo key explicitly', - ); - assertSourceIncludes( - 'FMP_API_KEY is required for research and screener endpoints', - 'FMP routes must surface an explicit missing-key error', + 'User FMP API key is required for research and screener endpoints', + 'FMP routes must surface an explicit per-user missing-key error', ); const fetchFmpCalls = apiServerSource.match(/fetchFmpJson\(url\)/g) ?? []; assert.equal(fetchFmpCalls.length, 4, 'each FMP route must fetch through fetchFmpJson(url)'); for (const [route, endpoint, errorMessage] of [ - ['/api/research/profile', '/api/v3/profile/${symbol}?apikey=${apiKey}', 'FMP profile fetch failed'], - ['/api/research/metrics', '/api/v3/key-metrics/${symbol}?limit=4&apikey=${apiKey}', 'FMP metrics fetch failed'], - ['/api/research/earnings', '/api/v3/historical/earning_calendar/${symbol}?limit=8&apikey=${apiKey}', 'FMP earnings fetch failed'], + ['/api/research/profile', 'https://financialmodelingprep.com/stable/profile?symbol=${encodeURIComponent(symbol)}&apikey=${apiKey}', 'FMP profile fetch failed'], + ['/api/research/metrics', 'https://financialmodelingprep.com/stable/key-metrics?symbol=${encodeURIComponent(symbol)}&limit=4&apikey=${apiKey}', 'FMP metrics fetch failed'], + ['/api/research/earnings', 'https://financialmodelingprep.com/stable/earnings-calendar?symbol=${encodeURIComponent(symbol)}&limit=8&apikey=${apiKey}', 'FMP earnings fetch failed'], ]) { assertSourceMatches( new RegExp(`this\\.app\\.get\\('${escapeRegExp(route)}'[\\s\\S]*if \\(!symbol\\)[\\s\\S]*status\\(400\\)\\.json\\(\\{ error: 'symbol required' \\}\\)`), @@ -194,8 +186,8 @@ function testFmpProxyContracts() { '/api/research/earnings must normalize to an earnings array wrapper', ); assertSourceIncludes( - 'https://financialmodelingprep.com/api/v3/stock-screener?${qs.toString()}', - '/api/screener must call FMP stock-screener', + 'https://financialmodelingprep.com/stable/company-screener?${qs.toString()}', + '/api/screener must call FMP stable company-screener', ); assertSourceIncludes( 'const ALLOWED_SCREENER_SECTORS = new Set([',