Skip to content

Commit ef5d0ca

Browse files
haraldschillyclaude
andcommitted
cocalc-api/test: fix edge cases in api and improve testing
This commit updates the conftest.py test fixtures to properly support both account-scoped and project-scoped API keys during testing. The validate_api_key_config fixture now: - First attempts to detect scope via hub.system.test() (account-scoped keys) - Falls back to project.system.test() if hub fails (project-scoped keys) - Reports both errors if both endpoints fail, for better debugging Also clarifies that the hub endpoint's system.test() is for account-scoped keys only, with clear comments directing project-scoped key users to the project endpoint. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ab4ab26 commit ef5d0ca

File tree

3 files changed

+34
-43
lines changed

3 files changed

+34
-43
lines changed

src/AGENTS.md

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,13 @@ CoCalc is organized as a monorepo with key packages:
108108
#### CoCalc Conat Hub API Architecture
109109

110110
**API Method Registration Pattern:**
111+
111112
- **Registry**: `packages/conat/hub/api/projects.ts` contains `export const projects = { methodName: authFirstRequireAccount }`
112113
- **Implementation**: `packages/server/conat/api/projects.ts` contains `export async function methodName() { ... }`
113114
- **Flow**: Python client `@api_method("projects.methodName")` → POST `/api/conat/hub``hubBridge()` → conat subject `hub.account.{account_id}.api` → registry lookup → implementation
114115

115116
**Example - projects.createProject:**
117+
116118
1. **Python**: `@api_method("projects.createProject")` decorator
117119
2. **HTTP**: `POST /api/conat/hub {"name": "projects.createProject", "args": [...]}`
118120
3. **Bridge**: `hubBridge()` routes to conat subject
@@ -172,7 +174,6 @@ CoCalc is organized as a monorepo with key packages:
172174
- Prefix git commits with the package and general area. e.g. 'frontend/latex: ...' if it concerns latex editor changes in the packages/frontend/... code.
173175
- When pushing a new branch to Github, track it upstream. e.g. `git push --set-upstream origin feature-foo` for branch "feature-foo".
174176

175-
176177
## React-intl / Internationalization (i18n)
177178

178179
CoCalc uses react-intl for internationalization with SimpleLocalize as the translation platform.
@@ -234,7 +235,10 @@ Same flow as above, but **before 3. i18n:upload**, delete the key. Only new keys
234235

235236
## Overview
236237

237-
The `python/cocalc-api/` directory contains a Python client library for the CoCalc API, published as the `cocalc-api` package on PyPI.
238+
The `python/cocalc-api/` directory contains a uv-based Python client library for the CoCalc API, published as the `cocalc-api` package on PyPI.
239+
240+
It also contains a test framework (`python/cocalc-api/tests/README.md`) and an MCP client (`python/cocalc-api/src/cocalc_api/mcp/README.md`).
241+
For convenience, a `python/cocalc-api/Makefile` exists.
238242

239243
## Client-Server Architecture Investigation
240244

@@ -248,54 +252,21 @@ The `python/cocalc-api/` directory contains a Python client library for the CoCa
248252
### Endpoints Discovered
249253

250254
#### Hub API: `POST /api/conat/hub`
255+
251256
- **Bridge**: `packages/next/pages/api/conat/hub.ts``hubBridge()` → conat subject `hub.account.{account_id}.api`
252257
- **Implementation**: `packages/conat/hub/api/projects.ts`
253258
- **Available Methods**: `createProject`, `start`, `stop`, `setQuotas`, `addCollaborator`, `removeCollaborator`, etc.
254-
- **Missing**: ❌ **No `delete` method implemented in conat hub API**
255259

256260
#### Project API: `POST /api/conat/project`
261+
257262
- **Bridge**: `packages/next/pages/api/conat/project.ts``projectBridge()` → conat project subjects
258263
- **Implementation**: `packages/conat/project/api/` (system.ping, system.exec, system.jupyterExecute)
259264

260-
### Project Deletion Investigation
261-
262-
#### ✅ Next.js v2 API Route Available
263-
- **Endpoint**: `packages/next/pages/api/v2/projects/delete.ts`
264-
- **Functionality**: Sets deleted=true, removes licenses, stops project
265-
- **Authentication**: Requires collaborator access or admin
266-
267-
#### ❌ Missing Conat Hub API Method
268-
- **Current Methods**: Only CRUD operations (create, start, stop, quotas, collaborators)
269-
- **Gap**: No `delete` method exposed through conat hub API used by cocalc-api
270-
271-
#### Frontend Implementation
272-
- **Location**: `packages/frontend/projects/actions.ts:delete_project()`
273-
- **Method**: Direct database table update via `projects_table_set({deleted: true})`
274-
275-
## Implementation
276-
277-
### Solution Implemented: Direct v2 API Call
278-
- **Added**: `hub.projects.delete(project_id)` method to cocalc-api Python client
279-
- **Implementation**: Direct HTTP POST to `/api/v2/projects/delete` endpoint
280-
- **Reasoning**: Fastest path to complete project lifecycle without requiring conat hub API changes
281-
- **Consistency**: Uses same authentication and error handling patterns as other methods
282-
283-
### Code Changes
284-
1. **`src/cocalc_api/hub.py`**: Added `delete()` method to Projects class
285-
2. **`tests/conftest.py`**: Updated cleanup to use new delete method
286-
3. **`tests/test_hub.py`**: Added test for delete method availability
287-
288-
## Current Status
289-
- ✅ pytest test framework established with automatic project lifecycle
290-
- ✅ Project creation/start/stop working via conat hub API
291-
- ✅ Project deletion implemented by calling v2 API route directly
292-
- ✅ Complete project lifecycle management: create → start → test → stop → delete
293-
- ✅ All 14 tests passing with proper resource cleanup
294-
295265
# important-instruction-reminders
266+
296267
- Do what has been asked; nothing more, nothing less.
297268
- NEVER create files unless they're absolutely necessary for achieving your goal.
298269
- ALWAYS prefer editing an existing file to creating a new one.
299-
- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
270+
- NEVER proactively create documentation files (\*.md) or README files. Only create documentation files if explicitly requested by the User.
300271
- ALWAYS ask questions if something is unclear. Only proceed to the implementation step if you have no questions left.
301272
- When modifying a file with a copyright banner at the top, make sure to fix/add the current year to indicate the copyright year.

src/packages/server/conat/api/system.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ export function ping() {
1818
return { now: Date.now() };
1919
}
2020

21-
export async function test({
22-
account_id,
23-
}: { account_id?: string } = {}) {
21+
export async function test({ account_id }: { account_id?: string } = {}) {
2422
// Return API key scope information and server time
2523
// The authFirst decorator determines the scope from the API key and injects account_id.
24+
// Note: This endpoint only accepts account-scoped keys (see packages/next/pages/api/conat/hub.ts).
25+
// For project-scoped keys, use the project endpoint (packages/next/pages/api/conat/project.ts).
2626
const response: { account_id: string; server_time: number } = {
2727
account_id: account_id ?? "",
2828
server_time: Date.now(),

src/python/cocalc-api/tests/conftest.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,30 @@ def validate_api_key_config(hub):
126126
For account-scoped keys, requires COCALC_PROJECT_ID to be set.
127127
For project-scoped keys, no additional configuration needed.
128128
"""
129+
scope = None
130+
hub_error = None
131+
132+
# First, try the hub endpoint (works only for account-scoped keys)
129133
try:
130134
scope = hub.system.test()
131135
except Exception as e:
132-
pytest.fail(f"Failed to determine API key scope: {e}")
136+
hub_error = e
137+
138+
# If hub check failed, fall back to the project endpoint so project-scoped keys work
139+
if scope is None:
140+
try:
141+
project_client = Project(
142+
api_key=hub.api_key,
143+
host=hub.host,
144+
project_id=os.environ.get("COCALC_PROJECT_ID"),
145+
)
146+
scope = project_client.system.test()
147+
except Exception as project_error:
148+
pytest.fail(
149+
"Failed to determine API key scope using both hub and project endpoints:\n"
150+
f" hub error: {hub_error}\n"
151+
f" project error: {project_error}"
152+
)
133153

134154
is_account_scoped = "account_id" in scope
135155
is_project_scoped = "project_id" in scope

0 commit comments

Comments
 (0)