|
10 | 10 | make_api_request, |
11 | 11 | ) |
12 | 12 | from basic_memory.cli.commands.cloud.cloud_utils import ( |
| 13 | + _workspace_headers, |
13 | 14 | create_cloud_project, |
14 | 15 | fetch_cloud_projects, |
15 | 16 | project_exists, |
@@ -165,6 +166,81 @@ async def api_request(**kwargs): |
165 | 166 | assert seen["create_payload"]["path"] == "my-project" |
166 | 167 |
|
167 | 168 |
|
| 169 | +def test_workspace_headers_with_workspace(): |
| 170 | + """_workspace_headers returns X-Workspace-ID when workspace is provided.""" |
| 171 | + assert _workspace_headers("tenant-123") == {"X-Workspace-ID": "tenant-123"} |
| 172 | + |
| 173 | + |
| 174 | +def test_workspace_headers_without_workspace(): |
| 175 | + """_workspace_headers returns empty dict when no workspace.""" |
| 176 | + assert _workspace_headers(None) == {} |
| 177 | + assert _workspace_headers() == {} |
| 178 | + |
| 179 | + |
| 180 | +@pytest.mark.asyncio |
| 181 | +async def test_cloud_utils_pass_workspace_header(config_home, config_manager): |
| 182 | + """fetch_cloud_projects, project_exists, and create_cloud_project pass workspace header.""" |
| 183 | + config = config_manager.load_config() |
| 184 | + config.cloud_host = "https://cloud.example.test" |
| 185 | + config_manager.save_config(config) |
| 186 | + |
| 187 | + auth = CLIAuth(client_id="cid", authkit_domain="https://auth.example.test") |
| 188 | + auth.token_file.parent.mkdir(parents=True, exist_ok=True) |
| 189 | + auth.token_file.write_text( |
| 190 | + '{"access_token":"token-123","refresh_token":null,"expires_at":9999999999,"token_type":"Bearer"}', |
| 191 | + encoding="utf-8", |
| 192 | + ) |
| 193 | + |
| 194 | + seen_headers: list[dict] = [] |
| 195 | + |
| 196 | + async def handler(request: httpx.Request) -> httpx.Response: |
| 197 | + seen_headers.append(dict(request.headers)) |
| 198 | + if request.method == "GET": |
| 199 | + return httpx.Response(200, json={"projects": []}) |
| 200 | + if request.method == "POST": |
| 201 | + payload = json.loads(request.content.decode("utf-8")) |
| 202 | + return httpx.Response( |
| 203 | + 200, |
| 204 | + json={ |
| 205 | + "message": "created", |
| 206 | + "status": "success", |
| 207 | + "default": False, |
| 208 | + "old_project": None, |
| 209 | + "new_project": {"name": payload["name"], "path": payload["path"]}, |
| 210 | + }, |
| 211 | + ) |
| 212 | + raise AssertionError(f"Unexpected: {request.method}") |
| 213 | + |
| 214 | + transport = httpx.MockTransport(handler) |
| 215 | + |
| 216 | + @asynccontextmanager |
| 217 | + async def http_client_factory(): |
| 218 | + async with httpx.AsyncClient( |
| 219 | + transport=transport, base_url="https://cloud.example.test" |
| 220 | + ) as client: |
| 221 | + yield client |
| 222 | + |
| 223 | + async def api_request(**kwargs): |
| 224 | + return await make_api_request(auth=auth, http_client_factory=http_client_factory, **kwargs) |
| 225 | + |
| 226 | + # fetch with workspace |
| 227 | + await fetch_cloud_projects(workspace="tenant-abc", api_request=api_request) |
| 228 | + assert seen_headers[-1].get("x-workspace-id") == "tenant-abc" |
| 229 | + |
| 230 | + # project_exists with workspace |
| 231 | + await project_exists("test", workspace="tenant-abc", api_request=api_request) |
| 232 | + assert seen_headers[-1].get("x-workspace-id") == "tenant-abc" |
| 233 | + |
| 234 | + # create with workspace |
| 235 | + await create_cloud_project("new-proj", workspace="tenant-abc", api_request=api_request) |
| 236 | + assert seen_headers[-1].get("x-workspace-id") == "tenant-abc" |
| 237 | + |
| 238 | + # Without workspace — header should not be present |
| 239 | + seen_headers.clear() |
| 240 | + await fetch_cloud_projects(api_request=api_request) |
| 241 | + assert "x-workspace-id" not in seen_headers[-1] |
| 242 | + |
| 243 | + |
168 | 244 | @pytest.mark.asyncio |
169 | 245 | async def test_make_api_request_prefers_api_key_over_oauth(config_home, config_manager): |
170 | 246 | """API key in config should be used without needing an OAuth token on disk.""" |
|
0 commit comments