2026-11-01 20:42:53 -05:00
""" 使用唯一的本地测试库运行迁移和集成测试,绝不清理原业务库。 """
import os
import argparse
import re
import secrets
import subprocess
import sys
from pathlib import Path
def main ( ) :
parser = argparse . ArgumentParser ( )
parser . add_argument ( ' --reuse-database ' )
parser . add_argument ( ' --migration-roundtrip ' , action = ' store_true ' )
parsed , test_args = parser . parse_known_args ( )
if parsed . reuse_database and not re . fullmatch ( r ' payroll_test_[0-9a-f] {12} ' , parsed . reuse_database ) :
raise SystemExit ( ' 只允许复用本运行器创建的隔离测试库 ' )
root = Path ( __file__ ) . resolve ( ) . parents [ 1 ]
python = root / " backend/.venv-workbench/Scripts/python.exe "
if not python . exists ( ) :
python = Path ( sys . executable )
suffix = secrets . token_hex ( 6 )
database , username = parsed . reuse_database or " payroll_test_ " + suffix , " pytest_ " + suffix
password = secrets . token_urlsafe ( 30 )
sql = ( " " if parsed . reuse_database else f " CREATE DATABASE ` { database } ` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; " ) + f " CREATE USER ' { username } ' @ ' % ' IDENTIFIED BY ' { password } ' ; GRANT ALL PRIVILEGES ON ` { database } `.* TO ' { username } ' @ ' % ' ; "
setup = subprocess . run (
[ " docker " , " exec " , " -i " , " ys-mysql-dev " , " sh " , " -c " , ' export MYSQL_PWD= " $MYSQL_ROOT_PASSWORD " ; exec mysql -uroot ' ] ,
input = sql . encode ( ) , capture_output = True ,
)
if setup . returncode :
raise SystemExit ( " 隔离库创建失败,未执行迁移或测试 " )
env = os . environ | {
" MYSQL_DATABASE " : database , " MYSQL_USER " : username , " MYSQL_PASSWORD " : password ,
" PAYROLL_TEST_DATABASE " : database , " REDIS_DB " : " 15 " , " PYTHONIOENCODING " : " utf-8 " ,
2026-11-02 22:20:55 -05:00
" UPLOAD_DIR " : str ( root / " .ai " / " isolated-uploads " / database ) ,
2026-11-01 20:42:53 -05:00
}
print ( " 独立测试库: " + database , flush = True )
commands = [ [ " -m " , " alembic " , " upgrade " , " head " ] ]
if parsed . migration_roundtrip :
commands + = [ [ " -m " , " alembic " , " downgrade " , " 07b8c9d0e1f2 " ] , [ " -m " , " alembic " , " upgrade " , " head " ] ]
args = test_args
commands . append ( [ " -m " , " pytest " , * ( args or [ " tests/integration " ] ) , " -q " , " --tb=short " ] )
for args in commands :
result = subprocess . run ( [ str ( python ) , * args ] , cwd = root / " backend " , env = env )
if result . returncode :
return result . returncode
return 0
if __name__ == " __main__ " :
raise SystemExit ( main ( ) )