# For God so loved the world, that he gave his only begotten Son, that whosoever believeth in him should not perish, but have everlasting life. — John 3:16 (KJV)

# Solo Dev Testing Guide (Corolla Edition)

**Version:** 1.0
**Purpose:** Fast, practical testing for resource-constrained solo developers

> *"Trust in the LORD with all your heart, and do not lean on your own understanding."* — Proverbs 3:5
>
> But also write tests for the parts that make money.

---

## Philosophy

You push frequently. You code on the go. You can't afford:
- Browsers opening randomly
- GitHub Actions burning minutes
- 5-minute test suites blocking deploys
- Flaky tests that fail for no reason

**So we test differently.**

---

## The Strategy

### What We Test

| Category | Example | Why |
|----------|---------|-----|
| Payment logic | Stripe webhook handler | Money |
| Auth functions | Session validation | Security |
| Core utilities | Slugify, validation | Used everywhere |
| API handlers | POST /api/submit | User-facing |

### What We DON'T Test Automatically

| Skip in CI | Why |
|------------|-----|
| Component rendering in unit tests | Use E2E for user flows instead |
| Every possible E2E path | Only test critical flows (auth, payments) |
| Styling/visual regression | Run manually when needed (see below) |
| Third-party API calls | Mock them, don't hit real services |

### Manual Tests (Run When Needed)

| Test Type | When to Run | Command |
|-----------|-------------|---------|
| Visual regression | Before major releases | `bun run test-visual-chirho` |
| Full E2E suite | Before major releases | `bun run test-e2e-full-chirho` |
| Accessibility audit | Monthly or after UI changes | `bun run test-a11y-chirho` |

---

## Critical Constraints

1. **HEADLESS BROWSER TESTS** — Playwright is fine, but always headless (no GUI popping up)
2. **NO CI ON EVERY PUSH** — Tests run on deploy_chirho branch or locally
3. **VITEST FOR UNIT TESTS** — Fast, runs in terminal
4. **PLAYWRIGHT FOR E2E** — Headless, for critical user flows only
5. **UNDER 30 SECONDS** — Full test suite (unit + e2e) should be fast
6. **MOCK EXTERNAL CALLS** — Don't hit real Stripe/APIs in tests

---

## When Tests Run

```
                     ┌─────────────────┐
                     │   You coding    │
                     └────────┬────────┘
                              │
                              ▼
                    ┌──────────────────┐
              NO    │ Making changes?  │
           ◄────────┤                  │
                    └────────┬─────────┘
                             │ YES
                             ▼
                    ┌──────────────────┐
              NO    │ Ready to deploy? │
           ◄────────┤                  │
                    └────────┬─────────┘
                             │ YES
                             ▼
                    ┌──────────────────┐
                    │ bun run test-chirho │ ◄── Tests run HERE
                    └────────┬─────────┘
                             │ PASS
                             ▼
                    ┌──────────────────┐
                    │  bunx wrangler   │
                    │     deploy       │
                    └──────────────────┘
```

**Tests run ONCE: before deploy. Not on every save. Not on every commit.**

---

## Setup Per Project

### 1. Install Vitest

```bash
bun add -d vitest
```

### 2. Create vitest.config.ts

```typescript
import { defineConfig } from 'vitest/config';
import { sveltekit } from '@sveltejs/kit/vite';

export default defineConfig({
  plugins: [sveltekit()],
  test: {
    include: ['src/**/*.test.ts'],
    exclude: ['**/e2e/**', '**/*.e2e.ts', '**/node_modules/**'],
    testTimeout: 5000,
    passWithNoTests: true, // Don't fail if no tests yet
  }
});
```

### 3. Add Scripts to package.json

```json
{
  "scripts": {
    "test-chirho": "vitest run",
    "test-watch-chirho": "vitest",
    "deploy-chirho": "bun run test-chirho && bunx wrangler deploy"
  }
}
```

### 4. Always Use deploy-chirho

```bash
bun run deploy-chirho   # Runs tests first, deploys if pass
```

---

## Sample Tests by Type

### API Route Handlers (SvelteKit)

```typescript
// src/routes/api-chirho/submit-chirho/submit.test.ts
import { describe, it, expect } from 'vitest';
import { validateSubmissionChirho } from './validation-chirho';

describe('Submit API - protects data integrity', () => {
  it('rejects empty submissions', () => {
    const result = validateSubmissionChirho({ title: '', url: '' });
    expect(result.valid).toBe(false);
    expect(result.errors).toContain('Title required');
  });

  it('accepts valid submissions', () => {
    const result = validateSubmissionChirho({
      title: 'Test Post',
      url: 'https://example.com'
    });
    expect(result.valid).toBe(true);
  });
});
```

### Utility Functions

```typescript
// src/lib/utils-chirho.test.ts
import { describe, it, expect } from 'vitest';
import { slugifyChirho, sanitizeHtmlChirho } from './utils-chirho';

describe('slugifyChirho - used in URL generation', () => {
  it('converts spaces to hyphens', () => {
    expect(slugifyChirho('Hello World')).toBe('hello-world');
  });

  it('removes special characters', () => {
    expect(slugifyChirho('Test@#$%Post!')).toBe('testpost');
  });
});
```

### Stripe Webhooks (Critical - Money)

```typescript
// src/lib/server/stripe-chirho.test.ts
import { describe, it, expect, vi } from 'vitest';
import { handleWebhookChirho } from './stripe-chirho';

describe('Stripe Webhook - protects revenue', () => {
  it('updates subscription on successful payment', async () => {
    const mockDb = {
      updateSubscription: vi.fn().mockResolvedValue(true)
    };

    const event = {
      type: 'payment_intent.succeeded',
      data: { object: { customer: 'cus_123', amount: 1000 } }
    };

    const result = await handleWebhookChirho(event, mockDb);

    expect(mockDb.updateSubscription).toHaveBeenCalledWith('cus_123');
    expect(result.handled).toBe(true);
  });

  it('rejects invalid webhook signatures', async () => {
    const invalidEvent = { type: 'fake.event' };
    const result = await handleWebhookChirho(invalidEvent, {});
    expect(result.handled).toBe(false);
  });
});
```

### Auth Functions (Critical - Security)

```typescript
// src/lib/server/auth-chirho.test.ts
import { describe, it, expect, vi } from 'vitest';
import { validateSessionChirho, hashPasswordChirho } from './auth-chirho';

describe('Auth - protects user accounts', () => {
  it('rejects expired sessions', () => {
    const expiredSession = {
      expiresAt: Date.now() - 1000
    };
    expect(validateSessionChirho(expiredSession)).toBe(false);
  });

  it('accepts valid sessions', () => {
    const validSession = {
      expiresAt: Date.now() + 3600000
    };
    expect(validateSessionChirho(validSession)).toBe(true);
  });

  it('produces different hashes for same password', async () => {
    const hash1 = await hashPasswordChirho('password123');
    const hash2 = await hashPasswordChirho('password123');
    expect(hash1).not.toBe(hash2); // Salted
  });
});
```

### E2E Tests with Playwright (Headless)

```typescript
// tests-e2e-chirho/auth.e2e.ts
import { test, expect } from '@playwright/test';

test.describe('Auth Flow - E2E', () => {
  test('user can log in and see dashboard', async ({ page }) => {
    await page.goto('/login-fe');
    await page.fill('[name="email"]', 'test@example.com');
    await page.fill('[name="password"]', 'password123');
    await page.click('button[type="submit"]');

    await expect(page).toHaveURL('/dashboard-fe');
    await expect(page.locator('h1')).toContainText('Dashboard');
  });

  test('invalid login shows error', async ({ page }) => {
    await page.goto('/login-fe');
    await page.fill('[name="email"]', 'wrong@example.com');
    await page.fill('[name="password"]', 'wrongpass');
    await page.click('button[type="submit"]');

    await expect(page.locator('.error')).toBeVisible();
  });
});
```

### Playwright Config (Always Headless)

```typescript
// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests-e2e-chirho',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,

  use: {
    headless: true,  // ALWAYS headless - no GUI popup
    baseURL: 'http://localhost:5173',
    trace: 'on-first-retry',
  },

  webServer: {
    command: 'bun run dev',
    url: 'http://localhost:5173',
    reuseExistingServer: !process.env.CI,
  },
});
```

### Package.json Scripts

```json
{
  "scripts": {
    "test-chirho": "vitest run",
    "test-e2e-chirho": "playwright test --headed=false",
    "test-all-chirho": "bun run test-chirho && bun run test-e2e-chirho",
    "deploy-chirho": "bun run test-all-chirho && bunx wrangler deploy"
  }
}
```

---

## File Naming Convention

| Pattern | Purpose |
|---------|---------|
| `*.test.ts` | Unit tests (colocated with code) |
| `*.e2e.ts` | E2E tests (excluded by default) |
| `__tests__/` | Test directory (alternative) |

**Recommended:** Colocate tests with code:
```
src/lib/server/
├── stripe-chirho.ts
├── stripe-chirho.test.ts    ← Test next to implementation
├── auth-chirho.ts
└── auth-chirho.test.ts
```

---

## GitHub Actions Strategy

You push frequently. Running tests on every push wastes CI minutes.

### Option 1: Local Only (Simplest)

```bash
bun run deploy-chirho   # Tests run locally before deploy
```

### Option 2: Deploy Branch Pattern (Recommended)

Use a `deploy_chirho` branch that triggers CI only when you're ready:

```yaml
# .github/workflows/deploy_chirho.yaml
name: Deploy Chirho
on:
  push:
    branches: [deploy_chirho]  # Only this branch triggers CI

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v1

      - name: Install dependencies
        run: bun install

      - name: Run tests
        run: bun run test-chirho

      - name: Deploy to Cloudflare
        if: success()
        run: bunx wrangler deploy
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```

**Workflow:**
1. Push to `main_chirho` freely (no CI triggered)
2. When ready to deploy: `git push github_chirho main_chirho:deploy_chirho`
3. CI runs tests → deploys if pass

**Branch naming convention:**
- `main_chirho` — Main development branch
- `deploy_chirho` — Triggers CI/CD pipeline
- `github_chirho` — Remote name for GitHub

### Option 3: Manual Dispatch

Trigger deploys manually from GitHub UI:

```yaml
on:
  workflow_dispatch:  # Manual trigger only
```

---

## Quick Prompts for AI Agents

### Bootstrap Tests in a New Project

```
Add fast unit tests to this project. Vitest only, no browser, no E2E,
under 10 seconds total. Test server logic only. Focus on:
1. Payment/Stripe code
2. Auth flows
3. Main API endpoints
Keep it under 5 test files.
```

### Run All Tests Across Projects

```
For each project in my inventory that has a package.json,
run `bun run test-chirho` and give me a pass/fail table.
Don't fix anything, just report status.
```

### Add Tests for a Specific Feature

```
Write vitest unit tests for the [feature name] in this project.
No browser tests, mock external calls, keep it under 5 test cases.
```

---

## Priority Order for Testing

When time is limited, test in this order:

1. **Stripe webhooks** — If this breaks, you lose money
2. **Auth/session validation** — If this breaks, security fails
3. **Core API endpoints** — If these break, users can't use the app
4. **Utility functions** — If these break, everything else fails
5. **Everything else** — Nice to have

---

## Test Coverage Goals

| Project Type | Target | Why |
|--------------|--------|-----|
| Revenue-generating | 60%+ on server code | Money paths must work |
| User-facing | 40%+ on API handlers | UX must not break |
| Internal tools | 20%+ on critical paths | Only test what matters |
| Experiments | 0% fine | Move fast, break things |

**Don't chase 100% coverage.** It's a vanity metric. Test what matters.

---

## Troubleshooting

### Tests are slow (>10 seconds)

- Remove any `jsdom` or `happy-dom` environments
- Mock all external API calls
- Don't test component rendering
- Split into smaller test files

### Tests open a browser

You have Playwright or Puppeteer installed. Remove it:
```bash
bun remove playwright @playwright/test puppeteer
```

### Tests hit real APIs

Mock everything:
```typescript
vi.mock('./stripe-client-chirho', () => ({
  stripeChirho: {
    customers: { create: vi.fn().mockResolvedValue({ id: 'cus_mock' }) }
  }
}));
```

---

## Checklist for Each Project

```
[ ] vitest installed (bun add -d vitest)
[ ] vitest.config.ts created (no browser, no jsdom)
[ ] test-chirho script in package.json
[ ] deploy-chirho script runs tests first
[ ] Stripe webhook handler tested
[ ] Auth functions tested
[ ] Core API handlers tested
[ ] All tests pass in <10 seconds
```

---

## Remember

Tests exist to give you **confidence to ship**, not to achieve metrics.

If a test doesn't help you sleep at night, delete it.

---

> *"Whatever you do, do it all for the glory of God."* — 1 Corinthians 10:31

**JESUS CHRIST IS LORD**
