> ## Documentation Index
> Fetch the complete documentation index at: https://docs.befailproof.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# परीक्षण

> यूनिट टेस्ट, E2E टेस्ट, और टेस्ट हेल्पर्स

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

***

## टेस्ट चलाना

```bash theme={null}
# एक बार सभी यूनिट टेस्ट चलाएँ
bun run test:run

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

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

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

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

***

## यूनिट टेस्ट

यूनिट टेस्ट `__tests__/` में रहते हैं और [Vitest](https://vitest.dev) को `jsdom` के साथ उपयोग करते हैं।

```text theme={null}
__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
```

### नीति यूनिट टेस्ट लिखना

```typescript theme={null}
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'` से आयात करने के समय उपयोग करती हैं:

```bash theme={null}
bun build src/index.ts --outdir dist --target node --format cjs
```

फिर टेस्ट चलाएँ:

```bash theme={null}
bun run test:e2e
```

जब भी आप सार्वजनिक हुक API को बदलें (`src/hooks/custom-hooks-registry.ts`, `src/hooks/policy-helpers.ts`, या `src/hooks/policy-types.ts`), तो `dist/` को पुनः बनाएँ।

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

```text theme={null}
__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`** - प्रति-टेस्ट अलग-थलग वातावरण:

```typescript theme={null}
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`** - बाइनरी को आह्वान करें:

```typescript theme={null}
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`** - तैयार पेलोड फैक्ट्रीज़:

```typescript theme={null}
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 टेस्ट लिखना

```typescript theme={null}
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` deny   | `0`        | `{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason":"..."}}` |
| `PostToolUse` deny  | `0`        | `{"hookSpecificOutput":{"additionalContext":"Blocked ... because: ..."}}`               |
| Instruct (non-Stop) | `0`        | `{"hookSpecificOutput":{"additionalContext":"Instruction from failproofai: ..."}}`      |
| Stop instruct       | `2`        | खाली stdout; stderr में कारण                                                            |
| Allow               | `0`        | खाली स्ट्रिंग                                                                           |

### Vitest कॉन्फ़िग

E2E टेस्ट `vitest.config.e2e.mts` के साथ उपयोग करते हैं:

* `environment: "node"` - कोई ब्राउज़र ग्लोबल्स की आवश्यकता नहीं
* `pool: "forks"` - वास्तविक प्रक्रिया अलगाव (टेस्ट सबप्रोसेस स्पॉन करते हैं)
* `testTimeout: 20_000` - प्रति टेस्ट 20s (बाइनरी स्टार्टअप + हुक eval)

`forks` पूल महत्वपूर्ण है: थ्रेड-आधारित वर्कर्स `globalThis` साझा करते हैं, जो सबप्रोसेस-स्पॉनिंग टेस्ट में हस्तक्षेप कर सकता है। प्रक्रिया-आधारित फोर्क्स इससे बचते हैं।

***

## CI

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

पूर्ण प्री-मर्ज चेकलिस्ट के लिए [योगदान देना](../CONTRIBUTING.md) देखें।
