You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
13 KiB
Python
311 lines
13 KiB
Python
import unittest
|
|
from datetime import datetime, timezone
|
|
|
|
from admin_app.core import AdminSettings
|
|
from admin_app.db.models import ToolDraft, ToolVersion
|
|
from admin_app.services.tool_management_service import ToolManagementService
|
|
from shared.contracts import ToolLifecycleStatus
|
|
|
|
|
|
class _FakeToolDraftRepository:
|
|
def __init__(self):
|
|
self.drafts: list[ToolDraft] = []
|
|
self.next_id = 1
|
|
|
|
def list_drafts(self, *, statuses=None) -> list[ToolDraft]:
|
|
drafts = sorted(
|
|
self.drafts,
|
|
key=lambda draft: draft.updated_at or draft.created_at or datetime.min.replace(tzinfo=timezone.utc),
|
|
reverse=True,
|
|
)
|
|
if statuses:
|
|
allowed = set(statuses)
|
|
drafts = [draft for draft in drafts if draft.status in allowed]
|
|
return drafts
|
|
|
|
def get_by_tool_name(self, tool_name: str) -> ToolDraft | None:
|
|
normalized = str(tool_name or "").strip().lower()
|
|
for draft in self.drafts:
|
|
if draft.tool_name == normalized:
|
|
return draft
|
|
return None
|
|
|
|
def create(
|
|
self,
|
|
*,
|
|
tool_name: str,
|
|
display_name: str,
|
|
domain: str,
|
|
description: str,
|
|
business_goal: str,
|
|
summary: str,
|
|
parameters_json: list[dict],
|
|
required_parameter_count: int,
|
|
current_version_number: int,
|
|
version_count: int,
|
|
owner_staff_account_id: int,
|
|
owner_display_name: str,
|
|
requires_director_approval: bool = True,
|
|
commit: bool = True,
|
|
) -> ToolDraft:
|
|
now = datetime(2026, 3, 31, 15, 0, tzinfo=timezone.utc)
|
|
draft = ToolDraft(
|
|
id=self.next_id,
|
|
draft_id=f"draft_fake_{self.next_id}",
|
|
tool_name=tool_name,
|
|
display_name=display_name,
|
|
domain=domain,
|
|
description=description,
|
|
business_goal=business_goal,
|
|
status=ToolLifecycleStatus.DRAFT,
|
|
summary=summary,
|
|
parameters_json=parameters_json,
|
|
required_parameter_count=required_parameter_count,
|
|
current_version_number=current_version_number,
|
|
version_count=version_count,
|
|
requires_director_approval=requires_director_approval,
|
|
owner_staff_account_id=owner_staff_account_id,
|
|
owner_display_name=owner_display_name,
|
|
created_at=now,
|
|
updated_at=now,
|
|
)
|
|
self.next_id += 1
|
|
self.drafts.append(draft)
|
|
return draft
|
|
|
|
def update_submission(
|
|
self,
|
|
draft: ToolDraft,
|
|
*,
|
|
display_name: str,
|
|
domain: str,
|
|
description: str,
|
|
business_goal: str,
|
|
summary: str,
|
|
parameters_json: list[dict],
|
|
required_parameter_count: int,
|
|
current_version_number: int,
|
|
version_count: int,
|
|
owner_staff_account_id: int,
|
|
owner_display_name: str,
|
|
requires_director_approval: bool = True,
|
|
commit: bool = True,
|
|
) -> ToolDraft:
|
|
draft.display_name = display_name
|
|
draft.domain = domain
|
|
draft.description = description
|
|
draft.business_goal = business_goal
|
|
draft.status = ToolLifecycleStatus.DRAFT
|
|
draft.summary = summary
|
|
draft.parameters_json = parameters_json
|
|
draft.required_parameter_count = required_parameter_count
|
|
draft.current_version_number = current_version_number
|
|
draft.version_count = version_count
|
|
draft.requires_director_approval = requires_director_approval
|
|
draft.owner_staff_account_id = owner_staff_account_id
|
|
draft.owner_display_name = owner_display_name
|
|
draft.updated_at = datetime(2026, 3, 31, 15, current_version_number, tzinfo=timezone.utc)
|
|
return draft
|
|
|
|
|
|
class _FakeToolVersionRepository:
|
|
def __init__(self):
|
|
self.versions: list[ToolVersion] = []
|
|
self.next_id = 1
|
|
|
|
def list_versions(self, *, tool_name=None, draft_id=None, statuses=None) -> list[ToolVersion]:
|
|
versions = sorted(
|
|
self.versions,
|
|
key=lambda version: (version.version_number, version.updated_at or version.created_at or datetime.min.replace(tzinfo=timezone.utc)),
|
|
reverse=True,
|
|
)
|
|
if tool_name:
|
|
normalized = str(tool_name).strip().lower()
|
|
versions = [version for version in versions if version.tool_name == normalized]
|
|
if draft_id is not None:
|
|
versions = [version for version in versions if version.draft_id == draft_id]
|
|
if statuses:
|
|
allowed = set(statuses)
|
|
versions = [version for version in versions if version.status in allowed]
|
|
return versions
|
|
|
|
def get_next_version_number(self, tool_name: str) -> int:
|
|
versions = self.list_versions(tool_name=tool_name)
|
|
return (versions[0].version_number if versions else 0) + 1
|
|
|
|
def create(
|
|
self,
|
|
*,
|
|
draft_id: int,
|
|
tool_name: str,
|
|
version_number: int,
|
|
summary: str,
|
|
description: str,
|
|
business_goal: str,
|
|
parameters_json: list[dict],
|
|
required_parameter_count: int,
|
|
owner_staff_account_id: int,
|
|
owner_display_name: str,
|
|
status: ToolLifecycleStatus = ToolLifecycleStatus.DRAFT,
|
|
requires_director_approval: bool = True,
|
|
commit: bool = True,
|
|
) -> ToolVersion:
|
|
now = datetime(2026, 3, 31, 16, version_number, tzinfo=timezone.utc)
|
|
version = ToolVersion(
|
|
id=self.next_id,
|
|
version_id=self.build_version_id(tool_name, version_number),
|
|
draft_id=draft_id,
|
|
tool_name=tool_name,
|
|
version_number=version_number,
|
|
status=status,
|
|
summary=summary,
|
|
description=description,
|
|
business_goal=business_goal,
|
|
parameters_json=parameters_json,
|
|
required_parameter_count=required_parameter_count,
|
|
requires_director_approval=requires_director_approval,
|
|
owner_staff_account_id=owner_staff_account_id,
|
|
owner_display_name=owner_display_name,
|
|
created_at=now,
|
|
updated_at=now,
|
|
)
|
|
self.next_id += 1
|
|
self.versions.append(version)
|
|
return version
|
|
|
|
@staticmethod
|
|
def build_version_id(tool_name: str, version_number: int) -> str:
|
|
normalized = str(tool_name or "").strip().lower()
|
|
return f"tool_version::{normalized}::v{int(version_number)}"
|
|
|
|
|
|
class AdminToolManagementServiceTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.draft_repository = _FakeToolDraftRepository()
|
|
self.version_repository = _FakeToolVersionRepository()
|
|
self.service = ToolManagementService(
|
|
settings=AdminSettings(admin_api_prefix="/admin"),
|
|
draft_repository=self.draft_repository,
|
|
version_repository=self.version_repository,
|
|
)
|
|
|
|
def test_create_draft_submission_persists_initial_tool_version(self):
|
|
payload = self.service.create_draft_submission(
|
|
{
|
|
"domain": "vendas",
|
|
"tool_name": "consultar_vendas_periodo",
|
|
"display_name": "Consultar vendas por periodo",
|
|
"description": "Consulta vendas consolidadas por periodo informado no painel.",
|
|
"business_goal": "Ajudar o time interno a acompanhar o desempenho comercial com mais agilidade.",
|
|
"parameters": [
|
|
{
|
|
"name": "periodo_inicio",
|
|
"parameter_type": "string",
|
|
"description": "Data inicial usada no filtro.",
|
|
"required": True,
|
|
},
|
|
{
|
|
"name": "periodo_fim",
|
|
"parameter_type": "string",
|
|
"description": "Data final usada no filtro.",
|
|
"required": True,
|
|
},
|
|
],
|
|
},
|
|
owner_staff_account_id=7,
|
|
owner_name="Equipe Interna",
|
|
)
|
|
|
|
self.assertEqual(payload["storage_status"], "admin_database")
|
|
self.assertEqual(payload["draft_preview"]["draft_id"], "draft_fake_1")
|
|
self.assertEqual(payload["draft_preview"]["version_id"], "tool_version::consultar_vendas_periodo::v1")
|
|
self.assertEqual(payload["draft_preview"]["version_number"], 1)
|
|
self.assertEqual(payload["draft_preview"]["version_count"], 1)
|
|
self.assertEqual(payload["draft_preview"]["status"], ToolLifecycleStatus.DRAFT)
|
|
self.assertEqual(payload["draft_preview"]["owner_name"], "Equipe Interna")
|
|
self.assertEqual(len(self.draft_repository.drafts), 1)
|
|
self.assertEqual(len(self.version_repository.versions), 1)
|
|
|
|
def test_create_draft_submission_reuses_root_draft_and_increments_version(self):
|
|
self.service.create_draft_submission(
|
|
{
|
|
"domain": "locacao",
|
|
"tool_name": "emitir_resumo_locacao",
|
|
"display_name": "Emitir resumo de locacao",
|
|
"description": "Resume o contrato atual de locacao para consulta administrativa.",
|
|
"business_goal": "Dar visibilidade rapida ao status do contrato e dos dados principais.",
|
|
"parameters": [],
|
|
},
|
|
owner_staff_account_id=3,
|
|
owner_name="Analista de Locacao",
|
|
)
|
|
|
|
payload = self.service.create_draft_submission(
|
|
{
|
|
"domain": "locacao",
|
|
"tool_name": "emitir_resumo_locacao",
|
|
"display_name": "Emitir resumo de locacao",
|
|
"description": "Resume o contrato atual de locacao e os principais eventos administrativos.",
|
|
"business_goal": "Dar visibilidade rapida ao status do contrato, do pagamento e dos dados principais.",
|
|
"parameters": [
|
|
{
|
|
"name": "contrato_id",
|
|
"parameter_type": "string",
|
|
"description": "Identificador do contrato consultado.",
|
|
"required": True,
|
|
}
|
|
],
|
|
},
|
|
owner_staff_account_id=4,
|
|
owner_name="Coordenacao de Locacao",
|
|
)
|
|
|
|
self.assertEqual(payload["draft_preview"]["version_id"], "tool_version::emitir_resumo_locacao::v2")
|
|
self.assertEqual(payload["draft_preview"]["version_number"], 2)
|
|
self.assertEqual(payload["draft_preview"]["version_count"], 2)
|
|
self.assertEqual(len(self.draft_repository.drafts), 1)
|
|
self.assertEqual(len(self.version_repository.versions), 2)
|
|
self.assertEqual(self.draft_repository.drafts[0].current_version_number, 2)
|
|
self.assertEqual(self.draft_repository.drafts[0].version_count, 2)
|
|
self.assertEqual(self.draft_repository.drafts[0].owner_display_name, "Coordenacao de Locacao")
|
|
|
|
def test_build_drafts_payload_returns_versioned_draft_summaries(self):
|
|
self.service.create_draft_submission(
|
|
{
|
|
"domain": "orquestracao",
|
|
"tool_name": "priorizar_contato_quente",
|
|
"display_name": "Priorizar contato quente",
|
|
"description": "Classifica contatos mais quentes para orientar o proximo passo do atendimento.",
|
|
"business_goal": "Dar mais foco comercial ao time interno ao identificar oportunidades mais urgentes.",
|
|
"parameters": [],
|
|
},
|
|
owner_staff_account_id=5,
|
|
owner_name="Equipe de Tools",
|
|
)
|
|
self.service.create_draft_submission(
|
|
{
|
|
"domain": "orquestracao",
|
|
"tool_name": "priorizar_contato_quente",
|
|
"display_name": "Priorizar contato quente",
|
|
"description": "Classifica contatos mais quentes com sinais adicionais para orientar o atendimento.",
|
|
"business_goal": "Dar mais foco comercial ao time interno ao identificar oportunidades quentes com mais contexto.",
|
|
"parameters": [],
|
|
},
|
|
owner_staff_account_id=6,
|
|
owner_name="Diretoria Comercial",
|
|
)
|
|
|
|
payload = self.service.build_drafts_payload()
|
|
|
|
self.assertEqual(payload["storage_status"], "admin_database")
|
|
self.assertEqual(len(payload["drafts"]), 1)
|
|
self.assertEqual(payload["drafts"][0]["tool_name"], "priorizar_contato_quente")
|
|
self.assertEqual(payload["drafts"][0]["current_version_number"], 2)
|
|
self.assertEqual(payload["drafts"][0]["version_count"], 2)
|
|
self.assertEqual(payload["drafts"][0]["owner_name"], "Diretoria Comercial")
|
|
self.assertEqual(payload["supported_statuses"], [ToolLifecycleStatus.DRAFT])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|