summaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
Diffstat (limited to 'qa')
-rw-r--r--qa/driver/browser-test.js67
-rwxr-xr-xqa/stub/fund-lab-python11
2 files changed, 78 insertions, 0 deletions
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