From 7dd0e3f42105569b6e6ec02aeeec9b5a8bbd9dd3 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 22 Sep 2026 03:25:00 +0800 Subject: Add real AKShare stock daily history probe slice (3d-19) --- qa/driver/browser-test.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++ qa/stub/fund-lab-python | 11 ++++++++ 2 files changed, 78 insertions(+) (limited to 'qa') diff --git a/qa/driver/browser-test.js b/qa/driver/browser-test.js index 424872a..401cb78 100644 --- a/qa/driver/browser-test.js +++ b/qa/driver/browser-test.js @@ -36,6 +36,7 @@ let lastNavDatesResponse = null; let lastNavSeriesResponse = null; let lastBondQuoteResponse = null; let lastStockQuoteResponse = null; +let lastStockDailyResponse = null; function check(name, ok, detail) { results.push({ name, ok, detail: detail || "" }); @@ -195,6 +196,11 @@ async function summaryLabelExists(page, label, name) { lastStockQuoteResponse = await r.json(); } catch {} } + if (r.url().includes("/market/stock-daily") && r.request().method() === "GET" && r.status() < 400) { + try { + lastStockDailyResponse = await r.json(); + } catch {} + } if (r.status() >= 400 && !/\/api\/instruments\//.test(r.url()) && !/\/api\/market\//.test(r.url()) && !/\/orders/.test(r.url()) && !/\/redemptions/.test(r.url()) && !/\/capital/.test(r.url())) { consoleErrors.push("resource " + r.status() + ": " + r.url()); } @@ -248,6 +254,7 @@ async function summaryLabelExists(page, label, name) { await rebalanceScenario(page); await bondsScenario(page); await stocksScenario(page); + await stockDailyScenario(page); } finally { check("G1 无浏览器控制台/页面错误", consoleErrors.length === 0, consoleErrors.slice(0, 3).join(" | ")); await browser.close(); @@ -764,6 +771,66 @@ async function stocksScenario(page) { await page.screenshot({ path: SHOTS + "/22-stocks.png" }); } +async function stockDailyScenario(page) { + // S3: 股票日线历史走真实 API(QA 环境由桩采集器提供,非前端 mock)。 + await page.waitForSelector(".stocks-panel", { timeout: 10000 }); + + lastStockDailyResponse = null; + await page.fill(".stock-code-input", "600519"); + await page.click(".stock-daily-action"); + await page.waitForSelector(".stock-daily-row", { timeout: 10000 }); + + const rows = await page.locator(".stock-daily-row").count(); + const daily = lastStockDailyResponse || {}; + const observations = Array.isArray(daily.observations) ? daily.observations : []; + const hasClose = observations.every((item) => typeof item.close === "string" && item.close.length > 0); + + check( + "S3a 真实可用股票日线显示日期行且数量>=3", + daily.code === "600519" && observations.length >= 3 && hasClose && rows >= 3, + `code=${daily.code} observations=${observations.length} rows=${rows}` + ); + + const token = process.env.QA_TOKEN || "qa-token"; + + await page.fill(".stock-code-input", "999999"); + await page.click(".stock-daily-action"); + await page.waitForFunction( + () => { + const element = document.querySelector(".stock-daily-unavailable"); + return element && !element.hidden && element.textContent.includes("股票日线不可用"); + }, + { timeout: 10000 } + ); + + const unavailableText = ((await page.textContent(".stock-daily-unavailable")) || "").trim(); + const rowsGone = (await page.locator(".stock-daily-row").count()) === 0; + + const failure = await page.evaluate(async (token) => { + const response = await fetch("/api/market/stock-daily?code=999999&days=5", { + headers: { Authorization: `Bearer ${token}` }, + }); + let body = null; + try { + body = await response.json(); + } catch {} + return { status: response.status, body }; + }, token); + + check( + "S3b 无有效日线时显示不可用提示且无 0/空表占位", + unavailableText.includes("股票日线不可用") && + rowsGone && + !unavailableText.includes("0.00") && + failure.status === 503 && + failure.body && + failure.body.error === "MARKET_DATA_UNAVAILABLE", + `message=${unavailableText.slice(0, 200)} status=${failure.status}` + ); + + await page.screenshot({ path: SHOTS + "/23-stock-daily.png" }); +} + async function postInvestmentPlanViaApi(page, fundId, token) { return page.evaluate( async ({ fundId, token }) => { diff --git a/qa/stub/fund-lab-python b/qa/stub/fund-lab-python index 5c6daa3..c9ce981 100755 --- a/qa/stub/fund-lab-python +++ b/qa/stub/fund-lab-python @@ -53,6 +53,17 @@ case "$OP" in exit 2 fi ;; + stock-daily) + # Synthetic daily bars mirror the live AKShare A-share history contract + # (>= 3 rows); unknown codes fail like the collector's "no valid daily + # history" error (-> API 503). + if [ "$CODE" = "600519" ]; then + printf '{"schema_version":"fund-lab.akshare.v1","operation":"stock-daily","source":"stub-synthetic","source_revision":"stub-1","_synthetic":true,"collected_at":"%s","instrument":{"code":"%s"},"observations":[{"date":"2026-09-17","close":"1266.98","volume":"1755380.0","amount":"2217338283.0"},{"date":"2026-09-18","close":"1257.12","volume":"2489087.0","amount":"3135849108.0"},{"date":"2026-09-21","close":"1252.57","volume":"2501689.0","amount":"3135910045.0"}]}\n' "$TS" "$CODE" + else + echo "AKShare collector failed: no valid daily history for $CODE" >&2 + exit 2 + fi + ;; *) echo "unknown operation: $OP" >&2 exit 2 -- cgit v1.2.3