Skip to content

Diff of Dev Testing

Changes between revision 2 - schemaDump.sh 가 저장소 안 .env 대신 ~/.config/ahawiki 설정에서 자격증명을 읽는다(9-02 체크아웃 소실 때 사본만 없어졌다). dev 스키마가 운영에서 갱신된다는 실측도 기록 and revision 3 - Bring the Dev pages back in step with the code: claims about behavior and structure checked against the source (2026-09-10 audit)
--- Dev Testing
+++ Dev Testing
@@ -11,21 +11,21 @@
  * `TestSchema` - `createAll()` 이 덤프에서 모든 테이블을 만든다. 골라 만든 부분집합이 아니다: 외래 키가 테이블들을 묶고 있고, 스펙이 자기가 필요하다고 믿는 것을 나열하는 것이 그 목록이 낡는 방식이다. 인메모리 DB 에서 안 쓰는 테이블은 비용이 없다.
 
 다른 것이 필요한 스펙은 공용 구성을 다시 적는 대신 거기에 얹는 것으로 말한다. `ApiV1FilterSpec` 은 진짜 필터 체인을 돌리므로 `play.http.filters` 만 덮고 나머지는 그대로 둔다.
 
 Guice 애플리케이션이 쓰는 데이터베이스 URL 과 직접 여는 `DriverManager` 연결의 URL 은 같은 `TestApplication.h2Url` 에서 와야 한다. 같은 이름의 철자 둘은 서로 다른 인메모리 데이터베이스 둘이고, 스펙은 불일치가 아니라 없는 테이블로 실패한다.
 
 == 스키마를 참으로 유지하기
 
 스키마를 바꾸면 `schemaDump.sh` 로 덤프를 새로 뜬다. 스펙은 자동으로 따라온다. 스펙이 의존하는 컬럼이 사라지면 조용히 낡는 사본 대신 실패하는 스펙으로 나타난다.
 
-스크립트는 `mysqldump` 가 쓰는 헤더를 지우는데, 지우는 이유가 둘이고 하나로 오해하기 쉽다. 호스트와 데이터베이스 이름은 공개 저장소에 있으면 안 되는 인프라 이름이라 로컬 설정을 가리키는 자리로 바꾼다. 서버 버전·완료 시각·`AUTO_INCREMENT` 카운터는 실행마다 바뀌는 잡음일 뿐인데, 두면 안 바뀐 스키마가 diff 로 도착한다. "진짜" 헤더를 되찾겠다고 `sed` 하나를 지우면, 그 줄이 눌러 두고 있던 둘 중 하나가 되돌아온다.
+스크립트는 `mysqldump` 가 쓰는 헤더를 지우는데, 지우는 이유가 둘이고 하나로 오해하기 쉽다. 호스트와 데이터베이스 이름은 공개 저장소에 있으면 안 되는 인프라 이름이라 로컬 설정을 가리키는 자리로 바꾼다. `mysqldump` 클라이언트 버전(`Distrib …, for …`)·완료 시각·`AUTO_INCREMENT` 카운터는 실행마다 바뀌는 잡음일 뿐인데, 두면 안 바뀐 스키마가 diff 로 도착한다. 서버 버전 줄(`-- Server version`)은 지우지 않으므로, 엔진을 올리면 그 한 줄이 diff 로 나온다 — 스키마가 바뀐 것이 아니다. "진짜" 헤더를 되찾겠다고 `sed` 하나를 지우면, 그 줄이 눌러 두고 있던 둘 중 하나가 되돌아온다.
 
 읽어 들일 때 손보는 것이 셋 있다. 각각 스키마에 대한 의견 차가 아니라 MySQL 과 H2 의 차이다:
 
 [[[#!Table tsv 1
 손본 것	왜
 외래 키를 `CREATE TABLE` 밖 `ALTER TABLE` 로	덤프는 알파벳 순이라 키가 아직 없는 테이블을 가리키기 일쑤다 — 게다가 `AccessLog` 와 `IpDeny` 는 서로를 참조해서 어떤 순서로도 안 된다
 `tinyint(1)` 을 `BOOLEAN` 으로	MySQL 에는 boolean 이 없어 `tinyint(1)` 로 덤프된다. 그대로 두면 H2 가 Integer 를 돌려주고 Boolean 을 기대하는 파서가 전부 실패한다
 유니크하지 않은 컬럼으로의 외래 키 제거	MySQL 은 아무 인덱스 접두로의 키를 받아 준다. H2 는 대상이 유니크해야 하고 그러려고 유니크 인덱스를 만든다 — `Page (site, name)` 에 그러면 페이지가 두 번째 revision 을 가질 수 없다
 ]]]
 
@@ -50,42 +50,42 @@
   | 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 local config)    Database: (from local config)/' \
   | diff schema/schema.sql -
 ]]]
 
 2026-08-17 실측: 동일. 양쪽 다 24개 테이블 488줄. 두 스키마는 갈라지지 않았고, 갈라지면 이것이 알아내는 방법이다.
 
 == 이렇게 만들며 찾은 것
 
-이것이 대체한 손으로 쓴 사본은 운영과 열여섯 군데가 달랐다 — 정수 폭 아홉, VARCHAR(255) 로 선언된 TEXT 컬럼 둘, 진짜 컬럼이라면 거부할 `targetType` 을 스펙이 넣을 수 있도록 VARCHAR 로 선언된 ENUM 컬럼 셋, evolution 55 가 떨어뜨린 `UserSite` 테이블 하나.
+이것이 대체한 손으로 쓴 사본은 운영과 여러 군데가 달랐다 — 정수 폭 아홉, 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` — 그래서 대역은 불필요했고 스펙은 이제 진짜 테이블을 쓴다.
 
 == 알려진 구멍
 
- * 스키마를 덤프 대신 evolution 에서 만들면 테스트가 마이그레이션 자체에 묶인다. 그런데 안 된다: 67개 파일 중 '''17개가 실패한다 — 41문장''', H2 가 MySQL 모드에서도 거부하는 MySQL 문법이다.
+ * 스키마를 덤프 대신 evolution 에서 만들면 테스트가 마이그레이션 자체에 묶인다. 그런데 안 된다: 2026-08-10 실측으로 67개 파일 중 '''17개가 실패했다 — 41문장''' — 그 뒤 들어온 69 도 `MODIFY … COLLATE` 에서 걸린다. H2 가 MySQL 모드에서도 거부하는 MySQL 문법이다.
 
 [[[#!Table tsv 1
 문법	예
 컬럼 위치 지정	`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 이 안 되는 곳에서 덤프가 되는 이유가 그것이다.
+ `ALTER` 만이 아니다 — `TRUNCATE`, `rename table`, 데이터를 고치는 `UPDATE` 도 있다. 덤프에는 하나도 없다 — `CREATE TABLE` 뿐이다 — evolution 이 안 되는 곳에서 덤프가 되는 이유가 그것이다.
 
  * `schema/schema.sql` 갱신은 여전히 손으로 하는 단계이고, 아무도 덤프하지 않은 스키마 변경은 스펙을 어제의 모양으로 테스트하게 놔둔다. 실패 양상은 전보다 순하다 — 스펙마다 제각각의 발명품 대신 스위트 전체가 하나의 낡은 스키마에서 돈다 — 하지만 그것을 일어나게 만드는 것은 없다.
 
  지금 있는 것은 물어보는 방법이고, 초록 스위트를 믿기 전에 돌려 볼 만큼 싸다:
 
 [[[#!Vim bash
 sh ./schemaDump.sh --check
 ]]]
 
- 라이브 스키마를 같은 정리를 거쳐 덤프해 커밋된 파일과 diff 하고, 다르면 그 차이와 함께 0 아닌 코드로 끝난다. 아무것도 쓰지 않는다. 두 경로가 한 함수에서 나오는 것은 일부러다: 그 파이프라인의 사본 둘은 `sed` 하나씩 갈라져, 검사가 있지도 않은 차이를 보고하기 시작한다.
+ 라이브 스키마를 같은 정리를 거쳐 덤프해 커밋된 파일과 diff 하고, 다르면 그 차이와 함께 0 아닌 코드로 끝난다. `schema/schema.sql` 은 건드리지 않는다(임시 파일 `schema/.schema.sql.tmp` 를 썼다가 지운다). 두 경로가 한 함수에서 나오는 것은 일부러다: 그 파이프라인의 사본 둘은 `sed` 하나씩 갈라져, 검사가 있지도 않은 차이를 보고하기 시작한다.
 
  빌드에 물려 놓지는 않았다. 로컬 설정과 거기 적힌 데이터베이스가 필요해서, 자격증명 없는 기계의 테스트 실행이 엉뚱한 이유로 실패하게 된다.