- audit.record_deploy 를 JSONL 파일 → Postgres deploy_audit 테이블로 (재배포 휘발 방지)
- psycopg 지연 import, _write 는 테스트에서 monkeypatch 지점, DATABASE_URL 사용
- 감사 기록 실패는 배포 완료를 막지 않고 UI 경고(audit_ok)
- /healthz: Pulp 무관 라이브니스 {"ok":true} (컨테이너 health check),
Pulp 연결 배지는 /pulp-status 로 분리 (대시보드가 폴링)
- config: DATABASE_URL 추가, audit_log_path 제거
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
178 lines
5.4 KiB
Python
178 lines
5.4 KiB
Python
from fastapi.testclient import TestClient
|
|
|
|
from app.main import app, get_pulp_client
|
|
|
|
client = TestClient(app)
|
|
|
|
VH = "/pulp/api/v3/repositories/rpm/rpm/abc/versions/3/"
|
|
REPO = {"name": "rocky9-baseos", "repo_config": {"gpgcheck": 1, "repo-gpgcheck": 1}}
|
|
VERSIONS = [
|
|
{
|
|
"pulp_href": "/pulp/api/v3/repositories/rpm/rpm/abc/versions/2/",
|
|
"content_summary": {"present": {"rpm.package": {"count": 4700}}},
|
|
},
|
|
{
|
|
"pulp_href": VH,
|
|
"content_summary": {"present": {"rpm.package": {"count": 4821}}},
|
|
},
|
|
]
|
|
DISTRIBUTIONS = [
|
|
{
|
|
"pulp_href": "/pulp/api/v3/distributions/rpm/rpm/dabc/",
|
|
"publication": "/pulp/api/v3/publications/rpm/rpm/abc-2/",
|
|
}
|
|
]
|
|
PUBLICATIONS = {
|
|
"/pulp/api/v3/publications/rpm/rpm/abc-2/": {
|
|
"repository_version": "/pulp/api/v3/repositories/rpm/rpm/abc/versions/2/"
|
|
}
|
|
}
|
|
|
|
|
|
class _FakePulp:
|
|
def __init__(
|
|
self,
|
|
repo=None,
|
|
distributions=None,
|
|
pub_state="completed",
|
|
dist_state="completed",
|
|
pub_created=True,
|
|
):
|
|
self._repo = REPO if repo is None else repo
|
|
self._distributions = DISTRIBUTIONS if distributions is None else distributions
|
|
self._pub_state = pub_state
|
|
self._dist_state = dist_state
|
|
self._pub_created = pub_created
|
|
self.created_with = None
|
|
self.dist_args = None
|
|
|
|
def get_repo(self, uuid):
|
|
return self._repo
|
|
|
|
def list_versions(self, uuid):
|
|
return VERSIONS
|
|
|
|
def list_distributions(self):
|
|
return self._distributions
|
|
|
|
def get_publication(self, href):
|
|
return PUBLICATIONS.get(href, {})
|
|
|
|
def create_publication(self, version_href):
|
|
self.created_with = version_href
|
|
return "/pulp/api/v3/tasks/pub/"
|
|
|
|
def update_distribution(self, dist_href, publication_href):
|
|
self.dist_args = (dist_href, publication_href)
|
|
return "/pulp/api/v3/tasks/dist/"
|
|
|
|
def wait_for_task(self, task_href, attempts=120, delay=1.0):
|
|
if "pub" in task_href:
|
|
created = (
|
|
["/pulp/api/v3/publications/rpm/rpm/new/"] if self._pub_created else []
|
|
)
|
|
return {"state": self._pub_state, "created_resources": created}
|
|
return {"state": self._dist_state, "created_resources": []}
|
|
|
|
|
|
def _use(fake):
|
|
app.dependency_overrides[get_pulp_client] = lambda: fake
|
|
|
|
|
|
def teardown_function():
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
def _post(confirm_name):
|
|
return client.post(
|
|
"/repos/abc/deploy",
|
|
data={"version_href": VH, "confirm_name": confirm_name},
|
|
)
|
|
|
|
|
|
def test_confirm_modal_shows_preview_and_type_to_confirm():
|
|
_use(_FakePulp())
|
|
resp = client.get("/repos/abc/deploy/confirm", params={"version_href": VH})
|
|
assert resp.status_code == 200
|
|
body = resp.text
|
|
assert "운영망에 배포" in body
|
|
assert "rocky9-baseos" in body # type-to-confirm 대상 이름
|
|
assert "배포 확정" in body
|
|
assert "+121" in body # 4821 - 4700 미리보기
|
|
|
|
|
|
def test_deploy_blocked_when_not_verified():
|
|
_use(_FakePulp(repo={"name": "r", "repo_config": {"gpgcheck": 0}}))
|
|
resp = client.post(
|
|
"/repos/abc/deploy", data={"version_href": VH, "confirm_name": "r"}
|
|
)
|
|
assert resp.status_code == 403
|
|
assert "검증" in resp.text
|
|
|
|
|
|
def test_deploy_rejects_name_mismatch():
|
|
_use(_FakePulp())
|
|
resp = _post("wrong-name")
|
|
assert resp.status_code == 400
|
|
assert "이름이 일치" in resp.text
|
|
|
|
|
|
def test_deploy_requires_existing_distribution():
|
|
_use(_FakePulp(distributions=[]))
|
|
resp = _post("rocky9-baseos")
|
|
assert resp.status_code == 404
|
|
assert "distribution" in resp.text
|
|
|
|
|
|
def test_deploy_publication_failure_keeps_prod_unchanged():
|
|
_use(_FakePulp(pub_state="failed"))
|
|
resp = _post("rocky9-baseos")
|
|
assert resp.status_code == 502
|
|
assert "운영망은 변경되지 않았습니다" in resp.text
|
|
|
|
|
|
def test_deploy_distribution_failure_reported():
|
|
_use(_FakePulp(dist_state="failed"))
|
|
resp = _post("rocky9-baseos")
|
|
assert resp.status_code == 502
|
|
assert "운영망 변경에 실패" in resp.text
|
|
|
|
|
|
def test_deploy_success_writes_audit_and_refreshes_list(monkeypatch):
|
|
# 감사 DB 쓰기는 _write 를 가로채 캡처(실 DB 불필요)
|
|
records = []
|
|
monkeypatch.setattr("app.audit._write", lambda entry: records.append(entry))
|
|
|
|
fake = _FakePulp()
|
|
_use(fake)
|
|
resp = _post("rocky9-baseos")
|
|
assert resp.status_code == 200
|
|
assert "배포 완료" in resp.text
|
|
assert 'hx-swap-oob="true"' in resp.text # 버전 목록 OOB 갱신
|
|
|
|
# publication 은 대상 버전으로, distribution 은 새 publication 으로 교체
|
|
assert fake.created_with == VH
|
|
assert fake.dist_args == (
|
|
"/pulp/api/v3/distributions/rpm/rpm/dabc/",
|
|
"/pulp/api/v3/publications/rpm/rpm/new/",
|
|
)
|
|
|
|
# 감사 기록: from=2, to=3, repo 이름
|
|
assert len(records) == 1
|
|
assert records[0]["from_version"] == 2
|
|
assert records[0]["to_version"] == 3
|
|
assert records[0]["repo_name"] == "rocky9-baseos"
|
|
|
|
|
|
def test_deploy_succeeds_but_flags_when_audit_fails(monkeypatch):
|
|
def _boom(entry):
|
|
raise RuntimeError("DATABASE_URL 미설정")
|
|
|
|
monkeypatch.setattr("app.audit._write", _boom)
|
|
_use(_FakePulp())
|
|
resp = _post("rocky9-baseos")
|
|
# 배포는 이미 완료 → 200 이되, 감사 기록 실패를 UI 에 경고
|
|
assert resp.status_code == 200
|
|
assert "배포 완료" in resp.text
|
|
assert "감사 로그 기록 실패" in resp.text
|