mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-04-28 04:20:38 +00:00
7f47799733
The TCBS `apipubaws.tcbs.com.vn` host returns HTTP 404/500 for every
request, so every ticker resolved as "Unknown stock ticker" and /trade_buy
was unusable. Switch price + symbol resolution to the KBS public endpoint
that vnstock currently defaults to (`kbbuddywts.kbsec.com.vn/iis-server/
investment/stocks/{TICKER}/data_day`). KBS needs no auth, returns JSON,
and is Worker-compatible.
- `prices.fetchStockPrice` now queries KBS with a 14-day lookback window
(covers weekends/holidays) and drops the TCBS-specific ×1000 scaling;
KBS returns real VND.
- `symbols.resolveSymbol` delegates to `fetchStockPrice` for existence
checks — empty `data_day` means unknown ticker.
- Update test fetch stubs to match the `kbsec` host and KBS response
shape (`{ symbol, data_day: [{ c }] }`).
65 lines
1.9 KiB
JavaScript
65 lines
1.9 KiB
JavaScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { createStore } from "../../../src/db/create-store.js";
|
|
import { comingSoonMessage, resolveSymbol } from "../../../src/modules/trading/symbols.js";
|
|
import { makeFakeKv } from "../../fakes/fake-kv-namespace.js";
|
|
|
|
/** Stub global.fetch to return KBS-like response */
|
|
function stubFetch(hasData = true) {
|
|
global.fetch = vi.fn(() =>
|
|
Promise.resolve({
|
|
ok: true,
|
|
json: () =>
|
|
Promise.resolve(
|
|
hasData
|
|
? { symbol: "TCB", data_day: [{ t: "2026-04-21 07:00", c: 25000 }] }
|
|
: { symbol: "NOPE", data_day: [] },
|
|
),
|
|
}),
|
|
);
|
|
}
|
|
|
|
describe("trading/symbols", () => {
|
|
let db;
|
|
|
|
beforeEach(() => {
|
|
db = createStore("trading", { KV: makeFakeKv() });
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("resolves a valid VN stock ticker via KBS", async () => {
|
|
stubFetch();
|
|
const result = await resolveSymbol(db, "TCB");
|
|
expect(result).toEqual({ symbol: "TCB", category: "stock", label: "TCB" });
|
|
expect(global.fetch).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("caches resolved symbol in KV", async () => {
|
|
stubFetch();
|
|
await resolveSymbol(db, "TCB");
|
|
// second call should use cache, not fetch
|
|
await resolveSymbol(db, "TCB");
|
|
expect(global.fetch).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("is case-insensitive", async () => {
|
|
stubFetch();
|
|
const result = await resolveSymbol(db, "tcb");
|
|
expect(result.symbol).toBe("TCB");
|
|
});
|
|
|
|
it("returns null for invalid ticker", async () => {
|
|
stubFetch(false);
|
|
const result = await resolveSymbol(db, "NOPE");
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("returns null for empty input", async () => {
|
|
const result = await resolveSymbol(db, "");
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("comingSoonMessage returns string", () => {
|
|
expect(comingSoonMessage()).toContain("coming soon");
|
|
});
|
|
});
|