Arag / tests /TEST_PLAN.md
AuthorBot
Unify token tracking, smart links, and personality settings across admin and chat.
a825fee
|
Raw
History Blame Contribute Delete
27.3 kB
# Author RAG β€” Master Test Plan
**Version:** 1.0
**Date:** 2026-06-19
**Status:** Active β€” pre-implementation baseline
**Owner:** Engineering
**Scope:** Full platform β€” SuperAdmin, Author Admin, Widget/Chat, RAG pipeline, analytics, notifications
---
## 1. Purpose & Goals
This document defines the complete verification strategy for Author RAG before any feature is considered production-ready. The platform's success depends on **100% consistency** across:
- Token accounting (single source of truth, enforced everywhere)
- Conversion UX (Buy Book CTA visible when configured and appropriate)
- Human tone (no robotic fallbacks; personality settings honored)
- Role isolation (SuperAdmin, Author, Visitor)
- Analytics integrity (every turn tracked; failures never break chat)
### 1.1 Known Critical Gaps (must pass after fix)
| ID | Gap | Impact | Primary Files |
|----|-----|--------|---------------|
| G-01 | `client_access.tokens_used` never incremented on chat | Admin & SuperAdmin show 0% usage | `tracker.py`, `admin/router.py`, `superadmin_service.py` |
| G-02 | Redis `tokens:{author_id}:current` incremented but never read | Parallel orphan counter | `tracker.py`, `subscription.py` |
| G-03 | `budget_exhausted:{author_id}` never set | Budget enforcement non-functional | `subscription.py`, `tracker.py` |
| G-04 | Admin `token-usage` ignores `bonus_tokens`; embed-token includes them | Inconsistent UI numbers | `admin/router.py` |
| G-05 | Smart Links save to `books.buy_url`; chat reads `links.purchase_url` | Buy Book button never appears | `admin/router.py`, `rag_pipeline.py`, `link_repo.py` |
| G-06 | `response_style` saved but not injected into prompts | Personality page is cosmetic | `prompter.py`, `rag_pipeline.py` |
| G-07 | Default fallback contains robotic phrase filtered by hack | Inconsistent fallback behavior | `rag_pipeline.py`, `user.py` |
| G-08 | `chatbot_is_active` not checked on chat | Suspended authors still chat | `subscription.py`, `chat.py` |
| G-09 | Token warning emails use placeholder `tokens_used=0` | Alerts never fire | `tasks/` |
| G-10 | No automated tests; CI runs `pytest \|\| true` | Regressions undetected | `.github/workflows/ci.yml` |
---
## 2. Test Strategy
### 2.1 Pyramid
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ E2E (5%) β”‚ Playwright β€” widget + admin SPAs
β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”
β”‚ Integration β”‚ FastAPI TestClient + real Redis/DB (15%)
β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”
β”‚ Unit (80%) β”‚ Pipeline, upsell, formatter, token math
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```
### 2.2 Environments
| Environment | DB | Redis | OpenAI | Purpose |
|-------------|-----|-------|--------|---------|
| **Unit** | SQLite in-memory / mocks | fakeredis | Mocked | Fast CI, logic only |
| **Integration** | PostgreSQL test DB | Redis container | Mocked or recorded fixtures | API contracts, token flow |
| **Staging** | Production-like PG | Production-like Redis | Real (limited budget) | Pre-release smoke |
| **Production** | Live | Live | Live | Synthetic monitors only |
### 2.3 Tooling
| Layer | Tool | Location |
|-------|------|----------|
| Unit / Integration | `pytest`, `pytest-asyncio`, `httpx`, `factory-boy` | `tests/unit/`, `tests/integration/` |
| BDD (acceptance) | `behave` | `tests/bdd/features/` (from `.agent/BDD_SPECS.md`) |
| E2E | Playwright (recommended) | `tests/e2e/` |
| Load | `locust` (optional) | `tests/load/` |
| Contract | OpenAPI schema validation | `tests/contract/` |
### 2.4 CI Gate (target state)
```yaml
# Fail CI on any test failure β€” remove `|| true`
pytest tests/ -v --cov=app --cov-fail-under=80
behave tests/bdd/features/
```
### 2.5 Test Data Fixtures
| Fixture | Description |
|---------|-------------|
| `superadmin_user` | Seeded from `SUPERADMIN_EMAIL` / `SUPERADMIN_PASSWORD` |
| `author_with_grant` | Author + active `client_access` + 500K token budget |
| `author_with_books` | 1–3 books in `ready` status with embeddings |
| `subscription_token` | Valid HMAC token for widget |
| `book_with_links` | `links` row with `purchase_url` + `preview_url` |
| `book_with_buy_url_only` | `books.buy_url` set, no `links` row (regression case) |
---
## 3. Architecture Verification Map
Every test suite maps to a subsystem. No subsystem ships without coverage.
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Visitor (Widget) β”‚
β”‚ widget.js β†’ /api/chat/{slug} β†’ subscription validation β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ RAG Pipeline (12 steps) β”‚
β”‚ guardrails β†’ intent β†’ session β†’ rewrite β†’ retrieve β†’ rerank β”‚
β”‚ β†’ context β†’ LLM β†’ faithfulness β†’ boundary β†’ upsell β†’ format β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β–Ό β–Ό β–Ό
analytics_core client_access links/books
(tracker, agg) (token budget) (purchase URLs)
β”‚ β”‚ β”‚
β–Ό β–Ό β–Ό
Author Admin UI SuperAdmin UI Celery tasks
```
---
## 4. Test Suites
### Suite A β€” Token Usage Consistency (P0 β€” BLOCKING)
**Objective:** One canonical token counter drives enforcement, admin UI, SuperAdmin UI, emails, and exports.
#### A.1 Single Source of Truth
| Test ID | Scenario | Steps | Expected |
|---------|----------|-------|----------|
| A-01 | Chat increments `tokens_used` | Send 1 chat message with known token usage (mock OpenAI returns `prompt_tokens=100, completion_tokens=50`) | `client_access.tokens_used` += 150 |
| A-02 | Chat increments Redis counter | Same as A-01 | `tokens:{author_id}:current` += 150 |
| A-03 | Counters stay in sync | Send 10 messages | `tokens_used` == Redis counter == sum of `chat_messages` tokens for period |
| A-04 | Regeneration counts tokens | Hallucination triggers regen (2 LLM calls) | Both calls' tokens counted |
| A-05 | Custom Q&A path counts tokens | Message hits custom Q&A override | Tokens still recorded if LLM called |
| A-06 | Failed LLM does not increment | OpenAI timeout / 500 | No token increment; error logged |
| A-07 | Analytics failure does not block chat | Mock DB write failure in `track_turn` | Visitor gets response; error logged at ERROR |
#### A.2 Budget Math (must be identical everywhere)
| Test ID | Formula Under Test | Surfaces |
|---------|-------------------|----------|
| A-10 | `total_budget = token_budget + bonus_tokens` | SuperAdmin grants, embed-token |
| A-11 | `remaining = total_budget - tokens_used` | Admin token page, embed-token, SuperAdmin |
| A-12 | `percent_used = tokens_used / total_budget * 100` | Admin gauge, SuperAdmin progress bar |
| A-13 | Bonus tokens increase remaining without changing expiry | SuperAdmin bonus modal |
**Consistency matrix (all must match after each chat turn):**
| Surface | Endpoint / Store | Includes Bonus? | Reads `tokens_used`? |
|---------|------------------|-----------------|----------------------|
| Author Token Usage page | `GET /api/admin/{slug}/token-usage` | MUST | MUST |
| Author Embed Token card | `GET /api/admin/{slug}/embed-token` | MUST | MUST |
| SuperAdmin Token Management | `GET /api/super/grants` | MUST | MUST |
| SuperAdmin Platform Analytics | `GET /api/super/health` or analytics | MUST | MUST |
| Subscription validation | `validate_subscription_token` | MUST | MUST |
| Analytics export CSV | `GET /api/admin/{slug}/export/analytics` | N/A (historical) | Aggregated from events |
| Weekly digest email | Celery task | MUST | MUST |
#### A.3 Budget Enforcement
| Test ID | Scenario | Expected |
|---------|----------|----------|
| A-20 | Usage at 79% | Chat works normally |
| A-21 | Usage at 80% | Warning email sent (if `notify_token_alerts=true`) within 60s |
| A-22 | Usage at 99% | Chat still works; UI shows red/warning state |
| A-23 | Usage at 100% | `budget_exhausted:{author_id}` set; next message returns graceful fallback (HTTP 200 per BDD), OpenAI NOT called |
| A-24 | SuperAdmin adds bonus at 100% | `budget_exhausted` cleared; chat resumes |
| A-25 | SuperAdmin reset tokens | `tokens_used=0`; policy on `bonus_tokens` documented and tested |
| A-26 | Extend subscription at 100% usage | Expiry extended; usage NOT reset unless explicitly reset |
#### A.4 SuperAdmin Token Operations
| Test ID | Action | Verify |
|---------|--------|--------|
| A-30 | Grant new subscription | `token_budget` set per plan; `tokens_used=0` |
| A-31 | Add bonus tokens | `bonus_tokens` incremented; audit log entry |
| A-32 | Reset token usage | `tokens_used=0`; Redis counters cleared; audit log |
| A-33 | Revoke subscription | Chat blocked (403); Redis blacklist set |
| A-34 | Token Management UI totals | Sum of all grants' `tokens_used` matches platform aggregate |
#### A.5 Author Admin Token Page
| Test ID | Scenario | Expected |
|---------|----------|----------|
| A-40 | Page load after 5 chats | Gauge shows non-zero `used` matching DB |
| A-41 | No active grant | Shows zeros with clear empty state (not error) |
| A-42 | Gauge color thresholds | <75% indigo, 75–90% amber, >90% red |
| A-43 | Matches embed-token card | Same `remaining` value on both pages |
---
### Suite B β€” Buy Book Button & Smart Links (P0 β€” BLOCKING)
**Objective:** When an author configures a purchase URL, the widget shows a "Buy Book" button at the correct times.
#### B.1 Data Model Unification
| Test ID | Scenario | Expected |
|---------|----------|----------|
| B-01 | Author saves Smart Link (`PUT /smart-links/{bookId}`) | `links.purchase_url` AND `books.buy_url` both set (or single source with fallback read) |
| B-02 | Author sets buy URL on book upload | URL persisted and available to chat |
| B-03 | `_get_book_links()` with only `books.buy_url` | Returns purchase URL (fallback) |
| B-04 | `_get_book_links()` with only `links` row | Returns purchase URL |
| B-05 | Preview URL | "Read Preview" button appears when configured (max 2 links) |
#### B.2 Upsell Engine β€” When Button Appears
| Test ID | Intent / Context | Turn | Expected `show_link` |
|---------|------------------|------|----------------------|
| B-10 | `purchase_intent` ("Where can I buy?") | 1 | `true` + Buy Book button in widget |
| B-11 | `full_story_request` | any | `true` |
| B-12 | `question`, book selected | 2+ | `true` (per `should_include_link`) |
| B-13 | `question`, high interest (β‰₯0.45) | 2+ | `true` |
| B-14 | `complaint` | any | `false` β€” no buy button |
| B-15 | `greeting` | 1 | `false` (RECIPROCITY only, no button) |
| B-16 | Strategy `DIRECT_CTA` | any | `true` |
| B-17 | No purchase URL configured | any | `false` β€” no empty/broken button |
| B-18 | `show_link=true` but URL is `#` | any | Button must NOT render (invalid URL guard) |
#### B.3 Widget Rendering
| Test ID | Scenario | Expected |
|---------|----------|----------|
| B-20 | API returns `links: [{label:"Buy Book", url:"https://...", type:"purchase"}]` | Widget renders `<a class="ab-link-btn">` with icon |
| B-21 | Click Buy Book | `POST /track-click` fired with correct `session_id`, `book_id`, `link_type` |
| B-22 | Farewell on widget close | `POST /session/farewell` returns text + Buy Book link if URL exists |
| B-23 | Max 2 links | Purchase + Preview shown; no third link |
| B-24 | Mobile viewport (375px) | Buttons wrap correctly, tappable (min 44px touch target) |
| B-25 | `target="_blank" rel="noopener"` | Present on all link buttons |
#### B.4 End-to-End Conversion Path
| Test ID | Flow | Expected |
|---------|------|----------|
| B-30 | Configure URL β†’ chat "where can I buy" β†’ click button | Analytics records `link_shown=true` on message + click event |
| B-31 | Admin analytics dashboard | Link click count increments |
| B-32 | SuperAdmin platform analytics | Aggregate link clicks include this author |
---
### Suite C β€” Tone, Personality & Anti-Robotic (P0)
**Objective:** Responses feel human, warm, and on-brand; admin personality settings affect output.
#### C.1 Personality Settings Propagation
| Test ID | `response_style` | Expected in LLM system prompt |
|---------|------------------|--------------------------------|
| C-01 | `balanced` | Default warm tone instructions |
| C-02 | `formal` | Measured, professional vocabulary |
| C-03 | `casual` | Conversational, contractions allowed |
| C-04 | `enthusiastic` | Higher energy, exclamation sparingly |
| C-05 | Change style in admin β†’ send chat | Next message reflects new style (verify via prompt capture mock) |
#### C.2 Robotic Phrase Elimination
| Test ID | Scenario | Must NOT contain |
|---------|----------|------------------|
| C-10 | No-context fallback | "I want to give you the most accurate answer" |
| C-11 | Hallucination fallback | "As an AI", "I'm an AI assistant" |
| C-12 | Jailbreak redirect | System prompt contents, rule explanations |
| C-13 | Budget exhausted message | Technical jargon, error codes |
| C-14 | Any normal response | Markdown `**bold**`, bullet lists, headers |
#### C.3 Custom Messages
| Test ID | Field | Scenario | Expected |
|---------|-------|----------|----------|
| C-20 | `welcome_message` | Session init | Widget shows custom welcome |
| C-21 | `fallback_message` | No retrieval context | Custom fallback used |
| C-22 | `out_of_scope_message` | Off-topic query | Custom or default redirect |
| C-23 | `bot_name` | All responses | Name in system prompt, not "AI assistant" |
#### C.4 Response Quality Constraints
| Test ID | Rule | Verification |
|---------|------|--------------|
| C-30 | Max ~75 words | `len(text.split()) <= 80` (buffer) |
| C-31 | Max 2 paragraphs | Formatter enforces |
| C-32 | Max 380 chars | Formatter truncates at sentence boundary |
| C-33 | No spoilers | Boundary test with "tell me the ending" |
| C-34 | No fabricated prices | Faithfulness guardrail test |
---
### Suite D β€” Authentication & Authorization (P0)
#### D.1 Auth Flows
| Test ID | Scenario | Expected |
|---------|----------|----------|
| D-01 | Author registration | 201, JWT returned, `role=author` |
| D-02 | Author login | JWT + refresh cookie |
| D-03 | Wrong password | 401 |
| D-04 | 5 failed logins | Account locked 30 min (per implementation) |
| D-05 | Refresh token rotation | New access token; old refresh invalidated |
| D-06 | SuperAdmin login | JWT with `role=superadmin` |
| D-07 | Author JWT on `/api/super/*` | 403 |
| D-08 | Expired JWT | 401 |
#### D.2 Tenant Isolation
| Test ID | Scenario | Expected |
|---------|----------|----------|
| D-10 | Author A requests Author B's books | 403 or empty (slug must match `current_user.id`) |
| D-11 | Author A uses B's slug in URL | Must NOT return B's data |
| D-12 | SuperAdmin lists all authors | Returns cross-tenant data (allowed) |
#### D.3 Subscription Token Security
| Test ID | Scenario | Expected |
|---------|----------|----------|
| D-20 | Valid token | Chat 200 |
| D-21 | Tampered HMAC | 403 |
| D-22 | Expired token | 403 |
| D-23 | Revoked token (Redis) | 403 immediately |
| D-24 | Revoked in DB | 403 |
| D-25 | Missing `X-Subscription-Token` header | 401/403 |
#### D.4 Suspension & Active State
| Test ID | Scenario | Expected |
|---------|----------|----------|
| D-30 | SuperAdmin suspends author | `chatbot_is_active=false` |
| D-31 | Suspended author β€” visitor chats | 403 with friendly message |
| D-32 | SuperAdmin unsuspends | Chat resumes |
---
### Suite E β€” RAG Pipeline (P1)
#### E.1 Pipeline Steps (integration)
| Test ID | Step | Verification |
|---------|------|--------------|
| E-01 | Guardrails | Jailbreak input β†’ redirect, not LLM |
| E-02 | Intent classification | "where can I buy" β†’ `purchase_intent` |
| E-03 | Book selector | 3 books + vague query β†’ selector popup in response |
| E-04 | Single book | Auto-select, no popup |
| E-05 | Book name in query | Fuzzy match >0.85 β†’ auto-select |
| E-06 | Faithfulness fail | Regenerate once; then safe fallback |
| E-07 | Boundary enforcer | Off-topic β†’ redirect |
| E-08 | Custom Q&A override | Exact match returns canned answer |
#### E.2 Book Lifecycle
| Test ID | Scenario | Expected |
|---------|----------|----------|
| E-10 | Upload PDF | Status: uploading β†’ parsing β†’ chunking β†’ embedding β†’ ready |
| E-11 | File >50MB | Rejected before processing |
| E-12 | Unsupported .xlsx | 400 with supported formats listed |
| E-13 | Deactivate book mid-session | Next message offers remaining books |
| E-14 | Delete last active book | Chatbot disabled + dashboard warning |
| E-15 | Book `status != ready` | Graceful "still indexing" message |
---
### Suite F β€” Admin Panel (P1)
#### F.1 Dashboard & Analytics
| Test ID | Page | Verify |
|---------|------|--------|
| F-01 | Dashboard | Session count, recent activity load |
| F-02 | Analytics (30d) | Charts match `analytics_daily` rollups |
| F-03 | Sessions / Readers | Geo, fingerprint (no PII) |
| F-04 | Conversations | Transcript matches `chat_messages` |
| F-05 | Export CSV | Downloads valid CSV with correct columns |
#### F.2 Configuration Pages
| Test ID | Page | Verify |
|---------|------|--------|
| F-10 | Books CRUD | Create, edit, reorder, activate/deactivate |
| F-11 | Smart Links | Save, load, URL validation |
| F-12 | Widget Config | Theme, position persist to session init |
| F-13 | Personality | All fields save and reload |
| F-14 | Notifications toggles | `notify_token_alerts` respected by email tasks |
| F-15 | Custom Q&A | CRUD + priority ordering |
| F-16 | Embed Code | Valid script tag with live token |
#### F.3 UI Consistency
| Test ID | Check | Expected |
|---------|-------|----------|
| F-20 | All API errors | Toast/alert with human-readable message |
| F-21 | Loading states | Skeleton/spinner on every async page |
| F-22 | Empty states | Helpful copy, not blank screens |
| F-23 | Form validation | Client + server validation aligned |
| F-24 | Navigation | Active nav item highlights correctly |
---
### Suite G β€” SuperAdmin Panel (P1)
| Test ID | Feature | Verify |
|---------|---------|--------|
| G-01 | Authors list | Pagination, search, status badges |
| G-02 | Author detail | Books, grant status, token bar |
| G-03 | Grant access | Token generated, email sent, audit logged |
| G-04 | Revoke / extend / bonus / reset | Each writes audit log + correct DB state |
| G-05 | Audit log | Immutable, chronological, filterable |
| G-06 | Platform health | DB, Redis, Chroma status |
| G-07 | Announcements | Delivered to author notifications |
| G-08 | Token Management totals | Match sum of individual grants (see Suite A) |
| G-09 | Suspend / unsuspend | Immediate effect on chat (Suite D) |
---
### Suite H β€” Widget & Session (P1)
| Test ID | Scenario | Expected |
|---------|----------|----------|
| H-01 | Session init | `bot_name`, `welcome_message`, `widget_theme` applied |
| H-02 | Multi-turn history | Context preserved across turns |
| H-03 | Session reset | History cleared, new session ID |
| H-04 | Rating (1–5 stars) | Stored on `chat_sessions` |
| H-05 | Feedback submission | Stored and visible in admin |
| H-06 | SSE ingestion events | Progress events during book upload |
| H-07 | Rate limiting | 61st request/min per IP β†’ 429 |
| H-08 | Widget <8KB | `widget.js` size budget met |
| H-09 | Offline / API error | User-friendly error bubble, retry option |
---
### Suite I β€” Email & Celery Tasks (P2)
| Test ID | Task | Trigger | Verify |
|---------|------|---------|--------|
| I-01 | Token 80% warning | Usage threshold | Email with correct numbers |
| I-02 | Subscription expiry 7d | Daily beat | Warning email |
| I-03 | Weekly digest (Monday 9am) | Beat + timezone | Sessions, top book, tokens, country |
| I-04 | Grant notification | SuperAdmin grant | Author receives access email |
| I-05 | Revocation notification | SuperAdmin revoke | Author notified within 5s |
| I-06 | Link health check | Scheduled | `purchase_url_ok` updated |
| I-07 | Analytics aggregation | Hourly | `analytics_daily` matches raw events |
---
### Suite J β€” Security & Edge Cases (P1)
| Test ID | Scenario | Expected |
|---------|----------|----------|
| J-01 | SQL injection in chat message | Sanitized; no DB error |
| J-02 | XSS in author `welcome_message` | Escaped in widget (`escHtml`) |
| J-03 | SSRF via buy URL | URL validation blocks internal IPs |
| J-04 | Path traversal on upload | Rejected |
| J-05 | CORS | Only configured origins |
| J-06 | Security headers | Present on all responses |
| J-07 | `/api/super/diag` | Must require auth (if not public by design) |
| J-08 | `/api/widget/token` | Must not issue tokens without author auth |
| J-09 | Concurrent chat + grant revoke | No race β€” revoke wins |
| J-10 | Redis down | Graceful degradation per layer (documented) |
---
## 5. Cross-Cutting Consistency Checklist
Run this checklist after **every** release candidate:
### 5.1 Numeric Consistency
- [ ] Send exactly 1 chat message with known token count
- [ ] Verify `client_access.tokens_used`
- [ ] Verify Redis `tokens:{author_id}:current`
- [ ] Verify Author Admin β†’ Token Usage page
- [ ] Verify Author Admin β†’ Embed Token card
- [ ] Verify SuperAdmin β†’ Token Management row
- [ ] Verify SuperAdmin β†’ Platform Analytics total
- [ ] Verify `chat_messages` sum for session
- [ ] Verify `analytics_events` row for turn
### 5.2 Buy Button Consistency
- [ ] Configure buy URL in Smart Links
- [ ] Ask "where can I buy this book?"
- [ ] Confirm `link_shown=true` in DB
- [ ] Confirm button visible in widget DOM
- [ ] Click button β†’ analytics event recorded
- [ ] Close widget β†’ farewell shows Buy Book
### 5.3 Tone Consistency
- [ ] Set personality to `casual` β†’ verify prompt
- [ ] Trigger no-context path β†’ no robotic phrases
- [ ] Trigger hallucination fallback β†’ warm safe message
- [ ] Verify no markdown in any response path
---
## 6. Edge Case Catalog
| Category | Edge Case | Expected Behavior |
|----------|-----------|-------------------|
| **Tokens** | Author with 0 budget grant | Clear error on grant; chat blocked |
| **Tokens** | Multiple rapid messages | Atomic increment; no lost counts |
| **Tokens** | Month rollover | Usage resets per billing period (define policy) |
| **Tokens** | SuperAdmin reset during active chat | Next message uses fresh budget |
| **Links** | URL with UTM params | Preserved exactly |
| **Links** | International domain (IDN) | Validated and stored |
| **Links** | HTTP vs HTTPS | HTTPS preferred; mixed content warning in admin |
| **Books** | 0 active books | Catalog setup message |
| **Books** | 50+ books | Selector pagination / scroll |
| **Chat** | Empty message | 400 validation error |
| **Chat** | 2000+ char message | Truncated or rejected gracefully |
| **Chat** | Unicode / emoji | Handled end-to-end |
| **Chat** | RTL language input | Widget displays correctly |
| **Session** | Same fingerprint, new session | Separate session IDs |
| **Auth** | Token refresh during admin action | Seamless retry |
| **Upload** | Duplicate SHA-256 | Skip/replace dialog |
| **Upload** | Network drop mid-upload | Resume from chunk (if implemented) |
---
## 7. Acceptance Criteria (Definition of Done)
A feature or fix is **DONE** only when:
1. All P0 tests in the relevant suite pass in CI
2. No new gaps introduced in the consistency matrix (Section 5)
3. BDD scenario added to `tests/bdd/features/` BEFORE merge (per `.agent/RULES.md`)
4. Unit test coverage β‰₯80% on touched modules
5. Manual smoke on staging: Author flow + Visitor flow + SuperAdmin flow
6. Audit log entry verified for every SuperAdmin mutation
7. Analytics event verified for every chat turn
8. Documentation updated if API contract changes
### Release Blockers (cannot ship if any fail)
- [ ] Suite A β€” all P0 tests green
- [ ] Suite B β€” all P0 tests green
- [ ] Suite C β€” all P0 tests green
- [ ] Suite D β€” D-10, D-11, D-30, D-31 green
- [ ] CI no longer uses `|| true`
---
## 8. Implementation Roadmap for Test Infrastructure
### Phase 1 β€” Foundation (Week 1)
1. Create `tests/conftest.py` with async DB, Redis, JWT helpers
2. Add factories: `UserFactory`, `ClientAccessFactory`, `BookFactory`, `LinkFactory`
3. Mock OpenAI client fixture returning deterministic token counts
4. Fix CI: remove `|| true`; add PostgreSQL + Redis services
### Phase 2 β€” P0 Unit Tests (Week 1–2)
1. `test_token_tracking.py` β€” Suite A
2. `test_upsell_engine.py` β€” Suite B (link logic)
3. `test_formatter.py` β€” link injection, max links
4. `test_prompter_personality.py` β€” Suite C
5. `test_subscription_validation.py` β€” Suite D
### Phase 3 β€” Integration Tests (Week 2–3)
1. `test_chat_api.py` β€” full message flow with mocked LLM
2. `test_admin_token_usage.py` β€” API response consistency
3. `test_superadmin_grants.py` β€” grant/bonus/reset flows
4. `test_smart_links_sync.py` β€” books ↔ links unification
### Phase 4 β€” BDD & E2E (Week 3–4)
1. Port `.agent/BDD_SPECS.md` β†’ `tests/bdd/features/*.feature`
2. Playwright: widget buy button visible + click
3. Playwright: admin token gauge updates after chat
### Phase 5 β€” Hardening (Ongoing)
1. Synthetic production monitor: health + token math spot-check
2. Load test: 60 req/min rate limit boundary
3. Chaos: Redis restart during chat
---
## 9. Traceability Matrix
| User-Reported Issue | Test Suite | Test IDs |
|--------------------|------------|----------|
| Tokens not counted on admin | A | A-01, A-40, A-43 |
| Tokens not counted on superadmin | A | A-01, A-34, G-08 |
| Robotic tone | C | C-10, C-11, C-01–C-05 |
| Buy now button not in chat | B | B-01, B-10, B-20, B-30 |
| Personality settings ignored | C | C-01–C-05 |
| Budget not enforced | A | A-20–A-26 |
| Inconsistent token numbers | A | A-10–A-13, Section 5.1 |
---
## 10. Sign-Off
| Role | Name | Date | Signature |
|------|------|------|-----------|
| Engineering Lead | | | |
| QA | | | |
| Product | | | |
---
*This plan should be treated as a living document. Update version and traceability matrix whenever gaps G-01 through G-10 are resolved or new features are added.*