docs(phase-6.1): update tracking after wave 1 — both plans complete
11 tests passing (7 shares + 4 audit), 309 total, 0 failures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
---
|
||||
plan: 06.1-01
|
||||
title: Promote test_shares.py stubs to real tests (SHARE-01..05)
|
||||
wave: 1
|
||||
depends_on: []
|
||||
phase: "6.1"
|
||||
requirements_addressed: [SHARE-01, SHARE-02, SHARE-03, SHARE-04, SHARE-05]
|
||||
files_modified:
|
||||
- backend/tests/test_shares.py
|
||||
- backend/tests/conftest.py
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
# Plan 06.1-01 — Promote test_shares.py stubs to real tests
|
||||
|
||||
## Objective
|
||||
|
||||
The backend shares API (`api/shares.py`) is fully implemented (POST /api/shares, GET /api/shares, GET /api/shares/received, DELETE /api/shares/{id}) but `test_shares.py` contains only 7 xfail stubs that call `pytest.xfail("not implemented yet")`. This plan replaces every stub with a real test that asserts correct behaviour.
|
||||
|
||||
## Context
|
||||
|
||||
- Backend implementation: `backend/api/shares.py` — fully implemented in Phase 4 Plan 04-04
|
||||
- Frontend implementation: `frontend/src/views/SharedView.vue` + `frontend/src/components/layout/AppSidebar.vue` — both complete
|
||||
- Test stubs: `backend/tests/test_shares.py` — 7 tests all call `pytest.xfail("not implemented yet")`
|
||||
- Test infrastructure: `backend/tests/conftest.py` provides `async_client`, `auth_user`, `admin_user`, `db_session`
|
||||
|
||||
**The shares tests need a second user.** The existing `auth_user` fixture creates one user per test. Sharing requires a sharer and a recipient. A `second_auth_user` fixture must be added to conftest.py.
|
||||
|
||||
## Tasks
|
||||
|
||||
---
|
||||
|
||||
### Task 1 — Add `second_auth_user` fixture to conftest.py
|
||||
|
||||
<read_first>
|
||||
- backend/tests/conftest.py — read the full `auth_user` fixture (lines 186-226) to copy the exact pattern
|
||||
</read_first>
|
||||
|
||||
<action>
|
||||
In `backend/tests/conftest.py`, add a new `second_auth_user` fixture immediately after the `auth_user` fixture (after line 226). It must:
|
||||
- Import and create a second User with role="user", is_active=True, password_must_change=False
|
||||
- Use a distinct handle: `f"user2_{user_id.hex[:8]}"` and email: `f"user2_{user_id.hex[:8]}@example.com"`
|
||||
- Create a Quota row: limit_bytes=104857600, used_bytes=0
|
||||
- Return the same dict shape: `{"user": user, "token": token, "headers": {"Authorization": f"Bearer {token}"}}`
|
||||
- Use `create_access_token(str(user_id), "user")` for the token
|
||||
</action>
|
||||
|
||||
<acceptance_criteria>
|
||||
- `conftest.py` contains `async def second_auth_user(db_session: AsyncSession)` decorated with `@pytest_asyncio.fixture`
|
||||
- The fixture handle prefix is `user2_` (distinct from `testuser_` in auth_user)
|
||||
- No duplicate imports — reuse existing imports at the top of conftest.py
|
||||
</acceptance_criteria>
|
||||
|
||||
---
|
||||
|
||||
### Task 2 — Implement real tests in test_shares.py
|
||||
|
||||
Replace all 7 stub bodies in `backend/tests/test_shares.py`. Each stub currently calls `pytest.xfail("not implemented yet")`. Replace the content of each test and add the `second_auth_user` parameter where two users are needed. Remove the `import os` (unused) and add the necessary imports.
|
||||
|
||||
<read_first>
|
||||
- backend/tests/test_shares.py — full file (stubs to replace)
|
||||
- backend/api/shares.py — endpoint request/response shapes
|
||||
- backend/db/models.py — Document and Share model fields (lines 162-263)
|
||||
- backend/tests/test_documents.py — pattern for creating Document ORM rows directly (lines 55-75)
|
||||
</read_first>
|
||||
|
||||
<action>
|
||||
Rewrite `backend/tests/test_shares.py` entirely. Add the necessary imports at the top:
|
||||
|
||||
```
|
||||
from __future__ import annotations
|
||||
import uuid as _uuid
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
```
|
||||
|
||||
Add a module-level helper `async def _make_doc(db_session, owner_user)` that creates and commits an `uploaded` Document row owned by `owner_user["user"]` — same pattern as test_documents.py: insert `Document(id=..., user_id=owner_user["user"].id, filename="test.txt", object_key=f"...", size_bytes=1000, status="uploaded")` using `db_session.add` + `await db_session.commit()`. Returns the str(doc_id).
|
||||
|
||||
Then implement each test without any `@pytest.mark.xfail` decorator (remove them all):
|
||||
|
||||
**test_share_success(async_client, auth_user, second_auth_user, db_session)**
|
||||
- Create a doc owned by auth_user via `_make_doc`
|
||||
- POST /api/shares with `{"document_id": doc_id, "recipient_handle": second_auth_user["user"].handle}`
|
||||
- Assert response status 201
|
||||
- Assert response body contains `"id"`, `"document_id"` == doc_id, `"recipient_id"` == str(second_auth_user["user"].id)
|
||||
- GET /api/shares/received with second_auth_user headers
|
||||
- Assert response 200 and the doc appears in items
|
||||
|
||||
**test_share_handle_not_found(async_client, auth_user, db_session)**
|
||||
- Create a doc owned by auth_user
|
||||
- POST /api/shares with `{"document_id": doc_id, "recipient_handle": "nonexistent_handle_xyz"}`
|
||||
- Assert status 404
|
||||
|
||||
**test_shared_with_me(async_client, auth_user, second_auth_user, db_session)**
|
||||
- Create a doc owned by auth_user
|
||||
- POST /api/shares to share with second_auth_user
|
||||
- GET /api/shares/received with second_auth_user headers
|
||||
- Assert status 200
|
||||
- Assert items list has at least one entry
|
||||
- Assert the first item has keys: "id", "filename", "content_type", "size_bytes", "created_at", "owner_handle"
|
||||
- Assert "extracted_text" is NOT a key in any item (T-04-04-03)
|
||||
- Assert item["owner_handle"] == auth_user["user"].handle
|
||||
|
||||
**test_share_no_quota_impact(async_client, auth_user, second_auth_user, db_session)**
|
||||
- Ensure second_auth_user has a Quota row with used_bytes=0 (the fixture already does this)
|
||||
- Create doc owned by auth_user, share with second_auth_user
|
||||
- GET /api/auth/me/quota with second_auth_user headers
|
||||
- Assert status 200
|
||||
- Assert quota["used_bytes"] == 0 (sharing does not charge recipient quota — T-04-04-04)
|
||||
|
||||
**test_revoke_share(async_client, auth_user, second_auth_user, db_session)**
|
||||
- Create doc, share with second_auth_user, capture share id from 201 response
|
||||
- DELETE /api/shares/{share_id} with auth_user headers
|
||||
- Assert 204
|
||||
- GET /api/shares/received with second_auth_user headers
|
||||
- Assert the revoked doc no longer appears in items
|
||||
|
||||
**test_share_revoke_wrong_owner_404(async_client, auth_user, second_auth_user, db_session)**
|
||||
- Create doc, share with second_auth_user, capture share id
|
||||
- DELETE /api/shares/{share_id} with second_auth_user headers (recipient, NOT owner)
|
||||
- Assert 404 (IDOR protection: 404, not 403 — T-04-04-02)
|
||||
|
||||
**test_share_duplicate(async_client, auth_user, second_auth_user, db_session)**
|
||||
- Create doc, share with second_auth_user (first share, 201)
|
||||
- POST /api/shares with same doc_id + same recipient_handle again
|
||||
- Assert 409
|
||||
</action>
|
||||
|
||||
<acceptance_criteria>
|
||||
- `test_shares.py` has zero `pytest.xfail` calls — every test has real assertions
|
||||
- `test_shares.py` has zero `@pytest.mark.xfail` decorators
|
||||
- `import os` is removed (was unused)
|
||||
- Every test function has the `@pytest.mark.asyncio` decorator OR the file has `pytestmark = pytest.mark.asyncio` at the top
|
||||
- Running `docker compose exec backend python -m pytest tests/test_shares.py -v` shows 7 PASSED (no XFAIL, no XPASS)
|
||||
- `test_share_no_quota_impact` asserts `used_bytes == 0` for recipient
|
||||
- `test_shared_with_me` asserts `"extracted_text" not in item` for each item in the response
|
||||
- `test_share_revoke_wrong_owner_404` asserts status code 404
|
||||
</acceptance_criteria>
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
```
|
||||
docker compose exec backend python -m pytest tests/test_shares.py -v
|
||||
```
|
||||
|
||||
Expected: 7 passed, 0 failed, 0 xfailed, 0 xpassed.
|
||||
|
||||
## Must-haves
|
||||
|
||||
- No test uses `pytest.xfail("not implemented yet")` — all 7 stubs replaced with real assertions
|
||||
- `second_auth_user` fixture creates a user with quota row and valid JWT, same pattern as `auth_user`
|
||||
- `test_share_no_quota_impact` proves SHARE-02 quota isolation: recipient quota unchanged after share
|
||||
- `test_shared_with_me` proves SHARE-02 visibility: recipient sees doc in /received
|
||||
- `test_share_revoke_wrong_owner_404` proves IDOR protection is tested
|
||||
Reference in New Issue
Block a user