Tests for shared lists & docs catch-up to post-MVP state
Integration tests for the invite/join/membership feature that shipped without any coverage: - internal/store/liststore_test.go: CreateList adds owner as member with invite code, GetLists returns owned+joined but not foreign lists, GetList access control (owner/member yes, stranger and soft-deleted no), JoinByInviteCode normalization/idempotency/role-keeping, lazy invite code generation. Runs against TEST_DATABASE_URL, skips otherwise. - internal/httpapi/api_test.go: full E2E over the real router — register, create list (code in response), invite endpoint, join (lowercase), cross-member op push/pull sync, stranger gets 404 on every list endpoint, invalid code 400, idempotent re-join, and 401 gating of all protected routes. - lists.go Invite handler: store errors now map through apiError, so non-members get 404 instead of 400 (consistent with Get/Push/Pull). Docs updated to the actual post-MVP state: AGENTS.md (post-MVP features, repo structure, roadmap with open points like join rate limiting), API.md (join/invite endpoints, invite_code fields, membership rules), SYNC.md (shared lists section), README (local-only default, sharing, integration test recipe).
This commit is contained in:
parent
85c790ed23
commit
67033e561c
7 changed files with 692 additions and 33 deletions
32
docs/API.md
32
docs/API.md
|
|
@ -57,29 +57,36 @@ Revoke active session token. Returns `204 No Content`.
|
|||
|
||||
## Lists (`/api/lists`)
|
||||
|
||||
All list endpoints return `404 Not Found` when the authenticated user is neither
|
||||
owner nor member of the list (no membership leaks), and `401` without a valid
|
||||
session. List responses carry an `invite_code` (8 characters) for sharing.
|
||||
|
||||
### `GET /api/lists`
|
||||
Fetch all active (non-deleted) lists owned by the authenticated user.
|
||||
Fetch all active (non-deleted) lists the authenticated user owns **or** joined
|
||||
as a member.
|
||||
- **Response (200 OK)**:
|
||||
```json
|
||||
{
|
||||
"lists": [
|
||||
{ "id": "<uuid>", "name": "Wocheneinkauf", "updated_at": "2026-08-05T19:00:00Z", "hlc_ts": 177000000000000 }
|
||||
{ "id": "<uuid>", "name": "Wocheneinkauf", "invite_code": "A1B2C3D4", "updated_at": "2026-08-05T19:00:00Z", "hlc_ts": 177000000000000 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/lists`
|
||||
Create a new list.
|
||||
Create a new list. The owner is automatically added to `list_members` (role
|
||||
`owner`) and an invite code is generated.
|
||||
- **Request**: `{ "name": "Supermarkt" }`
|
||||
- **Response (201 Created)**: `{ "id": "<uuid>", "name": "Supermarkt", ... }`
|
||||
- **Response (201 Created)**: `{ "id": "<uuid>", "name": "Supermarkt", "invite_code": "A1B2C3D4", ... }`
|
||||
|
||||
### `GET /api/lists/{id}`
|
||||
Fetch list detail including items.
|
||||
Fetch list detail including items (owner or member only).
|
||||
- **Response (200 OK)**:
|
||||
```json
|
||||
{
|
||||
"id": "<uuid>",
|
||||
"name": "Wocheneinkauf",
|
||||
"invite_code": "A1B2C3D4",
|
||||
"updated_at": "...",
|
||||
"hlc_ts": 177000000000000,
|
||||
"items": [
|
||||
|
|
@ -88,10 +95,25 @@ Fetch list detail including items.
|
|||
}
|
||||
```
|
||||
|
||||
### `POST /api/lists/{id}/invite`
|
||||
Return the invite code of a list (owner or member only). Generates a code on
|
||||
the fly for legacy lists without one.
|
||||
- **Request**: empty JSON object `{}`
|
||||
- **Response (200 OK)**: `{ "invite_code": "A1B2C3D4" }`
|
||||
|
||||
### `POST /api/lists/join`
|
||||
Join a shared list using its invite code (case-insensitive). Joining again is
|
||||
idempotent; the existing membership role is kept (an owner cannot be demoted).
|
||||
- **Request**: `{ "invite_code": "a1b2c3d4" }`
|
||||
- **Response (200 OK)**: the joined list (same shape as `GET /api/lists` items).
|
||||
- **400 Bad Request**: unknown or empty invite code.
|
||||
|
||||
---
|
||||
|
||||
## Sync & Ops (`/api/lists/{id}/ops`)
|
||||
|
||||
Both endpoints require membership (owner or member); otherwise `404 Not Found`.
|
||||
|
||||
### `POST /api/lists/{id}/ops`
|
||||
Push a batch of client operations (max 100).
|
||||
- **Request**:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue