Improve scan terminal freshness and AMSC overlay consistency
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
import {
|
||||
__applyPatchToScanRowForTest,
|
||||
__mergeScanTerminalIncrementalResponseForTest,
|
||||
} from "@/components/dashboard/scan-terminal/use-scan-terminal-query";
|
||||
import type { ScanTerminalResponse } from "@/lib/dashboard-types";
|
||||
import type { CityPatch } from "@/hooks/use-sse-patches";
|
||||
|
||||
function assert(condition: unknown, message: string) {
|
||||
if (!condition) throw new Error(message);
|
||||
}
|
||||
|
||||
export function runTests() {
|
||||
const patch: CityPatch = {
|
||||
type: "city_observation_patch.v1",
|
||||
city: "shenzhen",
|
||||
revision: 42,
|
||||
changes: {
|
||||
temp: 30.2,
|
||||
max_so_far: 30.4,
|
||||
signed_gap: 1.4,
|
||||
gap_to_target: -1.4,
|
||||
touch_distance: 0,
|
||||
current_reference: 30.4,
|
||||
edge: 0.06,
|
||||
edge_percent: 6,
|
||||
obs_time: "2026-06-14T06:30:00Z",
|
||||
},
|
||||
};
|
||||
|
||||
const next = __applyPatchToScanRowForTest(
|
||||
{
|
||||
city: "shenzhen",
|
||||
current_temp: 29.8,
|
||||
current_max_so_far: 30.0,
|
||||
signed_gap: 1.0,
|
||||
gap_to_target: -1.0,
|
||||
touch_distance: 0.2,
|
||||
current_reference: 30.0,
|
||||
edge: 0.04,
|
||||
edge_percent: 4,
|
||||
local_time: "old",
|
||||
target_threshold: 29,
|
||||
},
|
||||
patch,
|
||||
) as any;
|
||||
|
||||
assert(next.current_temp === 30.2, "SSE patch should update scan row current temperature");
|
||||
assert(next.current_max_so_far === 30.4, "SSE patch should use backend max_so_far instead of only Math.max(row, temp)");
|
||||
assert(next.signed_gap === 1.4, "SSE patch should update signed_gap when backend sends it");
|
||||
assert(next.gap_to_target === -1.4, "SSE patch should update gap_to_target when backend sends it");
|
||||
assert(next.touch_distance === 0, "SSE patch should update touch_distance when backend sends it");
|
||||
assert(next.current_reference === 30.4, "SSE patch should update current_reference when backend sends it");
|
||||
assert(next.edge === 0.06 && next.edge_percent === 6, "SSE patch should update edge fields when backend sends them");
|
||||
assert(next.local_time === "2026-06-14T06:30:00Z", "SSE patch should update observation time");
|
||||
assert(next.sse_revision === 42, "SSE patch should record the applied revision");
|
||||
|
||||
const derived = __applyPatchToScanRowForTest(
|
||||
{
|
||||
city: "shenzhen",
|
||||
current_temp: 28,
|
||||
current_max_so_far: 28,
|
||||
target_threshold: 30,
|
||||
target_label: "30+",
|
||||
},
|
||||
{
|
||||
type: "city_observation_patch.v1",
|
||||
city: "shenzhen",
|
||||
revision: 43,
|
||||
changes: {
|
||||
temp: 30.5,
|
||||
},
|
||||
},
|
||||
) as any;
|
||||
|
||||
assert(derived.current_max_so_far === 30.5, "SSE patch should still derive max_so_far from temp when backend omits it");
|
||||
assert(derived.signed_gap === 0.5, "SSE patch should derive signed_gap from updated max and row threshold");
|
||||
assert(derived.gap_to_target === -0.5, "SSE patch should derive gap_to_target from updated max and row threshold");
|
||||
assert(derived.touch_distance === 0, "SSE patch should derive zero touch_distance once threshold is reached");
|
||||
|
||||
const previous: ScanTerminalResponse = {
|
||||
generated_at: "2026-06-14T00:00:00Z",
|
||||
snapshot_id: "scan-old",
|
||||
status: "ready",
|
||||
stale: false,
|
||||
filters: {
|
||||
scan_mode: "tradable",
|
||||
min_price: 0.05,
|
||||
max_price: 0.95,
|
||||
min_edge_pct: 2,
|
||||
min_liquidity: 500,
|
||||
high_liquidity_only: false,
|
||||
market_type: "maxtemp",
|
||||
time_range: "today",
|
||||
limit: 180,
|
||||
},
|
||||
summary: {
|
||||
recommended_count: 2,
|
||||
visible_count: 2,
|
||||
candidate_total: 2,
|
||||
tradable_market_count: 2,
|
||||
total_volume: 1000,
|
||||
},
|
||||
top_signal: null,
|
||||
rows: [
|
||||
{ id: "row-1", rank: 1, city: "shenzhen", edge_percent: 4 },
|
||||
{ id: "row-removed", rank: 2, city: "paris", edge_percent: 3 },
|
||||
],
|
||||
};
|
||||
|
||||
const notModified = __mergeScanTerminalIncrementalResponseForTest(previous, {
|
||||
generated_at: "2026-06-14T00:01:00Z",
|
||||
snapshot_id: "scan-old",
|
||||
status: "not_modified",
|
||||
stale: false,
|
||||
filters: previous.filters,
|
||||
summary: previous.summary,
|
||||
top_signal: previous.top_signal,
|
||||
rows: [],
|
||||
diff: {
|
||||
mode: "not_modified",
|
||||
base_snapshot_id: "scan-old",
|
||||
snapshot_id: "scan-old",
|
||||
rows_changed: [],
|
||||
removed_row_ids: [],
|
||||
},
|
||||
});
|
||||
|
||||
assert(notModified.rows.length === 2, "not_modified scan diff should keep the previous rows");
|
||||
assert(notModified.status === "ready", "not_modified scan diff should normalize to a renderable ready payload");
|
||||
|
||||
const delta = __mergeScanTerminalIncrementalResponseForTest(previous, {
|
||||
generated_at: "2026-06-14T00:02:00Z",
|
||||
snapshot_id: "scan-new",
|
||||
status: "ready",
|
||||
stale: false,
|
||||
filters: previous.filters,
|
||||
summary: { ...previous.summary, recommended_count: 2 },
|
||||
top_signal: { id: "row-added", city: "tokyo" } as any,
|
||||
rows: [],
|
||||
diff: {
|
||||
mode: "row_delta",
|
||||
base_snapshot_id: "scan-old",
|
||||
snapshot_id: "scan-new",
|
||||
rows_changed: [
|
||||
{ id: "row-1", rank: 1, city: "shenzhen", edge_percent: 6 },
|
||||
{ id: "row-added", rank: 2, city: "tokyo", edge_percent: 5 },
|
||||
],
|
||||
removed_row_ids: ["row-removed"],
|
||||
},
|
||||
});
|
||||
|
||||
assert(delta.snapshot_id === "scan-new", "row_delta scan diff should advance snapshot_id");
|
||||
assert(delta.rows.length === 2, "row_delta scan diff should merge changed, added, and removed rows");
|
||||
assert(delta.rows[0].id === "row-1" && delta.rows[0].edge_percent === 6, "row_delta scan diff should replace changed rows");
|
||||
assert(delta.rows[1].id === "row-added", "row_delta scan diff should append new rows in rank order");
|
||||
}
|
||||
Reference in New Issue
Block a user