71 lines
3.5 KiB
TypeScript
71 lines
3.5 KiB
TypeScript
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");
|
||
});
|
||
});
|