zaezd / rules
EvilFreelancer/zaezd/.cursor/rules/testing.mdc
Vitest conventions and the mandatory unit-test set
Cursor rule1 starsChanged 43 days ago
---
description: "Vitest conventions and the mandatory unit-test set"
globs: tests/**/*.ts
alwaysApply: false
---
# Unit tests
Vitest, `tests/**/*.test.ts`. Unit tests protect the deterministic core; the observable
behavior of the product is protected by the Gherkin specs in `features/`
(see @bdd-specs.mdc). Write the spec first, the unit test second, the code third.
## Layout and naming
- File: `tests/<module>.test.ts`, mirroring the module under test.
- `describe('<module or function>')`, `it('<observable behavior>')`. The sentence in `it`
reads as a fact: `it('pulls check-in one day earlier for a morning event')`.
- Arrange / Act / Assert, in that order, with a blank line between the blocks.
- One behavior per test. A test that asserts four unrelated things hides which one broke.
## What is unit tested
The pure layer, exhaustively. Everything in `src/composer/` that has no I/O, plus payload
normalization in `src/sources/` and the cache key builder.
The mandatory set, from `specs/08-repozitoriy.md`; each one is a real defect that has been
observed or is one payload away:
1. An online event builds no trip.
2. An empty or unrecognized `venue` yields no precise marker.
3. Check-in is one day before `start_date` for a morning event.
4. An event finishing late adds one more night.
5. Arrival after `starts_at` minus 60 minutes is flagged and cannot be the primary package.
6. The hotel price is not multiplied by the number of nights (`price_basis: "stay_total"`).
7. The total price includes both the outbound and the return leg.
8. A hotel `geo_id` is never taken from a transport response.
9. A hotel checkout built without `offerpack_hash` is not labelled a cart.
10. Equivalent argument shapes produce one cache key.
11. A partial transport-mode failure is not turned into "there is no such transport".
12. A free-text event price stays out of the sum.
Twelve more that the plan review surfaced, each a defect one payload away:
13. An ISO time with an offset is compared on the right calendar day: 11:59+03:00 must not
cross noon or slide to the neighbouring date on the way through UTC.
14. Zero nights is a legal answer and produces a hotel-less package, not a forced night.
15. Every tie in the three package rules is broken by a stable secondary key.
16. Working days burnt differ between a night departure and a midday one, so "Без отпуска"
does not collapse into "Дешевле всего".
17. "от 7 000 ₽" parses as a lower bound and the total is labelled as one.
18. The cache does not store a rejected promise, coalesces identical in-flight requests and
respects its size ceiling.
19. `trip_id` round-trips Cyrillic, sorts its keys canonically, and rejects a corrupted
string, an unknown version and one over the length ceiling.
20. Every string from a source is escaped in text, in attributes, in URLs and inside the
embedded JSON.
21. A walking time is never derived from an OSRM car-profile answer.
22. A total assembled without a hotel is not called the full participation price.
23. Empty sets: no return leg, no hotel, no feasible pair at all.
24. In `replay` the reference date is the fixture's `recorded_at`, not the wall clock.
## Rules
- **No network.** Load recorded payloads from `fixtures/` through the same reader the
application uses. A test that needs a live source is misplaced: it belongs to a manual
smoke run.
- **No wall clock.** Pure functions take the current date as an argument. Never call
`Date.now()` or `new Date()` inside `src/composer/`; a test that depends on the day it
runs will go red on its own.
- **Assert on values, not on shapes.** `expect(total).toBe(16980)` beats
`expect(total).toBeGreaterThan(0)`.
- **Fixtures are typed on the way in.** A test asserts against the normalized domain type,
not against raw Tutu JSON, so a change in their payload breaks one module and not fifty
tests.
## Commands
```bash
npm test # vitest run
npm run test:watch # vitest, watch mode
npm run bdd # executable Gherkin specs
npm run verify # typecheck + lint + bdd + unit tests
```
`passWithNoTests` was removed from `vitest.config.ts` with the first real test, so an
accidentally empty run can no longer pass.
## References
@bdd-specs.mdc
@composer-core.mdc
@code-style.mdc
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

