/** * Integration tests for admin endpoints */ import { describe, test, expect, beforeAll } from "bun:test"; import { AttoshiClient } from "../helpers/api.js"; import { waitForServer, BASE_URL } from "../setup.js"; // Auth token for admin endpoints const AUTH_TOKEN = process.env.AUTH_TOKEN || "test-auth-token"; describe("Admin Endpoints", () => { const client = new AttoshiClient(); beforeAll(async () => { await waitForServer(); }); describe("POST /xrpc/cash.attoshi.initConfig", () => { test("returns 401 without auth token", async () => { const res = await fetch(`${BASE_URL}/xrpc/cash.attoshi.initConfig`, { method: "POST", }); expect(res.status).toBe(401); }); test("returns 401 with invalid auth token", async () => { const res = await fetch(`${BASE_URL}/xrpc/cash.attoshi.initConfig`, { method: "POST", headers: { Authorization: "Bearer invalid-token-12345" }, }); expect(res.status).toBe(401); }); test("returns 400 or 200 depending on config state", async () => { const { status, data } = await client.initConfig(AUTH_TOKEN); // Either config already exists (400) or was just created (200) expect([200, 400, 401]).toContain(status); if (status === 400) { // Config already exists expect(data.error).toBeDefined(); } }); }); describe("POST /xrpc/cash.attoshi.startFirehose", () => { test("returns 401 without auth token", async () => { const res = await fetch(`${BASE_URL}/xrpc/cash.attoshi.startFirehose`, { method: "POST", }); expect(res.status).toBe(401); }); test("returns 401 with Bearer but no token", async () => { const res = await fetch(`${BASE_URL}/xrpc/cash.attoshi.startFirehose`, { method: "POST", headers: { Authorization: "Bearer " }, }); expect(res.status).toBe(401); }); test("returns 401 with wrong auth scheme", async () => { const res = await fetch(`${BASE_URL}/xrpc/cash.attoshi.startFirehose`, { method: "POST", headers: { Authorization: `Basic ${AUTH_TOKEN}` }, }); expect(res.status).toBe(401); }); test("starts firehose with valid auth", async () => { const { status, data } = await client.startFirehose(AUTH_TOKEN); // 401 if token is wrong, otherwise 200 if (status === 200) { expect(["started", "already_connected", "connecting"]).toContain( data.status ); } else { expect(status).toBe(401); } }); }); describe("POST /xrpc/cash.attoshi.stopFirehose", () => { test("returns 401 without auth token", async () => { const res = await fetch(`${BASE_URL}/xrpc/cash.attoshi.stopFirehose`, { method: "POST", }); expect(res.status).toBe(401); }); test("stops firehose with valid auth", async () => { const { status, data } = await client.stopFirehose(AUTH_TOKEN); if (status === 200) { expect(data.status).toBe("stopped"); } else { expect(status).toBe(401); } }); }); describe("GET /xrpc/cash.attoshi.getFirehoseStatus", () => { test("returns 200 without auth requirement", async () => { const { status } = await client.getFirehoseStatus(); expect(status).toBe(200); }); test("returns connection status", async () => { const { data } = await client.getFirehoseStatus(); expect(data).toHaveProperty("connected"); expect(typeof data.connected).toBe("boolean"); }); test("returns reconnect attempts", async () => { const { data } = await client.getFirehoseStatus(); expect(data).toHaveProperty("reconnectAttempts"); expect(typeof data.reconnectAttempts).toBe("number"); expect(data.reconnectAttempts).toBeGreaterThanOrEqual(0); }); test("returns entity DID", async () => { const { data } = await client.getFirehoseStatus(); expect(data).toHaveProperty("entityDid"); expect(data.entityDid).toMatch(/^did:/); }); }); describe("GET /xrpc/cash.attoshi.getStats", () => { test("returns 200", async () => { const { status } = await client.getStats(); expect(status).toBe(200); }); test("returns UTXO count", async () => { const { data } = await client.getStats(); expect(data).toHaveProperty("utxoCount"); expect(typeof data.utxoCount).toBe("number"); expect(data.utxoCount).toBeGreaterThanOrEqual(0); }); test("returns spent count", async () => { const { data } = await client.getStats(); expect(data).toHaveProperty("spentCount"); expect(typeof data.spentCount).toBe("number"); expect(data.spentCount).toBeGreaterThanOrEqual(0); }); test("returns issuance count", async () => { const { data } = await client.getStats(); expect(data).toHaveProperty("issuanceCount"); expect(typeof data.issuanceCount).toBe("number"); expect(data.issuanceCount).toBeGreaterThanOrEqual(0); }); test("returns supply info", async () => { const { data } = await client.getStats(); expect(data).toHaveProperty("supply"); // Note: supply uses snake_case from SQLite row expect(data.supply).toHaveProperty("total_issued"); expect(data.supply).toHaveProperty("burned"); }); test("stats are consistent with other endpoints", async () => { const statsRes = await client.getStats(); const supplyRes = await client.getSupply(); expect(statsRes.data.issuanceCount).toBe(supplyRes.data.issuanceCount); // Note: stats.supply uses snake_case, supplyRes uses camelCase expect(statsRes.data.supply.total_issued).toBe(supplyRes.data.totalIssued); expect(statsRes.data.supply.burned).toBe(supplyRes.data.burned); }); }); });