diff --git a/backend/src/api/routes/git/_repo_lifecycle_routes.py b/backend/src/api/routes/git/_repo_lifecycle_routes.py index ffd2ebf8d..b16a0bb24 100644 --- a/backend/src/api/routes/git/_repo_lifecycle_routes.py +++ b/backend/src/api/routes/git/_repo_lifecycle_routes.py @@ -183,6 +183,7 @@ async def sync_dashboard( "source_env_id": source_env_id, } ) + return result except HTTPException: raise except Exception as e: diff --git a/backend/src/core/task_manager/event_bus.py b/backend/src/core/task_manager/event_bus.py index 35db0bfd8..d2673684e 100644 --- a/backend/src/core/task_manager/event_bus.py +++ b/backend/src/core/task_manager/event_bus.py @@ -69,6 +69,7 @@ class EventBus: # @POST Flusher task is created and scheduled in the event loop. def start(self) -> None: if self._flusher_task is None or self._flusher_task.done(): + asyncio.get_running_loop() self._flusher_task = asyncio.create_task(self.async_flusher_loop()) # #endregion start diff --git a/backend/src/plugins/storage/plugin.py b/backend/src/plugins/storage/plugin.py index 417a046c1..2641419fd 100644 --- a/backend/src/plugins/storage/plugin.py +++ b/backend/src/plugins/storage/plugin.py @@ -133,7 +133,10 @@ class StoragePlugin(PluginBase): # Use TaskContext logger if available, otherwise fall back to app logger log = context.logger if context else logger - logger.reason("Executing storage task", payload={"params": params}) + if context is not None and hasattr(log, "with_source"): + log = log.with_source("StoragePlugin.execute") + + log.reason("Executing storage task", payload={"params": params}) # endregion execute # region get_storage_root [TYPE Function] diff --git a/backend/tests/api/test_git_repo_lifecycle_routes.py b/backend/tests/api/test_git_repo_lifecycle_routes.py index 66f16017e..b0ad9073a 100644 --- a/backend/tests/api/test_git_repo_lifecycle_routes.py +++ b/backend/tests/api/test_git_repo_lifecycle_routes.py @@ -273,6 +273,12 @@ class TestPromoteDashboard: class TestDeployDashboard: """POST /repositories/{dashboard_ref}/deploy""" + @staticmethod + def _target_environment(): + target = MagicMock() + target.id = "preprod-1" + return target + def test_success(self): mock_plugin = MagicMock() mock_plugin.execute = AsyncMock(return_value={"status": "deployed"}) @@ -280,9 +286,10 @@ class TestDeployDashboard: with ( patch("src.plugins.git_plugin.GitPlugin", return_value=mock_plugin), patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)), + patch("src.api.routes.git._repo_lifecycle_routes._resolve_stage_environment", return_value=self._target_environment()), ): client = _make_client() - resp = client.post("/repositories/42/deploy", json={"environment_id": "prod"}) + resp = client.post("/repositories/42/deploy", json={"stage": "preprod"}) assert resp.status_code == 200 assert resp.json()["status"] == "deployed" @@ -293,9 +300,10 @@ class TestDeployDashboard: with ( patch("src.plugins.git_plugin.GitPlugin", return_value=mock_plugin), patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)), + patch("src.api.routes.git._repo_lifecycle_routes._resolve_stage_environment", return_value=self._target_environment()), ): client = _make_client() - resp = client.post("/repositories/42/deploy", json={"environment_id": "prod"}) + resp = client.post("/repositories/42/deploy", json={"stage": "preprod"}) assert resp.status_code == 500 def test_dashboard_not_found(self): @@ -304,6 +312,6 @@ class TestDeployDashboard: patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(side_effect=HTTPException(status_code=404, detail="Not found"))), ): client = _make_client() - resp = client.post("/repositories/bad-ref/deploy", json={"environment_id": "prod"}) + resp = client.post("/repositories/bad-ref/deploy", json={"stage": "prod"}) assert resp.status_code == 404 # #endregion Test.Api.GitRepoLifecycleRoutes diff --git a/backend/tests/api/test_git_repo_operations_routes.py b/backend/tests/api/test_git_repo_operations_routes.py index 631613822..7d278f717 100644 --- a/backend/tests/api/test_git_repo_operations_routes.py +++ b/backend/tests/api/test_git_repo_operations_routes.py @@ -357,7 +357,7 @@ class TestGenerateCommitMessage: def test_no_changes(self): mock_gs = MagicMock() - mock_gs.get_diff = AsyncMock(return_value=[None, None]) + mock_gs.get_diff = AsyncMock(return_value=None) with ( patch("src.api.routes.git._repo_operations_routes.get_git_service", return_value=mock_gs), diff --git a/backend/tests/api/test_git_repo_routes.py b/backend/tests/api/test_git_repo_routes.py index 151a688cd..3a391e4aa 100644 --- a/backend/tests/api/test_git_repo_routes.py +++ b/backend/tests/api/test_git_repo_routes.py @@ -63,7 +63,7 @@ def _make_client(overrides: dict | None = None) -> TestClient: class TestInitRepository: """POST /repositories/{dashboard_ref}/init""" - INIT_PAYLOAD = {"config_id": "cfg-1", "remote_url": "https://example.com/org/repo.git"} + INIT_PAYLOAD = {"config_id": "cfg-1", "remote_url": "https://gitea.example.com/org/repo.git"} def test_success_new_repo(self, mock_git_config): mock_db = MagicMock() diff --git a/backend/tests/api/test_storage.py b/backend/tests/api/test_storage.py index 6e54d9f24..b5fbe4f9a 100644 --- a/backend/tests/api/test_storage.py +++ b/backend/tests/api/test_storage.py @@ -115,6 +115,7 @@ class TestDeleteFile: def test_success(self): client, mock_loader = _make_client() mock_plugin = MagicMock() + mock_plugin.delete_file = AsyncMock() mock_loader.get_plugin.return_value = mock_plugin resp = client.delete(f"{P}/files/backups/test.txt") assert resp.status_code == 204 diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 3c4e78668..247d28bf8 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -105,10 +105,28 @@ def pytest_configure(config): f"\n[conftest] SQLite (global: {_TEST_DB_PATH}) + FK enforcement", file=sys.stderr, ) - print( - " Integration tests skipped by default. Use --run-integration to enable.\n", - file=sys.stderr, - ) + if config.getoption("--run-integration"): + print(" Integration tests enabled (--run-integration).\n", file=sys.stderr) + else: + print( + " Integration tests skipped by default. Use --run-integration to enable.\n", + file=sys.stderr, + ) + + +def pytest_collection_modifyitems(config, items): + """Expose tests under tests/integration to the integration marker. + + Integration tests are discovered by directory today, while pytest's + ``-m integration`` selector operates on markers. Applying the marker at + collection time keeps both invocation styles equivalent: + ``pytest tests/integration --run-integration`` and + ``pytest -m integration --run-integration``. + """ + integration_marker = pytest.mark.integration + for item in items: + if "/tests/integration/" in f"/{item.nodeid}": + item.add_marker(integration_marker) def pytest_unconfigure(config): diff --git a/frontend/src/lib/components/git/__tests__/GitFeatureWorkflow.test.ts b/frontend/src/lib/components/git/__tests__/GitFeatureWorkflow.test.ts index 4a40518d2..09ebb4c49 100644 --- a/frontend/src/lib/components/git/__tests__/GitFeatureWorkflow.test.ts +++ b/frontend/src/lib/components/git/__tests__/GitFeatureWorkflow.test.ts @@ -41,7 +41,7 @@ describe('GitFeatureWorkflow', () => { branches: [{ name: 'feature/already-tested', commit_hash: 'merged-123456', ahead_of_dev: 0 }], }); - expect(screen.getByText(/Нет активных доработок/)).toBeTruthy(); + expect(screen.getByText(/Нет отдельных доработок, ожидающих передачи в DEV/)).toBeTruthy(); expect(screen.getByText(/Черновики уже в DEV/)).toBeTruthy(); expect(screen.queryByText('Передать в DEV')).toBeNull(); }); @@ -52,7 +52,7 @@ describe('GitFeatureWorkflow', () => { branches: [{ name: 'feature/remote-only', is_remote: true, ahead_of_dev: 5 }], }); - expect(screen.getByText(/Нет отдельных черновиков/)).toBeTruthy(); + expect(screen.getByText(/Нет отдельных доработок, ожидающих передачи в DEV/)).toBeTruthy(); expect(screen.queryByText('remote only')).toBeNull(); }); });