Skip to content
Open
4 changes: 2 additions & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,15 @@ if git diff --cached --name-only | grep -q "^apps/backend/.*\.py$"; then
# Tests to skip: graphiti (external deps), merge_file_tracker/service_orchestrator/worktree/workspace (Windows path/git issues)
# Also skip tests that require optional dependencies (pydantic structured outputs)
IGNORE_TESTS="--ignore=../../tests/test_graphiti.py --ignore=../../tests/test_merge_file_tracker.py --ignore=../../tests/test_service_orchestrator.py --ignore=../../tests/test_worktree.py --ignore=../../tests/test_workspace.py --ignore=../../tests/test_finding_validation.py --ignore=../../tests/test_sdk_structured_output.py --ignore=../../tests/test_structured_outputs.py"

# Determine Python executable from venv
VENV_PYTHON=""
if [ -f ".venv/bin/python" ]; then
VENV_PYTHON=".venv/bin/python"
elif [ -f ".venv/Scripts/python.exe" ]; then
VENV_PYTHON=".venv/Scripts/python.exe"
fi

if [ -n "$VENV_PYTHON" ]; then
# Check if pytest is installed in venv
if $VENV_PYTHON -c "import pytest" 2>/dev/null; then
Expand Down
1 change: 1 addition & 0 deletions apps/backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ tests/

# Auto Claude data directory
.auto-claude/
/gitlab-integration-tests/
243 changes: 243 additions & 0 deletions apps/backend/__tests__/fixtures/gitlab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
"""
GitLab Test Fixtures
====================
Mock data and fixtures for GitLab integration tests.
"""

# Sample GitLab MR data
SAMPLE_MR_DATA = {
"iid": 123,
"id": 12345,
"title": "Add user authentication feature",
"description": "Implement OAuth2 login with Google and GitHub providers",
"author": {
"id": 1,
"username": "john_doe",
"name": "John Doe",
"email": "john@example.com",
},
"source_branch": "feature/oauth-auth",
"target_branch": "main",
"state": "opened",
"draft": False,
"merge_status": "can_be_merged",
"web_url": "https://gitlab.com/group/project/-/merge_requests/123",
"created_at": "2025-01-14T10:00:00.000Z",
"updated_at": "2025-01-14T12:00:00.000Z",
"labels": ["feature", "authentication"],
"assignees": [],
}

SAMPLE_MR_CHANGES = {
"id": 12345,
"iid": 123,
"project_id": 1,
"title": "Add user authentication feature",
"description": "Implement OAuth2 login",
"state": "opened",
"created_at": "2025-01-14T10:00:00.000Z",
"updated_at": "2025-01-14T12:00:00.000Z",
"merge_status": "can_be_merged",
"additions": 150,
"deletions": 20,
"changed_files_count": 5,
"changes": [
{
"old_path": "src/auth/__init__.py",
"new_path": "src/auth/__init__.py",
"diff": "@@ -0,0 +1,5 @@\n+from .oauth import OAuthHandler\n+from .providers import GoogleProvider, GitHubProvider",
"new_file": False,
"renamed_file": False,
"deleted_file": False,
},
{
"old_path": "src/auth/oauth.py",
"new_path": "src/auth/oauth.py",
"diff": "@@ -0,0 +1,50 @@\n+class OAuthHandler:\n+ def handle_callback(self, request):\n+ pass",
"new_file": True,
"renamed_file": False,
"deleted_file": False,
},
],
}

SAMPLE_MR_COMMITS = [
{
"id": "abc123def456",
"short_id": "abc123de",
"title": "Add OAuth handler",
"message": "Add OAuth handler",
"author_name": "John Doe",
"author_email": "john@example.com",
"authored_date": "2025-01-14T10:00:00.000Z",
"created_at": "2025-01-14T10:00:00.000Z",
},
{
"id": "def456ghi789",
"short_id": "def456gh",
"title": "Add Google provider",
"message": "Add Google provider",
"author_name": "John Doe",
"author_email": "john@example.com",
"authored_date": "2025-01-14T11:00:00.000Z",
"created_at": "2025-01-14T11:00:00.000Z",
},
]

# Sample GitLab issue data
SAMPLE_ISSUE_DATA = {
"iid": 42,
"id": 42,
"title": "Bug: Login button not working",
"description": "Clicking the login button does nothing",
"author": {
"id": 2,
"username": "jane_smith",
"name": "Jane Smith",
"email": "jane@example.com",
},
"state": "opened",
"labels": ["bug", "urgent"],
"assignees": [],
"milestone": None,
"web_url": "https://gitlab.com/group/project/-/issues/42",
"created_at": "2025-01-14T09:00:00.000Z",
"updated_at": "2025-01-14T09:30:00.000Z",
}

# Sample GitLab pipeline data
SAMPLE_PIPELINE_DATA = {
"id": 1001,
"iid": 1,
"project_id": 1,
"ref": "feature/oauth-auth",
"sha": "abc123def456",
"status": "success",
"source": "merge_request_event",
"created_at": "2025-01-14T10:30:00.000Z",
"updated_at": "2025-01-14T10:35:00.000Z",
"finished_at": "2025-01-14T10:35:00.000Z",
"duration": 300,
"web_url": "https://gitlab.com/group/project/-/pipelines/1001",
}

SAMPLE_PIPELINE_JOBS = [
{
"id": 5001,
"name": "test",
"stage": "test",
"status": "success",
"started_at": "2025-01-14T10:31:00.000Z",
"finished_at": "2025-01-14T10:34:00.000Z",
"duration": 180,
"allow_failure": False,
},
{
"id": 5002,
"name": "lint",
"stage": "test",
"status": "success",
"started_at": "2025-01-14T10:31:00.000Z",
"finished_at": "2025-01-14T10:32:00.000Z",
"duration": 60,
"allow_failure": False,
},
]

# Sample GitLab discussion/note data
SAMPLE_MR_DISCUSSIONS = [
{
"id": "d1",
"notes": [
{
"id": 1001,
"type": "DiscussionNote",
"author": {"username": "coderabbit[bot]"},
"body": "Consider adding error handling for OAuth failures",
"created_at": "2025-01-14T11:00:00.000Z",
"system": False,
"resolvable": True,
}
],
}
]

SAMPLE_MR_NOTES = [
{
"id": 2001,
"type": "DiscussionNote",
"author": {"username": "reviewer_user"},
"body": "LGTM, just one comment",
"created_at": "2025-01-14T12:00:00.000Z",
"system": False,
}
]

# Mock GitLab config
MOCK_GITLAB_CONFIG = {
"token": "glpat-test-token-12345",
"project": "group/project",
"instance_url": "https://gitlab.example.com",
}
Comment on lines +177 to +182
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid token-shaped secrets in fixtures.
The glpat-... pattern can trip secret scanners and leak into logs. Consider a clearly fake placeholder (e.g., gitlab-test-token) that won’t match PAT patterns.

🔧 Suggested tweak
-    "token": "glpat-test-token-12345",
+    "token": "gitlab-test-token",
🤖 Prompt for AI Agents
In `@apps/backend/__tests__/fixtures/gitlab.py` around lines 177 - 182, The
fixture uses a PAT-shaped token value in MOCK_GITLAB_CONFIG which can trigger
secret scanners; update the "token" value in MOCK_GITLAB_CONFIG to a clearly
fake placeholder that won't match PAT patterns (e.g., "gitlab-test-token" or
"TEST_GITLAB_TOKEN") so tests remain meaningful without emitting token-shaped
secrets; change only the token string in the MOCK_GITLAB_CONFIG dict.



def mock_mr_data(**overrides):
"""Create mock MR data with optional overrides."""
data = SAMPLE_MR_DATA.copy()
data.update(overrides)
return data


def mock_mr_changes(**overrides):
"""Create mock MR changes with optional overrides."""
data = SAMPLE_MR_CHANGES.copy()
data.update(overrides)
return data


def mock_issue_data(**overrides):
"""Create mock issue data with optional overrides."""
data = SAMPLE_ISSUE_DATA.copy()
data.update(overrides)
return data


def mock_pipeline_data(**overrides):
"""Create mock pipeline data with optional overrides."""
data = SAMPLE_PIPELINE_DATA.copy()
data.update(overrides)
return data


def mock_pipeline_jobs(**overrides):
"""Create mock pipeline jobs with optional overrides."""
data = SAMPLE_PIPELINE_JOBS.copy()
if overrides:
data[0].update(overrides)
return data
Comment on lines +213 to +218
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix shallow copy to prevent cross-test contamination.
list.copy() keeps the inner dicts shared; data[0].update() mutates the global fixture. Use a deep copy or copy each dict.

🐛 Suggested fix
+from copy import deepcopy
@@
 def mock_pipeline_jobs(**overrides):
     """Create mock pipeline jobs with optional overrides."""
-    data = SAMPLE_PIPELINE_JOBS.copy()
+    data = deepcopy(SAMPLE_PIPELINE_JOBS)
     if overrides:
         data[0].update(overrides)
     return data
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def mock_pipeline_jobs(**overrides):
"""Create mock pipeline jobs with optional overrides."""
data = SAMPLE_PIPELINE_JOBS.copy()
if overrides:
data[0].update(overrides)
return data
from copy import deepcopy
def mock_pipeline_jobs(**overrides):
"""Create mock pipeline jobs with optional overrides."""
data = deepcopy(SAMPLE_PIPELINE_JOBS)
if overrides:
data[0].update(overrides)
return data
🤖 Prompt for AI Agents
In `@apps/backend/__tests__/fixtures/gitlab.py` around lines 213 - 218,
mock_pipeline_jobs currently does a shallow copy of SAMPLE_PIPELINE_JOBS so
updating data[0] mutates the global fixture; change the function to clone the
inner dicts (e.g., use copy.deepcopy(SAMPLE_PIPELINE_JOBS) or build a new list
via [dict(item) for item in SAMPLE_PIPELINE_JOBS]) before applying overrides so
SAMPLE_PIPELINE_JOBS remains untouched when mock_pipeline_jobs updates data[0].



def get_mock_diff() -> str:
"""Get a mock diff string for testing."""
return """diff --git a/src/auth/oauth.py b/src/auth/oauth.py
new file mode 100644
index 0000000..abc1234
--- /dev/null
+++ b/src/auth/oauth.py
@@ -0,0 +1,50 @@
+class OAuthHandler:
+ def handle_callback(self, request):
+ pass
diff --git a/src/auth/providers.py b/src/auth/providers.py
new file mode 100644
index 0000000..def5678
--- /dev/null
+++ b/src/auth/providers.py
@@ -0,0 +1,30 @@
+class GoogleProvider:
+ pass
+
+class GitHubProvider:
+ pass
"""
Loading