uphold/tests/bot.test.js
2025-11-27 14:41:21 +00:00

151 lines
3.9 KiB
JavaScript

import {
describe,
it,
expect,
vi,
beforeEach,
afterEach,
beforeAll,
} from "vitest";
import nock from "nock";
import { BigNumber } from "bignumber.js";
import { Bot } from "../src/bot.js";
vi.mock("console", () => ({
log: vi.fn(),
error: vi.fn(),
}));
vi.mock("../src/db.js", () => ({
insertIntoDB: vi.fn(),
initDB: vi.fn(),
}));
describe("Test Suite", () => {
const PAIR = "BTC-USD";
const INTERVAL = 100;
const THRESHOLD = 0.01;
let bot;
beforeAll(() => {
// Ensure BigNumber config matches what we expect
BigNumber.config({
DECIMAL_PLACES: 10,
ROUNDING_MODE: BigNumber.ROUND_HALF_UP,
});
});
beforeEach(() => {
bot = new Bot(PAIR, INTERVAL, THRESHOLD);
vi.useFakeTimers();
});
afterEach(() => {
bot.stop();
vi.runAllTimers();
vi.useRealTimers();
nock.cleanAll();
vi.restoreAllMocks();
});
describe("Initialization and first fetch", () => {
it("should set lastPrice on first successful API call", async () => {
nock("https://api.uphold.com")
.get(`/v0/ticker/${PAIR}`)
.reply(200, { ask: "60000.50", bid: "59990.00", currency: "USD" });
await bot.check();
expect(bot.lastPrice).toBeDefined();
expect(bot.lastPrice.toFixed(2)).toBe("60000.50");
});
});
describe("Price change detection", () => {
beforeEach(async () => {
// Establish baseline
nock("https://api.uphold.com")
.get(`/v0/ticker/${PAIR}`)
.reply(200, { ask: "50000.00" });
await bot.check();
nock.cleanAll();
});
it("should NOT alert when change is below threshold", async () => {
const alertSpy = vi.spyOn(bot, "alert");
nock("https://api.uphold.com")
.get(`/v0/ticker/${PAIR}`)
.reply(200, { ask: "50004.99" }); // +0.00998%
await bot.check();
expect(alertSpy).not.toHaveBeenCalled();
});
it("should alert when price moves exactly 0.01% (threshold)", async () => {
const alertSpy = vi.spyOn(bot, "alert");
nock("https://api.uphold.com")
.get(`/v0/ticker/${PAIR}`)
.reply(200, { ask: "50005.00" });
await bot.check();
// Check specifically for arguments passed to alert
expect(alertSpy).toHaveBeenCalledWith("UP", "50005.00", 0.01);
});
it("should alert on downward move", async () => {
const alertSpy = vi.spyOn(bot, "alert");
nock("https://api.uphold.com")
.get(`/v0/ticker/${PAIR}`)
.reply(200, { ask: "49500.00" }); // -1%
await bot.check();
expect(alertSpy).toHaveBeenCalledWith("DOWN", "49500.00", -1);
});
});
describe("Alert method", () => {
it("should log correct message for price increase", () => {
bot.lastPrice = new BigNumber("50000");
const consoleSpy = vi.spyOn(console, "log");
bot.alert("UP", "50500.00", 1);
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("UP"));
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("1.00%"));
});
});
describe("Polling mechanism", () => {
it("should repeatedly call check()", async () => {
const checkSpy = vi.spyOn(bot, "check").mockResolvedValue();
bot.start();
vi.advanceTimersByTime(INTERVAL * 3);
await Promise.resolve();
expect(checkSpy).toHaveBeenCalledTimes(4);
});
});
describe("Error handling", () => {
it("should bubble up errors when check() is called directly", async () => {
nock("https://api.uphold.com")
.get(`/v0/ticker/${PAIR}`)
.replyWithError("Connection Error");
await expect(bot.check()).rejects.toThrow("Connection Error");
});
it("should reject invalid data formats", async () => {
nock("https://api.uphold.com")
.get(`/v0/ticker/${PAIR}`)
.reply(200, { bid: "100" });
await expect(bot.check()).rejects.toThrow("Invalid data format");
});
});
});