清理旧决策卡片、市场总览、跑道面板等已废弃组件
This commit is contained in:
@@ -1,80 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { buildCityDecisionState } from "@/components/dashboard/scan-terminal/city-decision-state";
|
||||
import type { MarketDecisionView } from "@/components/dashboard/scan-terminal/city-card-decision-utils";
|
||||
|
||||
function market(status: MarketDecisionView["status"]): MarketDecisionView {
|
||||
return {
|
||||
bucketLabel: "--",
|
||||
confidence: "--",
|
||||
edgeText: "--",
|
||||
impliedText: "--",
|
||||
modelText: "--",
|
||||
priceText: "--",
|
||||
reason: "",
|
||||
status,
|
||||
title: "",
|
||||
tone: "watch",
|
||||
};
|
||||
}
|
||||
|
||||
export function runTests() {
|
||||
const breakout = buildCityDecisionState({
|
||||
isEn: false,
|
||||
isHkoObservation: false,
|
||||
modelHighlyConsistent: true,
|
||||
needsNextBulletin: false,
|
||||
observationStale: false,
|
||||
observedHighBreak: true,
|
||||
observedLowBreak: false,
|
||||
observedLowLag: false,
|
||||
peakHasPassed: false,
|
||||
});
|
||||
|
||||
assert.equal(breakout.urgency, "now");
|
||||
assert.equal(breakout.recommendation, "watch");
|
||||
assert.match(breakout.primaryReason, /实测已突破模型上沿/);
|
||||
assert.match(breakout.primaryReason, /建议关注偏高温/);
|
||||
assert.ok(breakout.badges.some((badge) => badge.label === "实测突破"));
|
||||
|
||||
const marketUnavailable = buildCityDecisionState({
|
||||
isEn: false,
|
||||
isHkoObservation: false,
|
||||
modelHighlyConsistent: false,
|
||||
needsNextBulletin: false,
|
||||
observationStale: false,
|
||||
observedHighBreak: false,
|
||||
observedLowBreak: false,
|
||||
observedLowLag: false,
|
||||
peakHasPassed: false,
|
||||
});
|
||||
|
||||
|
||||
const fallback = buildCityDecisionState({
|
||||
isEn: false,
|
||||
isHkoObservation: false,
|
||||
modelHighlyConsistent: false,
|
||||
needsNextBulletin: false,
|
||||
observationStale: false,
|
||||
observedHighBreak: false,
|
||||
observedLowBreak: false,
|
||||
observedLowLag: false,
|
||||
peakHasPassed: false,
|
||||
});
|
||||
|
||||
assert.equal(fallback.recommendation, "watch");
|
||||
|
||||
const partialStream = buildCityDecisionState({
|
||||
isEn: false,
|
||||
isHkoObservation: false,
|
||||
modelHighlyConsistent: false,
|
||||
needsNextBulletin: true,
|
||||
observationStale: false,
|
||||
observedHighBreak: false,
|
||||
observedLowBreak: false,
|
||||
observedLowLag: true,
|
||||
peakHasPassed: false,
|
||||
});
|
||||
|
||||
assert.equal(partialStream.urgency, "soon");
|
||||
assert.equal(partialStream.recommendation, "wait");
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { buildCityDecisionState } from "@/components/dashboard/scan-terminal/city-decision-state";
|
||||
import type { MarketDecisionView } from "@/components/dashboard/scan-terminal/city-card-decision-utils";
|
||||
|
||||
const readyMarket: MarketDecisionView = {
|
||||
bucketLabel: "--",
|
||||
confidence: "--",
|
||||
edgeText: "--",
|
||||
impliedText: "--",
|
||||
modelText: "--",
|
||||
priceText: "--",
|
||||
reason: "",
|
||||
status: "ready",
|
||||
title: "",
|
||||
tone: "neutral",
|
||||
};
|
||||
|
||||
export function runTests() {
|
||||
const peakPassed = buildCityDecisionState({
|
||||
isEn: false,
|
||||
isHkoObservation: false,
|
||||
modelHighlyConsistent: true,
|
||||
needsNextBulletin: false,
|
||||
observationStale: false,
|
||||
observedHighBreak: false,
|
||||
observedLowBreak: false,
|
||||
observedLowLag: false,
|
||||
peakHasPassed: true,
|
||||
});
|
||||
|
||||
assert.equal(peakPassed.urgency, "past");
|
||||
assert.equal(peakPassed.recommendation, "avoid");
|
||||
assert.match(peakPassed.primaryReason, /峰值窗口已过/);
|
||||
assert.doesNotMatch(peakPassed.primaryReason, /值得关注/);
|
||||
assert.deepEqual(
|
||||
peakPassed.badges.map((badge) => badge.label),
|
||||
["峰值窗口已过", "模型高度一致"],
|
||||
);
|
||||
|
||||
const staleMetar = buildCityDecisionState({
|
||||
isEn: false,
|
||||
isHkoObservation: false,
|
||||
modelHighlyConsistent: false,
|
||||
needsNextBulletin: false,
|
||||
observationStale: true,
|
||||
observedHighBreak: false,
|
||||
observedLowBreak: false,
|
||||
observedLowLag: false,
|
||||
peakHasPassed: false,
|
||||
});
|
||||
|
||||
assert.equal(staleMetar.evidenceQuality, "stale");
|
||||
assert.equal(staleMetar.recommendation, "background");
|
||||
assert.ok(staleMetar.badges.some((badge) => badge.label === "METAR 过旧"));
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
buildMarketDecisionView,
|
||||
pickMarketBucketForWeatherCenter,
|
||||
} from "@/components/dashboard/scan-terminal/city-card-decision-utils";
|
||||
import type { MarketScan } from "@/lib/dashboard-types";
|
||||
|
||||
export function runTests() {
|
||||
const unavailable = buildMarketDecisionView({
|
||||
expectedHigh: 24,
|
||||
isEn: false,
|
||||
marketScan: { available: false },
|
||||
marketStatus: "ready",
|
||||
tempSymbol: "°C",
|
||||
});
|
||||
|
||||
assert.equal(unavailable.status, "unavailable");
|
||||
assert.equal(unavailable.title, "市场价格暂不可用");
|
||||
assert.match(unavailable.reason, /暂无可交易价格/);
|
||||
assert.match(unavailable.reason, /天气判断仍可参考/);
|
||||
assert.doesNotMatch(unavailable.reason, /未接入|系统缺失|系统坏/);
|
||||
|
||||
const mismatchedScan: MarketScan = {
|
||||
available: true,
|
||||
all_buckets: [
|
||||
{
|
||||
label: "40°C",
|
||||
temp: 40,
|
||||
unit: "C",
|
||||
model_probability: 0.2,
|
||||
market_price: 0.3,
|
||||
yes_buy: 0.31,
|
||||
},
|
||||
],
|
||||
market_price: 0.3,
|
||||
model_probability: 0.55,
|
||||
yes_buy: 0.31,
|
||||
};
|
||||
const mismatched = buildMarketDecisionView({
|
||||
expectedHigh: 24,
|
||||
isEn: false,
|
||||
marketScan: mismatchedScan,
|
||||
marketStatus: "ready",
|
||||
tempSymbol: "°C",
|
||||
});
|
||||
|
||||
assert.equal(pickMarketBucketForWeatherCenter(mismatchedScan, 24, "°C"), null);
|
||||
assert.equal(mismatched.status, "ready");
|
||||
assert.equal(mismatched.title, "市场温度桶需重新匹配");
|
||||
assert.equal(mismatched.edgeText, "--");
|
||||
assert.match(mismatched.reason, /温度桶与今日预计高点不够匹配/);
|
||||
|
||||
const matched = buildMarketDecisionView({
|
||||
expectedHigh: 24.3,
|
||||
isEn: false,
|
||||
marketScan: {
|
||||
available: true,
|
||||
all_buckets: [
|
||||
{
|
||||
label: "24°C",
|
||||
temp: 24,
|
||||
unit: "C",
|
||||
model_probability: 0.64,
|
||||
market_price: 0.41,
|
||||
yes_buy: 0.42,
|
||||
slug: "tokyo-high-24c",
|
||||
},
|
||||
],
|
||||
market_price: 0.41,
|
||||
model_probability: 0.64,
|
||||
yes_buy: 0.42,
|
||||
},
|
||||
marketStatus: "ready",
|
||||
tempSymbol: "°C",
|
||||
});
|
||||
|
||||
assert.equal(matched.status, "ready");
|
||||
assert.equal(matched.bucketLabel, "24°C");
|
||||
assert.equal(matched.priceText, "42¢");
|
||||
assert.match(matched.reason, /模型概率 64\.0%/);
|
||||
}
|
||||
@@ -27,10 +27,6 @@ export function runTests() {
|
||||
path.join(projectRoot, "components", "dashboard", "scan-terminal", "LiveTemperatureThresholdChart.tsx"),
|
||||
"utf8",
|
||||
);
|
||||
const overviewApiSource = fs.readFileSync(
|
||||
path.join(projectRoot, "..", "web", "services", "market_overview_api.py"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
assert(
|
||||
querySource.includes("DASHBOARD_REFRESH_POLICY_MS.scanRows"),
|
||||
@@ -42,10 +38,4 @@ export function runTests() {
|
||||
!chartSource.includes("window.setInterval"),
|
||||
"selected city detail chart should be on-demand and use model-layer cache instead of 60-second polling",
|
||||
);
|
||||
assert(
|
||||
!overviewApiSource.includes("/chat/completions") &&
|
||||
!overviewApiSource.includes("SCAN_CITY_AI_MODEL") &&
|
||||
!overviewApiSource.includes("_scan_ai_api_key"),
|
||||
"market overview must be deterministic and must not call AI providers",
|
||||
);
|
||||
}
|
||||
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
function assert(condition: unknown, message: string) {
|
||||
if (!condition) throw new Error(message);
|
||||
}
|
||||
|
||||
export function runTests() {
|
||||
const projectRoot = process.cwd();
|
||||
const queryPath = path.join(
|
||||
projectRoot,
|
||||
"components",
|
||||
"dashboard",
|
||||
"scan-terminal",
|
||||
"use-scan-terminal-query.ts",
|
||||
);
|
||||
const dashboardPath = path.join(
|
||||
projectRoot,
|
||||
"components",
|
||||
"dashboard",
|
||||
"ScanTerminalDashboard.tsx",
|
||||
);
|
||||
const airportEvidencePath = path.join(
|
||||
projectRoot,
|
||||
"components",
|
||||
"dashboard",
|
||||
"scan-terminal",
|
||||
"AirportEvidencePanel.tsx",
|
||||
);
|
||||
|
||||
const querySource = fs.readFileSync(queryPath, "utf8");
|
||||
const dashboardSource = fs.readFileSync(dashboardPath, "utf8");
|
||||
const airportEvidenceSource = fs.readFileSync(airportEvidencePath, "utf8");
|
||||
|
||||
assert(
|
||||
querySource.includes("fetchScanTerminal") &&
|
||||
querySource.includes("showLoading: false"),
|
||||
"web auto refresh must read cached scan data instead of forcing a full server scan",
|
||||
);
|
||||
assert(
|
||||
dashboardSource.includes("CityRegionList") &&
|
||||
dashboardSource.includes("Panel") &&
|
||||
dashboardSource.includes("decisionLabel"),
|
||||
"scan terminal must use new institutional terminal layout with CityRegionList + decisionLabel",
|
||||
);
|
||||
assert(
|
||||
airportEvidenceSource.includes("SETTLEMENT_RUNWAY_PAIRS") &&
|
||||
airportEvidenceSource.includes("chongqing") &&
|
||||
airportEvidenceSource.includes("seoul") &&
|
||||
!airportEvidenceSource.includes("busan:"),
|
||||
"settlement runway mapping must cover all active settlement cities without mixing in non-settlement airports",
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user