fix: add from __future__ import annotations to workspace_manager.py

Fixes NameError: ToolInstance not defined at runtime because
type annotations are evaluated at class definition time.
Deferring annotation evaluation with __future__ annotations
keeps TYPE_CHECKING imports from causing runtime crashes.

Also includes ruff formatting cleanup on workspace-related files.
This commit is contained in:
2026-05-31 23:41:45 +02:00
parent 5bba2bbd92
commit a5d64d1859
15 changed files with 831 additions and 699 deletions
@@ -60,7 +60,9 @@ def test_repo(db_session: AsyncSession, authenticated_client: TestClient):
class TestListWorkspaces:
"""Tests for GET /projects/{pid}/repositories/{rid}/workspaces."""
def test_list_empty(self, authenticated_client: TestClient, test_repo: GitRepository):
def test_list_empty(
self, authenticated_client: TestClient, test_repo: GitRepository
):
"""Returns empty list when no workspaces exist."""
response = authenticated_client.get(
f"/projects/{test_repo.project_id}/repositories/{test_repo.id}/workspaces"
@@ -69,7 +71,10 @@ class TestListWorkspaces:
assert response.json() == []
def test_list_with_workspaces(
self, authenticated_client: TestClient, db_session: AsyncSession, test_repo: GitRepository
self,
authenticated_client: TestClient,
db_session: AsyncSession,
test_repo: GitRepository,
):
"""Returns workspaces with instance counts."""
ws = Workspace(
@@ -99,7 +104,9 @@ class TestListWorkspaces:
class TestCreateWorkspace:
"""Tests for POST /projects/{pid}/repositories/{rid}/workspaces."""
def test_create_success(self, authenticated_client: TestClient, test_repo: GitRepository):
def test_create_success(
self, authenticated_client: TestClient, test_repo: GitRepository
):
"""Creates a workspace and clones the repo."""
mock_ws = Workspace(
id=uuid.uuid4(),
@@ -110,7 +117,9 @@ class TestCreateWorkspace:
path="/data/working-copies/test/feature-branch",
)
with patch.object(WorkspaceManager, "create", return_value=mock_ws) as mock_create:
with patch.object(
WorkspaceManager, "create", return_value=mock_ws
) as mock_create:
response = authenticated_client.post(
f"/projects/{test_repo.project_id}/repositories/{test_repo.id}/workspaces",
json={"name": "feature-branch", "branch": "feature"},
@@ -121,7 +130,9 @@ class TestCreateWorkspace:
assert data["branch"] == "feature"
mock_create.assert_called_once()
def test_create_missing_name(self, authenticated_client: TestClient, test_repo: GitRepository):
def test_create_missing_name(
self, authenticated_client: TestClient, test_repo: GitRepository
):
"""Returns 400 when name is missing."""
response = authenticated_client.post(
f"/projects/{test_repo.project_id}/repositories/{test_repo.id}/workspaces",
@@ -131,7 +142,10 @@ class TestCreateWorkspace:
assert "name" in response.json()["detail"]
def test_create_duplicate_name(
self, authenticated_client: TestClient, db_session: AsyncSession, test_repo: GitRepository
self,
authenticated_client: TestClient,
db_session: AsyncSession,
test_repo: GitRepository,
):
"""Returns 409 when workspace name already exists."""
ws = Workspace(
@@ -148,7 +162,9 @@ class TestCreateWorkspace:
asyncio.run(_commit())
with patch.object(WorkspaceManager, "create", side_effect=Exception("duplicate")):
with patch.object(
WorkspaceManager, "create", side_effect=Exception("duplicate")
):
response = authenticated_client.post(
f"/projects/{test_repo.project_id}/repositories/{test_repo.id}/workspaces",
json={"name": "dev", "branch": "main"},
@@ -160,7 +176,10 @@ class TestDeleteWorkspace:
"""Tests for DELETE /projects/{pid}/repositories/{rid}/workspaces/{wid}."""
def test_delete_without_instances(
self, authenticated_client: TestClient, db_session: AsyncSession, test_repo: GitRepository
self,
authenticated_client: TestClient,
db_session: AsyncSession,
test_repo: GitRepository,
):
"""Deletes workspace when no instances exist."""
ws = Workspace(
@@ -185,9 +204,14 @@ class TestDeleteWorkspace:
assert response.status_code == 200
assert response.json()["status"] == "deleted"
@pytest.mark.skip(reason="Async fixture interaction with sync tests — endpoint logic verified manually")
@pytest.mark.skip(
reason="Async fixture interaction with sync tests — endpoint logic verified manually"
)
def test_delete_with_instances_no_force(
self, authenticated_client: TestClient, db_session: AsyncSession, test_repo: GitRepository
self,
authenticated_client: TestClient,
db_session: AsyncSession,
test_repo: GitRepository,
):
"""Returns 409 when workspace has instances and force=False."""
ws = Workspace(
@@ -239,7 +263,10 @@ class TestDeleteWorkspace:
assert len(detail["instances"]) == 1
def test_delete_with_instances_force(
self, authenticated_client: TestClient, db_session: AsyncSession, test_repo: GitRepository
self,
authenticated_client: TestClient,
db_session: AsyncSession,
test_repo: GitRepository,
):
"""Deletes workspace when force=True even with instances."""
ws = Workspace(
@@ -268,7 +295,10 @@ class TestSyncWorkspace:
"""Tests for POST /projects/{pid}/repositories/{rid}/workspaces/{wid}/sync."""
def test_sync_success(
self, authenticated_client: TestClient, db_session: AsyncSession, test_repo: GitRepository
self,
authenticated_client: TestClient,
db_session: AsyncSession,
test_repo: GitRepository,
):
"""Sync succeeds and updates last_sync_at."""
ws = Workspace(
@@ -298,7 +328,10 @@ class TestSyncWorkspace:
assert data["pulled"] is True
def test_sync_branch_deleted(
self, authenticated_client: TestClient, db_session: AsyncSession, test_repo: GitRepository
self,
authenticated_client: TestClient,
db_session: AsyncSession,
test_repo: GitRepository,
):
"""Returns 409 when branch was deleted from remote."""
ws = Workspace(