0ec20b9c23
- Add git mount merge function tests - Add profile resolution tests with git mounts - Add integration tests for CRUD with git mounts - Add glob expansion tests (patterns, limits, repo boundary) - Add branch checkout tests (success and failure) - Add error handling tests for missing repos/invalid UUIDs All 52 tests pass.
140 lines
5.2 KiB
Python
140 lines
5.2 KiB
Python
"""Unit tests for git mount resolution in tool instances."""
|
|
|
|
import os
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from src.api.tool_instances import (
|
|
_checkout_branch,
|
|
_expand_glob_source,
|
|
_resolve_single_git_mount,
|
|
)
|
|
from src.services.config_profile_resolver import ResolvedProfile
|
|
|
|
|
|
class TestExpandGlobSource:
|
|
"""Unit tests for glob pattern expansion."""
|
|
|
|
def test_no_glob_single_file(self, tmp_path: Path) -> None:
|
|
"""Test non-glob path returns single file."""
|
|
test_file = tmp_path / "test.txt"
|
|
test_file.write_text("content")
|
|
|
|
result = _expand_glob_source(str(test_file), str(tmp_path))
|
|
assert len(result) == 1
|
|
assert result[0] == str(test_file)
|
|
|
|
def test_no_glob_missing_file(self, tmp_path: Path) -> None:
|
|
"""Test non-glob missing file returns empty list."""
|
|
missing_file = tmp_path / "missing.txt"
|
|
|
|
result = _expand_glob_source(str(missing_file), str(tmp_path))
|
|
assert len(result) == 0
|
|
|
|
def test_glob_pattern(self, tmp_path: Path) -> None:
|
|
"""Test glob pattern matches files."""
|
|
(tmp_path / "file1.txt").write_text("content1")
|
|
(tmp_path / "file2.txt").write_text("content2")
|
|
(tmp_path / "other.py").write_text("code")
|
|
|
|
result = _expand_glob_source(str(tmp_path / "*.txt"), str(tmp_path))
|
|
assert len(result) == 2
|
|
assert all(f.endswith(".txt") for f in result)
|
|
|
|
def test_glob_recursive(self, tmp_path: Path) -> None:
|
|
"""Test recursive glob pattern."""
|
|
subdir = tmp_path / "subdir"
|
|
subdir.mkdir()
|
|
(subdir / "nested.txt").write_text("content")
|
|
|
|
result = _expand_glob_source(str(tmp_path / "**" / "*.txt"), str(tmp_path))
|
|
assert len(result) == 1
|
|
assert "nested.txt" in result[0]
|
|
|
|
def test_glob_limit_enforced(self, tmp_path: Path) -> None:
|
|
"""Test that glob matches are limited to prevent abuse."""
|
|
# Create more than 100 files
|
|
for i in range(105):
|
|
(tmp_path / f"file{i}.txt").write_text("content")
|
|
|
|
result = _expand_glob_source(str(tmp_path / "*.txt"), str(tmp_path))
|
|
assert len(result) == 100 # MAX_GLOB_MATCHES limit
|
|
|
|
def test_glob_escapes_repo(self, tmp_path: Path) -> None:
|
|
"""Test that glob results outside repo are filtered."""
|
|
other_dir = tmp_path.parent / "other"
|
|
other_dir.mkdir(exist_ok=True)
|
|
(other_dir / "outside.txt").write_text("content")
|
|
|
|
result = _expand_glob_source(str(tmp_path.parent / "*" / "*.txt"), str(tmp_path))
|
|
# Should only include files within tmp_path, not other_dir
|
|
assert all(r.startswith(str(tmp_path)) for r in result)
|
|
|
|
|
|
class TestCheckoutBranch:
|
|
"""Unit tests for branch checkout."""
|
|
|
|
def test_checkout_existing_branch(self, tmp_path: Path) -> None:
|
|
"""Test checking out an existing branch."""
|
|
# Initialize git repo
|
|
os.system(f"cd {tmp_path} && git init && git config user.email 'test@test.com' && git config user.name 'Test'")
|
|
(tmp_path / "file.txt").write_text("content")
|
|
os.system(f"cd {tmp_path} && git add . && git commit -m 'initial'")
|
|
os.system(f"cd {tmp_path} && git branch feature")
|
|
|
|
_checkout_branch(str(tmp_path), "feature")
|
|
|
|
# Verify we're on feature branch
|
|
result = os.popen(f"cd {tmp_path} && git branch --show-current").read().strip()
|
|
assert result == "feature"
|
|
|
|
def test_checkout_nonexistent_branch(self, tmp_path: Path) -> None:
|
|
"""Test checking out a non-existent branch raises error."""
|
|
os.system(f"cd {tmp_path} && git init && git config user.email 'test@test.com' && git config user.name 'Test'")
|
|
(tmp_path / "file.txt").write_text("content")
|
|
os.system(f"cd {tmp_path} && git add . && git commit -m 'initial'")
|
|
|
|
with pytest.raises(RuntimeError, match="Failed to checkout branch"):
|
|
_checkout_branch(str(tmp_path), "nonexistent")
|
|
|
|
|
|
class TestResolveSingleGitMount:
|
|
"""Unit tests for resolving a single git mount."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_resolve_missing_repo(self, db_session) -> None:
|
|
"""Test that missing repo returns empty list."""
|
|
git_mount = {
|
|
"repo_id": "12345678-1234-1234-1234-123456789abc",
|
|
"source_path": ".",
|
|
"target_path": "/app",
|
|
}
|
|
|
|
result = await _resolve_single_git_mount(db_session, git_mount)
|
|
assert result == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_resolve_missing_target_path(self, db_session) -> None:
|
|
"""Test that missing target path returns empty list."""
|
|
git_mount = {
|
|
"repo_id": "12345678-1234-1234-1234-123456789abc",
|
|
"source_path": ".",
|
|
}
|
|
|
|
result = await _resolve_single_git_mount(db_session, git_mount)
|
|
assert result == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_resolve_invalid_repo_id(self, db_session) -> None:
|
|
"""Test that invalid repo_id returns empty list."""
|
|
git_mount = {
|
|
"repo_id": "not-a-uuid",
|
|
"source_path": ".",
|
|
"target_path": "/app",
|
|
}
|
|
|
|
result = await _resolve_single_git_mount(db_session, git_mount)
|
|
assert result == []
|