# 테스트 자동화 전략

**버전** 0.1 (2026-08-12) · **상태** 개발 착수용 초안

핵심은 하나다. **삭제 판단을 순수 함수로 떼어내 단위 테스트로 덮는다.**
이 연동의 사고는 전부 거기서 나는데, 그 로직은 DB도 구글 API도 없이 시험할 수 있다.

---

## 1. 계층

| 계층 | 무엇을 | 무엇 없이 도는가 | 비중 |
|---|---|---|---|
| **단위** | 대조 판단, 제목 조립, 날짜 변환, 임계 판정 | DB·네트워크·구글 API 전부 없이 | **가장 많이** |
| **계약** | API 요청·응답 형식 | 실제 WAS 없이(골든 파일) | 중간 |
| **통합** | 실제 조회 SQL과 응답 | 데모 서비스영역 필요 | 적게 |
| **E2E** | 동기화 한 바퀴 | 구글 API는 **가짜**로 | 아주 적게 |

위로 갈수록 빠르고 안정적이다. **아래로 갈수록 느리고 잘 깨진다** — 그래서 아래는 얇게 둔다.

---

## 2. 단위 — 여기에 힘을 준다

### 2.1 대조 로직을 순수 함수로 떼어낸다

```
reconcile(expected: Event[], existing: CalendarEvent[], opts) -> Plan
Plan = { create: Event[], patch: Patch[], delete: string[], abort?: string }
```

- 입력이 배열 둘과 설정뿐이다. **DB도 구글도 부르지 않는다.**
- 실행부는 `Plan`을 받아 그대로 수행하기만 한다. 판단과 수행을 섞지 않는다.
- 테스트 시나리오 6장(T-F-01~05)이 **전부 이 함수의 단위 테스트로 재현된다.**

**반드시 덮을 경우**

| 입력 | 기대 |
|---|---|
| `expected=[]`, `existing=100건` | `abort` — 임계 초과 |
| `existing`에 표식 없는 이벤트 | `delete`에 포함되지 않는다 |
| 창 밖 이벤트 | `delete`에 포함되지 않는다 |
| 같은 `reqNo`, `srcStatus` 20 → 30 | `patch` 1건, `create`·`delete` 0건 |
| 두 번 연속 적용 | 2회차 `Plan`이 비어 있다(멱등) |

### 2.2 그 밖의 순수 함수

- **제목 조립** — `[진행중]` 접두, 마스킹 치환, 표시명 공백일 때
- **날짜 변환** — 종일 이벤트 `end.date` **+1일**(T-E-02). 반차 시각 처리
- **기간 겹침 판정** — 달을 걸친 건(T-E-01)
- **임계 판정** — 분모가 0일 때(캘린더가 비었을 때) 나눗셈이 터지지 않는지

---

## 3. 계약 — 골든 파일

`docs/api-proposal/sample-request.json`·`sample-response.json`을 **골든 파일로 고정**한다.

- 응답 스키마가 바뀌면 테스트가 깨진다 → 의도한 변경이면 골든을 갱신한다
- 연동 쪽 파서는 **골든 파일만으로** 시험한다(WAS 불필요)
- 필수 필드 누락(`MASK_NM`, `CHG_DATE`가 NULL인 교육 건 등)을 여기서 잡는다

---

## 4. 통합 — 데모 서비스영역

실제 SQL이 도는지 확인한다. 느리므로 **적게** 둔다.

- 대상: 데모 `SERVAREA_ID='300'`
- 권한 경계(T-A-01~04)는 **여기서만** 진짜로 확인된다. 연동 계정의 권한을 바꿔 가며 돌린다
- 취소 시나리오(T-C-01~04)도 여기서 확인한다 — 취소승인이 원건을 40으로 바꾸는 것은
  제품 프로시저의 동작이라 모의로는 검증되지 않는다

---

## 5. E2E — 구글 API는 가짜로

**실제 구글 캘린더에 붙이지 않는다.** 느리고, 쿼터에 걸리고, 남의 캘린더를 더럽힌다.

- 캘린더 클라이언트를 인터페이스로 두고 **가짜 구현**을 끼운다(메모리 맵)
- 가짜는 이벤트 목록·생성·수정·삭제만 흉내 낸다. 표식은 그대로 보관한다
- 한 바퀴 시나리오: 신규 → 승인 전이 → 취소 → 재신청 → 대량 삭제 방어
- **실제 구글 연결은 수동 확인**으로 남긴다(회차마다 자동으로 돌릴 대상이 아니다)

---

## 6. 회귀 — 기준점 대조

사이트 `/baseline`의 기대값 JSON과 API 응답을 대조하는 스크립트를 둔다(T-Z-01).

```
1) GET /api/baseline            → 기대값
2) POST /getTaaCalendarApiList.do (같은 조건) → 실제
3) 건수 · title · masked · 기간 비교 → 다르면 실패
```

- **같은 날 뽑은 것끼리** 비교한다(기간 창이 오늘 기준으로 다시 계산된다)
- 이 하나가 통과하면 상태 필터·1426·기간 겹침이 함께 검증된다
- 인수 판정에 그대로 쓴다 — 사람 눈이 아니라 diff로 끝난다

---

## 7. CI 배치

| 시점 | 무엇 |
|---|---|
| 매 커밋 | 단위 + 계약 (수 초) |
| 병합 전 | 위 + E2E(가짜 구글) |
| 야간 | 위 + 통합(데모 서비스영역) + 기준점 대조 |
| 배포 전 | 전체 + 실제 구글 수동 확인 1회 |

---

## 8. 자동화하지 않는 것

정직하게 밝혀 둔다. 아래는 **사람이 본다.**

- 실제 구글 캘린더에서의 표시 모양(종일/시간, 색, 모바일 앱 동기화)
- 직원 구독 흐름(링크 클릭 → 추가)
- 공지문 문구가 읽히는지
- 옵션 1426 값이 **업무적으로** 맞는지 — 코드가 코드표에 있는지는 자동으로 보지만,
  "이 휴가 종류를 감추는 것이 맞는가"는 판단이다

---

## 9. 착수 순서

1. **대조 함수 시그니처부터 고정**한다. 실행부보다 먼저다
2. 그 함수의 단위 테스트를 **T-F-01~05로 먼저 쓴다**(구현 전에)
3. 골든 파일 계약 테스트
4. 통합·E2E는 API가 실제로 도는 뒤에

> 2번을 먼저 하는 까닭 — 대량 삭제 방어는 나중에 붙이면 반드시 빠진다. 정상 경로가 먼저
> 돌기 시작하면 "일단 되니까"로 넘어가고, 사고는 운영에서 난다.
