---
layout: 'page'
uri: '/skills/gk-testing'
position: 20
slug: 'skills-gk-testing'
parent: 'skills-ship'
navTitle: 'gk-testing'
title: 'GK — Testing'
description: 'Testování v gokicku — testfx harness s reálnou SQLite DB, architektonické konformní testy (zz_audit/zz_gap) a quality gate (lint + arch + test + race + vitest + docs). Use when píšeš handler/repo test, nevíš jak rozjet DB v testu, řešíš proč ti spadl zz_audit/zz_gap, nebo co musí projít před commitem.'
name: 'gk-testing'
---

# GK — Testing

Jak se v gokicku testuje: integrační testy nad **reálnou** SQLite databází přes
`testfx`, samokontrolní testy hlídající architekturu (`zz_audit` / `zz_gap`) a
quality gate, který to celé před commitem prožene.

## What & when
- Sáhni sem, když: píšeš test pro command/query handler nebo repozitář,
  potřebuješ v testu reálnou DB + migrace + reálné repo/hasher/JWT, řešíš proč
  ti spadl `zz_audit_test.go` / `zz_gap_test.go`, nebo si ověřuješ, co musí
  projít před commitem (lint, arch, test, race, vitest, docs).
- NEtýká se: jak psát samotný handler (`/gk-commands`, `/gk-queries`), pravidla
  vrstev (`/gk-architecture`), ani DI wiring (`/gk-di`).

## For non-tech / juniors
Testy tu nejsou mockované — místo „předstíráme databázi" se pro každý test
založí **opravdová** malá SQLite databáze v dočasném souboru, naběhnou na ni
migrace a test píše a čte reálná data. Po testu se smaže sama. Tomu lešení se
říká `testfx` (test fixtures = připravené testovací prostředí).

Druhá skupina testů jsou „hlídači architektury". Projekt má pravidlo „tahle
vrstva nesmí sahat na tamtu". Tyhle testy projdou zdrojový kód jako text a
selžou v momentě, kdy někdo pravidlo poruší — jsou pojmenované `zz_…`, aby se
v seznamu řadily nakonec.

A nakonec „quality gate" — jeden příkaz (`make lint` + `make test`), který
ověří, že všechno (styl, architektura, testy) je v pořádku, než to pošleš dál.

## How it works

### Kde testy žijí (layout)

- **Go testy** (`_test.go`) — VŽDY vedle svého balíčku (`app/**`, `cmd/**`, `tools/gk/**`), nikdy v `tests/`.
  Jazykový idiom, ne preference: white-box testy potřebují package scope
  (neexportované symboly) a `zz_*` konformanční gaty skenují vlastní adresář
  přes `runtime.Caller`. Přesun by je rozbil.
- **`tests/assets/`** — FE vitest testy (jediné místo, kde testy žijí mimo
  testovaný kód; Vue SFC nemá package-local konvenci).
- **`tests/e2e/`** — shell E2E harness durable-run engine (`make e2e`,
  proces-lifecycle: crash/drain/at-least-once/terminal).


### testfx — reálná DB v testu
`app/internal/testfx/testfx.go`, import path `gokick/app/internal/testfx`.
`testfx.New(t, dbPath)` otevře izolovanou SQLite na `dbPath`, spustí migrace a
vrátí `*Fixture` s reálnými implementacemi (`Users`, `PlatformUsers`, `Tokens`, `Runs`, `Tenants`, `PlatformTenants`, `Hasher`, `Jwt`, `DB`).. DB se zavře automaticky přes `t.Cleanup`. Logger je tichý
(`io.Discard`). Užitečné helpery na `*Fixture`:
- `SeedUser(t, nickname, password, role)` / `SeedRefreshToken(t, userID, expiresAt)` — naplnění dat
- `AssertTokenCount(t, n)` — kontrola počtu řádků v `refresh_tokens`
- `NewBuses()` — postaví Command/Query/EventBus přesně jako `container_provider` (plný middleware chain)
- `ExecCommand[R](ctx, cmdBus, name, cmd, fn)` — **sankcionovaný způsob**, jak v handler testu protáhnout command celým chainem (tx, audit, eventy). Handler balíček nesmí importovat `application/bus` přímo (arch-lint: komponenta `application` nemá grant na `bus` ani na `bus_middleware`), takže to běží přes testfx.

Mimo bus (přímé volání handleru) se eventy chytají přes
`shared.ContextWithEventCollector(ctx)` + `collector.Flush()` —
viz `app/application/user/command/create_user_test.go`.

### zz_audit / zz_gap — konformní / coverage testy
Sada kurátorovaných testů s prefixem `zz_` (řadí se nakonec), každý cituje
v hlavičkovém komentáři **claim-ID z ledgeru** (`overview-39`,
`infra-db-security-10`, …; ledger žije jen v komentářích, ne jako doc). Mají dvě
fyzické podoby:

1. **Parser walks** (`go/parser`) — projdou zdrojáky jako text a pinují pravidla vrstev:
   - `app/domain/zz_audit_test.go` — domain smí importovat jen stdlib + `uuid` + jiný `domain/` (overview-39).
   - `app/domain/zz_gap_test.go` — HTTP handler nesmí importovat `infrastructure/sqlite`, `infrastructure/security` ani `application/**/event` (overview-41).
2. **testfx-wired black-box testy** — postaví reálné prostředí a pinují konkrétní coverage claim, např. `app/infrastructure/sqlite/user/zz_gap_test.go` (DB-level `CHECK`/`UNIQUE` constraints přes raw insert).

**Proč existují vedle go-arch-lintu:** parser walks jsou **silnější než
arch-lint** tam, kde matice závislostí nepomůže — `presentation → infrastructure`
je *legální* hrana, takže arch-lint handleru nezabrání sáhnout přímo do
`sqlite`/`security`; `domain/zz_gap_test.go` ano. Mají i **anti-vacuity
kontroly** (pozitivní kontrola + re-anchoring cesty), aby rozbitý walk neprošel
„zeleně, ale nic nezkontroloval". testfx-wired `zz_` testy, co self-importují
svůj produkční balíček (vypadá to jako cyklus), jsou v `.go-arch-lint.yml` →
`excludeFiles`.

### Quality gate
`make test` = `yarn test` (vitest) + `go test ./app/... ./cmd/...` + `cd tools/gk && go test ./...` (dev nástroje tsgen/boundary/errfields/docpaths jsou vlastní modul, takže je `./app/...` nepokrývá).
`make lint` = ESLint + `vue-tsc` (type-check) + `knip` (dead code) + `golangci-lint` + `make arch-check` (go-arch-lint) + `format-check` (golines) + `ts-check` (Go→TS parita typů) + `boundary-check` (wire DTO hranice) + `errfields-check` (parita chybových polí) + `docpaths-check` (každá cesta a `/gk-*` odkaz v docs/skills musí existovat) + `documan-lint`.
CI (`.github/workflows/validate.yml`): job `validate` = `make install` → `make lint` → `make test` → `make build`, se `SKIP_DOCUMAN=1` (dokumentaci v CI validuje samostatný `.github/workflows/documan.yml` přes `docker/documan/Dockerfile`); paralelní job `e2e` spouští `make e2e` (durable-run process-lifecycle testy, viz `tests/e2e/README.md`).

## Recipe

### Napsat integrační test handleru / repozitáře
1. `fx := testfx.New(t, filepath.Join(t.TempDir(), "moje.db"))` — reálná DB + migrace.
2. Naplň data: `u := fx.SeedUser(t, "bob", "secret12", "user")`.
3. Postav handler s reálnými závislostmi z `fx` (`fx.Users`, `fx.Hasher`, …).
4. Voláš handler buď přímo (eventy přes `shared.ContextWithEventCollector` + `collector.Flush()`),
   nebo přes plný chain: `cmdBus, _, _ := fx.NewBuses()` + `testfx.ExecCommand[...](...)`.
5. Asertuj proti DB (`fx.Users.FindByNickname(...)`, `fx.AssertTokenCount(t, n)`).

### Než commitnu
1. `make format` — srovná styl (ESLint Stylistic + golines).
2. `make lint` — ESLint + tsc + knip + golangci-lint + arch-check + format-check + ts-check + boundary-check + errfields-check + docpaths-check (+ documan lokálně).
3. `make test` — vitest + `go test`.
4. `go test -race ./app/... ./cmd/...` — **manuální** krok na souběh; **není**
   v `make test` ani v CI, ale spouští se lokálně před většími změnami.

## Invariants & pitfalls
- **testfx je test-only.** Import `app/internal/testfx` patří jen do `*_test.go`.
  Komponenta `testfx` v `.go-arch-lint.yml` smí wirovat reálnou infrastrukturu
  právě proto, že ji produkční kód nikdy neimportuje.
- **Handler testy přes bus jedou `testfx.ExecCommand`, ne `application/bus` přímo** — jinak spadne `make arch-check` (komponenta `application` nemá grant na `bus` ani na `bus_middleware`; sankcionovaná cesta je testfx).
- **Nepřejmenuj `zz_`-testy bez kontextu.** Ruší se na nich claim-ID a
  anti-vacuity kontroly; prefix `zz_` je záměrný (řazení nakonec).
- **Nový bounded context = nový `domain_<ctx>` + grant v `mayDependOn`** napříč
  konzumenty (`application`, `sqlite_repos`, `testfx`, …), jinak arch-check padá.
  Viz `/gk-architecture`.
- **`-race` se hlídá ručně.** Není ve `make test` ani v `validate.yml` —
  nezapomeň ho pustit u změn, co se dotýkají souběhu (collector per-request, worker).
- **Každý test má izolovanou DB** přes `t.TempDir()` — nesdílej cestu mezi testy,
  ať jdou paralelně bez „database is locked".

## Related
- Skills: `/gk-architecture` (vrstvy + go-arch-lint), `/gk-commands`, `/gk-queries`
  (struktura handlerů), `/gk-repositories` (`r.Conn(ctx)`, raw-pool výjimky), `/gk-di`.
- Docs: [Architecture](/framework/architecture) (§ go-arch-lint, cross-domain izolace).
- Kód: `app/internal/testfx/testfx.go`, `app/domain/zz_audit_test.go`,
  `app/domain/zz_gap_test.go`, `app/infrastructure/sqlite/user/zz_gap_test.go`,
  `.go-arch-lint.yml`, `.golangci.yml`, `Makefile` (`test`, `lint`, `arch-check`),
  `.github/workflows/validate.yml`.

---

[← gk-deploy](/skills/gk-deploy.md) | [🏛️ ADRs (template) →](/adrs.md)
