Skip to content
created by Aha00aAha00a at 2026-08-21
last modified by Aha00aAha00a at 2026-09-15
revision: 4

Dev Testing

Dev

스펙은 MySQL 이 아니라 MySQL 모드의 H2 에서 돈다. 스펙이 도는 스키마는 실제 데이터베이스의 커밋된 덤프 schema/schema.sql 에서 만든다.

1. 공용 하네스

test/com/aha00a/tests 아래 두 파일이 모든 스펙에 필요한 것을 들고 있다.

  • TestApplication - 인메모리 SyncCacheApi 대역(진짜 캐시 모듈은 Redis 인데 테스트는 그것을 띄우지 않는다), H2 URL, 충돌 없는 데이터베이스 이름, 그리고 모든 스펙이 출발하는 Guice 구성.
  • TestSchema - createAll() 이 덤프에서 모든 테이블을 만든다. 골라 만든 부분집합이 아니다: 외래 키가 테이블들을 묶고 있고, 스펙이 자기가 필요하다고 믿는 것을 나열하는 것이 그 목록이 낡는 방식이다. 인메모리 DB 에서 안 쓰는 테이블은 비용이 없다.

다른 것이 필요한 스펙은 공용 구성을 다시 적는 대신 거기에 얹는 것으로 말한다. ApiV1FilterSpec 은 진짜 필터 체인을 돌리므로 play.http.filters 만 덮고 나머지는 그대로 둔다.

Guice 애플리케이션이 쓰는 데이터베이스 URL 과 직접 여는 DriverManager 연결의 URL 은 같은 TestApplication.h2Url 에서 와야 한다. 같은 이름의 철자 둘은 서로 다른 인메모리 데이터베이스 둘이고, 스펙은 불일치가 아니라 없는 테이블로 실패한다.

2. 스키마를 참으로 유지하기

스키마를 바꾸면 schemaDump.sh 로 덤프를 새로 뜬다. 스펙은 자동으로 따라온다. 스펙이 의존하는 컬럼이 사라지면 조용히 낡는 사본 대신 실패하는 스펙으로 나타난다.

스크립트는 mysqldump 가 쓰는 헤더를 지우는데, 지우는 이유가 둘이고 하나로 오해하기 쉽다. 호스트와 데이터베이스 이름은 공개 저장소에 있으면 안 되는 인프라 이름이라 .env 를 가리키는 자리로 바꾼다. 서버 버전·완료 시각·AUTO_INCREMENT 카운터는 실행마다 바뀌는 잡음일 뿐인데, 두면 안 바뀐 스키마가 diff 로 도착한다. "진짜" 헤더를 되찾겠다고 sed 하나를 지우면, 그 줄이 눌러 두고 있던 둘 중 하나가 되돌아온다.

읽어 들일 때 손보는 것이 셋 있다. 각각 스키마에 대한 의견 차가 아니라 MySQL 과 H2 의 차이다:

손본 것

왜

외래 키를 CREATE TABLE 밖 ALTER TABLE 로

덤프는 알파벳 순이라 키가 아직 없는 테이블을 가리키기 일쑤다 — 게다가 AccessLog 와 IpDeny 는 서로를 참조해서 어떤 순서로도 안 된다

tinyint(1) 을 BOOLEAN 으로

MySQL 에는 boolean 이 없어 tinyint(1) 로 덤프된다. 그대로 두면 H2 가 Integer 를 돌려주고 Boolean 을 기대하는 파서가 전부 실패한다

유니크하지 않은 컬럼으로의 외래 키 제거

MySQL 은 아무 인덱스 접두로의 키를 받아 준다. H2 는 대상이 유니크해야 하고 그러려고 유니크 인덱스를 만든다 — Page (site, name) 에 그러면 페이지가 두 번째 revision 을 가질 수 없다

셋째 것은 실제 fidelity 손실이다: Page (site, name) 으로의 키들은 테스트에서 강제되지 않는다. TestSchema 는 그것을 이름이 아니라 대상으로 정한다 — 대상이 유니크하지 않은 키는 전부 빠진다 — 그래서 개수는 덤프가 그때 담고 있는 만큼이지 여기 적어 둘 수는 없다. 2026-08-17 에는 셋이었다:

grep -c 'REFERENCES `Page` (`site`, `name`)' schema/schema.sql

그 밖의 모든 것 — 모든 NOT NULL, default, ENUM 값 목록 — 은 운영이 가진 그대로다.

마지막 문장은 보기보다 무겁다. 덤프는 운영이 아니라 개발 스키마에서 뜨기 때문이다. 아래 --check 는 커밋된 파일을 그것이 나온 스키마와 비교한다; 그 스키마를 이 주장의 대상인 운영과 비교하는 것은 없다. 한쪽에만 적용된 evolution 은 스펙이 운영에 없는 모양을 테스트하게 만들고, --check 는 여전히 파일이 최신이라고 말한다.

확인은 덤프 한 번과 diff 하나다. 헤더가 맞아떨어지도록 같은 정리를 거치고, 운영 스키마는 운영 저장소가 기록한 호스트와 관리자 자격증명으로 떠서, 커밋된 파일과 비교한다:

ssh <host> "mysqldump --defaults-file=<admin cnf> --default-character-set=utf8mb4 \
  --no-data --no-tablespaces --column-statistics=0 <production schema>" \
 sed 's/ AUTO_INCREMENT=[0-9]*//g' \
 sed 's/^-- Dump completed on .*//g' \
 sed 's/Distrib [0-9]*.[0-9]*.[0-9]*, for .*/Distrib #.#.#, for OS/g' \
 sed 's/^-- Host: .*/-- Host: (from .env)    Database: (from .env)/' \
 diff schema/schema.sql -

2026-08-17 실측: 동일. 양쪽 다 24개 테이블 488줄. 두 스키마는 갈라지지 않았고, 갈라지면 이것이 알아내는 방법이다.

3. 이렇게 만들며 찾은 것

이것이 대체한 손으로 쓴 사본은 운영과 열여섯 군데가 달랐다 — 정수 폭 아홉, VARCHAR(255) 로 선언된 TEXT 컬럼 둘, 진짜 컬럼이라면 거부할 targetType 을 스펙이 넣을 수 있도록 VARCHAR 로 선언된 ENUM 컬럼 셋, evolution 55 가 떨어뜨린 UserSite 테이블 하나.

덤프로 바꾸자 이번에는 픽스처에서 더 나왔다: 스펙들이 abbr 없는 Site 행(운영에서는 NOT NULL, default 없음), 뒤에 Page 가 없는 PageMeta 행, 필수 컬럼 여덟 개가 빠진 AccessLog 행을 넣고 있었다. 전부, 그것을 받아 줄 만큼 느슨한 스키마를 상대로 통과해 온 것이다.

UserMergeSpec 은 운영의 Page 에 User 로의 외래 키가 없다는 믿음으로, 그 키를 단 자기만의 최소 Page 테이블까지 만들어 갖고 있었다. 있다 — Page_User_seq_fk — 그래서 대역은 불필요했고 스펙은 이제 진짜 테이블을 쓴다.

4. 알려진 구멍

  • 스키마를 덤프 대신 evolution 에서 만들면 테스트가 마이그레이션 자체에 묶인다. 그런데 안 된다: 67개 파일 중 17개가 실패한다 — 41문장, H2 가 MySQL 모드에서도 거부하는 MySQL 문법이다.

문법

예

컬럼 위치 지정

ALTER TABLE Page MODIFY comment TEXT NOT NULL AFTER remoteAddress

... FIRST

ALTER TABLE Link ADD site INT DEFAULT 1 NOT NULL FIRST

한 문장에서 키를 지우고 더하기

ALTER TABLE Page DROP PRIMARY KEY, ADD PRIMARY KEY (site, name, revision)

TABLE 없는 TRUNCATE

TRUNCATE TermFrequency

MySQL 날짜 함수

UPDATE 안의 DATE_ADD(...)

전부 ALTER 다. 덤프에는 하나도 없다 — CREATE TABLE 뿐이다 — evolution 이 안 되는 곳에서 덤프가 되는 이유가 그것이다.

  • schema/schema.sql 갱신은 여전히 손으로 하는 단계이고, 아무도 덤프하지 않은 스키마 변경은 스펙을 어제의 모양으로 테스트하게 놔둔다. 실패 양상은 전보다 순하다 — 스펙마다 제각각의 발명품 대신 스위트 전체가 하나의 낡은 스키마에서 돈다 — 하지만 그것을 일어나게 만드는 것은 없다.

지금 있는 것은 물어보는 방법이고, 초록 스위트를 믿기 전에 돌려 볼 만큼 싸다:

sh ./schemaDump.sh --check

라이브 스키마를 같은 정리를 거쳐 덤프해 커밋된 파일과 diff 하고, 다르면 그 차이와 함께 0 아닌 코드로 끝난다. 아무것도 쓰지 않는다. 두 경로가 한 함수에서 나오는 것은 일부러다: 그 파이프라인의 사본 둘은 sed 하나씩 갈라져, 검사가 있지도 않은 차이를 보고하기 시작한다.

빌드에 물려 놓지는 않았다. .env 와 닿는 데이터베이스가 필요해서, 자격증명 없는 기계의 테스트 실행이 엉뚱한 이유로 실패하게 된다.

5. See Also

5.2. Similar Pages

Similar pages by cosine similarity. Words after page name are term frequency.

  • 32.79% Dev SchemaOrgVocabulary schema(17:45), 없는(8:8), 없다(6:9), 같은(5:10), 에서(6:8), 것은(6:7), test(5:6), 버전(2:9), 것이(4:6), 아니라(5:4)
  • 32.67% Dev Database schema(17:3), page(11:8), sql(14:4), evolution(5:10), 다른(3:12), date(1:14), table(8:3), default(4:7), 것이(4:7), 없는(8:2)

5.3. Adjacent Pages

Control
≤ 32
all
1.0x
1.0x
80
-120
ON
Metrics
Nodes(visible/total)0/0
Links(visible/total)0/0
Avg degree0.00
Depth coverage0
Queue(fetch/graph)0 / 0
Zoom(scale)1.00x
Ctrl/⌘ + Scroll: Zoom
Root 1-hop 2-hop+