"""Tempo service unit tests (FASE-1.4).""" from datetime import datetime from unittest.mock import MagicMock, patch from uuid import uuid4 import pytest from app.domain.exceptions import InvalidOperationError from app.models.jira_link import JiraLink, JiraLinkEntityType from app.services import tempo_service def test_auto_log_test_worklog_skips_when_disabled(monkeypatch, db): """No env kill-switch, no admin token, no personal token — must no-op.""" monkeypatch.setattr("app.services.tempo_service.settings.TEMPO_ENABLED", False) test = MagicMock() test.id = uuid4() user = MagicMock() user.tempo_api_token = None assert tempo_service.auto_log_test_worklog(db, test, user, "red_team_execution") is None @patch("tempoapiclient.client_v4.Tempo") def test_auto_log_test_worklog_proceeds_with_admin_token_even_when_disabled( mock_tempo_cls, monkeypatch, db, admin_user ): """Regression: TEMPO_ENABLED defaults False with no UI toggle — an admin who configures a Tempo token via Settings must not be silently blocked.""" monkeypatch.setattr("app.services.tempo_service.settings.TEMPO_ENABLED", False) from app.models.system_config import SystemConfig db.add(SystemConfig(key="tempo.admin_token", value="admin-tempo-token")) test_id = uuid4() link = JiraLink( entity_type=JiraLinkEntityType.test, entity_id=test_id, jira_issue_key="TST-11", jira_issue_id="10011", created_by=admin_user.id, ) db.add(link) admin_user.tempo_api_token = None # relying on the admin token, not a personal one admin_user.jira_account_id = "jira-account-123" db.commit() mock_tempo_instance = MagicMock() mock_tempo_instance.create_worklog.return_value = {"id": "wl-2"} mock_tempo_cls.return_value = mock_tempo_instance test = MagicMock() test.id = test_id test.name = "Phishing simulation" test.red_started_at = datetime(2026, 5, 18, 10, 0, 0) test.updated_at = datetime(2026, 5, 18, 12, 0, 0) test.created_at = test.updated_at result = tempo_service.auto_log_test_worklog(db, test, admin_user, "red_team_execution") assert result == {"id": "wl-2"} mock_tempo_instance.create_worklog.assert_called_once() def test_get_tempo_client_raises_when_disabled(monkeypatch): monkeypatch.setattr("app.services.tempo_service.settings.TEMPO_ENABLED", False) with pytest.raises(InvalidOperationError, match="not enabled"): tempo_service.get_tempo_client() @patch("app.services.tempo_service.log_worklog") def test_auto_log_test_worklog_calls_tempo(mock_log_worklog, monkeypatch, db, admin_user): monkeypatch.setattr("app.services.tempo_service.settings.TEMPO_ENABLED", True) mock_log_worklog.return_value = {"id": "wl-1"} test_id = uuid4() link = JiraLink( entity_type=JiraLinkEntityType.test, entity_id=test_id, jira_issue_key="TST-10", jira_issue_id="10010", created_by=admin_user.id, ) db.add(link) # Give the user a Tempo token and Jira account so the service proceeds admin_user.tempo_api_token = "test-tempo-token" admin_user.jira_account_id = "jira-account-123" db.commit() test = MagicMock() test.id = test_id test.name = "Phishing simulation" test.red_started_at = datetime(2026, 5, 18, 10, 0, 0) test.updated_at = datetime(2026, 5, 18, 12, 0, 0) test.created_at = test.updated_at result = tempo_service.auto_log_test_worklog( db, test, admin_user, "red_team_execution", ) assert result == {"id": "wl-1"} mock_log_worklog.assert_called_once() kwargs = mock_log_worklog.call_args.kwargs assert kwargs["jira_issue_id"] == 10010 assert kwargs["time_spent_seconds"] > 0 assert "[Aegis]" in kwargs["description"]