Skip to main content

tdd

PitLog test patterns and the anti-rationalization table. The Red-Green-Refactor cycle itself is already mandated in AGENTS.md — this skill encodes only the project-specific patterns (dependency injection over database fixtures, invariant tests, the exact-answer fixture race) and the excuses for skipping the cycle. TRIGGER when writing or modifying code in packages/ or apps/api/src/. DO NOT trigger for documentation, YAML config, CI workflow files, or styling-only changes.

설치로 이동

소스 정보

저장소
weaties/pitlog
최근 소스 활동
2026년 8월 19일 15:32
감지된 SKILL.md 언어
영어
스타
0
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
tdd
description
PitLog test patterns and the anti-rationalization table. The Red-Green-Refactor cycle itself is already mandated in AGENTS.md — this skill encodes only the project-specific patterns (dependency injection over database fixtures, invariant tests, the exact-answer fixture race) and the excuses for skipping the cycle. TRIGGER when writing or modifying code in packages/ or apps/api/src/. DO NOT trigger for documentation, YAML config, CI workflow files, or styling-only changes.
# TDD — PitLog patterns AGENTS.md already mandates: **failing test → implement → green → lint.** This skill encodes only the bits that aren't obvious from reading the existing tests. ## Patterns **Unit tests never touch a database or a browser.** Inject the dependency instead. The tenancy gates take a `MembershipResolver` precisely so the whole role matrix can be tested with a plain object: ```ts function resolverFor(memberships: Partial<Record<string, Role>>): MembershipResolver { return async (userId, teamId) => { /* … */ } } ``` The real query is exercised once, by the browser smoke test. Do not stand up Postgres to test a rule about roles. **API route tests drive Hono in-process** — no port, no network: ```ts const res = await app.request(`/teams/${TEAM}/thing`) expect(res.status).toBe(403) ``` **Test the invariant, not the instance.** `packages/db/src/schema.test.ts` asserts that *every* domain table carries `team_id`, enumerated from the schema module, rather than listing the tables that do. A new table cannot quietly skip the rule. When you add a cross-cutting requirement, write the test that way. **The fixture race is the planner's test bed** (SPEC §7). Its numbers are chosen so the correct answer is exact — 6 stints × 75 min + 5 stops × 6 min = 8 h to the second, 3 drivers × 2 stints = perfectly equal seat time. Assert **equality** on seat time and stint boundaries, not tolerance. A planner that is "close" on fairness is wrong. Import it from `@pitlog/domain`; it has no database dependency: ```ts import { EIGHT_HOUR_RACE, KNOWN_GOOD_SOLUTION } from '@pitlog/domain' ``` **Determinism is a test requirement.** `generateFixtureLaps()` uses a seeded Mulberry32 PRNG, not `Math.random()`, and there is a test asserting two calls are identical. Any new fixture data follows the same rule — a fixture that differs per run cannot be a known-good answer. **Browser tests are thin.** `tests/e2e/` proves the app boots, login works, and the shell renders. Behavioural depth belongs in Vitest, which runs in under a second. ## What must be TDD'd Logic, always: the stint planner, the burn-rate model, the sync merge, the rule engine, expense splitting, the role gates, anything that computes a number a human will act on at 3am. Exempt: scaffolding, config files, CI workflows, Tailwind classes, route wiring that only calls into tested code. ## Don't rationalize skipping the cycle The cycle only helps if you don't talk yourself out of it. | Rationalization | Rebuttal | |---|---| | "This change is too small to need a test." | Size predicts neither breakage nor regression. The failing test is what proves the change does what you think. | | "I'll write the test after I see it work." | Test-after rationalizes whatever the code already does, bugs included. | | "It's just a route handler — I'll eyeball it." | Route handlers are the easiest place to ship a silent 500 or a missing `team_id` filter. `app.request()` costs you three lines. | | "Testing this properly needs a database." | Then the dependency is in the wrong place. Inject it. That is why `MembershipResolver` exists. | | "Tests pass, so it's correct." | Passing tests are evidence, not proof. Confirm the test would actually fail without your change — if you didn't see it red, you don't know. | | "The planner is close enough on fairness." | The fixture race has an exact answer on purpose. "Close" means a driver gets short-changed on seat time at a real race. | | "I'll use `Math.random()` in the fixture, it's only test data." | Then the known-good solution isn't known. Seed the PRNG. | | "Lint is noisy here, I'll skip the gate." | There is no pre-existing-error allowlist in this repo and there must never be one. Zero, always. | | "I'll add the `team_id` filter in a follow-up." | That follow-up is a data leak across a tenancy boundary. Do it now. | ## Running things ```bash make test # vitest, sub-second make e2e # playwright; needs make up && make migrate && make seed first make check # typecheck + lint + test — the pre-PR gate npx vitest packages/domain # watch one package while iterating ``` There is **no pre-existing-error allowlist** in this repo, deliberately (see AGENTS.md → Decisions). If lint or typecheck is red, fix it.
GitHub에서 보기