from pathlib import Path ROOT = Path(__file__).resolve().parents[1] CD_WORKFLOW = ROOT / ".gitea/workflows/cd.yaml" def test_cd_health_check_allows_slow_rebuild_warmup(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") assert "等待服務啟動(30s)" in workflow assert "seq 1 12" in workflow assert "等待 15s" in workflow def test_cd_health_check_validates_internal_and_external_health(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") assert "docker exec momo-pro-system curl" in workflow assert "http://127.0.0.1:80/health" in workflow assert "https://mo.wooo.work/health" in workflow assert 'internal=$INTERNAL_CODE, external=$EXTERNAL_CODE' in workflow def test_cd_sync_mode_hot_reloads_app_without_container_restart(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") gunicorn_config = (ROOT / "gunicorn.conf.py").read_text(encoding="utf-8") assert "docker compose up -d --no-deps momo-app" in workflow assert "docker kill -s HUP momo-pro-system" in workflow assert "docker compose restart scheduler telegram-bot" in workflow assert "docker compose restart momo-app" not in workflow assert "preload_app = False" in gunicorn_config def test_cd_sync_mode_treats_gunicorn_config_as_bind_mounted_runtime_file(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") assert "- 'gunicorn.conf.py'" in workflow assert "gunicorn\\.conf\\.py" not in workflow def test_cd_reloads_monitoring_config_when_prometheus_targets_change(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") assert "- 'monitoring/prometheus.yml'" in workflow assert "monitoring/prometheus\\.yml|monitoring/blackbox\\.yml" in workflow assert "cd /home/ollama/momo-pro/monitoring" in workflow assert "docker compose up -d prometheus blackbox-exporter" in workflow assert "docker compose restart prometheus blackbox-exporter" in workflow def test_cd_sync_mode_repairs_app_mount_drift_once(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") assert "--inplace" in workflow assert "momo-app mount drift detected" in workflow assert 'grep -qx "/app/app.py"' in workflow assert 'grep -qx "/app/config.py"' in workflow assert "docker compose up -d --no-deps --force-recreate momo-app" in workflow def test_cd_rebuild_builds_image_before_stopping_running_containers(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") build_index = workflow.index("docker compose build --no-cache momo-app") stop_index = workflow.index("docker stop momo-pro-system") up_index = workflow.index("docker compose up -d --no-deps --force-recreate momo-app scheduler telegram-bot") assert build_index < stop_index < up_index def test_cd_applies_full_v5_migration_range_idempotently(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") assert "024-099 範圍" in workflow for pattern in [ "migrations/02[4-9]_*.sql", "migrations/03[0-9]_*.sql", "migrations/04[0-9]_*.sql", "migrations/05[0-9]_*.sql", "migrations/06[0-9]_*.sql", "migrations/07[0-9]_*.sql", "migrations/08[0-9]_*.sql", "migrations/09[0-9]_*.sql", ]: assert pattern in workflow assert "sort | uniq" in workflow assert "for m in $V5_MIGRATIONS" in workflow def test_cd_observability_production_smoke_has_timeout(): workflow = CD_WORKFLOW.read_text(encoding="utf-8") assert "bash ./scripts/quick_review.sh --observability-smoke --base-url https://mo.wooo.work --timeout 12" in workflow