Files
kite/.planning/phases/06.1-close-v1-audit-gaps/06.1-01-PLAN.md
T
curo1305 1e4654aad5 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>
2026-05-30 23:18:04 +02:00

7.5 KiB

plan, title, wave, depends_on, phase, requirements_addressed, files_modified, autonomous
plan title wave depends_on phase requirements_addressed files_modified autonomous
06.1-01 Promote test_shares.py stubs to real tests (SHARE-01..05) 1
6.1
SHARE-01
SHARE-02
SHARE-03
SHARE-04
SHARE-05
backend/tests/test_shares.py
backend/tests/conftest.py
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>
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

<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>
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

<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