メインコンテンツまでスキップ

テスト

決定の正しさはコンパイル時に自動で検証されます。あなたがテストするのは 1 つ、統合だけです。

決定は自動で検証される

npx ninka compile は手形から決定ベクトルを導出し、独立したリファレンス評価器で期待結果を計算して、ビルド済みの WASM に対して実行します。食い違えばビルドは失敗します。CI では npx ninka verify が、コミット済みの WASM に対してベクトルを再実行します。

npx ninka compile # ここで検証が走る
npx ninka verify # CI で再実行する
テストコードで決定を再実装しないでください

自前の「許可すべき/拒否すべき」テストはポリシーを二重に持つことになり、手形からドリフトします。許可する内容を変えたいなら手形を変更してください。

統合をテストする

各ハンドラーが正しい入力で check() を呼び、返ってきたブール値を適用しているかをテストします。境界をモックし、決定ではなくハンドラーの振る舞いを確かめます。

import { describe, it, expect, vi, beforeEach } from "vitest";
import { check } from "@/lib/authz";
import { POST } from "./route";

vi.mock("@/lib/authz", async (importOriginal) => ({
...(await importOriginal<typeof import("@/lib/authz")>()),
check: vi.fn(),
}));
const checkMock = vi.mocked(check);

describe("POST /documents/:id/edit", () => {
beforeEach(() => checkMock.mockReset());

it("calls check() with the correct input", async () => {
checkMock.mockResolvedValue(true);
await POST(makeRequest({ id: "doc-1", patch: { title: "New" } }));
expect(checkMock).toHaveBeenCalledWith(
"document-access",
expect.objectContaining({
action: { name: "edit" },
resource: { type: "document", properties: { owner_id: "user-9" } },
}),
);
});

it("returns 403 on deny", async () => {
checkMock.mockResolvedValue(false);
const res = await POST(makeRequest({ id: "doc-1", patch: { title: "New" } }));
expect(res.status).toBe(403);
});

it("proceeds on allow", async () => {
checkMock.mockResolvedValue(true);
const res = await POST(makeRequest({ id: "doc-1", patch: { title: "New" } }));
expect(res.status).toBe(200);
});
});

実際のポリシーを読み込む E2E テストでも、期待される決定を手でコード化しないでください。実際の入力でハンドラーを駆動し、HTTP の結果を確かめます。

関連項目