test_deps.py•8.02 kB
"""Tests for dependency injection functions in deps.py."""
from datetime import datetime, timezone
from pathlib import Path
import pytest
import pytest_asyncio
from fastapi import HTTPException
from basic_memory.deps import get_project_config, get_project_id
from basic_memory.models.project import Project
from basic_memory.repository.project_repository import ProjectRepository
@pytest_asyncio.fixture
async def project_with_spaces(project_repository: ProjectRepository) -> Project:
    """Create a project with spaces in the name for testing permalink normalization."""
    project_data = {
        "name": "My Test Project",
        "description": "A project with spaces in the name",
        "path": "/my/test/project",
        "is_active": True,
        "is_default": False,
        "created_at": datetime.now(timezone.utc),
        "updated_at": datetime.now(timezone.utc),
    }
    return await project_repository.create(project_data)
@pytest_asyncio.fixture
async def project_with_special_chars(project_repository: ProjectRepository) -> Project:
    """Create a project with special characters for testing permalink normalization."""
    project_data = {
        "name": "Project: Test & Development!",
        "description": "A project with special characters",
        "path": "/project/test/dev",
        "is_active": True,
        "is_default": False,
        "created_at": datetime.now(timezone.utc),
        "updated_at": datetime.now(timezone.utc),
    }
    return await project_repository.create(project_data)
@pytest.mark.asyncio
async def test_get_project_config_with_spaces(
    project_repository: ProjectRepository, project_with_spaces: Project
):
    """Test that get_project_config normalizes project names with spaces."""
    # The project name has spaces: "My Test Project"
    # The permalink should be: "my-test-project"
    assert project_with_spaces.name == "My Test Project"
    assert project_with_spaces.permalink == "my-test-project"
    # Call get_project_config with the project name (not permalink)
    # This simulates what happens when the project name comes from URL path
    config = await get_project_config(
        project="My Test Project", project_repository=project_repository
    )
    # Verify we got the correct project config
    assert config.name == "My Test Project"
    assert config.home == Path("/my/test/project")
@pytest.mark.asyncio
async def test_get_project_config_with_permalink(
    project_repository: ProjectRepository, project_with_spaces: Project
):
    """Test that get_project_config works when already given a permalink."""
    # Call with the permalink directly
    config = await get_project_config(
        project="my-test-project", project_repository=project_repository
    )
    # Verify we got the correct project config
    assert config.name == "My Test Project"
    assert config.home == Path("/my/test/project")
@pytest.mark.asyncio
async def test_get_project_config_with_special_chars(
    project_repository: ProjectRepository, project_with_special_chars: Project
):
    """Test that get_project_config normalizes project names with special characters."""
    # The project name has special chars: "Project: Test & Development!"
    # The permalink should be: "project-test-development"
    assert project_with_special_chars.name == "Project: Test & Development!"
    assert project_with_special_chars.permalink == "project-test-development"
    # Call get_project_config with the project name
    config = await get_project_config(
        project="Project: Test & Development!", project_repository=project_repository
    )
    # Verify we got the correct project config
    assert config.name == "Project: Test & Development!"
    assert config.home == Path("/project/test/dev")
@pytest.mark.asyncio
async def test_get_project_config_not_found(project_repository: ProjectRepository):
    """Test that get_project_config raises HTTPException when project not found."""
    with pytest.raises(HTTPException) as exc_info:
        await get_project_config(
            project="Nonexistent Project", project_repository=project_repository
        )
    assert exc_info.value.status_code == 404
    assert "Project 'Nonexistent Project' not found" in exc_info.value.detail
@pytest.mark.asyncio
async def test_get_project_id_with_spaces(
    project_repository: ProjectRepository, project_with_spaces: Project
):
    """Test that get_project_id normalizes project names with spaces."""
    # Call get_project_id with the project name (not permalink)
    project_id = await get_project_id(
        project_repository=project_repository, project="My Test Project"
    )
    # Verify we got the correct project ID
    assert project_id == project_with_spaces.id
@pytest.mark.asyncio
async def test_get_project_id_with_permalink(
    project_repository: ProjectRepository, project_with_spaces: Project
):
    """Test that get_project_id works when already given a permalink."""
    # Call with the permalink directly
    project_id = await get_project_id(
        project_repository=project_repository, project="my-test-project"
    )
    # Verify we got the correct project ID
    assert project_id == project_with_spaces.id
@pytest.mark.asyncio
async def test_get_project_id_with_special_chars(
    project_repository: ProjectRepository, project_with_special_chars: Project
):
    """Test that get_project_id normalizes project names with special characters."""
    # Call get_project_id with the project name
    project_id = await get_project_id(
        project_repository=project_repository, project="Project: Test & Development!"
    )
    # Verify we got the correct project ID
    assert project_id == project_with_special_chars.id
@pytest.mark.asyncio
async def test_get_project_id_not_found(project_repository: ProjectRepository):
    """Test that get_project_id raises HTTPException when project not found."""
    with pytest.raises(HTTPException) as exc_info:
        await get_project_id(project_repository=project_repository, project="Nonexistent Project")
    assert exc_info.value.status_code == 404
    assert "Project 'Nonexistent Project' not found" in exc_info.value.detail
@pytest.mark.asyncio
async def test_get_project_id_fallback_to_name(
    project_repository: ProjectRepository, test_project: Project
):
    """Test that get_project_id falls back to name lookup if permalink lookup fails.
    This test verifies the fallback behavior in get_project_id where it tries
    get_by_name if get_by_permalink returns None.
    """
    # The test_project fixture has name "test-project" and permalink "test-project"
    # Since both are the same, we can't easily test the fallback with existing fixtures
    # So this test just verifies the normal path works with test_project
    project_id = await get_project_id(project_repository=project_repository, project="test-project")
    assert project_id == test_project.id
@pytest.mark.asyncio
async def test_get_project_config_case_sensitivity(
    project_repository: ProjectRepository, project_with_spaces: Project
):
    """Test that get_project_config handles case variations correctly.
    Permalink normalization should convert to lowercase, so different case
    variations of the same name should resolve to the same project.
    """
    # Create project with mixed case: "My Test Project" -> permalink "my-test-project"
    # Try with different case variations
    config1 = await get_project_config(
        project="My Test Project", project_repository=project_repository
    )
    config2 = await get_project_config(
        project="my test project", project_repository=project_repository
    )
    config3 = await get_project_config(
        project="MY TEST PROJECT", project_repository=project_repository
    )
    # All should resolve to the same project
    assert config1.name == config2.name == config3.name == "My Test Project"
    assert config1.home == config2.home == config3.home == Path("/my/test/project")