mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
* docs: rename to lowercase + drop leftover plans Rename docs in docs/ to lowercase kebab-case for consistency and update all references in CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md, packages/server/CLAUDE.md, and inter-doc links. Drop two leftover design plan docs: - docs/ATTACHMENT_BASED_REVIEW_CONTEXT_PLAN.md - docs/plan-approval-normalization.md * docs: drop stale uppercase entries from case-insensitive rename * feat(website): power /docs from public-docs/ markdown tree Move website docs out of TSX route components and into a root-level public-docs/ directory of plain markdown files with frontmatter (title, description, nav, order). - Add packages/website/src/docs.ts loader using import.meta.glob with ?raw to compile the markdown into the bundle at build time. - Replace the 9 hand-written docs/*.tsx routes with a single $.tsx catch-all that renders any slug via react-markdown. - Drive the docs sidebar nav from frontmatter order/nav. - Auto-discover docs routes in vite.config.ts so the sitemap stays in sync without manual edits. * fix(website): bind dev server to 0.0.0.0 so port collisions trigger fallback `host: "127.0.0.1"` (or unset) lets macOS coexist with another process holding an IPv6 dual-stack `*:8082` socket, so Vite never sees EADDRINUSE and silently binds alongside it. Forcing IPv4 wildcard makes the conflict real, and Vite's default `strictPort: false` falls through to the next free port. * fix(website): restore docs page styling after markdown migration Add a .docs-prose class that mirrors the styling the original docs/*.tsx components hand-rolled (h1/h2/h3 sizes, paragraph/list spacing, link colors, code blocks, callout-style blockquotes). ReactMarkdown was emitting unstyled HTML because the previous wrapper class only had inline-code rules — headings and code blocks fell back to user-agent defaults.
127 lines
3.7 KiB
Markdown
127 lines
3.7 KiB
Markdown
# Testing
|
|
|
|
## Philosophy
|
|
|
|
Tests prove behavior, not structure. Every test should answer: "what user-visible or API-visible behavior does this verify?"
|
|
|
|
## Test-driven development
|
|
|
|
Work in vertical slices: one test, one implementation, repeat. Each test responds to what you learned from the previous cycle.
|
|
|
|
```
|
|
RIGHT (vertical):
|
|
RED→GREEN: test1→impl1
|
|
RED→GREEN: test2→impl2
|
|
RED→GREEN: test3→impl3
|
|
|
|
WRONG (horizontal):
|
|
RED: test1, test2, test3, test4, test5
|
|
GREEN: impl1, impl2, impl3, impl4, impl5
|
|
```
|
|
|
|
Writing all tests first then all implementation produces bad tests — you end up testing imagined behavior instead of actual behavior.
|
|
|
|
## Determinism first
|
|
|
|
Tests must produce the same result every run:
|
|
|
|
- No conditional assertions or branching paths
|
|
- No reliance on timing, randomness, or network jitter
|
|
- No weak assertions (`toBeTruthy`, `toBeDefined`)
|
|
- Assert the full intended behavior, not fragments
|
|
|
|
```typescript
|
|
// Bad: conditional and weak
|
|
it("creates a tool call", async () => {
|
|
const result = await createToolCall(input);
|
|
if (result.ok) {
|
|
expect(result.id).toBeDefined();
|
|
}
|
|
});
|
|
|
|
// Good: deterministic and explicit
|
|
it("returns timeout error when provider times out", async () => {
|
|
const result = await createToolCall(input);
|
|
expect(result).toEqual({
|
|
ok: false,
|
|
error: { code: "PROVIDER_TIMEOUT", waitedMs: 30000 },
|
|
});
|
|
});
|
|
```
|
|
|
|
## Flaky tests are a bug
|
|
|
|
Never remove a test because it's flaky. Find the variance source (time, randomness, race condition, shared state, non-deterministic output, environment drift) and fix it.
|
|
|
|
## Real dependencies over mocks
|
|
|
|
Mocks are not the default. They require an explicit decision.
|
|
|
|
- **Database**: real test database, not a mock
|
|
- **APIs**: real APIs with test/sandbox credentials, not request mocks
|
|
- **File system**: temporary directory that gets cleaned up, not fs mocks
|
|
|
|
Ask: "will this still hold with real dependencies at runtime?" If no, don't mock.
|
|
|
|
### Use swappable adapters instead
|
|
|
|
When you need test isolation, design code so dependencies are injectable:
|
|
|
|
```typescript
|
|
interface EmailSender {
|
|
send(to: string, body: string): Promise<void>;
|
|
}
|
|
|
|
// Production
|
|
const realSender: EmailSender = { send: sendgrid.send };
|
|
|
|
// Test: in-memory adapter
|
|
function createTestEmailSender() {
|
|
const sent: Array<{ to: string; body: string }> = [];
|
|
return {
|
|
send: async (to: string, body: string) => {
|
|
sent.push({ to, body });
|
|
},
|
|
sent,
|
|
};
|
|
}
|
|
```
|
|
|
|
## End-to-end means end-to-end
|
|
|
|
When a test is labeled end-to-end, it calls the real service. No environment variable gates, no conditional skipping, no mocking the external dependency.
|
|
|
|
## Test organization
|
|
|
|
- Collocate tests with implementation: `thing.ts` + `thing.test.ts`
|
|
- Extract complex setup into reusable helpers
|
|
- Test bodies should read like plain English
|
|
- Build a vocabulary of test helpers that make complex flows simple
|
|
|
|
## Agent authentication in tests
|
|
|
|
Agent providers handle their own auth. Do not add auth checks, environment variable gates, or conditional skips to tests. If auth fails, report it.
|
|
|
|
## Debugging with tests
|
|
|
|
Use the test as your debugging ground:
|
|
|
|
1. Add temporary logging to the code under test
|
|
2. Run the test, observe actual values
|
|
3. Trace the flow end-to-end through test output
|
|
4. Confirm each assumption with actual output
|
|
5. Remove logging when done
|
|
|
|
The test output is the source of truth, not your reading of the code.
|
|
|
|
## Design for testability
|
|
|
|
If code isn't testable, refactor it. Signs:
|
|
|
|
- You want to reach for a mock
|
|
- You can't inject a dependency
|
|
- You need to test private internals
|
|
- Setup requires too much global state
|
|
|
|
Aim for deep modules: small interface, deep implementation. Fewer methods = fewer tests needed, simpler params = simpler setup.
|