sdlc-hub/web/src/api/client.test.ts

71 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { beforeEach, describe, expect, it } from "vitest";
import { clearToken, getAuth, setToken } from "../auth/token.ts";
import { json, mockFetch } from "../test/fetchMock.ts";
import { api, ApiError, AuthError, checkToken, dropTokenOn, HubDownError } from "./client.ts";
describe("api", () => {
beforeEach(() => setToken("tok"));
it("шлёт токен и параметры, пустые параметры выбрасывает", async () => {
const fetch = mockFetch(() => json(200, { issues: [] }));
await api.get("/issues", { sprint: "S 2", q: "", epic: null, limit: 50 });
const [url, init] = fetch.mock.calls[0];
expect(url).toBe("/issues?sprint=S+2&limit=50");
expect((init!.headers as Record<string, string>).Authorization).toBe("Bearer tok");
expect(init!.cache).toBe("no-store");
});
it("PUT отправляет JSON", async () => {
const fetch = mockFetch((_m, _u, body) => json(200, body));
expect(await api.put("/issues/A-1/note", { rest_text: "x" })).toEqual({ rest_text: "x" });
expect((fetch.mock.calls[0][1]!.headers as Record<string, string>)["Content-Type"]).toBe("application/json; charset=utf-8");
});
it("AuthError хранит токен запроса; dropTokenOn не трогает новый токен (T3.5)", async () => {
mockFetch(() => json(401, { error: "unauthorized" }));
const e = await api.get("/board").catch((err: unknown) => err) as AuthError;
expect(e).toBeInstanceOf(AuthError);
expect(e.token).toBe("tok");
setToken("new-tok");
dropTokenOn(e);
expect(getAuth()).toEqual({ token: "new-tok", reason: "none" });
setToken("tok");
dropTokenOn(e);
expect(getAuth()).toEqual({ token: "", reason: "bad" });
});
it("401 — AuthError, 5xx и сеть — HubDownError, прочие 4xx — ApiError с detail", async () => {
mockFetch(() => json(401, { error: "unauthorized" }));
await expect(api.get("/board")).rejects.toBeInstanceOf(AuthError);
mockFetch(() => json(503, {}));
await expect(api.get("/board")).rejects.toBeInstanceOf(HubDownError);
mockFetch(() => { throw new TypeError("Failed to fetch"); });
await expect(api.get("/board")).rejects.toBeInstanceOf(HubDownError);
mockFetch(() => json(422, { error: "bad_note", detail: "неизвестные поля заметки: x" }));
const e = await api.put("/issues/A-1/note", { x: 1 }).catch((err: unknown) => err);
expect(e).toBeInstanceOf(ApiError);
expect(e).toMatchObject({ status: 422, error: "bad_note", detail: "неизвестные поля заметки: x" });
});
it("без токена — AuthError без запроса", async () => {
clearToken();
const fetch = mockFetch(() => json(200, {}));
await expect(api.get("/board")).rejects.toBeInstanceOf(AuthError);
expect(fetch).not.toHaveBeenCalled();
});
it("токен с недопустимыми символами — AuthError без запроса", async () => {
setToken("ток ен");
const fetch = mockFetch(() => json(200, {}));
await expect(api.get("/board")).rejects.toBeInstanceOf(AuthError);
expect(fetch).not.toHaveBeenCalled();
});
it("checkToken проверяет переданный токен, не сохранённый", async () => {
const fetch = mockFetch(() => json(200, { kind_labels: {} }));
await checkToken("candidate");
expect((fetch.mock.calls[0][1]!.headers as Record<string, string>).Authorization).toBe("Bearer candidate");
expect(fetch.mock.calls[0][0]).toBe("/settings");
});
});