Neul 개발 문서

테스트

vitest 유닛 · API 통합 · Playwright e2e · Appium 네이티브, 그리고 회귀 테스트 정책

Neul 은 버그 성격에 맞는 레벨에서 테스트를 작성합니다. 핵심 원칙은 "고친 버그는 영구 회귀 테스트로 남긴다" 입니다.

회귀 테스트 정책 (항상)

버그/이슈를 고치면 그 케이스에 대한 영구 회귀 테스트를 반드시 커밋합니다. 일회성 검증 스크립트로 끝내지 않습니다. "그 버그를 잡았을 테스트"를 작성 → 실행 → 수정과 함께 커밋하세요.

외부 의존(실제 Google OAuth, AI 키 없는 경로 등)은 목킹/스텁으로 결정적으로 만듭니다. 이 정책은 저장소의 AGENTS.md 에 명시되어 있고, Stop 훅(작업 종료 시점) 에서 테스트 보강을 환기시키는 방식으로 강제됩니다.

레벨별 가이드

유닛 — packages/shared

순수 로직·수식 검증. vitest 로 실행합니다.

pnpm test          # turbo run test (= vitest, packages/shared)

통합 — apps/api

라우트·서버 데이터 흐름 검증. handler.fetch + 로컬 Postgres 를 사용합니다. 시간 의존을 결정적으로 만들기 위해 TZ=UTC 로 실행합니다.

pnpm test:integration   # = TZ=UTC vitest run (apps/api)

스펙 위치: apps/api/src/__tests__/*.integration.test.ts (api.integration.test.ts = 라우트/데이터 흐름, google-oauth.integration.test.ts = 로그인 검증 크립토)

통합 테스트는 로컬 Postgres 가 떠 있어야 합니다(pnpm db:up). 날짜 경계 로직은 런타임(Workers=UTC)과 동일하게 UTC 로 비교되므로, 테스트도 TZ=UTC 로 맞춥니다.

Google 로그인 모킹 — 목 OIDC 발행자

실제 Google 없이 구글 로그인 전 경로(코드 교환 → 서명 검증 → aud/iss 체크) 를 결정적으로 테스트합니다. jose 로 RS256 키쌍을 만들어 자체 서명 id_token + JWKS 를 발급하는 목 OIDC 발행자를 in-process 로 세웁니다(외부 프로세스 불필요).

  • 라우트는 APP_ENVprod 가 아닐 때만 테스트용 env 오버라이드를 적용합니다:
    • GOOGLE_TOKEN_URL — 토큰 교환 엔드포인트(우리 코드의 fetch 라 스텁 가능).
    • GOOGLE_JWKS_JSON로컬 JWKS(JSON). createLocalJWKSet 로 원격 fetch 없이 서명 검증.
    • GOOGLE_ISSUER — 허용 issuer.
  • 운영(APP_ENV=prod)에서는 이 오버라이드를 무시하고 항상 실제 Google 로 검증합니다 — 목 키로 우회 불가(테스트가 이 가드도 함께 검증).

josecreateRemoteJWKSet 은 Node 에서 global fetch 를 거치지 않아 스텁이 안 먹습니다. 그래서 키 검증은 로컬 JWKS(createLocalJWKSet)로, 토큰 교환만 fetch 스텁으로 가로챕니다. 이는 dotfive 의 oauth2-mock-server(별도 프로세스) 와 같은 목 발행자 원리를 우리 in-process 하네스에 맞춘 것입니다.

e2e (웹) — e2e-web

UI·데이터 바인딩 플로우 검증. Playwright 로 실행합니다.

pnpm e2e:web       # = playwright test (e2e-web/*.spec.ts)

동일 스펙으로 배포본 원격 검증도 가능합니다. 환경변수로 대상만 바꿉니다:

E2E_BASE_URL=https://<web> E2E_API_URL=https://<api> pnpm e2e:web

e2e (네이티브) — e2e-native

iOS/Android 네이티브 셸 검증. Appium + WebdriverIO 로 실행합니다.

pnpm appium                    # Appium 서버 (chromedriver autodownload)
pnpm e2e:ios                   # APPIUM_PLATFORM=ios  wdio (e2e-native/wdio.ios.conf.ts)
pnpm e2e:android               # APPIUM_PLATFORM=android wdio (e2e-native/wdio.android.conf.ts)

모바일 dev 네트워킹(CORS/PNA, 클리어텍스트)은 iOS 시뮬레이터 + Android 에뮬레이터 양쪽에서 검증되었습니다 — 아키텍처 › CORS와 모바일 네트워킹 참고.

한눈에 보기

레벨위치러너명령
유닛packages/sharedvitestpnpm test
통합apps/api/src/__tests__vitest (TZ=UTC) + 로컬 PGpnpm test:integration
e2e(웹)e2e-web/*.spec.tsPlaywrightpnpm e2e:web
e2e(네이티브)e2e-native/*Appium + WDIOpnpm e2e:ios / pnpm e2e:android

On this page