Skip to content

ADR-10: TestStatus 데이터 계약

🇺🇸 English Version

날짜작성자리포지토리
2024-12-29@KubrickCodecore, collector, web

컨텍스트

데이터 흐름 문제

Specvital은 멀티 서비스 파이프라인을 통해 테스트 메타데이터 처리:

Core Parser → Collector → Database → Web API → Frontend

각 서비스가 자체 TestStatus 타입을 정의하여 발생 가능한 문제:

  • 데이터 손실: 변환 중 상태 값 누락
  • 의미론적 드리프트: 동일 enum 값이 다른 의미
  • 조용한 실패: 매핑 오류가 감지되지 않음

발생한 문제

개발 중 심각한 데이터 무결성 이슈 발견:

Core 정의:      active, focused, skipped, todo, xfail (5개 상태)
Collector 정의: active, skipped, todo (3개 상태)

영향:

  • focused 테스트가 active로 잘못 매핑
  • xfail 테스트가 todo로 잘못 매핑
  • 사용자에게 부정확한 테스트 카운트 및 누락된 상태 표시

중요성

상태의미론적 의미손실 시 사용자 영향
active정상 테스트, 실행됨기준선, 영향 없음
focused디버그 전용 테스트 (.only, fit)CI 경고 미발생
skipped의도적으로 제외잘못된 skip 카운트
todo플레이스홀더, 미구현TODO 추적 누락
xfail실패 예상 (pytest xfail)부정확한 실패 예상치

결정

모든 서비스에서 손실 없는 1:1 TestStatus 매핑 강제

정규 상태 정의

모든 서비스는 정확히 이 5개 상태를 지원해야 함:

go
// 정규 TestStatus enum (출처: core)
type TestStatus string

const (
    TestStatusActive  TestStatus = "active"   // 정상 테스트
    TestStatusFocused TestStatus = "focused"  // .only, fit - 디버그 모드
    TestStatusSkipped TestStatus = "skipped"  // .skip, xit - 제외됨
    TestStatusTodo    TestStatus = "todo"     // 플레이스홀더 테스트
    TestStatusXfail   TestStatus = "xfail"    // 실패 예상
)

서비스 정렬

서비스위치상태
Corepkg/domain/status.go소스 오브 트루스
Collectorinternal/domain/analysis/inventory.goCore에서 1:1 매핑
Databaseschema.sql의 test_status ENUM1:1 매핑
Web APIOpenAPI TestStatus 스키마1:1 매핑

고려된 대안

옵션 A: 문자열 패스스루 (기각)

  • enum 검증 없이 원시 문자열로 상태 전달
  • 기각 이유: 컴파일 타임 안전성 없음, 오타로 인한 조용한 실패

옵션 B: 서브셋 매핑 (이전 상태)

  • Collector가 단순화된 3-상태 모델 사용
  • focused → active, xfail → todo 매핑
  • 기각 이유: 데이터 손실, 의미론적 손상

옵션 C: 엄격한 1:1 매핑 (선택됨)

  • 모든 서비스가 동일한 enum 값 정의
  • 모든 케이스를 포함하는 명시적 switch 문
  • 알 수 없는 값은 panic/error (fail-fast)

구현

Core (소스 오브 트루스)

go
// pkg/domain/status.go
type TestStatus string

const (
    TestStatusActive  TestStatus = "active"
    TestStatusSkipped TestStatus = "skipped"
    TestStatusTodo    TestStatus = "todo"
    TestStatusFocused TestStatus = "focused"
    TestStatusXfail   TestStatus = "xfail"
)

Collector (소비자)

go
// internal/domain/analysis/inventory.go
type TestStatus string

const (
    TestStatusActive  TestStatus = "active"
    TestStatusFocused TestStatus = "focused"
    TestStatusSkipped TestStatus = "skipped"
    TestStatusTodo    TestStatus = "todo"
    TestStatusXfail   TestStatus = "xfail"
)

매핑 레이어

go
// internal/adapter/mapping/core_domain.go
func convertCoreTestStatus(coreStatus domain.TestStatus) analysis.TestStatus {
    switch coreStatus {
    case domain.TestStatusFocused:
        return analysis.TestStatusFocused
    case domain.TestStatusSkipped:
        return analysis.TestStatusSkipped
    case domain.TestStatusTodo:
        return analysis.TestStatusTodo
    case domain.TestStatusXfail:
        return analysis.TestStatusXfail
    default:
        return analysis.TestStatusActive
    }
}

데이터베이스 스키마

sql
CREATE TYPE public.test_status AS ENUM (
    'active',
    'skipped',
    'todo',
    'focused',
    'xfail'
);

Web API (OpenAPI)

yaml
TestStatus:
  type: string
  enum:
    - active
    - focused
    - skipped
    - todo
    - xfail
  description: |
    테스트 상태 표시자:
    - active: 실행될 정상 테스트
    - focused: 단독 실행 표시 테스트 (예: it.only)
    - skipped: 건너뛰기 표시 테스트 (예: it.skip)
    - todo: 구현 예정 플레이스홀더 테스트
    - xfail: 실패 예상 테스트 (pytest xfail)

결과

긍정적

데이터 무결성:

  • 파이프라인에서 정보 손실 없음
  • 모든 상태 유형에 대한 정확한 테스트 카운트
  • focused 테스트에 대한 신뢰할 수 있는 CI 경고

타입 안전성:

  • 타입 enum으로 컴파일 타임 검증
  • 명시적 매핑으로 조용한 실패 방지
  • 상태 값에 대한 IDE 자동완성

API 명확성:

  • 프론트엔드가 정확한 상태 정보 수신
  • 모든 엔드포인트에서 일관된 동작
  • 자체 문서화되는 enum 값

부정적

조정 오버헤드:

  • 새 상태 추가 시 4곳 모두 변경 필요:
    • Core: pkg/domain/status.go
    • Collector: internal/domain/analysis/inventory.go
    • Database: ENUM 변경 마이그레이션
    • Web: OpenAPI 스키마 업데이트
  • 배포 중 버전 불일치 위험

스키마 진화:

  • PostgreSQL ENUM 변경은 마이그레이션 필요
  • 상태 값 쉽게 제거 불가 (deprecate만 가능)
  • ENUM 값 순서가 저장에 영향

향후 변경 가이드라인

새 상태 추가

  1. Core pkg/domain/status.go에 먼저 추가
  2. Collector 도메인 및 매핑 레이어에 추가
  3. ENUM 추가를 위한 데이터베이스 마이그레이션 생성
  4. OpenAPI 스키마 업데이트
  5. 순서대로 배포: Database → Collector → Web → Core

상태 폐기

  1. 문서에서 deprecated로 표시
  2. Collector에서 deprecated 상태를 대체 상태로 매핑
  3. deprecated 상태 출력을 중단하도록 파서 업데이트
  4. 마이그레이션 기간 후 OpenAPI에서 제거

참조

Open-source test coverage insights