18bd0de3fa
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m42s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m44s
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m42s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m26s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m13s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m22s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 4m29s
CI/CD Pipeline / Integration Tests (push) Successful in 2m16s
CI/CD Pipeline / Unit Tests (push) Failing after 5m46s
CI/CD Pipeline / Build Staging API Image (push) Successful in 11m28s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 3m18s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 30s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m13s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m58s
204 lines
6.4 KiB
Python
Executable File
204 lines
6.4 KiB
Python
Executable File
"""
|
|
In-Memory 项目仓储 + 小仓储测试.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
|
|
from packages.adapters.in_memory.classification_job_repository import InMemoryClassificationJobRepository
|
|
from packages.adapters.in_memory.ingest_job_repository import InMemoryIngestJobRepository
|
|
from packages.adapters.in_memory.project_repository import InMemoryProjectRepository
|
|
from packages.domain import ClassificationJob, IngestJob, Project
|
|
|
|
# ── Project Repository ──────────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def project_repo():
|
|
return InMemoryProjectRepository()
|
|
|
|
|
|
@pytest.fixture
|
|
def make_project():
|
|
counter = 0
|
|
|
|
def _make(owner_id: str = "user_1", name: str = "测试项目"):
|
|
nonlocal counter
|
|
counter += 1
|
|
return Project(
|
|
id=f"proj_{counter}",
|
|
owner_user_id=owner_id,
|
|
name=name,
|
|
)
|
|
|
|
return _make
|
|
|
|
|
|
class TestInMemoryProjectRepository:
|
|
"""项目仓储."""
|
|
|
|
def test_save_and_find_by_id(self, project_repo, make_project):
|
|
proj = make_project()
|
|
project_repo.save(proj)
|
|
found = project_repo.find_by_id(proj.id)
|
|
assert found is not None
|
|
assert found.id == proj.id
|
|
assert found.name == "测试项目"
|
|
|
|
def test_find_by_id_not_found(self, project_repo):
|
|
assert project_repo.find_by_id("nonexistent") is None
|
|
|
|
def test_find_by_owner(self, project_repo, make_project):
|
|
for i in range(3):
|
|
project_repo.save(make_project(owner_id="user_1", name=f"p{i}"))
|
|
project_repo.save(make_project(owner_id="user_2", name="other"))
|
|
|
|
result = project_repo.find_by_owner_user_id("user_1")
|
|
assert len(result) == 3
|
|
assert all(p.owner_user_id == "user_1" for p in result)
|
|
|
|
def test_count_by_owner(self, project_repo, make_project):
|
|
for i in range(5):
|
|
project_repo.save(make_project(owner_id="user_1", name=f"p{i}"))
|
|
project_repo.save(make_project(owner_id="user_2", name="other"))
|
|
assert project_repo.count_by_owner("user_1") == 5
|
|
assert project_repo.count_by_owner("user_2") == 1
|
|
assert project_repo.count_by_owner("user_3") == 0
|
|
|
|
def test_delete_existing(self, project_repo, make_project):
|
|
proj = make_project()
|
|
project_repo.save(proj)
|
|
result = project_repo.delete(proj.id)
|
|
assert result is True
|
|
assert project_repo.find_by_id(proj.id) is None
|
|
|
|
def test_delete_nonexistent(self, project_repo):
|
|
assert project_repo.delete("nonexistent") is False
|
|
|
|
def test_save_updates(self, project_repo, make_project):
|
|
proj = make_project(name="v1")
|
|
project_repo.save(proj)
|
|
proj.name = "v2"
|
|
project_repo.save(proj)
|
|
found = project_repo.find_by_id(proj.id)
|
|
assert found.name == "v2"
|
|
|
|
def test_find_accessible_projects(self, project_repo, make_project):
|
|
p1 = make_project(owner_id="user_1", name="owned")
|
|
project_repo.save(p1)
|
|
# 自己拥有的项目可访问
|
|
result = project_repo.find_accessible_projects("user_1")
|
|
assert len(result) >= 1
|
|
assert any(p.id == p1.id for p in result)
|
|
|
|
|
|
# ── Classification Job Repository ──────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def classif_repo():
|
|
return InMemoryClassificationJobRepository()
|
|
|
|
|
|
class TestInMemoryClassificationJobRepository:
|
|
"""分类任务仓储."""
|
|
|
|
def test_create_and_get(self, classif_repo):
|
|
job = ClassificationJob(
|
|
id="job_1",
|
|
project_id="p1",
|
|
asset_id="a1",
|
|
status="pending",
|
|
)
|
|
classif_repo.create(job)
|
|
found = classif_repo.get("job_1")
|
|
assert found is not None
|
|
assert found.id == "job_1"
|
|
assert found.status == "pending"
|
|
|
|
def test_get_not_found(self, classif_repo):
|
|
assert classif_repo.get("nonexistent") is None
|
|
|
|
def test_update(self, classif_repo):
|
|
job = ClassificationJob(
|
|
id="job_1",
|
|
project_id="p1",
|
|
asset_id="a1",
|
|
status="pending",
|
|
)
|
|
classif_repo.create(job)
|
|
job.status = "completed"
|
|
classif_repo.update(job)
|
|
found = classif_repo.get("job_1")
|
|
assert found.status == "completed"
|
|
|
|
def test_update_nonexistent_creates(self, classif_repo):
|
|
"""update 对不存在的也会写入(dict 赋值)"""
|
|
job = ClassificationJob(
|
|
id="new_job",
|
|
project_id="p1",
|
|
asset_id="a1",
|
|
status="running",
|
|
)
|
|
classif_repo.update(job)
|
|
found = classif_repo.get("new_job")
|
|
assert found is not None
|
|
|
|
|
|
# ── Ingest Job Repository ──────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def ingest_repo():
|
|
return InMemoryIngestJobRepository()
|
|
|
|
|
|
class TestInMemoryIngestJobRepository:
|
|
"""导入任务仓储."""
|
|
|
|
def test_create_and_get(self, ingest_repo):
|
|
job = IngestJob(
|
|
id="ingest_1",
|
|
project_id="p1",
|
|
library_id="lib1",
|
|
storage_key="key1",
|
|
status="pending",
|
|
)
|
|
ingest_repo.create(job)
|
|
found = ingest_repo.get("ingest_1")
|
|
assert found is not None
|
|
assert found.id == "ingest_1"
|
|
assert found.status == "pending"
|
|
|
|
def test_get_not_found(self, ingest_repo):
|
|
assert ingest_repo.get("nonexistent") is None
|
|
|
|
def test_update(self, ingest_repo):
|
|
job = IngestJob(
|
|
id="ingest_1",
|
|
project_id="p1",
|
|
library_id="lib1",
|
|
storage_key="key1",
|
|
status="pending",
|
|
)
|
|
ingest_repo.create(job)
|
|
job.status = "completed"
|
|
ingest_repo.update(job)
|
|
found = ingest_repo.get("ingest_1")
|
|
assert found.status == "completed"
|
|
|
|
def test_update_nonexistent_creates(self, ingest_repo):
|
|
job = IngestJob(
|
|
id="new_ingest",
|
|
project_id="p1",
|
|
library_id="lib1",
|
|
storage_key="key1",
|
|
status="running",
|
|
)
|
|
ingest_repo.update(job)
|
|
found = ingest_repo.get("new_ingest")
|
|
assert found is not None
|