|
|
|
@ -6,9 +6,15 @@ from fastapi.testclient import TestClient
|
|
|
|
from admin_app.api.dependencies import get_current_staff_principal, get_tool_management_service
|
|
|
|
from admin_app.api.dependencies import get_current_staff_principal, get_tool_management_service
|
|
|
|
from admin_app.app_factory import create_app
|
|
|
|
from admin_app.app_factory import create_app
|
|
|
|
from admin_app.core import AdminSettings, AuthenticatedStaffPrincipal
|
|
|
|
from admin_app.core import AdminSettings, AuthenticatedStaffPrincipal
|
|
|
|
from admin_app.db.models import ToolDraft, ToolVersion
|
|
|
|
from admin_app.db.models import ToolArtifact, ToolDraft, ToolMetadata, ToolVersion
|
|
|
|
|
|
|
|
from admin_app.db.models.tool_artifact import ToolArtifactKind
|
|
|
|
from admin_app.services import ToolManagementService
|
|
|
|
from admin_app.services import ToolManagementService
|
|
|
|
from shared.contracts import StaffRole, ToolLifecycleStatus
|
|
|
|
from shared.contracts import (
|
|
|
|
|
|
|
|
GENERATED_TOOL_ENTRYPOINT,
|
|
|
|
|
|
|
|
StaffRole,
|
|
|
|
|
|
|
|
ToolLifecycleStatus,
|
|
|
|
|
|
|
|
build_generated_tool_module_name,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _FakeToolDraftRepository:
|
|
|
|
class _FakeToolDraftRepository:
|
|
|
|
@ -73,7 +79,10 @@ class _FakeToolVersionRepository:
|
|
|
|
def list_versions(self, *, tool_name=None, draft_id=None, statuses=None) -> list[ToolVersion]:
|
|
|
|
def list_versions(self, *, tool_name=None, draft_id=None, statuses=None) -> list[ToolVersion]:
|
|
|
|
versions = sorted(
|
|
|
|
versions = sorted(
|
|
|
|
self.versions,
|
|
|
|
self.versions,
|
|
|
|
key=lambda version: (version.version_number, version.updated_at or version.created_at or datetime.min.replace(tzinfo=timezone.utc)),
|
|
|
|
key=lambda version: (
|
|
|
|
|
|
|
|
version.version_number,
|
|
|
|
|
|
|
|
version.updated_at or version.created_at or datetime.min.replace(tzinfo=timezone.utc),
|
|
|
|
|
|
|
|
),
|
|
|
|
reverse=True,
|
|
|
|
reverse=True,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
if tool_name:
|
|
|
|
if tool_name:
|
|
|
|
@ -110,12 +119,153 @@ class _FakeToolVersionRepository:
|
|
|
|
return f"tool_version::{normalized}::v{int(version_number)}"
|
|
|
|
return f"tool_version::{normalized}::v{int(version_number)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _FakeToolMetadataRepository:
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
self.metadata_entries: list[ToolMetadata] = []
|
|
|
|
|
|
|
|
self.next_id = 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def list_metadata(self, *, tool_name=None, statuses=None) -> list[ToolMetadata]:
|
|
|
|
|
|
|
|
metadata_entries = sorted(
|
|
|
|
|
|
|
|
self.metadata_entries,
|
|
|
|
|
|
|
|
key=lambda metadata: (
|
|
|
|
|
|
|
|
metadata.version_number,
|
|
|
|
|
|
|
|
metadata.updated_at or metadata.created_at or datetime.min.replace(tzinfo=timezone.utc),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
reverse=True,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
if tool_name:
|
|
|
|
|
|
|
|
normalized = str(tool_name).strip().lower()
|
|
|
|
|
|
|
|
metadata_entries = [metadata for metadata in metadata_entries if metadata.tool_name == normalized]
|
|
|
|
|
|
|
|
if statuses:
|
|
|
|
|
|
|
|
allowed = set(statuses)
|
|
|
|
|
|
|
|
metadata_entries = [metadata for metadata in metadata_entries if metadata.status in allowed]
|
|
|
|
|
|
|
|
return metadata_entries
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_by_tool_version_id(self, tool_version_id: int) -> ToolMetadata | None:
|
|
|
|
|
|
|
|
for metadata in self.metadata_entries:
|
|
|
|
|
|
|
|
if metadata.tool_version_id == tool_version_id:
|
|
|
|
|
|
|
|
return metadata
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create(self, **kwargs) -> ToolMetadata:
|
|
|
|
|
|
|
|
version_number = kwargs["version_number"]
|
|
|
|
|
|
|
|
now = datetime(2026, 3, 31, 18, version_number, tzinfo=timezone.utc)
|
|
|
|
|
|
|
|
metadata = ToolMetadata(
|
|
|
|
|
|
|
|
id=self.next_id,
|
|
|
|
|
|
|
|
metadata_id=self.build_metadata_id(kwargs["tool_name"], version_number),
|
|
|
|
|
|
|
|
created_at=now,
|
|
|
|
|
|
|
|
updated_at=now,
|
|
|
|
|
|
|
|
**kwargs,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.next_id += 1
|
|
|
|
|
|
|
|
self.metadata_entries.append(metadata)
|
|
|
|
|
|
|
|
return metadata
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_metadata(self, metadata: ToolMetadata, **kwargs) -> ToolMetadata:
|
|
|
|
|
|
|
|
metadata.display_name = kwargs["display_name"]
|
|
|
|
|
|
|
|
metadata.domain = kwargs["domain"]
|
|
|
|
|
|
|
|
metadata.description = kwargs["description"]
|
|
|
|
|
|
|
|
metadata.parameters_json = kwargs["parameters_json"]
|
|
|
|
|
|
|
|
metadata.status = kwargs["status"]
|
|
|
|
|
|
|
|
metadata.author_staff_account_id = kwargs["author_staff_account_id"]
|
|
|
|
|
|
|
|
metadata.author_display_name = kwargs["author_display_name"]
|
|
|
|
|
|
|
|
metadata.updated_at = datetime(2026, 3, 31, 18, metadata.version_number, tzinfo=timezone.utc)
|
|
|
|
|
|
|
|
return metadata
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def upsert_version_metadata(self, **kwargs) -> ToolMetadata:
|
|
|
|
|
|
|
|
existing = self.get_by_tool_version_id(kwargs["tool_version_id"])
|
|
|
|
|
|
|
|
if existing is None:
|
|
|
|
|
|
|
|
return self.create(**kwargs)
|
|
|
|
|
|
|
|
return self.update_metadata(existing, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
|
|
def build_metadata_id(tool_name: str, version_number: int) -> str:
|
|
|
|
|
|
|
|
normalized = str(tool_name or "").strip().lower()
|
|
|
|
|
|
|
|
return f"tool_metadata::{normalized}::v{int(version_number)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _FakeToolArtifactRepository:
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
self.artifacts: list[ToolArtifact] = []
|
|
|
|
|
|
|
|
self.next_id = 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def list_artifacts(self, *, tool_name=None, tool_version_id=None, artifact_stage=None, artifact_kind=None) -> list[ToolArtifact]:
|
|
|
|
|
|
|
|
artifacts = sorted(
|
|
|
|
|
|
|
|
self.artifacts,
|
|
|
|
|
|
|
|
key=lambda artifact: (
|
|
|
|
|
|
|
|
artifact.version_number,
|
|
|
|
|
|
|
|
artifact.updated_at or artifact.created_at or datetime.min.replace(tzinfo=timezone.utc),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
reverse=True,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
if tool_name:
|
|
|
|
|
|
|
|
normalized = str(tool_name).strip().lower()
|
|
|
|
|
|
|
|
artifacts = [artifact for artifact in artifacts if artifact.tool_name == normalized]
|
|
|
|
|
|
|
|
if tool_version_id is not None:
|
|
|
|
|
|
|
|
artifacts = [artifact for artifact in artifacts if artifact.tool_version_id == tool_version_id]
|
|
|
|
|
|
|
|
if artifact_stage:
|
|
|
|
|
|
|
|
artifacts = [artifact for artifact in artifacts if artifact.artifact_stage == artifact_stage]
|
|
|
|
|
|
|
|
if artifact_kind:
|
|
|
|
|
|
|
|
artifacts = [artifact for artifact in artifacts if artifact.artifact_kind == artifact_kind]
|
|
|
|
|
|
|
|
return artifacts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_by_tool_version_and_kind(self, tool_version_id: int, artifact_kind) -> ToolArtifact | None:
|
|
|
|
|
|
|
|
for artifact in self.artifacts:
|
|
|
|
|
|
|
|
if artifact.tool_version_id == tool_version_id and artifact.artifact_kind == artifact_kind:
|
|
|
|
|
|
|
|
return artifact
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create(self, **kwargs) -> ToolArtifact:
|
|
|
|
|
|
|
|
version_number = kwargs["version_number"]
|
|
|
|
|
|
|
|
now = datetime(2026, 3, 31, 19, version_number, self.next_id, tzinfo=timezone.utc)
|
|
|
|
|
|
|
|
artifact = ToolArtifact(
|
|
|
|
|
|
|
|
id=self.next_id,
|
|
|
|
|
|
|
|
artifact_id=self.build_artifact_id(kwargs["tool_name"], version_number, kwargs["artifact_kind"]),
|
|
|
|
|
|
|
|
created_at=now,
|
|
|
|
|
|
|
|
updated_at=now,
|
|
|
|
|
|
|
|
checksum=kwargs.get("checksum") or f"fake-checksum-{self.next_id}",
|
|
|
|
|
|
|
|
**kwargs,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.next_id += 1
|
|
|
|
|
|
|
|
self.artifacts.append(artifact)
|
|
|
|
|
|
|
|
return artifact
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_artifact(self, artifact: ToolArtifact, **kwargs) -> ToolArtifact:
|
|
|
|
|
|
|
|
artifact.artifact_status = kwargs["artifact_status"]
|
|
|
|
|
|
|
|
artifact.storage_kind = kwargs["storage_kind"]
|
|
|
|
|
|
|
|
artifact.summary = kwargs["summary"]
|
|
|
|
|
|
|
|
artifact.payload_json = kwargs["payload_json"]
|
|
|
|
|
|
|
|
artifact.checksum = kwargs["checksum"]
|
|
|
|
|
|
|
|
artifact.author_staff_account_id = kwargs["author_staff_account_id"]
|
|
|
|
|
|
|
|
artifact.author_display_name = kwargs["author_display_name"]
|
|
|
|
|
|
|
|
artifact.updated_at = datetime(2026, 3, 31, 19, artifact.version_number, tzinfo=timezone.utc)
|
|
|
|
|
|
|
|
return artifact
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def upsert_version_artifact(self, **kwargs) -> ToolArtifact:
|
|
|
|
|
|
|
|
existing = self.get_by_tool_version_and_kind(kwargs["tool_version_id"], kwargs["artifact_kind"])
|
|
|
|
|
|
|
|
if existing is None:
|
|
|
|
|
|
|
|
return self.create(**kwargs)
|
|
|
|
|
|
|
|
return self.update_artifact(existing, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
|
|
def build_artifact_id(tool_name: str, version_number: int, artifact_kind) -> str:
|
|
|
|
|
|
|
|
normalized = str(tool_name or "").strip().lower()
|
|
|
|
|
|
|
|
return f"tool_artifact::{normalized}::v{int(version_number)}::{artifact_kind.value}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
def _build_client_with_role(
|
|
|
|
def _build_client_with_role(
|
|
|
|
self,
|
|
|
|
self,
|
|
|
|
role: StaffRole,
|
|
|
|
role: StaffRole,
|
|
|
|
settings: AdminSettings | None = None,
|
|
|
|
settings: AdminSettings | None = None,
|
|
|
|
) -> tuple[TestClient, object, _FakeToolDraftRepository, _FakeToolVersionRepository]:
|
|
|
|
) -> tuple[
|
|
|
|
|
|
|
|
TestClient,
|
|
|
|
|
|
|
|
object,
|
|
|
|
|
|
|
|
_FakeToolDraftRepository,
|
|
|
|
|
|
|
|
_FakeToolVersionRepository,
|
|
|
|
|
|
|
|
_FakeToolMetadataRepository,
|
|
|
|
|
|
|
|
_FakeToolArtifactRepository,
|
|
|
|
|
|
|
|
]:
|
|
|
|
app = create_app(
|
|
|
|
app = create_app(
|
|
|
|
settings
|
|
|
|
settings
|
|
|
|
or AdminSettings(
|
|
|
|
or AdminSettings(
|
|
|
|
@ -125,10 +275,14 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
)
|
|
|
|
)
|
|
|
|
draft_repository = _FakeToolDraftRepository()
|
|
|
|
draft_repository = _FakeToolDraftRepository()
|
|
|
|
version_repository = _FakeToolVersionRepository()
|
|
|
|
version_repository = _FakeToolVersionRepository()
|
|
|
|
|
|
|
|
metadata_repository = _FakeToolMetadataRepository()
|
|
|
|
|
|
|
|
artifact_repository = _FakeToolArtifactRepository()
|
|
|
|
service = ToolManagementService(
|
|
|
|
service = ToolManagementService(
|
|
|
|
settings=app.state.admin_settings,
|
|
|
|
settings=app.state.admin_settings,
|
|
|
|
draft_repository=draft_repository,
|
|
|
|
draft_repository=draft_repository,
|
|
|
|
version_repository=version_repository,
|
|
|
|
version_repository=version_repository,
|
|
|
|
|
|
|
|
metadata_repository=metadata_repository,
|
|
|
|
|
|
|
|
artifact_repository=artifact_repository,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
app.dependency_overrides[get_current_staff_principal] = lambda: AuthenticatedStaffPrincipal(
|
|
|
|
app.dependency_overrides[get_current_staff_principal] = lambda: AuthenticatedStaffPrincipal(
|
|
|
|
id=11,
|
|
|
|
id=11,
|
|
|
|
@ -138,10 +292,10 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
is_active=True,
|
|
|
|
is_active=True,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
app.dependency_overrides[get_tool_management_service] = lambda: service
|
|
|
|
app.dependency_overrides[get_tool_management_service] = lambda: service
|
|
|
|
return TestClient(app), app, draft_repository, version_repository
|
|
|
|
return TestClient(app), app, draft_repository, version_repository, metadata_repository, artifact_repository
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_overview_returns_metrics_workflow_and_actions_for_colaborador(self):
|
|
|
|
def test_tools_overview_returns_metrics_workflow_and_actions_for_colaborador(self):
|
|
|
|
client, app, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
client, app, _, _, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
response = client.get("/admin/tools/overview", headers={"Authorization": "Bearer token"})
|
|
|
|
response = client.get("/admin/tools/overview", headers={"Authorization": "Bearer token"})
|
|
|
|
finally:
|
|
|
|
finally:
|
|
|
|
@ -153,13 +307,15 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
self.assertEqual(payload["mode"], "admin_tool_draft_governance")
|
|
|
|
self.assertEqual(payload["mode"], "admin_tool_draft_governance")
|
|
|
|
self.assertEqual(payload["metrics"][0]["value"], "18")
|
|
|
|
self.assertEqual(payload["metrics"][0]["value"], "18")
|
|
|
|
self.assertIn("persisted_versions", [item["key"] for item in payload["metrics"]])
|
|
|
|
self.assertIn("persisted_versions", [item["key"] for item in payload["metrics"]])
|
|
|
|
|
|
|
|
self.assertIn("persisted_metadata", [item["key"] for item in payload["metrics"]])
|
|
|
|
|
|
|
|
self.assertIn("persisted_artifacts", [item["key"] for item in payload["metrics"]])
|
|
|
|
self.assertIn("active", [item["code"] for item in payload["workflow"]])
|
|
|
|
self.assertIn("active", [item["code"] for item in payload["workflow"]])
|
|
|
|
self.assertIn("/admin/tools/contracts", [item["href"] for item in payload["actions"]])
|
|
|
|
self.assertIn("/admin/tools/contracts", [item["href"] for item in payload["actions"]])
|
|
|
|
self.assertIn("/admin/tools/drafts/intake", [item["href"] for item in payload["actions"]])
|
|
|
|
self.assertIn("/admin/tools/drafts/intake", [item["href"] for item in payload["actions"]])
|
|
|
|
self.assertIn("artefatos", payload["next_steps"][0].lower())
|
|
|
|
self.assertIn("artefatos", payload["next_steps"][0].lower())
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_contracts_return_shared_contract_snapshot(self):
|
|
|
|
def test_tools_contracts_return_shared_contract_snapshot(self):
|
|
|
|
client, app, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
client, app, _, _, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
response = client.get("/admin/tools/contracts", headers={"Authorization": "Bearer token"})
|
|
|
|
response = client.get("/admin/tools/contracts", headers={"Authorization": "Bearer token"})
|
|
|
|
finally:
|
|
|
|
finally:
|
|
|
|
@ -176,8 +332,33 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
self.assertIn("string", [item["code"] for item in payload["parameter_types"]])
|
|
|
|
self.assertIn("string", [item["code"] for item in payload["parameter_types"]])
|
|
|
|
self.assertIn("published_tool", payload["publication_fields"])
|
|
|
|
self.assertIn("published_tool", payload["publication_fields"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_draft_intake_blocks_tool_name_reserved_by_core_catalog(self):
|
|
|
|
|
|
|
|
client, app, draft_repository, version_repository, metadata_repository, artifact_repository = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
|
|
|
|
"/admin/tools/drafts/intake",
|
|
|
|
|
|
|
|
headers={"Authorization": "Bearer token"},
|
|
|
|
|
|
|
|
json={
|
|
|
|
|
|
|
|
"domain": "vendas",
|
|
|
|
|
|
|
|
"tool_name": "consultar_estoque",
|
|
|
|
|
|
|
|
"display_name": "Consultar estoque paralelo",
|
|
|
|
|
|
|
|
"description": "Tentativa de sobrescrever a tool core de estoque com uma versao administrativa.",
|
|
|
|
|
|
|
|
"business_goal": "Validar que a API bloqueia nomes reservados do runtime core.",
|
|
|
|
|
|
|
|
"parameters": [],
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
|
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.assertEqual(response.status_code, 422)
|
|
|
|
|
|
|
|
self.assertIn("catalogo core do sistema", response.json()["detail"])
|
|
|
|
|
|
|
|
self.assertEqual(len(draft_repository.drafts), 0)
|
|
|
|
|
|
|
|
self.assertEqual(len(version_repository.versions), 0)
|
|
|
|
|
|
|
|
self.assertEqual(len(metadata_repository.metadata_entries), 0)
|
|
|
|
|
|
|
|
self.assertEqual(len(artifact_repository.artifacts), 0)
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_drafts_return_single_root_draft_with_current_version_after_reintake(self):
|
|
|
|
def test_tools_drafts_return_single_root_draft_with_current_version_after_reintake(self):
|
|
|
|
client, app, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
client, app, _, _, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
first_response = client.post(
|
|
|
|
first_response = client.post(
|
|
|
|
"/admin/tools/drafts/intake",
|
|
|
|
"/admin/tools/drafts/intake",
|
|
|
|
@ -218,8 +399,8 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
self.assertEqual(payload["drafts"][0]["version_count"], 2)
|
|
|
|
self.assertEqual(payload["drafts"][0]["version_count"], 2)
|
|
|
|
self.assertEqual(payload["supported_statuses"], ["draft"])
|
|
|
|
self.assertEqual(payload["supported_statuses"], ["draft"])
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_draft_intake_persists_admin_draft_with_version_metadata(self):
|
|
|
|
def test_tools_draft_intake_persists_admin_draft_with_version_metadata_and_artifacts(self):
|
|
|
|
client, app, draft_repository, version_repository = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
client, app, draft_repository, version_repository, metadata_repository, artifact_repository = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
response = client.post(
|
|
|
|
response = client.post(
|
|
|
|
"/admin/tools/drafts/intake",
|
|
|
|
"/admin/tools/drafts/intake",
|
|
|
|
@ -256,9 +437,13 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
self.assertGreaterEqual(len(payload["warnings"]), 1)
|
|
|
|
self.assertGreaterEqual(len(payload["warnings"]), 1)
|
|
|
|
self.assertEqual(len(draft_repository.drafts), 1)
|
|
|
|
self.assertEqual(len(draft_repository.drafts), 1)
|
|
|
|
self.assertEqual(len(version_repository.versions), 1)
|
|
|
|
self.assertEqual(len(version_repository.versions), 1)
|
|
|
|
|
|
|
|
self.assertEqual(len(metadata_repository.metadata_entries), 1)
|
|
|
|
|
|
|
|
self.assertEqual(len(artifact_repository.artifacts), 2)
|
|
|
|
|
|
|
|
artifact_kinds = {artifact.artifact_kind for artifact in artifact_repository.artifacts}
|
|
|
|
|
|
|
|
self.assertEqual(artifact_kinds, {ToolArtifactKind.GENERATION_REQUEST, ToolArtifactKind.VALIDATION_REPORT})
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_review_queue_requires_director_review_permission(self):
|
|
|
|
def test_tools_review_queue_requires_director_review_permission(self):
|
|
|
|
client, app, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
client, app, _, _, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
response = client.get("/admin/tools/review-queue", headers={"Authorization": "Bearer token"})
|
|
|
|
response = client.get("/admin/tools/review-queue", headers={"Authorization": "Bearer token"})
|
|
|
|
finally:
|
|
|
|
finally:
|
|
|
|
@ -271,7 +456,7 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_review_queue_is_available_for_diretor(self):
|
|
|
|
def test_tools_review_queue_is_available_for_diretor(self):
|
|
|
|
client, app, _, _ = self._build_client_with_role(StaffRole.DIRETOR)
|
|
|
|
client, app, _, _, _, _ = self._build_client_with_role(StaffRole.DIRETOR)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
response = client.get("/admin/tools/review-queue", headers={"Authorization": "Bearer token"})
|
|
|
|
response = client.get("/admin/tools/review-queue", headers={"Authorization": "Bearer token"})
|
|
|
|
finally:
|
|
|
|
finally:
|
|
|
|
@ -284,7 +469,7 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
self.assertIn("validated", payload["supported_statuses"])
|
|
|
|
self.assertIn("validated", payload["supported_statuses"])
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_publications_require_director_publication_permission(self):
|
|
|
|
def test_tools_publications_require_director_publication_permission(self):
|
|
|
|
client, app, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
client, app, _, _, _, _ = self._build_client_with_role(StaffRole.COLABORADOR)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
response = client.get("/admin/tools/publications", headers={"Authorization": "Bearer token"})
|
|
|
|
response = client.get("/admin/tools/publications", headers={"Authorization": "Bearer token"})
|
|
|
|
finally:
|
|
|
|
finally:
|
|
|
|
@ -297,7 +482,7 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_publications_return_bootstrap_catalog_for_diretor(self):
|
|
|
|
def test_tools_publications_return_bootstrap_catalog_for_diretor(self):
|
|
|
|
client, app, _, _ = self._build_client_with_role(StaffRole.DIRETOR)
|
|
|
|
client, app, _, _, _, _ = self._build_client_with_role(StaffRole.DIRETOR)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
response = client.get("/admin/tools/publications", headers={"Authorization": "Bearer token"})
|
|
|
|
response = client.get("/admin/tools/publications", headers={"Authorization": "Bearer token"})
|
|
|
|
finally:
|
|
|
|
finally:
|
|
|
|
@ -313,6 +498,48 @@ class AdminToolsWebTests(unittest.TestCase):
|
|
|
|
self.assertEqual(first["status"], "active")
|
|
|
|
self.assertEqual(first["status"], "active")
|
|
|
|
self.assertEqual(first["implementation_module"], "app.services.tools.handlers")
|
|
|
|
self.assertEqual(first["implementation_module"], "app.services.tools.handlers")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_tools_publications_prefer_persisted_metadata_after_intake(self):
|
|
|
|
|
|
|
|
client, app, _, _, _, _ = self._build_client_with_role(StaffRole.DIRETOR)
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
intake_response = client.post(
|
|
|
|
|
|
|
|
"/admin/tools/drafts/intake",
|
|
|
|
|
|
|
|
headers={"Authorization": "Bearer token"},
|
|
|
|
|
|
|
|
json={
|
|
|
|
|
|
|
|
"domain": "revisao",
|
|
|
|
|
|
|
|
"tool_name": "consultar_revisao_aberta",
|
|
|
|
|
|
|
|
"display_name": "Consultar revisao aberta",
|
|
|
|
|
|
|
|
"description": "Consulta revisoes abertas com filtros administrativos para a oficina.",
|
|
|
|
|
|
|
|
"business_goal": "Ajudar o time a localizar revisoes abertas com mais contexto operacional.",
|
|
|
|
|
|
|
|
"parameters": [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "placa",
|
|
|
|
|
|
|
|
"parameter_type": "string",
|
|
|
|
|
|
|
|
"description": "Placa usada na busca da revisao.",
|
|
|
|
|
|
|
|
"required": True,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.get("/admin/tools/publications", headers={"Authorization": "Bearer token"})
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
|
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.assertEqual(intake_response.status_code, 200)
|
|
|
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
payload = response.json()
|
|
|
|
|
|
|
|
self.assertEqual(payload["source"], "admin_metadata_catalog")
|
|
|
|
|
|
|
|
self.assertEqual(len(payload["publications"]), 1)
|
|
|
|
|
|
|
|
publication = payload["publications"][0]
|
|
|
|
|
|
|
|
self.assertEqual(publication["publication_id"], "tool_metadata::consultar_revisao_aberta::v1")
|
|
|
|
|
|
|
|
self.assertEqual(publication["tool_name"], "consultar_revisao_aberta")
|
|
|
|
|
|
|
|
self.assertEqual(publication["status"], "draft")
|
|
|
|
|
|
|
|
self.assertEqual(publication["parameter_count"], 1)
|
|
|
|
|
|
|
|
self.assertEqual(publication["author_name"], "Equipe de Tools")
|
|
|
|
|
|
|
|
self.assertEqual(publication["implementation_module"], build_generated_tool_module_name("consultar_revisao_aberta"))
|
|
|
|
|
|
|
|
self.assertEqual(publication["implementation_callable"], GENERATED_TOOL_ENTRYPOINT)
|
|
|
|
|
|
|
|
self.assertEqual(publication["parameters"][0]["name"], "placa")
|
|
|
|
|
|
|
|
self.assertEqual(publication["parameters"][0]["parameter_type"], "string")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if __name__ == "__main__":
|
|
|
|
unittest.main()
|
|
|
|
unittest.main()
|
|
|
|
|