Files
PolyWeather/frontend/components/dashboard/scan-terminal/__tests__/buildCityDecisionState.test.ts
T

107 lines
3.4 KiB
TypeScript

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";
import type { AiCityForecastState } from "@/components/dashboard/scan-terminal/types";
function market(status: MarketDecisionView["status"]): MarketDecisionView {
return {
bucketLabel: "--",
confidence: "--",
edgeText: "--",
impliedText: "--",
modelText: "--",
priceText: "--",
reason: "",
status,
title: "",
tone: "watch",
};
}
function ai(status: AiCityForecastState["status"], extra: Partial<AiCityForecastState> = {}): AiCityForecastState {
return { status, ...extra };
}
export function runTests() {
const breakout = buildCityDecisionState({
aiCityForecast: null,
aiForecast: ai("ready"),
aiRuleEvidenceMode: false,
isEn: false,
isHkoObservation: false,
marketDecisionView: market("ready"),
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({
aiCityForecast: null,
aiForecast: ai("ready"),
aiRuleEvidenceMode: false,
isEn: false,
isHkoObservation: false,
marketDecisionView: market("unavailable"),
modelHighlyConsistent: false,
needsNextBulletin: false,
observationStale: false,
observedHighBreak: false,
observedLowBreak: false,
observedLowLag: false,
peakHasPassed: false,
});
assert.equal(marketUnavailable.marketStatus, "unavailable");
assert.match(marketUnavailable.primaryReason, /暂无可交易价格/);
assert.doesNotMatch(marketUnavailable.primaryReason, /未接入|系统缺失|系统坏/);
const fallback = buildCityDecisionState({
aiCityForecast: null,
aiForecast: ai("ready", { payload: { degraded: true } }),
aiRuleEvidenceMode: true,
isEn: false,
isHkoObservation: false,
marketDecisionView: market("ready"),
modelHighlyConsistent: false,
needsNextBulletin: false,
observationStale: false,
observedHighBreak: false,
observedLowBreak: false,
observedLowLag: false,
peakHasPassed: false,
});
assert.equal(fallback.aiStatus, "fallback");
assert.equal(fallback.aiStatusLabel, "规则证据模式");
assert.notEqual(fallback.aiStatusLabel, "AI 解读已完成");
const partialStream = buildCityDecisionState({
aiCityForecast: null,
aiForecast: ai("loading", { streamText: "partial" }),
aiRuleEvidenceMode: false,
isEn: false,
isHkoObservation: false,
marketDecisionView: market("ready"),
modelHighlyConsistent: false,
needsNextBulletin: true,
observationStale: false,
observedHighBreak: false,
observedLowBreak: false,
observedLowLag: true,
peakHasPassed: false,
});
assert.equal(partialStream.aiStatus, "deepseek-loading");
assert.equal(partialStream.aiStatusLabel, "快速判断已完成");
}