Publish curated ITWorx-ModelForge source from 990401737f96c8d46a9f7b0d84994516cadcb6a0

This commit is contained in:
Public Source Publisher committed 2026-09-30 09:23:17 +00:00
1 parent 7082ab955a
commit 848f0d8f8a
434 files changed
+67298 -4315

No files matched your search

+345
View File
@@ -0,0 +1,345 @@
from datetime import UTC, datetime, timedelta
from unittest.mock import Mock
from uuid import uuid4
import pytest
from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient
from sqlalchemy import create_engine, func, select
from sqlalchemy.orm import Session
from modelforge_api.api.authorization import (
Principal,
PrincipalRole,
authenticate_operator_principal,
)
from modelforge_api.api.routes.model_updates import get_model_update_service, router
from modelforge_api.domain.model_updates import ModelWatchCreate, ModelWatchUpdate
from modelforge_api.persistence.models import (
ArtifactJob,
AuditEvent,
Base,
CapabilityDeployment,
Model,
ModelRevision,
ModelUpdateWatch,
)
from modelforge_api.services.acquisition import AcquisitionService, ProviderBoundaryError
from modelforge_api.services.audit import AuditContext
from modelforge_api.services.model_updates import ModelUpdateService
from modelforge_api.services.registry import RegistryConflict, RegistryService
@pytest.fixture
def setup():
engine = create_engine("sqlite://")
Base.metadata.create_all(engine)
with Session(engine) as session:
model = Model(key="selected", display_name="Selected", upstream_provider="huggingface", upstream_source="org/selected")
session.add(model)
session.flush()
revision = ModelRevision(model_id=model.id, upstream_revision="main", resolved_commit_sha="a" * 40)
session.add(revision)
session.commit()
acquisition = Mock(spec=AcquisitionService)
def refresh(model_id, ref):
candidate = session.scalar(select(ModelRevision).where(ModelRevision.resolved_commit_sha == "b" * 40))
if candidate is None:
session.add(ModelRevision(model_id=model_id, upstream_revision=ref, resolved_commit_sha="b" * 40))
session.commit()
return Mock(resolved_commit_sha="b" * 40)
acquisition.refresh_model.side_effect = refresh
service = ModelUpdateService(session, acquisition, AuditContext("operator", "test"))
request = ModelWatchCreate(model_id=model.id, baseline_revision_id=revision.id)
yield session, service, acquisition, request
engine.dispose()
def test_tracking_and_notice_history_explain_registry_delete_refusal(setup):
session, service, _, request = setup
watch = service.create(request)
service.check(watch.id)
notice, = service.notices()
service.dismiss(notice.id)
service.change(watch.id, ModelWatchUpdate(
expected_version=watch.version,
expected_baseline_revision_id=watch.baseline_revision_id,
baseline_revision_id=watch.baseline_revision_id, enabled=False,
reason="Keep historical tracking while paused",
))
registry = RegistryService(session)
for revision_id, relation in (
(watch.baseline_revision_id, "update_tracking_baseline"),
(notice.candidate_revision_id, "update_candidate_history"),
):
with pytest.raises(RegistryConflict) as blocked:
registry.delete("model_revision", revision_id)
assert any(item["relation"] == relation for item in blocked.value.details["dependencies"])
assert session.get(ModelRevision, revision_id) is not None
with pytest.raises(RegistryConflict) as blocked_model:
registry.delete("model", request.model_id)
assert {"update_tracking_baseline", "update_candidate_history"}.issubset(
{item["relation"] for item in blocked_model.value.details["dependencies"]})
def test_check_deduplicates_dismissed_notice_and_never_deploys(setup):
session, service, acquisition, request = setup
watch = service.create(request)
assert service.create(request).id == watch.id
for _ in range(2):
assert service.check(watch.id).last_error_code is None
notice, = service.notices()
assert notice.baseline_commit_sha == "a" * 40
assert notice.candidate_commit_sha == "b" * 40
service.dismiss(notice.id)
service.check(watch.id)
assert len(service.notices()) == 1
assert service.notices()[0].status == "dismissed"
assert session.scalar(select(func.count()).select_from(ArtifactJob)) == 0
assert session.scalar(select(func.count()).select_from(CapabilityDeployment)) == 0
acquisition.create_plan.assert_not_called()
acquisition.execute_plan.assert_not_called()
def test_unchanged_and_provider_failure_preserve_baseline(setup):
session, service, acquisition, request = setup
watch = service.create(request)
acquisition.refresh_model.side_effect = None
acquisition.refresh_model.return_value = Mock(resolved_commit_sha="a" * 40)
service.check(watch.id)
assert service.notices() == []
acquisition.refresh_model.side_effect = ProviderBoundaryError("private provider details")
failed = service.check(watch.id)
assert failed.last_error_code == "upstream_unavailable"
assert failed.baseline_commit_sha == watch.baseline_commit_sha
assert failed.last_checked_at is not None
assert service.notices() == []
assert "private provider details" not in failed.model_dump_json()
assert session.get(ModelUpdateWatch, watch.id).check_token is None
def test_concurrent_check_refused_and_expired_lease_recoverable(setup):
session, service, acquisition, request = setup
watch = service.create(request)
item = session.get(ModelUpdateWatch, watch.id)
item.check_until = datetime.now(UTC) + timedelta(minutes=1)
item.check_token = uuid4()
session.commit()
with pytest.raises(RegistryConflict):
service.check(watch.id)
acquisition.refresh_model.assert_not_called()
item.check_until = datetime.now(UTC) - timedelta(seconds=1)
session.commit()
service.check(watch.id)
assert len(service.notices()) == 1
def test_unexpected_failure_records_failure_and_releases_lease(setup):
session, service, acquisition, request = setup
watch = service.create(request)
acquisition.refresh_model.side_effect = RuntimeError("test crash")
with pytest.raises(RuntimeError):
service.check(watch.id)
item = session.get(ModelUpdateWatch, watch.id)
assert item.last_error_code == "check_failed"
assert item.check_token is None
assert item.baseline_commit_sha == watch.baseline_commit_sha
def test_baseline_mismatch_and_stale_baseline_refused(setup):
session, service, _acquisition, request = setup
watch = service.create(request)
service.check(watch.id)
revision = session.scalar(select(ModelRevision).where(ModelRevision.resolved_commit_sha == "b" * 40))
with pytest.raises(RegistryConflict, match="explicit tracking update"):
service.create(request.model_copy(update={"baseline_revision_id": revision.id}))
other = Model(key="other", display_name="Other", upstream_provider="huggingface", upstream_source="org/other")
session.add(other)
session.commit()
with pytest.raises(RegistryConflict, match="own exact"):
service.create(request.model_copy(update={"model_id": other.id}))
def test_watch_bound_and_pagination(setup):
_, service, _, request = setup
for index in range(100):
service.create(request.model_copy(update={"upstream_ref": f"branch-{index}"}))
with pytest.raises(RegistryConflict, match="100"):
service.create(request)
assert len(service.watches(limit=100)) == 100
assert len(service.watches(limit=10, offset=95)) == 5
@pytest.mark.parametrize("failure", [False, True])
def test_superseded_check_cannot_clear_successor_or_publish_notice(setup, failure):
session, service, acquisition, request = setup
watch = service.create(request)
successor = uuid4()
def supersede(model_id, ref):
item = session.get(ModelUpdateWatch, watch.id)
item.check_token = successor
item.check_until = datetime.now(UTC) + timedelta(minutes=5)
session.commit()
if failure:
raise RuntimeError("superseded failure")
return Mock(resolved_commit_sha="a" * 40)
acquisition.refresh_model.side_effect = supersede
with pytest.raises(RuntimeError if failure else RegistryConflict):
service.check(watch.id)
item = session.get(ModelUpdateWatch, watch.id)
assert item.check_token == successor
assert item.last_checked_at is None
assert service.notices() == []
def test_metadata_audit_uses_authenticated_context(setup):
_, service, acquisition, _ = setup
assert acquisition.audit is service.audit
assert acquisition.audit.context.actor_id == "test"
@pytest.mark.parametrize("role,status", [(None,401),(PrincipalRole.VIEWER,403),(PrincipalRole.OPERATOR,200),(PrincipalRole.ADMIN,200)])
@pytest.mark.parametrize("action", ["create", "check", "dismiss", "change"])
def test_mutation_roles(role, status, action, setup):
_, service, _, request = setup
watch = service.create(request)
service.check(watch.id)
notice = service.notices()[0]
app = FastAPI()
app.include_router(router)
def principal():
if role is None:
raise HTTPException(401)
return Principal("test", role, "test")
mocked = Mock()
mocked.create.return_value = watch
mocked.check.return_value = watch
mocked.dismiss.return_value = notice
mocked.change.return_value = watch
app.dependency_overrides[authenticate_operator_principal] = principal
app.dependency_overrides[get_model_update_service] = lambda: mocked
with TestClient(app) as client:
if action == "create":
response = client.post("/api/v1/model-updates/watches", json=request.model_dump(mode="json"))
elif action == "change":
response = client.patch(f"/api/v1/model-updates/watches/{watch.id}", json={
"expected_version": watch.version, "expected_baseline_revision_id": str(watch.baseline_revision_id),
"baseline_revision_id": str(watch.baseline_revision_id), "enabled": False, "reason": "Pause tracking",
})
else:
path = f"watches/{watch.id}/check" if action == "check" else f"notices/{notice.id}/dismiss"
response = client.post(f"/api/v1/model-updates/{path}")
assert response.status_code == status
assert getattr(mocked, action).call_count == int(status == 200)
def test_list_bounds_require_viewer():
app = FastAPI()
app.include_router(router)
mocked = Mock()
mocked.watches.return_value = []
app.dependency_overrides[get_model_update_service] = lambda: mocked
app.dependency_overrides[authenticate_operator_principal] = lambda: Principal("viewer", PrincipalRole.VIEWER, "test")
with TestClient(app) as client:
assert client.get("/api/v1/model-updates/watches").status_code == 200
assert client.get("/api/v1/model-updates/watches?limit=101").status_code == 422
assert client.get("/api/v1/model-updates/watches?offset=-1").status_code == 422
def tracking_change(watch, **changes):
return ModelWatchUpdate(expected_version=watch.version,
expected_baseline_revision_id=watch.baseline_revision_id,
baseline_revision_id=changes.get("baseline_revision_id", watch.baseline_revision_id),
enabled=changes.get("enabled", watch.enabled), reason="Reviewed tracking change")
def test_pause_resume_fences_in_flight_check_and_stale_changes(setup):
session, service, acquisition, request = setup
watch = service.create(request)
original = acquisition.refresh_model.side_effect
def interrupted(model_id, ref):
candidate = original(model_id, ref)
paused = service.change(watch.id, tracking_change(watch, enabled=False))
assert not paused.enabled and paused.version == 2
assert session.get(ModelUpdateWatch, watch.id).check_token is None
with pytest.raises(RegistryConflict, match="changed"):
service.change(watch.id, tracking_change(watch, enabled=True))
resumed = service.change(watch.id, tracking_change(paused, enabled=True))
assert resumed.enabled and resumed.version == 3
return candidate
acquisition.refresh_model.side_effect = interrupted
with pytest.raises(RegistryConflict, match="superseded"):
service.check(watch.id)
assert service.notices() == []
assert service.watches()[0].last_checked_at is None
def test_paused_watch_and_not_due_check_do_not_call_provider(setup):
_, service, acquisition, request = setup
watch = service.create(request)
paused = service.change(watch.id, tracking_change(watch, enabled=False))
with pytest.raises(RegistryConflict, match="paused"):
service.check(watch.id)
acquisition.refresh_model.assert_not_called()
service.change(watch.id, tracking_change(paused, enabled=True))
service.check(watch.id)
acquisition.refresh_model.reset_mock()
with pytest.raises(RegistryConflict, match="not due"):
service.check(watch.id, if_checked_before=datetime.now(UTC) - timedelta(hours=1))
acquisition.refresh_model.assert_not_called()
def test_baseline_advance_is_audited_cas_and_preserves_notice_provenance(setup):
session, service, _, request = setup
watch = service.create(request)
service.check(watch.id)
notice, = service.notices()
advanced = service.change(watch.id, tracking_change(watch, baseline_revision_id=notice.candidate_revision_id))
assert advanced.baseline_commit_sha == notice.candidate_commit_sha
assert advanced.version == watch.version + 1
assert advanced.last_checked_at is None
assert service.notices()[0] == notice
audit = session.scalar(select(AuditEvent).where(AuditEvent.action == "MODEL_WATCH_TRACKING_CHANGED"))
assert audit.details["previous"]["baseline_revision_id"] == str(watch.baseline_revision_id)
assert audit.details["baseline_revision_id"] == str(notice.candidate_revision_id)
assert audit.details["reason"] == "Reviewed tracking change"
with pytest.raises(RegistryConflict, match="changed"):
service.change(watch.id, tracking_change(watch, enabled=False))
assert session.scalar(select(func.count()).select_from(CapabilityDeployment)) == 0
def test_baseline_advance_rejects_other_models_and_active_check(setup):
session, service, _, request = setup
watch = service.create(request)
service.check(watch.id)
notice = service.notices()[0]
item = session.get(ModelUpdateWatch, watch.id)
item.check_token = uuid4()
item.check_until = datetime.now(UTC) + timedelta(minutes=1)
session.commit()
with pytest.raises(RegistryConflict, match="active check"):
service.change(watch.id, tracking_change(watch, baseline_revision_id=notice.candidate_revision_id))
other = Model(key="other", display_name="Other", upstream_provider="huggingface", upstream_source="org/other")
session.add(other)
session.flush()
other_revision = ModelRevision(model_id=other.id, upstream_revision="main", resolved_commit_sha="c" * 40)
session.add(other_revision)
session.commit()
with pytest.raises(RegistryConflict, match="same model"):
service.change(watch.id, tracking_change(watch, baseline_revision_id=other_revision.id))
paused = service.change(watch.id, tracking_change(watch, enabled=False))
updated = service.change(watch.id, tracking_change(paused, baseline_revision_id=notice.candidate_revision_id))
assert not updated.enabled
assert session.get(ModelUpdateWatch, watch.id).check_token is None
@pytest.mark.parametrize("env,enabled,active", [("production",True,True),("production",False,False),("development",True,False)])
def test_monitor_status_reports_actual_environment_gate(env, enabled, active):
from modelforge_api.settings import Settings, get_settings
app = FastAPI()
app.include_router(router)
app.dependency_overrides[authenticate_operator_principal] = lambda: Principal("viewer", PrincipalRole.VIEWER, "test")
app.dependency_overrides[get_settings] = lambda: Settings(env=env, model_update_monitor_enabled=enabled,
model_update_check_interval_seconds=7200)
with TestClient(app) as client:
assert client.get("/api/v1/model-updates/monitor").json() == {"enabled": active, "check_interval_seconds": 7200}