diff --git a/src/basic_memory/api/v2/routers/project_router.py b/src/basic_memory/api/v2/routers/project_router.py index bd67256e..77cfb01b 100644 --- a/src/basic_memory/api/v2/routers/project_router.py +++ b/src/basic_memory/api/v2/routers/project_router.py @@ -48,7 +48,7 @@ async def list_projects( A list of all projects with metadata """ projects = await project_service.list_projects() - default_project = project_service.default_project + default_project = await project_service.get_default_project_name() project_items = [ ProjectItem( diff --git a/src/basic_memory/services/project_service.py b/src/basic_memory/services/project_service.py index 2d8413de..018187ea 100644 --- a/src/basic_memory/services/project_service.py +++ b/src/basic_memory/services/project_service.py @@ -82,6 +82,21 @@ class ProjectService: """ return self.config_manager.default_project + async def get_default_project_name(self) -> str: + """Get the default project name, falling back to the database. + + ConfigManager reads from the local config file, which doesn't exist + in cloud mode. When it returns None, fall back to the is_default + flag stored in the database. + """ + default = self.config_manager.default_project + if default is not None: + return default + db_default = await self.repository.get_default_project() + if db_default is not None: + return db_default.name + raise ValueError("No default project configured") + @property def current_project(self) -> Optional[str]: """Get the name of the currently active project. diff --git a/tests/api/v2/test_project_router.py b/tests/api/v2/test_project_router.py index 2bbdf5c3..dec6e343 100644 --- a/tests/api/v2/test_project_router.py +++ b/tests/api/v2/test_project_router.py @@ -11,6 +11,21 @@ from basic_memory.schemas.project_info import ProjectItem, ProjectStatusResponse from basic_memory.schemas.v2 import ProjectResolveResponse +@pytest.mark.asyncio +async def test_list_projects(client: AsyncClient, test_project: Project, v2_projects_url): + """Test listing projects returns default_project from the database.""" + response = await client.get(f"{v2_projects_url}/") + + assert response.status_code == 200 + data = response.json() + + # default_project must be populated from the is_default flag in the database + assert data["default_project"] == test_project.name + + project_names = [p["name"] for p in data["projects"]] + assert test_project.name in project_names + + @pytest.mark.asyncio async def test_get_project_by_id(client: AsyncClient, test_project: Project, v2_projects_url): """Test getting a project by its external_id UUID.""" @@ -361,9 +376,10 @@ async def test_legacy_v1_list_projects_endpoint(client: AsyncClient, test_projec assert response.status_code == 200 data = response.json() assert "projects" in data - assert "default_project" in data - # Verify the test project is in the list + # default_project must be populated, not null + assert data["default_project"] == test_project.name + project_names = [p["name"] for p in data["projects"]] assert test_project.name in project_names