test: 쇼핑몰 미설치 업그레이드와 후설치 경로 검증 보강

전체 마이그레이션의 쇼핑몰 SQL 64개를 격리된 접두어에서 검사해 미설치 상태의 건너뛰기와 후설치 후 업그레이드를 확인한다. 쇼핑몰 추가 여부에 따른 운영 절차를 문서화한다.
This commit is contained in:
whitedot
2026-09-09 07:58:05 +00:00
parent 545e6a504d
commit e74486e171
2 changed files with 67 additions and 1 deletions
+60 -1
View File
@@ -99,8 +99,64 @@ function g5_shop_install_test_fail($message)
exit(1);
}
// 모든 마이그레이션의 쇼핑몰 SQL만 격리 접두어로 실행한다.
// 공통 스키마가 섞인 파일에서도 쇼핑몰 문장의 조건을 빠짐없이 검사한다.
function g5_shop_install_test_migrations($prefix, $installed)
{
$checked = 0;
foreach (g5_migration_discover() as $migration) {
if (isset($migration['error'])) {
g5_shop_install_test_fail($migration['error']);
}
foreach ($migration['statements'] as $statement) {
if (strpos($statement['sql'], '`' . $prefix) === false) {
continue;
}
$checked++;
if (!preg_match('/^(?:CREATE TABLE(?: IF NOT EXISTS)?|ALTER TABLE|UPDATE)\s+`' . preg_quote($prefix, '/') . '/i', $statement['sql'])) {
g5_shop_install_test_fail('격리된 쇼핑몰 테이블을 대상으로 하지 않는 SQL입니다: ' . $migration['id']);
}
$should_run = g5_migration_should_run_statement($statement['conditions']);
if (!$installed && $should_run) {
g5_shop_install_test_fail('쇼핑몰 미설치 상태에서 실행되는 SQL이 있습니다: ' . $migration['id']);
}
if ($should_run) {
$result = g5_migration_execute_statement($statement);
if ($result['error'] !== '') {
g5_shop_install_test_fail($migration['id'] . ': ' . $result['error']);
}
}
}
}
if (!$checked) {
g5_shop_install_test_fail('쇼핑몰 마이그레이션 검사 대상이 없습니다.');
}
return $checked;
}
register_shutdown_function('g5_shop_install_test_cleanup');
g5_shop_install_test_cleanup();
// SQL과 조건에서 사용되는 쇼핑몰 자리표시자를 모두 임시 테이블에 연결한다.
$shop_keys = array();
foreach (glob(G5_PATH . '/migrations/*.sql') as $migration_file) {
preg_match_all('/\{\{(g5_shop_[a-z0-9_]+_table)\}\}/', file_get_contents($migration_file), $matches);
foreach ($matches[1] as $key) {
$shop_keys[$key] = true;
}
}
$source_shop_prefix = defined('G5_SHOP_TABLE_PREFIX') ? G5_SHOP_TABLE_PREFIX : G5_TABLE_PREFIX . 'shop_';
foreach ($shop_keys as $key => $unused) {
$original = g5_migration_replace_placeholders('{{' . $key . '}}');
if (strpos($original, $source_shop_prefix) !== 0) {
g5_shop_install_test_fail('쇼핑몰 테이블 접두어를 격리하지 못했습니다: ' . $key);
}
$g5[$key] = $prefix . substr($original, strlen($source_shop_prefix));
}
$checked_statements = g5_shop_install_test_migrations($prefix, false);
if (g5_shop_install_existing_tables($prefix) !== array()) {
g5_shop_install_test_fail('쇼핑몰 미설치 검사 후 테이블이 생성되었습니다.');
}
if (!@mkdir($data_path, G5_DIR_PERMISSION) || @file_put_contents($config_file, "<?php\n// 기존 설정 보존 표식\n?>") === false) {
g5_shop_install_test_fail('격리 테스트 설정 파일을 준비하지 못했습니다.');
}
@@ -120,6 +176,9 @@ foreach ($tables as $name) {
g5_shop_install_test_fail('쇼핑몰 테이블이 누락되었습니다: ' . $prefix . $name);
}
}
// 미설치 업그레이드 뒤 후설치는 과거 이력과 관계없이 최신 설치 SQL을 사용한다.
// 쇼핑몰을 먼저 설치한 경우의 업그레이드 SQL도 최신 스키마에서 정상 처리돼야 한다.
g5_shop_install_test_migrations($prefix, true);
$default = sql_fetch("SELECT de_shop_skin, de_shop_mobile_skin, de_sms_cont1, de_sms_cont2, de_sms_cont3, de_sms_cont4, de_sms_cont5 FROM `{$prefix}default` LIMIT 1", false);
if (!isset($default['de_shop_skin']) || $default['de_shop_skin'] !== 'basic' || $default['de_shop_mobile_skin'] !== 'basic') {
g5_shop_install_test_fail('쇼핑몰 기본 설정이 올바르게 생성되지 않았습니다.');
@@ -148,4 +207,4 @@ if ($retry['success']) {
g5_shop_install_test_fail('이미 설치된 쇼핑몰의 중복 설치를 차단하지 못했습니다.');
}
echo "쇼핑몰 후설치·설정 보존·중복 실행 차단 검사가 통과했습니다.\n";
echo '쇼핑몰 SQL ' . $checked_statements . "개 미설치 건너뛰기·후설치 후 업그레이드·설정 보존·중복 실행 차단 검사가 통과했습니다.\n";
+7
View File
@@ -48,6 +48,13 @@ php bin/check-runtime-ddl.php
쇼핑몰을 설치하지 않은 사이트에서는 DB 업그레이드 화면의 `쇼핑몰 설치` 버튼으로 최신 쇼핑몰 스키마를 추가할 수 있다. 같은 접두어의 쇼핑몰 테이블이 하나라도 있으면 기존 데이터 훼손을 막기 위해 설치를 중단하며, 기존 `data/dbconfig.php`의 DB 접속 정보와 토큰 키는 유지하고 쇼핑몰 설정만 추가한다. `php bin/check-shop-install.php`로 격리된 접두어에서 후설치, 설정 보존과 중복 실행 차단을 검사할 수 있다.
쇼핑몰 미설치 사이트의 업그레이드는 다음 두 방식으로 진행할 수 있다.
- **쇼핑몰 없이 업그레이드:** `쇼핑몰 설치`를 누르지 않고 기존 상태 이력 등록 또는 마이그레이션 실행을 진행한다. 쇼핑몰 관련 SQL은 기존 쇼핑몰 테이블의 존재 조건을 확인하므로 해당 테이블이 없는 설치에서는 실행하지 않는다. 공통 스키마와 쇼핑몰 변경이 한 파일에 있어도 쇼핑몰 문장만 건너뛴다. 쇼핑몰 항목의 성공 이력이 생겨도 쇼핑몰이 설치됐다는 뜻은 아니다.
- **쇼핑몰을 추가하고 업그레이드:** `쇼핑몰 설치`로 최신 쇼핑몰 스키마를 생성한 뒤 공통 DB 마이그레이션을 진행한다. 쇼핑몰 없이 업그레이드를 먼저 완료한 뒤 후설치해도 최신 설치 SQL을 사용하므로 과거에 건너뛴 쇼핑몰 이력을 삭제하거나 다시 실행할 필요가 없다.
이 구분은 `G5_USE_SHOP` 값만으로 판단하지 않고 실제 쇼핑몰 테이블 존재 여부를 사용한다. 기존 쇼핑몰 테이블을 남겨 둔 채 기능만 비활성화한 사이트는 미설치 사이트와 다르며, 기존 테이블의 스키마 보정 대상이 될 수 있다. `check-shop-install.php`는 전체 마이그레이션의 쇼핑몰 SQL을 검사해 미설치 상태의 모든 문장이 건너뛰어지는지, 후설치 뒤에는 오류 없이 처리되는지도 검증한다.
`check-runtime-ddl.php`는 관리자·게시판·라이브러리·쇼핑몰 등 일반 실행 경로에 DDL 문이 다시 추가되지 않았는지 검사한다. 테이블이나 컬럼 변경은 이 검사를 우회하지 말고 버전형 마이그레이션으로 작성한다.
게시판 삭제 시 게시판별 테이블을 함께 삭제하는 것처럼 사용자가 명시적으로 요청한 데이터 생명주기 작업은 마이그레이션과 구분해 제한된 허용 목록으로 관리한다. 일반 레코드 생성 과정에서 `AUTO_INCREMENT`를 초기화하는 DDL은 허용하지 않는다.