> ## 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
    custom-hooks-loader.test.ts   # טוען ESM, ייבוא מעבר, טיפול בשגיאות
    manager.test.ts               # פעולות התקנה/הסרה/רשימה
  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` האמיתי כתהליך משנה, משדרות מטען JSON ל-stdin, ובוחנות את פלט ה-stdout וקוד היציאה. זה בוחן את נתיב ההשתלבות המלא ש-Claude Code משתמש בו.

### הגדרה

בדיקות E2E מפעילות את הקובץ הבינארי ישירות ממקור המאגר. לפני ההפעלה הראשונה, בנה את ערכת ה-CJS שקבצי hook מותאמים משתמשים בהם בעת ייבוא מ-`'failproofai'`:

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

לאחר מכן הפעל את הבדיקות:

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

בנה מחדש את `dist/` בכל פעם שתשנה את ה-API הציבורי של hook (`src/hooks/custom-hooks-registry.ts`, `src/hooks/policy-helpers.ts`, או `src/hooks/policy-types.ts`).

### מבנה בדיקת 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       # טעינה והערכה של hook מותאם
    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    - ספרייה זמנית; העבר כ-payload.cwd כדי לבחור ב-.failproofai/policies-config.json
// 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` - 20 שניות לכל בדיקה (הפעלת קובץ בינארי + הערכת hook)

ה-`forks` pool חשוב: עובדים מבוססי thread משתפים את `globalThis`, מה שיכול להפריע לבדיקות המשדרות תהליכים משניים. forks מבוססי תהליך מונעים זאת.

***

## CI

הריצה המלאה ב-CI (`bun run lint && bunx tsc --noEmit && bun run test:run && bun run build`) חייבת להעביר לפני מיזוג. הצד E2E פועל כעבודת CI נפרדת במקביל.

ראה [תרומה](../CONTRIBUTING.md) לרשימת הבדיקה המלאה לפני מיזוג.
