मुख्य सामग्री पर जाएं
failproofai के दो टेस्ट सूट हैं: यूनिट टेस्ट (तेज़, मॉक किए गए) और एंड-टू-एंड टेस्ट (वास्तविक सबप्रोसेस आमंत्रण)।

टेस्ट चलाना

# सभी यूनिट टेस्ट एक बार चलाएं
bun run test:run

# यूनिट टेस्ट को वॉच मोड में चलाएं
bun run test

# E2E टेस्ट चलाएं (सेटअप की आवश्यकता है - नीचे देखें)
bun run test:e2e

# बिल्ड किए बिना टाइप-चेक करें
bunx tsc --noEmit

# लिंट करें
bun run lint

यूनिट टेस्ट

यूनिट टेस्ट __tests__/ में रहते हैं और Vitest के साथ happy-dom का उपयोग करते हैं।
__tests__/
  hooks/
    builtin-policies.test.ts      # प्रत्येक बिल्टइन के लिए पॉलिसी लॉजिक
    hooks-config.test.ts          # कॉन्फ़िग लोडिंग और स्कोप मर्जिंग
    policy-evaluator.test.ts      # पैरम इंजेक्शन और मूल्यांकन क्रम
    custom-hooks-registry.test.ts # globalThis रजिस्ट्री add/get/clear
    custom-hooks-loader.test.ts   # ESM लोडर, ट्रांजिटिव इम्पोर्ट, त्रुटि प्रबंधन
    manager.test.ts               # install/remove/list ऑपरेशन
  components/
    sessions-list.test.tsx        # सेशन लिस्ट कंपोनेंट
    project-list.test.tsx         # प्रोजेक्ट लिस्ट कंपोनेंट
    ...
  lib/
    logger.test.ts
    paths.test.ts
    date-filters.test.ts
    telemetry.test.ts
    ...
  actions/
    get-hooks-config.test.ts
    get-hook-activity.test.ts
    ...
  contexts/
    ThemeContext.test.tsx
    AutoRefreshContext.test.tsx

पॉलिसी यूनिट टेस्ट लिखना

import { describe, it, expect, beforeEach } from "vitest";
import { getBuiltinPolicies } from "../../src/hooks/builtin-policies";
import { allow, deny } from "../../src/hooks/policy-types";

describe("block-sudo", () => {
  const policy = getBuiltinPolicies().find((p) => p.name === "block-sudo")!;

  it("denies sudo commands", () => {
    const ctx = {
      eventType: "PreToolUse" as const,
      payload: {},
      toolName: "Bash",
      toolInput: { command: "sudo apt install nodejs" },
      params: { allowPatterns: [] },
    };
    expect(policy.fn(ctx)).toEqual(deny("sudo command blocked by failproofai"));
  });

  it("allows non-sudo commands", () => {
    const ctx = {
      eventType: "PreToolUse" as const,
      payload: {},
      toolName: "Bash",
      toolInput: { command: "ls -la" },
      params: { allowPatterns: [] },
    };
    expect(policy.fn(ctx)).toEqual(allow());
  });

  it("allows patterns in allowPatterns", () => {
    const ctx = {
      eventType: "PreToolUse" as const,
      payload: {},
      toolName: "Bash",
      toolInput: { command: "sudo systemctl status nginx" },
      params: { allowPatterns: ["sudo systemctl status"] },
    };
    expect(policy.fn(ctx)).toEqual(allow());
  });
});

एंड-टू-एंड टेस्ट

E2E टेस्ट वास्तविक failproofai बाइनरी को सबप्रोसेस के रूप में आमंत्रित करते हैं, stdin को JSON पेलोड पाइप करते हैं, और stdout आउटपुट और एक्जिट कोड पर दावे लगाते हैं। यह पूर्ण इंटीग्रेशन पथ का परीक्षण करता है जो Claude Code उपयोग करता है।

सेटअप

E2E टेस्ट बाइनरी को रेपो सोर्स से सीधे चलाते हैं। पहली बार चलाने से पहले, CJS बंडल बनाएं जो कस्टम हुक फाइलें 'failproofai' से इम्पोर्ट करते समय उपयोग करती हैं:
bun build src/index.ts --outdir dist --target node --format cjs
फिर टेस्ट चलाएं:
bun run test:e2e
जब भी आप सार्वजनिक हुक API (src/hooks/custom-hooks-registry.ts, src/hooks/policy-helpers.ts, या src/hooks/policy-types.ts) में बदलाव करें तो dist/ को फिर से बनाएं।

E2E टेस्ट संरचना

__tests__/e2e/
  helpers/
    hook-runner.ts      # बाइनरी स्पॉन करें, पेलोड JSON पाइप करें, एक्जिट कोड + stdout + stderr कैप्चर करें
    fixture-env.ts      # कॉन्फ़िग फाइलों के साथ प्रति-टेस्ट अलग-थलग अस्थायी निर्देशिकाएं
    payloads.ts         # प्रत्येक इवेंट टाइप के लिए Claude-सटीक पेलोड फैक्ट्रीज़
  hooks/
    builtin-policies.e2e.test.ts   # वास्तविक सबप्रोसेस के साथ प्रत्येक बिल्टइन पॉलिसी
    custom-hooks.e2e.test.ts       # कस्टम हुक लोडिंग और मूल्यांकन
    config-scopes.e2e.test.ts      # प्रोजेक्ट/स्थानीय/वैश्विक में कॉन्फ़िग मर्जिंग
    policy-params.e2e.test.ts      # प्रत्येक पैरामीटराइज्ड पॉलिसी के लिए पैरामीटर इंजेक्शन

E2E हेल्पर्स का उपयोग करना

FixtureEnv - प्रति-टेस्ट अलग-थलग वातावरण:
import { createFixtureEnv } from "../helpers/fixture-env";

const env = createFixtureEnv();
// env.cwd    - अस्थायी निर्देशिका; .failproofai/policies-config.json पिक अप करने के लिए payload.cwd के रूप में पास करें
// env.home   - अलग-थलग होम निर्देशिका; कोई वास्तविक ~/.failproofai लीक नहीं

env.writeConfig({
  enabledPolicies: ["block-sudo"],
  policyParams: {
    "block-sudo": { allowPatterns: ["sudo systemctl status"] },
  },
});
createFixtureEnv() स्वचालित रूप से afterEach क्लीनअप पंजीकृत करता है। runHook - बाइनरी को आमंत्रित करें:
import { runHook } from "../helpers/hook-runner";
import { Payloads } from "../helpers/payloads";

const result = await runHook(
  "PreToolUse",
  Payloads.preToolUse.bash("sudo apt install nodejs", env.cwd),
  { homeDir: env.home }
);

expect(result.exitCode).toBe(0);
expect(result.parsed?.hookSpecificOutput?.permissionDecision).toBe("deny");
Payloads - तैयार पेलोड फैक्ट्रीज़:
Payloads.preToolUse.bash(command, cwd)
Payloads.preToolUse.write(filePath, content, cwd)
Payloads.preToolUse.read(filePath, cwd)
Payloads.postToolUse.bash(command, output, cwd)
Payloads.postToolUse.read(filePath, content, cwd)
Payloads.notification(message, cwd)
Payloads.stop(cwd)

E2E टेस्ट लिखना

import { describe, it, expect } from "vitest";
import { createFixtureEnv } from "../helpers/fixture-env";
import { runHook } from "../helpers/hook-runner";
import { Payloads } from "../helpers/payloads";

describe("block-rm-rf (E2E)", () => {
  it("denies rm -rf", async () => {
    const env = createFixtureEnv();
    env.writeConfig({ enabledPolicies: ["block-rm-rf"] });

    const result = await runHook(
      "PreToolUse",
      Payloads.preToolUse.bash("rm -rf /", env.cwd),
      { homeDir: env.home }
    );

    expect(result.exitCode).toBe(0);
    expect(result.parsed?.hookSpecificOutput?.permissionDecision).toBe("deny");
  });

  it("allows non-recursive rm", async () => {
    const env = createFixtureEnv();
    env.writeConfig({ enabledPolicies: ["block-rm-rf"] });

    const result = await runHook(
      "PreToolUse",
      Payloads.preToolUse.bash("rm /tmp/file.txt", env.cwd),
      { homeDir: env.home }
    );

    expect(result.exitCode).toBe(0);
    expect(result.stdout).toBe("");  // allow → empty stdout
  });
});

E2E प्रतिक्रिया आकार

निर्णयएक्जिट कोडstdout
PreToolUse deny0{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason":"..."}}
PostToolUse deny0{"hookSpecificOutput":{"additionalContext":"Blocked ... because: ..."}}
Instruct (गैर-Stop)0{"hookSpecificOutput":{"additionalContext":"Instruction from failproofai: ..."}}
Stop instruct2खाली stdout; stderr में कारण
Allow0खाली स्ट्रिंग

Vitest कॉन्फ़िग

E2E टेस्ट vitest.config.e2e.mts का उपयोग करते हैं:
  • environment: "node" - कोई ब्राउजर ग्लोबल्स की आवश्यकता नहीं
  • pool: "forks" - सच्चा प्रोसेस इसोलेशन (टेस्ट सबप्रोसेस स्पॉन करते हैं)
  • testTimeout: 20_000 - प्रति टेस्ट 20 सेकंड (बाइनरी स्टार्टअप + हुक eval)
forks पूल महत्वपूर्ण है: थ्रेड-आधारित वर्कर्स globalThis साझा करते हैं, जो सबप्रोसेस-स्पॉनिंग टेस्ट में हस्तक्षेप कर सकता है। प्रोसेस-आधारित फोर्क इससे बचते हैं।

CI

पूर्ण CI रन (bun run lint && bunx tsc --noEmit && bun run test:run && bun run build) मर्ज करने से पहले पास होना आवश्यक है। E2E सूट समानांतर में एक अलग CI जॉब के रूप में चलता है। पूर्ण प्री-मर्ज चेकलिस्ट के लिए Contributing देखें।