from __future__ import annotations from datetime import timedelta from sqlalchemy import create_engine from sqlalchemy.orm import Session from supply_infra.db.models.pipeline_lock import PipelineLock from supply_infra.db.models.pipeline_run import PipelineRun from supply_infra.db.models.pipeline_step_run import PipelineStepRun from supply_infra.db.repositories.pipeline_step_run_repo import ( PipelineStepRunRepository, ) from supply_infra.pipeline.dates import china_now def _session() -> Session: engine = create_engine("sqlite+pysqlite:///:memory:") PipelineRun.__table__.create(engine) PipelineStepRun.__table__.create(engine) PipelineLock.__table__.create(engine) return Session(engine, expire_on_commit=False) def _run(run_id: str) -> PipelineRun: now = china_now() return PipelineRun( run_id=run_id, dedupe_key=f"supply_pipeline:20260727:{run_id}", pipeline_key="supply_pipeline", biz_dt="20260727", trigger_type="test", run_mode="full", dry_run=True, status="queued", deadline_at=now + timedelta(days=1), config_snapshot_json={}, date_snapshot_json={}, ) def _step( *, step_run_id: str, run_id: str, status: str, order: int, ) -> PipelineStepRun: return PipelineStepRun( step_run_id=step_run_id, run_id=run_id, step_key=f"step-{step_run_id}", step_order=order, attempt=1, status=status, critical=True, dependency_snapshot_json=[], input_snapshot_json={}, timeout_seconds=10, max_attempts=1, retryable=False, ) def test_claim_respects_global_active_step_cap() -> None: with _session() as session: session.add_all( [ _run("run-1"), _run("run-2"), _step( step_run_id="running", run_id="run-1", status="running", order=1, ), _step( step_run_id="ready", run_id="run-2", status="ready", order=1, ), ] ) session.flush() claimed = PipelineStepRunRepository(session).claim_next_ready( owner="worker-2", lease_seconds=120, max_active_steps=1, now=china_now(), ) assert claimed is None ready = session.get(PipelineStepRun, "ready") assert ready is not None assert ready.status == "ready" def test_claim_does_not_overlap_a_different_pipeline_run() -> None: with _session() as session: session.add_all( [ _run("run-1"), _run("run-2"), _step( step_run_id="running", run_id="run-1", status="running", order=1, ), _step( step_run_id="ready", run_id="run-2", status="ready", order=1, ), ] ) session.flush() claimed = PipelineStepRunRepository(session).claim_next_ready( owner="worker-2", lease_seconds=120, max_active_steps=2, now=china_now(), ) assert claimed is None