From 0cfe1a7cedca5c4c4ce0f855cc7133b5f8aeb562 Mon Sep 17 00:00:00 2001 From: jevb Date: Wed, 1 Apr 2026 15:51:50 +0200 Subject: [PATCH] chore: untrack docs/brain/ files (local-only vault) These were accidentally force-added. The vault is gitignored and should remain local-only. Files are preserved on disk. --- docs/brain/02-Tasks/Backlog.md | 670 --------- docs/brain/02-Tasks/In Progress.md | 51 - docs/brain/06-Specs/TESTING-STRATEGY.md | 1333 ------------------ docs/brain/08-Guides/Server-Configuration.md | 154 -- docs/brain/08-Guides/Testing-Tools.md | 316 ----- 5 files changed, 2524 deletions(-) delete mode 100644 docs/brain/02-Tasks/Backlog.md delete mode 100644 docs/brain/02-Tasks/In Progress.md delete mode 100644 docs/brain/06-Specs/TESTING-STRATEGY.md delete mode 100644 docs/brain/08-Guides/Server-Configuration.md delete mode 100644 docs/brain/08-Guides/Testing-Tools.md diff --git a/docs/brain/02-Tasks/Backlog.md b/docs/brain/02-Tasks/Backlog.md deleted file mode 100644 index a6b2b004..00000000 --- a/docs/brain/02-Tasks/Backlog.md +++ /dev/null @@ -1,670 +0,0 @@ -# Backlog - -**Goal:** Ship v1.2, then build gaming-native features that -differentiate OwnCord from Discord/TeamSpeak/Mumble. - -Last task ID: T-457. New tasks start at T-458. - ---- - -## Core Stability — Bugs Found 2026-03-28 (audit + Codex review) - -- [x] **T-165:** Fix BUG-046 — wrap voice `switchActiveDevice` in try-catch with device fallback — 2026-03-28 -- [x] **T-166:** Fix BUG-047 — block send until uploads complete or cancel in-flight uploads — 2026-03-28 -- [x] **T-167:** Fix BUG-048 — add client-side file size/type validation before upload (incl. paste path) — 2026-03-28 -- [x] **T-168:** Fix BUG-049 — migrate VAD from requestAnimationFrame to setTimeout — 2026-03-28 -- [x] **T-169:** Fix BUG-050 — clear stale audio elements on voice auto-reconnect — 2026-03-28 -- [x] **T-170:** Fix BUG-051 — add origin/path check to LiveKit HTTP proxy handler — 2026-03-28 -- [x] **T-171:** Fix BUG-052 — replace swallowed `.catch(() => {})` with debug/warn logging — 2026-03-28 -- [x] **T-172:** Fix BUG-053 — add TOFU fingerprint pinning to LiveKit TLS proxy — 2026-03-28 -- [x] **T-173:** Fix BUG-054 — implement account deletion (server endpoint + client UI) — 2026-03-28 - -## Cleanup — Found 2026-03-28 - -- [x] **T-182:** Fix BUG-055 — remove 4 stale vitest coverage exclusions — 2026-03-28 -- [x] **T-183:** Fix BUG-056 — fix livekit-session.test.ts proxy URL test (mock Tauri invoke) — 2026-03-28 - -## Refactoring - -- [x] **T-184:** Refactor `livekitSession.ts` — remove duplicate audio pipeline fields/methods, delegate entirely to `_audioPipeline`. 1,438 → 1,171 lines (267 lines removed) — 2026-03-29 -- [x] **T-185:** Add unit tests for delete account UI flow in `settings-overlay.test.ts` — 7 tests: trigger, cancel, password validation, callback, disabled state, error display, input clearing — 2026-03-29 - -## Code Quality — Found 2026-03-29 (full-project audit) - -- [x] **T-190:** Propagate `context.Context` from WS upgrade through all handlers — added ctx to Client struct, updated MessageHandler signature, threaded ctx through all 17 WS handlers across 9 files. Added ExecContext/QueryRowContext/QueryContext/BeginTx to DB wrapper. — 2026-03-29 -- [x] **T-191:** Add ESLint v9 config with `@typescript-eslint/no-floating-promises`, `no-unused-vars`, `consistent-return` — installed eslint v9.39.4 + typescript-eslint, created flat config, fixed 61 lint violations across 22 files — 2026-03-29 - ---- - -## 2FA Client Integration — 2026-03-29 - -### High Priority - -- [x] **T-192:** Client 2FA enrollment/disable settings UI — AccountTab TOTP section, auth store state, SettingsOverlay wiring — 2026-03-29 -- [x] **T-193:** Client 2FA test coverage — Unit tests for TOTP settings flows, api.ts TOTP methods, auth store totp_enabled state — 2026-03-29 -- [x] **T-194:** Full regression validation pass — `go test ./...`, `npm test`, `golangci-lint`, `npm run lint` — 2026-03-29 - -### Medium Priority - -- [ ] **T-195:** User profile/password/session management endpoints — PATCH /users/me, PUT /users/me/password, GET/DELETE /users/me/sessions (server-side) -- [ ] **T-196:** DM sidebar incremental DOM update — Replace full DOM rebuild at SidebarArea.ts:753 with reconciliation - -## Bugs — 2026-03-29 - -### High Priority - -- [ ] **T-202:** Admin panel tab navigation broken — Clicking Audit Logs, Members, or other tabs in the `/admin` panel does not switch pages. Likely regression from recent `handlers_settings.go` or `logstream.go` changes. Investigate JS console errors and admin static files. - -## Code Review Findings — 2026-03-29 - -### High Priority - -- [x] **T-197:** Fix double `updateUser` call on TOTP confirm/disable — Verified false positive: `updateUser` called once in MainPage.ts, `onEnrolled`/`onDisabled` only re-render UI — 2026-03-31 -- [x] **T-198:** Add TOTP audit log events — Added `database.LogAudit(...)` calls for totp_verified, totp_enabled, totp_disabled — 2026-03-31 -- [x] **T-199:** Safe default for `registration_open` on upgrade — Changed default from `false` to `true` in `isRegistrationOpen()` — 2026-03-31 -- [x] **T-200:** Extract TOTP handlers to `totp_handler.go` — Moved 4 handlers + 4 types; auth_handler.go 829 → 583 lines — 2026-03-31 - -### Medium Priority - -- [x] **T-201:** TOTP constant-time code comparison — Replaced `==` with `subtle.ConstantTimeCompare` in `totp.go:207` — 2026-03-31 - ---- - -## Project Map Dashboard Enhancements — 2026-03-31 - -*Transform `tools/project-map/` from a tool you visit into a daily driver. -Eliminate cold-start friction, capture bugs in real-time, automate scans, -and surface trends. See plan in session log for full rationale.* - -### Phase PM-1: Morning Experience (the cold-start killer) - -- [ ] **T-203:** Briefing as default landing view — Restructure dashboard.html renderSession() to lead with briefing content, not the session card. Briefing should be the top-level content on load. -- [ ] **T-204:** Last session summary inline — Enhance morning-briefing.mjs to include lastSession details: files touched, commits made, duration, modules worked on. Render as compact summary card. -- [ ] **T-205:** "Time since last session" prominent indicator — Upgrade from text greeting to colored badge (green <1d, yellow 2-3d, red >3d) with exact hours/days. -- [ ] **T-206:** Agent results surfaced at top — Move completed agent results above the session plan in briefing. Add result count badge to Session tab label. -- [ ] **T-207:** One-click "Start Session" with plan acceptance — Enhance Start Session button to auto-accept suggested plan. Add plan items to session start POST body. - -### Phase PM-2: Bug Capture During Live Testing - -- [ ] **T-208:** Persistent quick-add bug bar — Fixed-position bar at dashboard bottom (always visible). Text input + module dropdown + severity selector + submit. -- [ ] **T-209:** Bug creation backend endpoint — `POST /api/bugs` in server.mjs. New lib/bug-writer.mjs for ID generation (next T-XXX), markdown formatting, atomic file write to Backlog.md. -- [ ] **T-210:** Severity tags and auto-tagging — Support crash/broken/cosmetic severity. Auto-add `found-in-testing` tag. Update backlog-parser.mjs to recognize new tags. -- [ ] **T-211:** Mobile-friendly bug capture page — `GET /api/bugs/quick` serves minimal standalone HTML form. Responsive, large touch targets, no navigation chrome. -- [ ] **T-212:** SSE broadcast on bug creation — Broadcast `bug-created` event via SSE. Dashboard refreshes backlog data on receipt. - -### Phase PM-3: Proactive Scanning - -- [ ] **T-213:** Scan staleness configuration — New `.cache/scan-config.json` with per-scan-type day thresholds (security: 7d, code-review: 14d, debt: 3d, tests: 7d). `POST /api/scan-config` to override. -- [ ] **T-214:** Auto-scan on server start — New lib/scan-scheduler.mjs. On startup, check each module's last scan date against thresholds. Track timestamps in `.cache/scan-history.json`. -- [ ] **T-215:** Scan results auto-create backlog entries — Post-process completed agent results in agent-manager.mjs. Parse for issue indicators, auto-create backlog entries via bug-writer.mjs with `agent-discovered` tag. -- [ ] **T-216:** Dashboard "due scans" indicator — Show count of modules due for scanning in briefing. "Run all due scans" button batch-queues agent jobs for all due modules. - -### Phase PM-4: Session Polish - -- [ ] **T-217:** Session plan preview with editing — Add checkboxes to plan items. Unchecked items excluded from session. Pass accepted items to `POST /api/session/start` body. -- [ ] **T-218:** Browser tab title with session state — Update `document.title` dynamically. Active: `"▶ 47min | OwnCord"` (60s interval). Idle: `"Project Map | OwnCord"`. -- [ ] **T-219:** Session end summary with improvement metrics — Compute coverage deltas, backlog items closed (from commit task IDs), tests added (git diff stats). Display in summary modal. -- [ ] **T-220:** Smooth session auto-recovery UX — Visible notification bar when stale session detected. "Resume or discard?" with stale session stats (started at, files changed). - -### Phase PM-5: Dashboard UX - -- [ ] **T-221:** SSE-driven visible updates — Toast notifications on file-change events. Flash Agents tab on job-update. Small event log in header showing last 3 SSE events. -- [ ] **T-222:** Loading states with cached data — Show cached data from localStorage on initial load. Fetch fresh in background, swap in. Show "cached · refreshing..." indicator. -- [ ] **T-223:** Keyboard shortcuts — S: start/end session, Q: focus bug bar, N: Focus Next tab, R: refresh, 1-9: switch tabs. Help overlay on `?` key. -- [ ] **T-224:** Collapsible sections — Toggle buttons (▸/▾) on each card h2. Persist collapsed state in localStorage. -- [ ] **T-225:** Dark theme contrast audit — Verify CSS variables against WCAG AA (4.5:1). Adjust `--text-dim` and other colors if needed. - -### Phase PM-6: Agent Workflow Improvements - -- [ ] **T-226:** Batch queue endpoint — `POST /api/jobs/batch` with filter criteria (e.g. `coverage_below: 50`). Resolves filter against data, creates one job per matching module. -- [ ] **T-227:** Agent result diffing — Track result metadata (issue count, findings hash) in scan-history.json. Compare previous vs current results. Show added/removed issues in result overlay. -- [ ] **T-228:** Estimated completion indicator — Track actual job durations in scan-history.json per type. Compute rolling average. Display "~3m remaining" on running jobs. -- [ ] **T-229:** Browser notifications on job completion — Request `Notification.permission` on load. Fire `new Notification()` on completed job SSE events. Optional sound toggle. - -### Phase PM-7: Data Continuity - -- [ ] **T-230:** Weekly summary auto-generation — New lib/weekly-summary.mjs. Aggregate sessions, time, tasks closed, coverage deltas, bugs found. Write to `docs/brain/03-Sessions/weekly-YYYY-WW.md`. Trigger on server start if missing for previous week. -- [ ] **T-231:** Trend lines on dashboard — Mini sparkline charts for coverage, debt, velocity over time. Store historical snapshots in `.cache/trend-data.json` (appended each collectData, max 90 entries). -- [ ] **T-232:** Export/snapshot and restore — `GET /api/export` bundles all .cache/ JSON into single download. `POST /api/import` restores from snapshot. Dashboard button in header. - -### Phase PM-8: Small but High-Impact - -- [ ] **T-233:** Dynamic favicon — Two inline SVG favicons (cyan active, dim idle). Swap on session state change via `link[rel="icon"]`. -- [ ] **T-234:** "What did I do last week" view — Filtered History tab view for past 7 days. Aggregate files, commits, tasks, coverage changes. Auto-triggered when `daysSince > 1` in briefing. -- [ ] **T-235:** VS Code direct links — Wrap all file path displays (debt markers, churn, coverage) with `vscode://file/${ROOT}/${path}:${line}` links. Pass ROOT from server via `/api/data` response. -- [ ] **T-236:** New files: lib/bug-writer.mjs — Backlog entry creation with atomic writes, next-ID generation, markdown formatting -- [ ] **T-237:** New files: lib/scan-scheduler.mjs — Staleness tracking, threshold config, auto-scan scheduling logic -- [ ] **T-238:** New files: lib/weekly-summary.mjs — Weekly aggregate report generation and vault writing -- [ ] **T-239:** New files: .cache/scan-config.json, scan-history.json, trend-data.json — Persistent state for scanning and trends -- [ ] **T-240:** Dashboard.html size audit — If dashboard exceeds 1500 lines after all phases, split JS into separate file - ---- - -## Unified Sidebar — Deferred Items (from 2026-03-27 redesign) - -- [x] **T-161:** Relocate MemberList into unified sidebar as collapsible section — SidebarArea.ts:625-743, with resize handle and localStorage persistence — verified 2026-03-29 -- [x] **T-162:** Wire DM conversations to real data source — SidebarArea.ts:408-419 reads from dmStore, renders with status/unread/timestamps — verified 2026-03-29 -- [x] **T-163:** Wire quick-switch overlay disconnect/reconnect flow — SidebarArea.ts:844-850, stores target in sessionStorage, calls clearAuth() — verified 2026-03-29 -- [x] **T-164:** Add per-server collapsible section state persistence to localStorage — ui.store.ts:124-176, keyed by server hostname — verified 2026-03-29 - ---- - -## Phase 1: Fix Bugs & Wire Dead Features - -*Everything that's broken or exists but isn't connected.* - -### P0 — Bugs & Broken Code - -- [x] **T-033**: Fix voice state broadcast silent DB failures — 2026-03-21 -- [x] **T-034**: Fix file storage partial write cleanup — 2026-03-21 -- [x] **T-053**: Voice leave ghost session cleanup — 2026-03-21 -- [x] **T-054**: Dispatcher payload validation — 2026-03-21 -- [x] **T-072**: Fix Arrow-up edit-last-message listener — 2026-03-21 - -### P0 — Dead Features (code exists, not wired) - -- [x] **T-066**: Add pin button to message action bar — 2026-03-21 -- [x] **T-067**: Wire MemberList context menu to AdminActions — 2026-03-21 - ---- - -## Phase 2: Server Reliability & Correctness - -*Make the Go server robust and production-grade.* - -### P1 — Critical Reliability - -- [x] **T-031**: hub.GracefulStop() already called in main.go — verified 2026-03-21 -- [x] **T-032**: Add panic recovery wrapper around Hub.Run() — 2026-03-21 -- [x] **T-035**: Add WS invalid payload counter — 2026-03-21 -- [x] **T-106**: Typed message structs in Go — 2026-03-21 -- [x] **T-107**: Sentinel errors in db package — 2026-03-21 - -### P1 — Performance (free wins) - -- [x] **T-108**: SQLite pragma tuning — 2026-03-21 -- [x] **T-052**: Batch permission query — verified already done 2026-03-21 - -### P1 — Graceful Shutdown - -- [x] **T-109**: Server graceful shutdown with connection draining — 2026-03-21 - ---- - -## Phase 3: Client Reliability & Performance - -*Make the client robust for long sessions.* - -### P1 — Memory & Lifecycle - -- [x] **T-110**: Disposable component lifecycle pattern — 2026-03-21 -- [x] **T-056**: Cap messages store per channel (500 max) — 2026-03-21 -- [x] **T-055**: Orphaned attachment cleanup job — 2026-03-21 - -### P2 — Performance - -- [x] **T-111**: Virtual scrolling — already implemented (verified 2026-03-21) -- [x] **T-112**: Lazy loading — already implemented (verified 2026-03-21) - ---- - -## Phase 4: Protocol & Reconnection - -*Make the WebSocket protocol resilient to disconnects.* - -### P1 — Message Delivery Reliability - -- [x] **T-113**: Sequence numbers on server broadcasts — 2026-03-21 -- [x] **T-114**: Client reconnection with state recovery — 2026-03-21 -- [x] **T-115**: Server-side heartbeat monitoring — 2026-03-21 - ---- - -## Phase 5: Code Quality & Standards - -*Clean up code structure for maintainability.* - -### P2 — Server Code Quality - -- [x] **T-116**: Structured logging level audit — 2026-03-21 -- [x] **T-036**: Add request correlation IDs — 2026-03-21 -- [x] **T-050**: Extract WS error constants (14 constants) — 2026-03-21 -- [x] **T-051**: Split voice_handlers.go into 4 files — 2026-03-21 - -### P2 — Client Code Quality - -- [x] **T-117**: TypeScript strict mode — already enabled, removed 3 unnecessary casts — 2026-03-21 -- [x] **T-049**: Refactor MainPage → ChatArea + SidebarArea — 2026-03-21 -- [x] **T-118**: Shared protocol schema + 7 drift issues found — 2026-03-21 -- [x] **T-119**: LiveKit track lifecycle — already correct, verified — 2026-03-21 - -### P2 — Store Improvements - -- [x] **T-120**: shallowEqual comparator (Map/Set/Array/Object) — 2026-03-21 - ---- - -## Phase 6: Testing & Verification - -*Ensure the solid base is verified.* - -### P2 — Integration Tests - -- [x] **T-121**: WebSocket integration tests — 2026-03-21 -- [x] **T-122**: LiveKit voice test script — 2026-03-21 - -### P3 — Security Hardening - -- [x] **T-123**: Tighten Tauri CSP — 2026-03-21 -- [x] **T-057**: Presence update failure ack — 2026-03-21 -- [x] **T-075**: Mic permission denial notification — 2026-03-21 - ---- - -## Phase 7: Polish & Remaining Items - -*Nice-to-haves that improve the experience.* - -### P3 — Client Polish - -- [x] **T-073**: Persist LogsTab filter and level preferences — 2026-03-21 -- [x] **T-058**: Metrics endpoint (/api/v1/metrics) — 2026-03-21 - ---- - -## Deferred (Pre-Roadmap Features) - -*Existing deferred features from stabilization era. Still valid.* - -- [ ] **T-059**: Implement User Profile Popup component -- [ ] **T-060**: Implement Friends/DMs View -- [ ] **T-061**: Implement Status Picker component -- [ ] **T-062**: Implement DM Profile Sidebar -- [ ] **T-063**: Implement Soundboard component (protocol types exist, no UI) -- [ ] **T-024**: Implement screen sharing -- [ ] **T-023**: Add TOTP 2FA support — Login challenge flow: DONE; Server enable/confirm/disable endpoints: DONE; Client enrollment UI: IN PROGRESS (see [[02-Tasks/In Progress|T-192]]); Client test coverage: TODO (see T-193) -- [ ] **T-027**: Code signing certificate for SmartScreen -- [ ] **T-028**: Windows Service mode -- [ ] **T-029**: Custom emoji support -- [ ] **T-030**: Client auto-update via Tauri updater - ---- - -## Feature Roadmap — Community Essentials (Phase R1) - -*Low effort, high impact. Complete before first public release.* -*See [[00-Overview/Feature-Roadmap]] for full context and research.* - -### P2 — Core Community Features - -- [ ] **T-124**: Native polls — new `poll` message type with question, options, real-time vote counts via WebSocket -- [ ] **T-125**: Media gallery — per-channel gallery view filtering messages by images/videos/GIFs, grid layout with lightbox -- [ ] **T-126**: Event/session scheduler — "Next LAN Party" scheduler with date, time, RSVP, countdown timer in sidebar -- [ ] **T-127**: Server activity feed — sidebar widget showing recent joins, voice sessions, files shared, milestones -- [ ] **T-128**: Pinned notes — simple markdown pages per channel, wiki-lite for server rules, game configs, network guides - ---- - -## Feature Roadmap — Gaming DNA (Phase R2) - -*Revive the Xfire spirit. What made Xfire special, brought to 2026.* -*See [[00-Overview/Feature-Roadmap]] for Xfire research.* - -### P2 — Game Integration - -- [ ] **T-129**: Game detection + "Now Playing" — Rust-side process scanner detects running games, shows in user status, configurable game library -- [ ] **T-130**: Game time tracking — track playtime per game per user, lifetime stats on profile, server-wide "most played" leaderboard -- [ ] **T-131**: LAN game server browser — mDNS/UDP broadcast auto-discovery of game servers on LAN, show name/map/players/ping, click to join -- [ ] **T-132**: Screenshot capture + gallery — global hotkey to capture screenshot (Rust), auto-upload to channel, shared gallery with captions -- [ ] **T-133**: Friends activity view — "Friends of Friends Playing" tab, see what friends' friends are playing, one-click join or add friend - ---- - -## Feature Roadmap — Voice Power Features (Phase R3) - -*Features from TeamSpeak/Mumble that Discord lacks.* -*See [[00-Overview/Feature-Roadmap]] for competitive analysis.* - -### P2 — Voice Enhancements - -- [ ] **T-134**: Whisper lists — bind hotkey to whisper to specific users/groups across channels, stay in your channel but talk privately -- [ ] **T-135**: Positional/spatial audio — 3D audio positioning based on in-game coordinates, voices from player direction -- [ ] **T-136**: Voice channel nesting — sub-channels within voice channels (Team 1, Team 2), drag-and-drop between sub-channels -- [ ] **T-137**: Priority speaker — designated users talk over others, auto-duck other voices when priority speaker talks - ---- - -## Feature Roadmap — LAN Party Toolkit (Phase R4) - -*The killer differentiator. No competitor offers this integrated experience.* -*See [[00-Overview/Feature-Roadmap]] for LAN party tool research.* - -### P3 — LAN Party Features - -- [ ] **T-138**: Tournament brackets — single/double elimination, round robin, Swiss, auto-generated schedule, report results in-chat, live bracket display -- [ ] **T-139**: Seat map — visual seat map for venue, claim/reserve seats, see who sits where, show online status per seat -- [ ] **T-140**: Local leaderboard — per-event scoring across games, configurable points system, live leaderboard widget -- [ ] **T-141**: LanCache status widget — integration with LanCache.NET, show cache hit rate, downloaded games, bandwidth saved -- [ ] **T-142**: Shared music queue — collaborative playlist for venue, vote to skip, "now playing" display - ---- - -## Feature Roadmap — Platform & Extensibility (Phase R5) - -*Turn OwnCord from a product into a platform.* -*See [[00-Overview/Feature-Roadmap]] for platform research.* - -### P3 — Extensibility - -- [ ] **T-143**: Custom themes — theme engine with CSS variables, community theme sharing, dark/light/custom palettes -- [ ] **T-144**: Webhook integrations — incoming webhooks (post from external services), outgoing webhooks (trigger actions on events) -- [ ] **T-145**: Bot framework — bot accounts via REST API, slash commands, interactive messages, scheduled tasks -- [ ] **T-146**: Plugin system — server-side (Go) + client-side (TypeScript) plugins, API for custom channel types and widgets -- [ ] **T-147**: Backup/restore — one-command backup of SQLite DB + uploads + config, restore to new machine, scheduled backups -- [ ] **T-148**: Admin monitoring dashboard — CPU, RAM, disk, connected users, voice channels, bandwidth, Prometheus export - ---- - -## Feature Roadmap — Future Vision (Phase R6) - -*Exploratory. Emerging tech for long-term differentiation.* -*See [[00-Overview/Feature-Roadmap]] for trend research.* - -### P4 — Exploratory - -- [ ] **T-149**: AI noise cancellation — on-device noise suppression using lightweight ML models, no cloud dependency -- [ ] **T-150**: Real-time voice translation — AI-powered live translation between languages in voice chat -- [ ] **T-151**: In-game overlay — transparent overlay with voice controls, chat, FPS/ping via Rust DirectX/Vulkan hooks -- [ ] **T-152**: Local streaming — stream screen to a channel within OwnCord, LAN-optimized, LiveKit-based -- [ ] **T-153**: Chat summarization — AI-powered "catch up" on missed messages, local model or optional cloud - ---- - -## Code & Security Review — 2026-04-01 - -Full codebase review across 4 sections: Server Core, Server Realtime, Client & Tauri, Security. -Reviewed by 4 parallel agents. Findings deduplicated and prioritized below. - -### CRITICAL (P0) — Must Fix - -- [x] **T-241:** Fix hub panic recovery loop — added `h.Stop()` in defer when `panicCount >= 3` — 2026-04-01 -- [x] **T-242:** Fix ring buffer `EventsSince` replay — returns non-nil empty slice instead of nil — 2026-04-01 -- [x] **T-243:** Fix PTT event listener leak — stores unsubscribe handle, calls it in `stopPtt()` and before re-init — 2026-04-01 -- [x] **T-244:** Fix `verifyTotp` hardcoded `acceptInvalidCerts: true` — now respects `config.allowSelfSigned` — 2026-04-01 -- [x] **T-245:** Fix `ptt_listen_for_key` blocking Tauri thread pool — moved to `tokio::task::spawn_blocking` — 2026-04-01 - -### HIGH (P1) — Fix Before Release - -**Server Core:** -- [x] **T-246:** Fix TOTP rate-limit slot consumed before body decode — moved limiter check after body decode — 2026-04-01 -- [x] **T-247:** Fix TOTP dual rate-limit counters — added `limiter.Reset(totpKey)` on successful verification — 2026-04-01 -- [x] **T-248:** Fix TOTP enable allows silent secret rotation — returns 409 if `TOTPSecret` already set — 2026-04-01 -- [x] **T-249:** Fix global search leaks restricted channel content — pre-computes accessible channel IDs for FTS WHERE clause — 2026-04-01 -- [x] **T-250:** Fix `DeleteAccount` hard-coded role IDs — queries roles by name instead of ID — 2026-04-01 -- [x] **T-251:** Fix `BackupToSafe` relative path — uses `absClean` in allowlist check and VACUUM INTO — 2026-04-01 - -**Server Realtime:** -- [x] **T-252:** Fix voice join camera slot TOCTOU — atomic `EnableCameraIfUnderLimit` DB method with conditional UPDATE — 2026-04-01 -- [x] **T-253:** Fix readPump replaced-connection TOCTOU — snapshots voiceChID before unregister, guards handleVoiceLeave — 2026-04-01 -- [x] **T-254:** Fix voice join sets state before token — moved `setVoiceState` after token send, rollback takes broadcast flag — 2026-04-01 -- [x] **T-255:** Fix updater `downloadFile` overflow — uses probe pattern like storage.Save — 2026-04-01 -- [x] **T-256:** Fix LiveKit webhook reads body before auth — checks Authorization header first — 2026-04-01 -- [x] **T-257:** Fix `storage.Save` fsync + double close — added `f.Sync()`, used closed flag to prevent double close — 2026-04-01 - -**Client & Tauri:** -- [x] **T-258:** Fix WS event race — generation counter discards stale events from previous connections — 2026-04-01 -- [x] **T-259:** Fix AudioWorklet load race — pipeline generation counter guards stale `.then()` callbacks — 2026-04-01 -- [x] **T-260:** Fix screenshare mute state lost on reconnect — only clears mute map in full `leaveVoice()`, not reconnect — 2026-04-01 -- [x] **T-261:** Fix `handleVoiceToken` unbounded recursion — replaced with iterative `while (pendingJoin)` loop — 2026-04-01 -- [x] **T-262:** Fix `store.ts` re-entrant setState — added updating guard with pending queue — 2026-04-01 -- [x] **T-263:** Fix notification AudioContext leak — exported `cleanupNotificationAudio()`, called on logout — 2026-04-01 - -**Security:** -- [x] **T-264:** SEC: Fix default WS origin — empty `allowedOrigins` now denies cross-origin (safe default) — 2026-04-01 - -### MEDIUM (P2) — Fix When Possible - -**Server Core:** -- [ ] **T-265:** Fix `handleDeleteAccount` lockout key allows DoS — attacker with stolen token sends 3 bad passwords to lock out legitimate deletion. Consider IP+userID key — `Server/api/auth_handler.go:452-488` -- [ ] **T-266:** Fix `handleUpload` leaks internal storage error details in 400 response — log raw error, return generic message — `Server/api/upload_handler.go:117-124` -- [ ] **T-267:** Document `handleServeFile` intentional `Cache-Control` override of global `no-store` header — `Server/api/upload_handler.go:203` -- [ ] **T-268:** Fix `parseBooleanSettingValue` blocks all logins on typo — log warning and return safe default instead of 500 — `Server/api/auth_handler.go:575-584` -- [ ] **T-269:** Fix `handleCloseDM` raw JSON via `fmt.Sprintf` — use typed struct + `json.Marshal` — `Server/api/dm_handler.go:216` -- [ ] **T-270:** Fix rate limiter timestamps slice never shrinks memory — periodically replace with new slice — `Server/auth/ratelimit.go:60-65` - -**Server Realtime:** -- [ ] **T-271:** Fix `wrapWithSeq` produces invalid JSON `{"seq":N,}` for empty objects — handle `msg == "{}"` case — `Server/ws/hub.go:446-458` -- [ ] **T-272:** Fix `handleReconnect` registers client before `writePump` starts — messages queued in 256-buffer can overflow under burst — `Server/ws/serve.go:119-127` -- [ ] **T-273:** Fix `handleChatEdit` permission check inconsistency — checks `SendMessages` only, inconsistent with delete handler's `ManageMessages || isOwner` — `Server/ws/handlers_chat.go:281-288` -- [ ] **T-274:** Fix stale voice sweep broadcasts `voice_leave` to all clients including those who never saw the join — `Server/ws/hub.go:533-535` -- [ ] **T-275:** Fix LiveKit YAML credential sanitization incomplete — expand blocklist to include `>`, `|`, `!`, `%` or use proper YAML library — `Server/ws/livekit_process.go:82-108` -- [ ] **T-276:** Fix updater `CheckForUpdate` cache stampede — multiple goroutines call `fetchLatestRelease` concurrently. Use `singleflight.Group` — `Server/updater/updater.go:121-152` - -**Client & Tauri:** -- [ ] **T-277:** Fix WS dedup cache unreliable eviction — single-entry Set eviction under flood loses coverage. Use bounded LRU — `Client/tauri-client/src/lib/ws.ts:204-207` -- [ ] **T-278:** Fix `AudioPipeline.setupAudioPipeline` — `source` node not stored/disconnected on teardown — `Client/tauri-client/src/lib/audioPipeline.ts:89-138` -- [ ] **T-279:** Refactor `verifyTotp` to use `doFetch` instead of duplicating fetch logic — `Client/tauri-client/src/lib/api.ts:208-249` -- [ ] **T-280:** Fix `prependMessages` `hasMore` wrong when channel has exactly MAX+1 messages — `Client/tauri-client/src/lib/stores/messages.store.ts:170-183` -- [ ] **T-281:** Fix `DeviceManager` fallback unmutes user silently — check `localMuted` before re-enabling mic on device removal — `Client/tauri-client/src/lib/deviceManager.ts:87-93` -- [ ] **T-282:** Fix PTT `initPtt` double listener race — set `listening = true` before first await, or use promise lock — `Client/tauri-client/src/lib/ptt.ts:47-75` -- [ ] **T-283:** Fix `ws_disconnect` asymmetric cleanup — proxy loop exit leaves `inner.port` stale — `Client/tauri-client/src-tauri/src/livekit_proxy.rs:297-315` - -**Security:** -- [ ] **T-284:** SEC: Add per-username login failure tracking to complement per-IP tracking — `Server/api/auth_handler.go:306` -- [ ] **T-285:** SEC: Lower login route-level rate limit from 60/min to 10/min — `Server/api/auth_handler.go:76` -- [ ] **T-286:** SEC: Add admin role hierarchy check — prevent non-owner admins from assigning Owner role — `Server/admin/handlers_users.go:100` -- [ ] **T-287:** SEC: Add MIME type allowlist for served files — prevents stored XSS via upload in browser contexts — `Server/api/upload_handler.go:104` -- [ ] **T-288:** SEC: Bind partial tokens to IP address — `Server/api/totp_handler.go:89` -- [ ] **T-289:** SEC: Add per-user session cap — no limit on concurrent sessions currently — `Server/db/auth_queries.go:184` -- [ ] **T-290:** SEC: Add WS message flood limit — no per-client message rate enforcement in hub — `Server/ws/serve.go:201` -- [ ] **T-291:** SEC: Sanitize error messages in update handler response — `Server/admin/update_handlers.go:44` -- [ ] **T-292:** SEC: Review config startup logging for secrets exposure — `Server/config/config.go:231` - -### LOW (P3) — Nice to Have - -- [ ] **T-293:** Add max bound to invite `ExpiresInHours` (cap at 8760 = 1 year) — `Server/api/invite_handler.go:72-75` -- [ ] **T-294:** Propagate `context.Context` through `GetServerStats` (6 sequential queries without cancellation) — `Server/db/admin_queries.go:27-54` -- [ ] **T-295:** Document lock ordering invariant (`hub.mu` → `c.mu`) at top of `hub.go` — `Server/ws/hub.go` - ---- - -## Task Summary - -| Phase | Focus | Tasks | Priority | Status | -|-------|-------|-------|----------|--------| -| 1-7 | Stabilization (original) | 37 | P0-P3 | All done | -| Audit | Security + code quality (2026-03-29) | 8 done | P1 | All done | -| PM-1–8 | Project Map Dashboard Enhancements | 38 | P2 | Pending | -| Deferred | Pre-roadmap features | 11 | P2-P3 | Pending | -| R1 | Community Essentials | 5 | P2 | Pending | -| R2 | Gaming DNA (Xfire) | 5 | P2 | Pending | -| R3 | Voice Power Features | 4 | P2 | Pending | -| R4 | LAN Party Toolkit | 5 | P3 | Pending | -| R5 | Platform & Extensibility | 6 | P3 | Pending | -| R6 | Future Vision | 5 | P4 | Pending | -| Review | Code & Security Review (2026-04-01) | 55 | P0-P3 | Pending | -| **Total new** | | **134 tasks** | | - -Recommended order for PM phases: PM-8 (quick wins) → PM-1 → PM-5 → PM-4 → PM-2 → PM-3 → PM-7 → PM-6. -Recommended order for features: Deferred (quick wins) → R1 → R2 → R3 → R4 → R5 → R6. -Within each phase, tasks are independent and can be parallelized. - ---- - -## Client Full Review — 2026-04-01 - -4-agent parallel review (security, code quality, bugs, architecture). -Findings below are NEW items not already tracked in the 2026-04-01 server+client review above. - -### CRITICAL (P0) — Must Fix - -- [ ] **T-382:** Fix BUG-082 — Remove `devtools` from default Cargo features; use `cfg(debug_assertions)` gate — `src-tauri/Cargo.toml:15`, `src-tauri/src/commands.rs:145-152` -- [ ] **T-383:** Fix BUG-076 — Add ref count decrement in drag-reorder cleanup; abort controller when count hits 0 — `src/components/channel-sidebar/drag-reorder.ts:26-31` -- [ ] **T-384:** Fix BUG-077 — Add null guard before `connect(config!)` in WS reconnect — `src/lib/ws.ts:154` - -### HIGH (P1) — Fix Before Release - -**Security:** -- [ ] **T-385:** SEC: Make HTTP `dangerous-settings` opt-in per-request — Remove from `Cargo.toml` default, pass cert settings per-request instead of globally — `src-tauri/Cargo.toml:25`, `src/lib/api.ts:94` -- [ ] **T-386:** SEC: Add HTTP certificate pinning — Extend TOFU fingerprint store to cover REST API / file upload calls, not just WebSocket — `src/lib/api.ts:81-107` -- [ ] **T-387:** SEC: Add WebSocket message authentication — Sign/HMAC critical message types to prevent injection via compromised proxy — `src/lib/ws.ts:165-241` - -**Bugs:** -- [ ] **T-388:** Fix BUG-078 — Add abort check after room creation in LiveKit reconnect; wrap in try/finally — `src/lib/livekitSession.ts:350-397` -- [ ] **T-389:** Fix BUG-079 — Call `removeAutoplayUnlock()` in all voice cleanup paths, not just `handleDisconnected` — `src/lib/livekitSession.ts:280-308` -- [ ] **T-390:** Fix BUG-080 — Clear both `intervalId` and `qualityDebounceTimer` in connection stats cleanup — `src/lib/connectionStats.ts:130-192` -- [ ] **T-391:** Fix BUG-081 — Call `stopVadPolling()` at start of `teardownAudioPipeline()` — `src/lib/audioPipeline.ts:30,315,345` - -**Code Quality:** -- [ ] **T-392:** Add logging to ~20 silent catch blocks in message-list components — `src/components/message-list/media.ts:46,85,150,204`, `attachments.ts:59,101,132,157,172`, `embeds.ts:152,220,247,319` -- [ ] **T-393:** Replace 2 unsafe `as any` casts with proper types — `src/lib/audioPipeline.ts:75` (LiveKit processor), `src/lib/livekitSession.ts:103` (pendingJoin) - -### MEDIUM (P2) — Fix When Possible - -**Code Quality:** -- [ ] **T-394:** Split `livekitSession.ts` further — Still 1,293 lines after T-184 reduction. Extract `VoiceChannelManager` and `TrackManager` sub-modules (~430 lines each) -- [ ] **T-395:** Split `SidebarArea.ts` (919 lines) and `ChannelSidebar.ts` (864 lines) — Extract sub-components: `SidebarHeader`, `ChannelSidebarManager`, `DmSidebarManager` -- [ ] **T-396:** Fix array mutations in store updates — Use spread operator instead of `.push()` — `src/stores/channels.store.ts:169`, `src/stores/members.store.ts:168-171` -- [ ] **T-397:** Standardize error narrowing — Replace bare `catch { }` with `catch (err: unknown)` across codebase — `src/main.ts:160,198`, `src/lib/api.ts:152`, others - -**Security:** -- [ ] **T-398:** SEC: Tighten settings key validation — Replace prefix allowlist with explicit key allowlist — `src-tauri/src/commands.rs:21-29` - -**Bugs (Potential):** -- [ ] **T-399:** Fix AdminActions context menu listener leaks — Add `signal` parameter to `addEventListener` calls — `src/components/AdminActions.ts:63,104,107` -- [ ] **T-400:** Fix unhandled promise in image fetch — Add `.catch()` to `fetchImageAsDataUrl` void call — `src/components/message-list/attachments.ts:323-326` - -### LOW (P3) — Nice to Have - -- [ ] **T-401:** Replace PTT polling (254 keys per 20ms) with `SetWindowsHookEx` WH_KEYBOARD_LL for efficiency — `src-tauri/src/ptt.rs:36-50` -- [ ] **T-402:** Add client unit test infrastructure — Currently 0 unit tests; add initial coverage for stores, lib/api.ts, lib/ws.ts -- [ ] **T-403:** Add IPC command rate limiting in Tauri backend — Prevent renderer spam of expensive commands — `src-tauri/src/lib.rs` - ---- - -## Server Deep Review — 2026-04-01 - -Full server review by 3 parallel agents: security, code quality, bug hunt. -Findings: 2 CRITICAL security, 5 HIGH security, 6 HIGH bugs, 4 MEDIUM bugs, 12 MEDIUM quality, 7 MEDIUM security, 5 LOW security, 8 LOW quality. - -### P0 — Bugs (HIGH) — Fix Immediately - -- [ ] **T-404:** Fix BUG-084 — broadcast filter silently drops messages for clients with `channelID==0` (no `channel_focus` sent) — `Server/ws/hub.go:562` -- [ ] **T-405:** Fix BUG-085 — ring buffer `EventsSince` off-by-one: oldest boundary event dropped on reconnect. Change `<` to `<=` — `Server/ws/ringbuffer.go:54` -- [ ] **T-406:** Fix BUG-086 — `leaveVoiceChannelWithRetry` goroutine has no context/cancellation, leaks on hub stop — `Server/ws/voice_leave.go:71-93` -- [ ] **T-407:** Fix BUG-087 — `GracefulStop` not idempotent, `lkProcess.Stop()` called twice on concurrent shutdown. Wrap in `sync.Once` — `Server/ws/hub.go:213-234` -- [ ] **T-408:** Fix BUG-088 — voice channel capacity check unreliable during channel switch when DB leave fails — `Server/ws/voice_join.go:74-98` -- [ ] **T-409:** Fix BUG-089 — `go RemoveParticipant` goroutine in `handleFreshConnect` races with `registerNow` voice state transfer — `Server/ws/serve.go:153` - -### P0 — Security (CRITICAL) — Fix Immediately - -- [ ] **T-410:** SEC: Make `BackupTo` unexported or accept `time.Time` only — exported function with raw string path is SQL injection risk for future callers — `Server/db/admin_queries.go:369` -- [ ] **T-411:** SEC: Add localhost fallback for LiveKit webhook CIDR — empty `AdminAllowedCIDRs` exposes webhook to public internet — `Server/api/middleware.go:255-257` - -### P1 — Security (HIGH) — Fix Before Release - -- [ ] **T-412:** SEC: Block SVG/HTML uploads or force `Content-Disposition: attachment` — stored XSS via malicious SVG — `Server/storage/storage.go` + `Server/api/upload_handler.go:202` -- [ ] **T-413:** SEC: Add rate limiting to TOTP confirmation endpoint — brute-forceable 6-digit code space — `Server/api/totp_handler.go:180-238` -- [ ] **T-414:** SEC: Add auth middleware to LiveKit proxy endpoint — unauthenticated WS connections accepted — `Server/api/livekit_proxy.go` -- [ ] **T-415:** SEC: Move GitHub token from config YAML to environment variable — plaintext secret in config file — `Server/config/config.go:31` -- [ ] **T-416:** SEC: Persist brute-force lockouts to SQLite — in-memory rate limiter wiped on restart — `Server/auth/ratelimit.go` - -### P1 — Bugs (MEDIUM) - -- [ ] **T-417:** Fix BUG-090 — `sanitizeFTSQuery` truncates at byte boundary, not rune boundary — invalid UTF-8 for multi-byte chars — `Server/db/message_queries.go:23` -- [ ] **T-418:** Fix rate limiter `Check` vs `Allow` window pruning divergence — overly aggressive rate limiting at boundary — `Server/api/totp_handler.go:70` -- [ ] **T-419:** Fix BUG-091 — updater `downloadFile` double-closes `*os.File` on error — unsafe on Windows — `Server/updater/updater.go:416` -- [ ] **T-420:** Fix `refreshSettingsLocked` called without lock in `NewHub` — latent data race if settings read before `Run()` — `Server/ws/hub.go:74` - -### P2 — Security (MEDIUM) - -- [ ] **T-421:** SEC: Add per-connection bandwidth throttle on WS — 1MB messages at rate limit = excessive data — `Server/ws/serve.go:38` -- [ ] **T-422:** SEC: Sanitize `handleLiveKitHealth` error details — leaks internal hostnames/ports to admin callers — `Server/api/router.go:237-256` -- [ ] **T-423:** SEC: Log warning when WS session token sent over plaintext (TLS off) — `Server/ws/serve.go:277-303` -- [ ] **T-424:** SEC: Add emoji length validation before DB insert — no cap on emoji string length — `Server/db/message_queries.go:149` -- [ ] **T-425:** SEC: Omit token hash from admin session listings — `Server/db/admin_queries.go:126` -- [ ] **T-426:** SEC: Tighten `data/` directory permissions from 0o755 to 0o700 — `Server/main.go:72` -- [ ] **T-427:** SEC: Add `Vary: Origin` header to file serving endpoint — CORS caching issue — `Server/api/upload_handler.go:205` - -### P2 — Code Quality (MEDIUM) - -- [ ] **T-428:** Extract `handleSearch` into `searchSingleChannel`/`searchAllChannels` helpers — 180 lines, 4+ nesting levels — `Server/api/channel_handler.go:261-441` -- [ ] **T-429:** Extract `generateAndSendVoiceToken` from `handleVoiceJoin` — 186 lines with 9 steps — `Server/ws/voice_join.go:32-186` -- [ ] **T-430:** Move `refreshSettingsLocked` to use `db.GetSetting()` instead of raw SQL — `Server/ws/hub.go:105-108` -- [ ] **T-431:** Add `slog.Warn` wrapper for `LogAudit` failures instead of `_ =` — `Server/api/auth_handler.go:212+` -- [ ] **T-432:** Extract DM participant authorization check into shared helper — copy-pasted in 3 REST handlers — `Server/api/channel_handler.go:170,470,558` -- [ ] **T-433:** Consolidate 4 voice control toggle handlers into table-driven `handleVoiceToggle` — `Server/ws/voice_controls.go` -- [ ] **T-434:** Add named constants for ring buffer size (1000), broadcast channel (256), login lockout (9) — `Server/ws/hub.go:68-69` -- [ ] **T-435:** Remove duplicate `ClearVoiceState` — identical to `LeaveVoiceChannel` — `Server/db/voice_queries.go:82,217` - ---- - -## Deep Research Pass 3 — Beyond Linters & Checkers (2026-04-01) - -Tools that find bugs linters fundamentally cannot. Full research report: `docs/research/deep-research-pass3-beyond-linters.md` - -### Phase 1 — Done (2026-04-01) - -- [x] **T-436:** Add goleak goroutine leak detection — `TestMain` in all 9 test packages — 2026-04-01 -- [x] **T-437:** Add go-deadlock detection — `syncutil/` package with build-tagged mutex shims, updated 8 files (13 mutexes), CI step with `-tags deadlock` — 2026-04-01 - -### Phase 2 — Go Native Fuzzing (HIGH, low effort) - -- [ ] **T-438:** Write `Fuzz*` tests for WS message handlers — target `ws/handlers.go` (main dispatch), `handlers_chat.go`, `handlers_presence.go`, `handlers_reaction.go`, `voice_controls.go` — find panics/nil derefs from malformed input -- [ ] **T-439:** Write `Fuzz*` tests for REST API JSON parsers — target `api/auth_handler.go`, `dm_handler.go`, `profile_handler.go`, `totp_handler.go`, `invite_handler.go`, `upload_handler.go` -- [ ] **T-440:** Add fuzz CI step — `go test -fuzz=Fuzz -fuzztime=30s` per package in `.github/workflows/ci.yml` (or separate scheduled workflow) - -### Phase 3 — Authorization Matrix Testing (CRITICAL) - -- [ ] **T-441:** Write table-driven authz matrix tests — exhaustive role/endpoint permutations for all REST endpoints (admin vs member vs guest vs unauthenticated) — `Server/api/authz_matrix_test.go` -- [ ] **T-442:** Write WS authz matrix tests — verify members can't perform admin-only WS operations (channel create/delete, kick, ban) — `Server/ws/authz_matrix_test.go` - -### Phase 4 — Mutation Testing (HIGH) - -- [ ] **T-443:** Add go-gremlins mutation testing — `.gremlins.yaml` config targeting ws/, api/, auth/, db/ packages. Separate weekly CI workflow `.github/workflows/mutation.yml` -- [ ] **T-444:** Add Stryker mutation testing for client — `stryker.config.mjs`, run against Vitest suite. Add to weekly mutation workflow - -### Phase 5 — Protocol Contract Testing (HIGH) - -- [ ] **T-445:** Formalize WebSocket protocol schema — expand `docs/protocol-schema.json` to full JSON Schema for all 40 message types. Add Go contract test (`ws/contract_test.go`) and Vitest contract test. Add Zod runtime validation on client - -### Phase 6 — Load & Chaos Testing (MEDIUM-HIGH) - -- [ ] **T-446:** Add k6 WebSocket load tests — `Server/scripts/k6/ws-load.js` simulating 500 concurrent users: connect, auth, send, receive. Manual-trigger CI workflow -- [ ] **T-447:** Add toxiproxy chaos tests — integration tests for reconnection/replay buffer under network faults (latency, drops, resets). Build-tagged `//go:build integration` - -### Phase 7 — Defense in Depth (MEDIUM) - -- [x] **T-448:** Add Coraza WAF middleware — embed `corazawaf/coraza/v3` as chi middleware with OWASP CRS rules. Config toggle `waf_enabled` + `waf_paranoia_level` — 2026-04-01 -- [ ] **T-449:** Tauri security audit — audit CSP in `tauri.conf.json`, check `withGlobalTauri`, scope HTTP permissions, validate IPC command inputs. Write report to `docs/security/tauri-audit.md` -- [ ] **T-450:** Tauri production capability hardening — create tightened `capabilities/production.json`, restrict `connect-src`/HTTP permissions to configured server URL only - ---- - -## Mutation Testing — Surviving Mutants (2026-04-01) - -Stryker mutation testing results. Files below 60% mutation score need -additional tests to kill surviving mutants. LiveKit/browser-API files -require deeper SDK mocking. - -### P1 — High Survivor Count (170+ mutants) - -- [ ] **T-451:** Kill surviving mutants in `livekitSession.ts` (25.88%, 170 survivors) — largest gap. Needs deep LiveKit SDK mocking: Room, LocalParticipant, RemoteParticipant, Track lifecycle. Focus on voice join/leave, camera toggle, reconnect, and cleanup paths — `src/lib/livekitSession.ts` - -### P2 — Medium Survivor Count (14-44 mutants) - -- [ ] **T-452:** Kill surviving mutants in `media-visibility.ts` (52.63%, 44 survivors) — IntersectionObserver-dependent. Mock observer callbacks, test visibility transitions, lazy-load triggers — `src/lib/media-visibility.ts` -- [ ] **T-453:** Kill surviving mutants in `streamPreview.ts` (55.37%, 37 survivors) — LiveKit video track preview. Mock track attach/detach, element creation, thumbnail rendering — `src/lib/streamPreview.ts` -- [ ] **T-454:** Kill surviving mutants in `screenShare.ts` (9.68%, 30 survivors) — Needs getDisplayMedia mock, track lifecycle, screenshare toggle, and spotlight view logic — `src/lib/screenShare.ts` -- [ ] **T-455:** Kill surviving mutants in `safe-render.ts` (54.84%, 14 survivors) — DOM rendering edge cases, sanitization guards, fallback rendering — `src/lib/safe-render.ts` - -### P3 — Low Survivor Count (6-7 mutants, mostly no-coverage) - -- [ ] **T-456:** Kill surviving mutants in `roomEventHandlers.ts` (8.08%, 7 survivors) — LiveKit room event callbacks. Mock Room.on() events: participantConnected/Disconnected, trackSubscribed/Unsubscribed — `src/lib/roomEventHandlers.ts` -- [ ] **T-457:** Kill surviving mutants in `livekitDiagnostics.ts` (3.19%, 6 survivors) — LiveKit diagnostics. Mock preflight checks, network quality indicators — `src/lib/livekitDiagnostics.ts` - -### Completed (2026-04-01) - -- [x] **notifications.ts** — improved 39.36% → 79.79% (+40.43%) — 32 tests added -- [x] **audioPipeline.ts** — improved 53.61% → 64.43% (+10.82%) — 61 tests added -- [x] **ws.ts** — improved 46.67% → 50.39% (+3.72%) — 50 tests added diff --git a/docs/brain/02-Tasks/In Progress.md b/docs/brain/02-Tasks/In Progress.md deleted file mode 100644 index 822335d1..00000000 --- a/docs/brain/02-Tasks/In Progress.md +++ /dev/null @@ -1,51 +0,0 @@ -# In Progress - -Tasks currently being worked on. - -## Active - -*No tasks currently in progress.* - -## Completed This Session (2026-04-01) - -- [x] **T-438:** Install & configure mutation testing tools (Stryker + go-gremlins) - - Stryker config at `Client/tauri-client/stryker.config.mjs` - - go-gremlins config at `Server/.gremlins.yaml` - - npm scripts: `test:mutate`, `test:mutate:dry` - - Added to TESTING-STRATEGY.md Section 15 -- [x] **T-439:** Install & configure k6 WebSocket load testing - - Script at `Server/scripts/k6/ws-load.js` - - Added to CLAUDE.md build commands -- [x] **T-440:** Install & configure toxiproxy chaos testing - - Script at `Server/scripts/toxiproxy/chaos-test.sh` - - Added to CLAUDE.md build commands -- [x] **T-441:** Install & configure Coraza WAF (OWASP CRS middleware) - - Config fields: `server.waf_enabled`, `server.waf_paranoia_level` - - Code at `Server/api/waf.go` (opt-in via config) - - Added to Server-Configuration.md -- [x] **T-442:** Add Zod runtime schema validation - - Installed via npm, already in package.json -- [x] **T-443:** Fix IPv6 loopback detection in livekitSession.ts - - `::1` loopback now handled correctly in `resolveLiveKitUrl` -- [x] **T-444:** Fix Go migration split statements for CREATE TRIGGER - - `splitStatements` now handles multi-line `BEGIN...END` blocks -- [x] **T-445:** Resolve all 57 oxlint warnings → 0 - - `.sort()` → `.toSorted()` - - `.reverse()` → `.toReversed()` - - Event listener fixes, no-await-in-loop fixes, etc. -- [x] **T-446:** Add 182 new tests (2,962 → 3,144 total) - - Key files: ws.ts, notifications.ts, audioPipeline.ts, livekitSession.ts -- [x] **T-447:** Documentation updates for all new tools - - Updated CLAUDE.md with new commands - - Created Testing-Tools.md guide - - Updated TESTING-STRATEGY.md with mutation section - - Updated Server-Configuration.md with WAF config - -## Queued (Not Yet Started) - -### Test Coverage Session 1 (remaining) -- API upgrades: auth_handler, channel_handler, dm_handler, invite_handler, upload_handler, middleware, contract edge cases -- Auth upgrades: password, ratelimit, session, totp, tls edge cases -- DB missing: invite_queries_test.go, role_queries_test.go -- DB upgrades: message_queries, channel_queries, dm_queries, migrate -- Config upgrades: env overrides, malformed YAML diff --git a/docs/brain/06-Specs/TESTING-STRATEGY.md b/docs/brain/06-Specs/TESTING-STRATEGY.md deleted file mode 100644 index 1a19af6c..00000000 --- a/docs/brain/06-Specs/TESTING-STRATEGY.md +++ /dev/null @@ -1,1333 +0,0 @@ -# Testing Strategy: OwnCord - -Comprehensive testing infrastructure for the Go server and -Tauri v2 desktop client. - -Coverage target: **80%+** for all code. -Methodology: **TDD** (write tests first). - -See also: [[06-Specs/CLIENT-ARCHITECTURE|CLIENT-ARCHITECTURE.md]], -[[06-Specs/GO-PATTERNS|GO-PATTERNS.md]], -[[04-Decisions/DEC-007-native-e2e-cdp|DEC-007 Native E2E via CDP]], -[[06-Specs/E2E-BEST-PRACTICES|E2E-BEST-PRACTICES.md]] - ---- - -## Table of Contents - -1. [Test Stack Overview](#1-test-stack-overview) -2. [Directory Structure](#2-directory-structure) -3. [NPM Test Scripts](#3-npm-test-scripts) -4. [Vitest Configuration](#4-vitest-configuration) -5. [Unit Test Patterns](#5-unit-test-patterns) -6. [Integration Test Patterns](#6-integration-test-patterns) -7. [E2E Test Patterns (Mocked)](#7-e2e-test-patterns-mocked) -8. [E2E Test Patterns (Production Build)](#8-e2e-test-patterns-production-build) -9. [Native E2E Testing (CDP)](#9-native-e2e-testing-cdp) -10. [Playwright Configuration Reference](#10-playwright-configuration-reference) -11. [Mock Utilities](#11-mock-utilities) -12. [Test Fixtures](#12-test-fixtures) -13. [Rust Tests](#13-rust-tests) -14. [Go Server Tests](#14-go-server-tests) -15. [Mutation Testing](#15-mutation-testing) -16. [Audit Regression Tests](#16-audit-regression-tests) -17. [Coverage Configuration](#17-coverage-configuration) -18. [CI Pipeline](#18-ci-pipeline) -19. [Writing New Tests](#19-writing-new-tests) - ---- - -## 1. Test Stack Overview - -``` -+---------------------------------------------------------------------+ -| Layer | Tool | Environment | Purpose | -|--------------|----------------|-----------------------|---------------| -| Unit | Vitest 3.x | jsdom | Functions, | -| | | | stores, utils | -| Integration | Vitest 3.x | jsdom + mocked WS/ | Store dispatch| -| | | fetch | flows | -| E2E (mocked) | Playwright 1.x | Chromium + Vite dev | UI journeys | -| E2E (prod) | Playwright 1.x | Chromium + Vite | Built dist/ | -| | | preview | verification | -| E2E (native) | Playwright 1.x | Real Tauri exe + | Production | -| | | WebView2 CDP | integration | -| Rust | cargo test | native | Tauri commands| -| | | | FFI | -| Go Server | go test | native + in-memory | Handlers, DB, | -| | | SQLite | WS, auth | -+---------------------------------------------------------------------+ -``` - -### Test Runner Flow - -``` - npm test - | - vitest run (all) - / \ - tests/unit/ tests/integration/ - (~67 files) (~1 file) - | | - jsdom env jsdom env - mock Tauri IPC mock WS + fetch - mock WebSocket full store flows - mock fetch - - - npm run test:e2e - | - playwright test - | - tests/e2e/*.spec.ts - (ignore native/) - | - Vite dev server (:1420) - Chromium browser - Route interception - - - npm run test:e2e:native - | - playwright test - --config native.ts - | - tests/e2e/native/*.spec.ts - | - Launch owncord-client.exe - CDP on 127.0.0.1:9222 - Real WebView2 page -``` - ---- - -## 2. Directory Structure - -``` -Client/tauri-client/ -+-- tests/ -| +-- unit/ # Vitest unit tests -| | +-- types.test.ts # Protocol type validation -| | +-- api.test.ts # REST client paths + errors -| | +-- ws.test.ts # WS connect, dispatch, reconnect -| | +-- rate-limiter.test.ts # Rate enforcement -| | +-- permissions.test.ts # Bitfield operations -| | +-- chat.test.ts # Message grouping, typing -| | +-- voice.store.test.ts # Voice store state -| | +-- voice-widget.test.ts # Voice widget UI -| | +-- voice-channel.test.ts # Voice channel component -| | +-- voice-disconnect.test.ts # Voice disconnect handling -| | +-- voice-callbacks.test.ts # Voice callback wiring -| | +-- video-mode-controller.test.ts # Video mode switching -| | +-- livekit-session.test.ts # LiveKit session management -| | +-- profiles.test.ts # Server profile CRUD -| | +-- gif-picker.test.ts # GIF picker / Tenor -| | +-- ptt.test.ts # Push-to-talk -| | +-- renderers.test.ts # Message content rendering -| | +-- notifications.test.ts # Desktop notifications -| | +-- video-grid.test.ts # Video tile grid -| | +-- dispatcher.test.ts # WS message dispatcher -| | +-- dom.test.ts # DOM utilities -| | +-- logger.test.ts # Client logging -| | +-- safe-render.test.ts # XSS-safe rendering -| | +-- tenor.test.ts # Tenor API client -| | +-- window-state.test.ts # Window focus tracking -| | +-- router.test.ts # Client-side routing -| | +-- store.test.ts # Generic store factory -| | +-- auth.store.test.ts # Auth store -| | +-- channels.store.test.ts # Channels store -| | +-- messages.store.test.ts # Messages store -| | +-- members.store.test.ts # Members store -| | +-- ui.store.test.ts # UI store -| | +-- themes.test.ts # Theme system -| | +-- reconcile.test.ts # State reconciliation -| | +-- context-menu.test.ts # Context menu component -| | +-- dm-sidebar.test.ts # DM sidebar component -| | +-- quick-switcher.test.ts # Quick switch overlay -| | +-- rnnoise-worklet.test.ts # Noise suppression worklet -| | +-- .test.ts # (~67 unit test files total) -| +-- integration/ -| | +-- stores.test.ts # Full store hydration flows -| +-- e2e/ # Playwright E2E specs (~29 spec files) -| | +-- connect-page.spec.ts # Login/register flow -| | +-- register-flow.spec.ts # Registration flow -| | +-- logout-flow.spec.ts # Logout flow -| | +-- main-layout.spec.ts # App layout after auth -| | +-- message-send-flow.spec.ts # Send/receive messages -| | +-- message-input.spec.ts # Message input component -| | +-- message-list.spec.ts # Message list rendering -| | +-- message-actions.spec.ts # Message context actions -| | +-- message-edit-delete.spec.ts # Edit/delete messages -| | +-- reply-flow.spec.ts # Reply to messages -| | +-- channel-sidebar.spec.ts # Channel navigation -| | +-- channel-switch-messages.spec.ts # Channel switching -| | +-- chat-header.spec.ts # Chat header component -| | +-- voice-widget.spec.ts # Voice controls -| | +-- voice-channel.spec.ts # Voice channel E2E -| | +-- settings-overlay.spec.ts # Settings panel -| | +-- overlays.spec.ts # Overlay components -| | +-- connected-overlay.spec.ts # Connected overlay -| | +-- member-list.spec.ts # Member list -| | +-- user-bar.spec.ts # User bar component -| | +-- typing-indicator.spec.ts # Typing indicators -| | +-- typing-indicator-ws.spec.ts # WS typing events -| | +-- emoji-insertion.spec.ts # Emoji picker insertion -| | +-- toast.spec.ts # Toast notifications -| | +-- banners-toasts.spec.ts # Banners and toasts -| | +-- server-strip.spec.ts # Server strip component -| | +-- connect-settings.spec.ts # Connection settings -| | +-- health-status.spec.ts # Health status indicator -| | +-- totp-flow.spec.ts # TOTP authentication -| | +-- helpers.ts # E2E test utilities -| | +-- native-fixture.ts # CDP fixture for native E2E -| | +-- native/ # Native E2E specs -| | +-- smoke.spec.ts # App launches successfully -| | +-- auth-flow.spec.ts # Real login/register -| | +-- chat-operations.spec.ts -| | +-- channel-navigation.spec.ts -| | +-- overlays.spec.ts -| | +-- settings-overlay.spec.ts -| | +-- voice-controls.spec.ts -| | +-- app-layout.spec.ts -| | +-- helpers.ts # Native test helpers -| +-- helpers/ -| +-- mock-ws.ts # Mock WebSocket class -| +-- fixtures.ts # Sample protocol payloads -| +-- test-utils.ts # DOM helpers, store reset - -Server/ -+-- db/ -| +-- db_test.go # DB open, migrate, helpers -| +-- auth_queries_test.go # User + session CRUD -| +-- channel_queries_test.go # Channel CRUD -| +-- message_queries_test.go # Message CRUD + search -| +-- role_invite_queries_test.go # Role + invite operations -| +-- voice_queries_test.go # Voice state operations -| +-- admin_queries_test.go # Admin + audit log -| +-- attachment_queries_test.go # Attachment CRUD -| +-- backup_test.go # Database backup -| +-- migrate_test.go # Migration tracking -+-- ws/ -| +-- handlers_test.go # WS message handlers -| +-- ws_integration_test.go # Hub + client integration -| +-- hub_test.go # Hub lifecycle tests -| +-- messages_test.go # Message builder functions -| +-- export_test.go # Exposed internals for testing -| +-- authz_test.go # Permission checks -| +-- serve_test.go # WS upgrade + auth -| +-- origin_test.go # Origin validation -| +-- livekit_test.go # LiveKit integration -| +-- voice_handlers_test.go # Voice join/leave/controls -| +-- coverage_boost_test.go # Coverage gap tests -+-- api/ -| +-- router_test.go # Router construction -| +-- middleware_test.go # Auth, rate limit, security -| +-- auth_handler_test.go # Login/register handlers -| +-- invite_handler_test.go # Invite CRUD -| +-- channel_handler_test.go # Channel REST handlers -| +-- channel_authz_test.go # Channel authorization -| +-- clientip_test.go # IP extraction + proxies -| +-- contract_test.go # API response shapes -+-- auth/ -| +-- helpers_test.go # Ban/session expiry helpers -| +-- password_test.go # bcrypt hashing -| +-- session_test.go # Token generation -| +-- tls_test.go # TLS cert generation -| +-- ratelimit_test.go # Rate limiter -| +-- ratelimit_cleanup_test.go # Expiry cleanup -+-- config/ -| +-- config_test.go # Config loading + env overrides -+-- permissions/ -| +-- permissions_test.go # Bitfield operations -+-- storage/ -| +-- storage_test.go # File storage + security -+-- updater/ -| +-- updater_test.go # Version comparison -+-- admin/ - +-- admin_handler_test.go # Admin panel handlers - +-- api_test.go # Admin API endpoints - +-- api_edge_cases_test.go # Edge case coverage - +-- handlers_backup_test.go # Backup operations - +-- handlers_channels_test.go # Channel admin - +-- setup_handler_test.go # First-run setup - +-- update_handlers_test.go # Server update - +-- middleware_and_spawn_test.go # Admin middleware - +-- middleware_coverage_test.go # Coverage gaps -``` - ---- - -## 3. NPM Test Scripts - -From `package.json`: - -```json -{ - "test": "vitest run", - "test:unit": "vitest run tests/unit", - "test:integration": "vitest run tests/integration", - "test:e2e": "playwright test", - "test:e2e:prod": "npm run build && playwright test --config playwright.config.prod.ts", - "test:e2e:native": "playwright test --config playwright.config.native.ts", - "test:e2e:ui": "playwright test --ui", - "test:watch": "vitest", - "test:coverage": "vitest run --coverage", - "typecheck": "tsc --noEmit", - "typecheck:build": "tsc -p tsconfig.build.json --noEmit" -} -``` - -| Command | What It Does | When to Use | -|---------|-------------|-------------| -| `npm test` | Run all vitest tests (unit + integration) | CI, quick check | -| `npm run test:unit` | Unit tests only | During development | -| `npm run test:integration` | Integration tests only | After store changes | -| `npm run test:e2e` | Playwright against Vite dev server | After UI changes | -| `npm run test:e2e:prod` | Build + serve + Playwright | Pre-release validation | -| `npm run test:e2e:native` | Real Tauri exe + CDP | Pre-release validation | -| `npm run test:e2e:ui` | Playwright UI mode (interactive) | Debugging E2E failures | -| `npm run test:watch` | Vitest watch mode | Active development | -| `npm run test:coverage` | Coverage report with V8 | Coverage audit | -| `npm run typecheck` | Full typecheck (all sources) | CI, before commit | -| `npm run typecheck:build` | Typecheck build-only tsconfig | Prod E2E gate | - ---- - -## 4. Vitest Configuration - -From `vitest.config.ts`: - -```typescript -import { defineConfig } from "vitest/config"; -import { resolve } from "path"; - -export default defineConfig({ - resolve: { - alias: { - "@lib": resolve(__dirname, "src/lib"), - "@stores": resolve(__dirname, "src/stores"), - "@components": resolve(__dirname, "src/components"), - "@pages": resolve(__dirname, "src/pages"), - "@styles": resolve(__dirname, "src/styles"), - }, - }, - test: { - environment: "jsdom", - include: ["tests/**/*.test.ts"], - coverage: { - provider: "v8", - include: ["src/**/*.ts"], - exclude: [ - "src/main.ts", // entry point - "src/**/*.d.ts", // type declarations - "src/lib/window-state.ts", // Tauri-only APIs - "src/lib/credentials.ts", // Windows Credential Manager - "src/lib/audio.ts", // Web Audio API - "src/lib/vad.ts", // Voice Activity Detection - "src/lib/webrtc.ts", // WebRTC (needs real browser) - "src/lib/voiceSession.ts", // LiveKit session mgmt - "src/lib/noise-suppression.ts", // RNNoise WASM - "src/lib/updater.ts", // Tauri updater plugin - "src/pages/MainPage.ts", // Top-level composition - "src/components/UpdateNotifier.ts", // Tauri updater UI - ], - thresholds: { - statements: 75, - branches: 75, - functions: 75, - lines: 75, - }, - }, - }, -}); -``` - -### Key Configuration Details - -- **Environment**: `jsdom` -- simulates a browser DOM for unit tests -- **Path aliases**: Match Vite/TypeScript aliases so imports like - `@lib/api` resolve correctly in tests -- **Coverage provider**: V8 (native, fast, accurate line coverage) -- **Coverage exclusions**: Files that depend on Tauri-specific APIs, - Web Audio, WebRTC, or WASM are excluded because they cannot run - in jsdom -- **Thresholds**: 75% across all metrics (statements, branches, - functions, lines). Build fails if coverage drops below threshold. - ---- - -## 5. Unit Test Patterns - -### Protocol Type Validation - -Every [[06-Specs/PROTOCOL|PROTOCOL.md]] message type has a test -verifying TypeScript type correctness: - -```typescript -// tests/unit/types.test.ts -import { describe, it, expect } from "vitest"; -import type { VoiceConfigPayload } from "@lib/types"; - -describe("VoiceConfigPayload", () => { - it("uses threshold_mode not mode", () => { - const payload: VoiceConfigPayload = { - channel_id: 1, - bitrate: 64000, - threshold_mode: "auto", - }; - expect(payload.threshold_mode).toBe("auto"); - // @ts-expect-error - 'mode' should NOT exist - expect(payload.mode).toBeUndefined(); - }); -}); -``` - -### API Client Path Verification - -```typescript -// tests/unit/api.test.ts -import { describe, it, expect, vi, beforeEach } from "vitest"; - -// Mock Tauri HTTP plugin (vi.hoisted ensures fn is available at mock time) -const { mockFetch } = vi.hoisted(() => ({ mockFetch: vi.fn() })); -vi.mock("@tauri-apps/plugin-http", () => ({ fetch: mockFetch })); - -import { createApiClient } from "../../src/lib/api"; - -describe("API Client", () => { - let api: ReturnType; - - beforeEach(() => { - mockFetch.mockReset(); - api = createApiClient({ host: "localhost:8443", token: "test-token" }, vi.fn()); - }); - - it("login calls /api/v1/auth/login", async () => { - mockFetch.mockResolvedValue(jsonResponse({ token: "t" })); - await api.login("user", "pass"); - const url = mockFetch.mock.calls[0]?.[0] as string; - expect(url).toContain("/api/v1/auth/login"); - }); -}); -``` - -### Rate Limiter - -```typescript -// tests/unit/rate-limiter.test.ts -describe("RateLimiter", () => { - beforeEach(() => { vi.useFakeTimers(); }); - - it("blocks rapid fire within window", () => { - const limiter = new RateLimiter({ windowMs: 3000, maxCount: 1 }); - expect(limiter.canSend("typing:1")).toBe(true); - limiter.record("typing:1"); - expect(limiter.canSend("typing:1")).toBe(false); - }); - - it("allows after window expires", () => { - const limiter = new RateLimiter({ windowMs: 3000, maxCount: 1 }); - limiter.record("typing:1"); - vi.advanceTimersByTime(3001); - expect(limiter.canSend("typing:1")).toBe(true); - }); - - it("isolates keys", () => { - const limiter = new RateLimiter({ windowMs: 3000, maxCount: 1 }); - limiter.record("typing:1"); - expect(limiter.canSend("typing:2")).toBe(true); - }); -}); -``` - -### Permission Bitfield - -```typescript -// tests/unit/permissions.test.ts -describe("permissions", () => { - it("ADMINISTRATOR bypasses all checks", () => { - expect( - hasPermission(Permissions.ADMINISTRATOR, Permissions.BAN_MEMBERS) - ).toBe(true); - }); - - it("checks specific bit", () => { - const perms = Permissions.SEND_MESSAGES | Permissions.READ_MESSAGES; - expect(hasPermission(perms, Permissions.SEND_MESSAGES)).toBe(true); - expect(hasPermission(perms, Permissions.BAN_MEMBERS)).toBe(false); - }); -}); -``` - -### Store Tests - -Each reactive store has a dedicated test file: - -```typescript -// tests/unit/auth.store.test.ts -describe("auth store", () => { - beforeEach(() => authStore.reset()); - - it("sets user on login", () => { - authStore.setUser({ id: 1, username: "admin", role: "admin" }); - expect(authStore.getState().user?.username).toBe("admin"); - }); - - it("clears user on logout", () => { - authStore.setUser({ id: 1, username: "admin", role: "admin" }); - authStore.logout(); - expect(authStore.getState().user).toBeNull(); - }); -}); -``` - -### Component Tests - -Components are tested by rendering into jsdom and querying the DOM: - -```typescript -// tests/unit/.test.ts -describe("MessageInput", () => { - it("sends message on Enter", () => { - const container = document.createElement("div"); - const component = new MessageInput(container, { channelId: 1 }); - - const input = container.querySelector("textarea")!; - input.value = "Hello world"; - input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" })); - - expect(mockWs.lastSent.type).toBe("chat_send"); - expect(mockWs.lastSent.payload.content).toBe("Hello world"); - }); -}); -``` - ---- - -## 6. Integration Test Patterns - -### Store Hydration Flow - -Tests the complete ready payload → store population pipeline: - -```typescript -// tests/integration/stores.test.ts -describe("store hydration", () => { - it("ready payload populates channels, members, voice", () => { - const ws = new MockWebSocket(); - setupStoreSubscriptions(ws); - - ws.simulateMessage({ - type: "ready", - payload: readyFixture, - }); - - expect(channelsStore.getState().channels).toHaveLength(3); - expect(membersStore.getState().members).toHaveLength(5); - expect(voiceStore.getState().voiceStates.size).toBe(2); - }); - - it("typing event expires after 5 seconds", () => { - vi.useFakeTimers(); - const ws = new MockWebSocket(); - setupStoreSubscriptions(ws); - - ws.simulateMessage({ - type: "typing", - payload: { channel_id: 1, user_id: 2, username: "alex" }, - }); - - expect(uiStore.getState().typingUsers.size).toBe(1); - vi.advanceTimersByTime(5001); - expect(uiStore.getState().typingUsers.size).toBe(0); - }); -}); -``` - -### Chat Round Trip (Pattern Example) - -Recommended pattern for testing the full send -> ack -> broadcast -pipeline (not yet implemented as a standalone file): - -```typescript -// tests/integration/chat.test.ts (proposed) -describe("chat send/receive", () => { - it("send -> ack -> broadcast", () => { - const ws = new MockWebSocket(); - const sentId = messagesStore.sendMessage(1, "Hello"); - - expect(ws.lastSent.type).toBe("chat_send"); - expect(ws.lastSent.payload.content).toBe("Hello"); - - ws.simulateMessage({ - type: "chat_send_ok", - id: sentId, - payload: { message_id: 42 }, - }); - - ws.simulateMessage({ - type: "chat_message", - payload: { - id: 42, - channel_id: 1, - author: { id: 1, username: "me", role: "member" }, - content: "Hello", - }, - }); - - const msgs = messagesStore.getState().messagesByChannel.get(1); - expect(msgs).toHaveLength(1); - expect(msgs![0].content).toBe("Hello"); - expect(msgs![0].author.role).toBe("member"); // string, NOT number - }); -}); -``` - ---- - -## 7. E2E Test Patterns (Mocked) - -### Login Flow - -```typescript -// tests/e2e/connect-page.spec.ts -import { test, expect } from "@playwright/test"; - -test("user can login and see main page", async ({ page }) => { - await page.route("**/api/auth/login", (route) => - route.fulfill({ - status: 200, - body: JSON.stringify({ token: "test-token" }), - }) - ); - - await page.goto("/"); - await page.fill('[data-testid="host-input"]', "localhost:8443"); - await page.fill('[data-testid="username-input"]', "testuser"); - await page.fill('[data-testid="password-input"]', "password"); - await page.click('[data-testid="login-button"]'); - - await expect(page.locator('[data-testid="channel-sidebar"]')).toBeVisible(); -}); -``` - -### Chat E2E - -```typescript -// tests/e2e/message-send-flow.spec.ts -test("user can send and receive messages", async ({ page }) => { - await loginAsTestUser(page); - await page.click('[data-testid="channel-general"]'); - - await page.fill('[data-testid="message-input"]', "Hello world"); - await page.keyboard.press("Enter"); - - await expect( - page.locator('[data-testid="message-content"]').last() - ).toHaveText("Hello world"); -}); -``` - -### E2E Helper Patterns - -```typescript -// tests/e2e/helpers.ts -export async function loginAsTestUser(page: Page) { - // Mock auth endpoint - await page.route("**/api/auth/login", (route) => - route.fulfill({ status: 200, body: JSON.stringify({ token: "tk" }) }) - ); - - // Mock WebSocket ready payload - await page.addInitScript(() => { - // Inject mock WS that sends ready payload on open - }); - - await page.goto("/"); - // Fill login form and submit... -} -``` - ---- - -## 8. E2E Test Patterns (Production Build) - -The `test:e2e:prod` script builds the app first, then serves the -production dist/ with `vite preview` and runs Playwright against it. - -```bash -npm run test:e2e:prod -# Equivalent to: -# npm run build -> tsc && vite build -# playwright test --config playwright.config.prod.ts -``` - -The prod config (`playwright.config.prod.ts`) differs from the -dev config: -- **baseURL**: `http://localhost:4173` (vite preview port) -- **webServer command**: `npm run preview` (serves built dist/) -- Same test specs as dev E2E (excludes native/) - ---- - -## 9. Native E2E Testing (CDP) - -Tests the real Tauri production app via WebView2 Chrome DevTools -Protocol (CDP) connection. See [[04-Decisions/DEC-007-native-e2e-cdp]] -for the architectural decision. - -### How It Works - -``` -1. npm run tauri build -- creates owncord-client.exe -2. Test fixture launches exe with env: - WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--remote-debugging-port=9222 -3. Playwright connects: chromium.connectOverCDP('http://127.0.0.1:9222') -4. Tests interact with the REAL WebView2 page (no mocks) -``` - -### Architecture - -``` -+---------------------------------------------------------------+ -| Test Process (Playwright) | -| | -| native-fixture.ts | -| +-- spawn child process: owncord-client.exe | -| +-- wait for CDP port ready | -| +-- chromium.connectOverCDP('http://127.0.0.1:9222') | -| +-- provide `page` to test spec | -| +-- on test end: kill child process | -| | -| native/*.spec.ts | -| +-- Use `page` to interact with real app | -| +-- Real server connection (or OWNCORD_SKIP_SERVER_TESTS=1) | -+---------------------------------------------------------------+ - | | - v v - owncord-client.exe chatserver.exe - (WebView2 window) (Go server) -``` - -### When to Use - -| Use Mocked E2E | Use Native E2E | -|----------------|----------------| -| UI logic, layout, component behavior | Tauri IPC integration | -| Fast feedback during development | Production build verification | -| CI on every push | Pre-release validation | -| Mock server responses | Real server connection | - -### Running Native E2E - -```bash -# Requires built exe first -npm run tauri build -npm run test:e2e:native - -# Skip server-dependent tests -OWNCORD_SKIP_SERVER_TESTS=1 npm run test:e2e:native - -# With real server (configure test credentials) -OWNCORD_SERVER_URL=localhost:8443 OWNCORD_TEST_USER=testuser \ - OWNCORD_TEST_PASS=password npm run test:e2e:native -``` - -### Native Test Specs - -| Spec | Tests | -|------|-------| -| `smoke.spec.ts` | App launches, window visible, title correct | -| `auth-flow.spec.ts` | Real login/register against server | -| `chat-operations.spec.ts` | Send/receive messages, edit, delete | -| `channel-navigation.spec.ts` | Switch channels, create, rename | -| `overlays.spec.ts` | Settings, invite, member list overlays | -| `settings-overlay.spec.ts` | User settings persistence | -| `voice-controls.spec.ts` | Voice join/leave, mute/deafen | -| `app-layout.spec.ts` | Sidebar, chat area, responsive layout | - -### Login Timeout - -Native E2E tests use a 60-second login timeout due to potential -server rate limiting. See [[feedback_native_e2e_timing]]. - ---- - -## 10. Playwright Configuration Reference - -### Default Config (`playwright.config.ts`) - -| Setting | Value | Purpose | -|---------|-------|---------| -| testDir | `./tests/e2e` | E2E test directory | -| testIgnore | `**/native/**` | Exclude native tests | -| timeout | 30,000ms | Per-test timeout | -| expect.timeout | 5,000ms | Assertion timeout | -| fullyParallel | true | Parallel test execution | -| retries | 1 (CI: 2) | Retry failed tests | -| workers | undefined (CI: 1) | Parallel workers | -| baseURL | `http://localhost:1420` | Vite dev server | -| actionTimeout | 10,000ms | Click/fill timeout | -| navigationTimeout | 15,000ms | Page navigation timeout | -| screenshot | only-on-failure | Capture on failure | -| trace | on-first-retry | Trace on first retry | -| video | on-first-retry | Video on first retry | -| reducedMotion | reduce | Disable animations | -| webServer.command | `npm run dev` | Start Vite dev server | -| webServer.timeout | 60,000ms | Dev server start timeout | - -### Native Config (`playwright.config.native.ts`) - -| Setting | Value | Purpose | -|---------|-------|---------| -| testDir | `./tests/e2e/native` | Native test directory | -| timeout | 60,000ms | Higher (real app startup) | -| expect.timeout | 10,000ms | Higher for real network | -| fullyParallel | false | Sequential execution | -| workers | 1 | Single worker (one app) | -| retries | 2 | More retries for flakiness | -| actionTimeout | 15,000ms | Real app is slower | -| navigationTimeout | 30,000ms | Real server latency | -| webServer | none | Fixture launches app | - -### Production Config (`playwright.config.prod.ts`) - -| Setting | Value | Purpose | -|---------|-------|---------| -| baseURL | `http://localhost:4173` | Vite preview port | -| webServer.command | `npm run preview` | Serve production build | -| Same as default for all other settings. | | | - -### CI-Specific Reporter - -``` -CI: [["html", { open: "never" }], ["junit", { outputFile: "test-results/junit.xml" }]] -Dev: "html" -``` - ---- - -## 11. Mock Utilities - -### MockWebSocket - -```typescript -// tests/helpers/mock-ws.ts -export class MockWebSocket { - sent: any[] = []; - listeners: Map = new Map(); - - send(data: string) { - this.sent.push(JSON.parse(data)); - } - - get lastSent() { - return this.sent[this.sent.length - 1]; - } - - simulateMessage(msg: any) { - const handlers = this.listeners.get("message") ?? []; - handlers.forEach((h) => h({ data: JSON.stringify(msg) })); - } - - addEventListener(event: string, handler: Function) { - const list = this.listeners.get(event) ?? []; - list.push(handler); - this.listeners.set(event, list); - } -} -``` - -### MockFetch - -```typescript -// tests/helpers/mock-fetch.ts -export function createMockFetch(routes: Record) { - return vi.fn((url: string, init?: RequestInit) => { - const path = new URL(url).pathname; - const handler = routes[path]; - if (!handler) { - return Promise.resolve({ - ok: false, - status: 404, - json: () => Promise.resolve({ error: "NOT_FOUND" }), - }); - } - return Promise.resolve({ - ok: true, - status: 200, - json: () => Promise.resolve(handler), - }); - }); -} -``` - -### Tauri IPC Mock - -For unit tests that use `invoke()`: - -```typescript -// Mock Tauri IPC for jsdom -globalThis.__TAURI_INTERNALS__ = { - invoke: vi.fn((cmd: string, args?: Record) => { - switch (cmd) { - case "get_settings": return Promise.resolve({}); - case "save_settings": return Promise.resolve(); - case "load_credential": return Promise.resolve(null); - default: return Promise.reject(`unknown command: ${cmd}`); - } - }), -}; -``` - ---- - -## 12. Test Fixtures - -### Ready Payload Fixture - -```typescript -// tests/helpers/fixtures.ts -export const readyFixture = { - server: { name: "Test Server", icon: null }, - channels: [ - { id: 1, name: "general", type: "text", category: "Text Channels", - position: 0, unread_count: 3, last_message_id: 100 }, - { id: 2, name: "random", type: "text", category: "Text Channels", - position: 1, unread_count: 0, last_message_id: 50 }, - { id: 3, name: "Voice Chat", type: "voice", category: "Voice Channels", - position: 0 }, - ], - members: [ - { id: 1, username: "admin", role: "admin", status: "online" }, - { id: 2, username: "user1", role: "member", status: "online" }, - ], - voice_states: [ - { user_id: 1, channel_id: 3, username: "admin", - muted: false, deafened: false, speaking: false, - camera: false, screenshare: false }, - ], - roles: [ - { id: 1, name: "Owner", color: "#e74c3c", position: 100, - permissions: 0x7FFFFFFF }, - { id: 2, name: "Admin", color: "#f1c40f", position: 50, - permissions: 0x3FFFFFFF }, - { id: 3, name: "Member", color: null, position: 0, - permissions: 0x3 }, - ], -}; - -export const chatMessageFixture = { - id: 42, - channel_id: 1, - author: { id: 2, username: "user1", role: "member", avatar: null }, - content: "Hello everyone!", - timestamp: "2026-03-15T12:00:00Z", - attachments: [], - reactions: [], - reply_to: null, - edited: false, -}; -``` - ---- - -## 13. Rust Tests - -Rust tests live in `src-tauri/src/` using the standard `#[cfg(test)]` -attribute. There are **25 unit tests** across 4 modules: - -| Module | Tests | What They Cover | -|--------|-------|-----------------| -| `commands.rs` | IPC command handlers | Argument validation, return shapes | -| `ws_proxy.rs` | WebSocket proxy | URL construction, TLS/plain branching, header forwarding | -| `livekit_proxy.rs` | LiveKit proxy | URL schemes (ws/wss), path construction, error cases | -| `credentials.rs` | Credential Manager | Save/load/delete round-trip, missing-credential handling | - -### Credentials Test (example) - -```rust -// src-tauri/src/credentials.rs -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_save_and_load_credential() { - let host = "test-server.local"; - let token = "test-token-12345"; - let username = "testuser"; - - save_credential( - host.to_string(), - username.to_string(), - token.to_string(), - None, - ).unwrap(); - - let loaded = load_credential(host.to_string()).unwrap(); - assert!(loaded.is_some()); - let cred = loaded.unwrap(); - assert_eq!(cred.username, username); - assert_eq!(cred.token, token); - assert!(cred.password.is_none()); - - delete_credential(host.to_string()).unwrap(); - let deleted = load_credential(host.to_string()).unwrap(); - assert!(deleted.is_none()); - } -} -``` - -### Running Rust Tests - -```bash -cd Client/tauri-client/src-tauri -cargo test # run all Rust tests -cargo test -- --nocapture # with stdout output -``` - -Note: Credential tests interact with Windows Credential Manager -and require a logged-in Windows user session. - ---- - -## 14. Go Server Tests - -### Database Test Setup - -All Go tests use in-memory SQLite for isolation: - -```go -func openMemory(t *testing.T) *db.DB { - t.Helper() - database, err := db.Open(":memory:") - if err != nil { - t.Fatalf("Open(':memory:') error: %v", err) - } - t.Cleanup(func() { _ = database.Close() }) - return database -} -``` - -Migration is applied via `db.Migrate(database)` for full schema or -`db.MigrateFS(database, fs)` for custom test schemas. - -### WS Hub Test Setup - -```go -func newTestHub(t *testing.T) (*ws.Hub, *db.DB) { - t.Helper() - database := openTestDB(t) // in-memory + MigrateFS - limiter := auth.NewRateLimiter() - hub := ws.NewHub(database, limiter) - return hub, database -} -``` - -For voice tests, the schema includes voice_states: - -```go -func newVoiceHub(t *testing.T) (*ws.Hub, *db.DB) { - database := openVoiceTestDB(t) // includes voice_states DDL - limiter := auth.NewRateLimiter() - hub := ws.NewHub(database, limiter) - go hub.Run() - t.Cleanup(func() { hub.Stop() }) - return hub, database -} -``` - -### Seed Helpers - -```go -func seedTestUser(t *testing.T, database *db.DB, username string) int64 -func seedOwnerUser(t *testing.T, database *db.DB, username string) *db.User -func seedTestChannel(t *testing.T, database *db.DB, name string) int64 -func seedVoiceChan(t *testing.T, database *db.DB, name string) int64 -``` - -### Exposing Unexported Functions - -`ws/export_test.go` (compiled only during `go test`): - -```go -package ws // same package to access unexported symbols - -func (h *Hub) BuildAuthOKForTest(user *db.User, roleName string) []byte { - return h.buildAuthOK(user, roleName) -} -``` - -### HTTP Handler Tests - -```go -func TestSomeHandler(t *testing.T) { - database := openMemory(t) - db.Migrate(database) - - r := chi.NewRouter() - r.Post("/api/v1/endpoint", handleEndpoint(database)) - - body := `{"key": "value"}` - req := httptest.NewRequest("POST", "/api/v1/endpoint", strings.NewReader(body)) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - - if w.Code != http.StatusOK { - t.Errorf("got %d, want 200", w.Code) - } -} -``` - -### Go Test Commands - -```bash -cd Server -go test ./... # all tests -go test ./... -cover # with coverage -go test -race ./... # with race detector -go test -v ./ws/... # verbose, ws package only -go test -run TestSpecific ./db/ # single test -go test -count=1 ./... # disable test caching -``` - -### Go Test Conventions - -- `t.Helper()` in every test helper function -- `t.Cleanup()` for teardown (runs after test completes) -- `t.Fatalf()` for setup failures; `t.Errorf()` for assertions -- `t.TempDir()` for file-based tests (auto-cleaned) -- `testing/fstest.MapFS` for custom migration schemas -- Package `_test` suffix for black-box tests (e.g., `db_test`) -- Same-package for white-box access (e.g., `ws/export_test.go`) - ---- - -## 15. Mutation Testing - -Mutation testing verifies that your tests are actually catching bugs. -It introduces small code mutations and checks if tests fail. - -### Client (Stryker) - -Config: `Client/tauri-client/stryker.config.mjs` - -```bash -# Run full mutation testing (verbose, detailed reports) -npm run test:mutate - -# Dry run only (no mutations, reports what would be tested) -npm run test:mutate:dry -``` - -**Output:** -- HTML report in `reports/mutation/` with kill/survive metrics -- JSON report in `reports/mutation.json` for CI integration -- Survival percentage indicates weak tests - -**Mutation types:** Conditional mutations, arithmetic mutations, logical mutations, -array mutations, string mutations, etc. (Stryker's default presets). - -### Server (go-gremlins) - -Config: `Server/.gremlins.yaml` - -```bash -# Run gremlins mutation testing -go-gremlins mutate ./... - -# With coverage report integration -go test ./... -cover && go-gremlins mutate ./... -``` - -**Output:** -- Mutation score (percentage of mutations killed by tests) -- List of surviving mutations (tests should catch these) -- JSON report `gremlins-report.json` for CI - -**Mutation types:** Boundary mutations, logical mutations, assignment mutations, -arithmetic mutations, etc. - -### Interpreting Results - -- **Kill Rate > 95%**: Excellent test suite, strong mutation detection -- **Kill Rate 80-95%**: Good coverage, may have edge case gaps -- **Kill Rate < 80%**: Weak tests, mutation survivors indicate untested code paths - -Surviving mutations are not always bugs — some may be unreachable code. -Investigate each survivor: - -1. Is the mutation reachable? -2. Does a test currently cover this path? -3. If not, add a test that kills the mutation. - ---- - -## 16. Audit Regression Tests - -Tests that prevent re-introducing bugs found during audits: - -| Test | What It Prevents | Severity | -|------|------------------|----------| -| API paths use `/api/v1/` prefix | Ensures client calls correct versioned endpoints | CRITICAL | -| VoiceConfig has `threshold_mode` | Deserialization failure | CRITICAL | -| VoiceSpeakers has `threshold_mode` | Deserialization failure | CRITICAL | -| Message role is string not number | Wrong role colors in UI | HIGH | -| member_leave handler exists | Members not removed on disconnect | HIGH | -| voice_leave includes channel_id | Server voice state mismatch | HIGH | -| Reaction sends WS message | Feature completely non-functional | HIGH | -| Status picker has no "invisible" | Server rejects invalid value | MEDIUM | - ---- - -## 17. Coverage Configuration - -### Client (Vitest + V8) - -``` -Provider: v8 (native V8 coverage) -Include: src/**/*.ts -Exclude: main.ts, *.d.ts, Tauri-only modules, WebRTC, WASM -Thresholds: 75% statements, branches, functions, lines -``` - -### Server (Go built-in) - -```bash -# Generate coverage report -go test ./... -coverprofile=coverage.out - -# View in browser -go tool cover -html=coverage.out - -# Check specific package -go test -cover ./ws/... -``` - -### Coverage Exclusion Rationale - -Client files excluded from coverage are modules that require -platform-specific runtime features unavailable in jsdom: - -| Excluded File | Reason | Status | -|--------------|--------|--------| -| `window-state.ts` | Tauri WebviewWindow API | Active | -| `credentials.ts` | Windows Credential Manager IPC | Active | -| `audio.ts` | Web Audio API (AudioContext) | **Stale** -- file removed in LiveKit migration | -| `vad.ts` | Voice Activity Detection (AudioWorklet) | **Stale** -- file removed in LiveKit migration | -| `webrtc.ts` | WebRTC peer connections | **Stale** -- file removed in LiveKit migration | -| `voiceSession.ts` | LiveKit SDK (WebRTC) | **Stale** -- renamed to `livekitSession.ts` (new file is NOT excluded; has `livekit-session.test.ts`) | -| `noise-suppression.ts` | RNNoise WASM module | Active | -| `updater.ts` | Tauri updater plugin | Active | -| `MainPage.ts` | Composition root (tested via E2E) | Active | -| `UpdateNotifier.ts` | Tauri updater UI component | Active | - -**Action needed:** Clean up `vitest.config.ts` exclusions to remove the -4 stale entries (`audio.ts`, `vad.ts`, `webrtc.ts`, `voiceSession.ts`). - -Active exclusions are covered by native E2E tests instead. - ---- - -## 18. CI Pipeline - -```yaml -# .github/workflows/client-test.yml -name: Client Tests -on: [push, pull_request] - -jobs: - test: - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - uses: dtolnay/rust-toolchain@stable - - - name: Install dependencies - working-directory: Client/tauri-client - run: npm ci - - - name: Run unit tests - working-directory: Client/tauri-client - run: npm run test:unit - - - name: Run integration tests - working-directory: Client/tauri-client - run: npm run test:integration - - - name: Check coverage - working-directory: Client/tauri-client - run: npm run test:coverage -- --reporter=json - # Fails if below 75% thresholds - - - name: Install Playwright browsers - working-directory: Client/tauri-client - run: npx playwright install --with-deps - - - name: Run E2E tests (mocked) - working-directory: Client/tauri-client - run: npm run test:e2e - - - name: Build Tauri - working-directory: Client/tauri-client - run: npm run tauri build - - - name: Run Rust tests - working-directory: Client/tauri-client/src-tauri - run: cargo test - - server-test: - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: '1.25' - - - name: Run Go tests - working-directory: Server - run: go test ./... -cover -race -``` - ---- - -## 18. Writing New Tests - -### Adding a Client Unit Test - -1. Create `tests/unit/.test.ts` -2. Import from `@lib/` using path aliases -3. Use `describe` / `it` / `expect` from vitest -4. Mock external dependencies (`vi.fn()`, `vi.mock()`) -5. Run: `npm run test:unit -- --filter ` - -### Adding a Client E2E Test - -1. Create `tests/e2e/.spec.ts` -2. Use `page.route()` to mock server responses -3. Use `data-testid` attributes for reliable selectors -4. Use helpers from `tests/e2e/helpers.ts` -5. Run: `npx playwright test ` - -### Adding a Go Server Test - -1. Create `/_test.go` -2. Use `openMemory(t)` + `db.Migrate(database)` for DB setup -3. Use `httptest.NewRecorder` for HTTP handler tests -4. Use `NewTestClient` / `NewTestClientWithUser` for WS tests -5. Run: `go test -v .//... -run TestFeature` - -### Test Naming Conventions - -| Layer | File Pattern | Function Pattern | -|-------|-------------|------------------| -| Client unit | `.test.ts` | `describe("") > it("...")` | -| Client E2E | `.spec.ts` | `test("user can ...")` | -| Go unit | `_test.go` | `TestFuncName(t *testing.T)` | -| Go integration | `_integration_test.go` | `TestIntegration_Scenario` | -| Rust | `#[test] fn test_*()` | Within `#[cfg(test)] mod tests` | diff --git a/docs/brain/08-Guides/Server-Configuration.md b/docs/brain/08-Guides/Server-Configuration.md deleted file mode 100644 index d7247b2c..00000000 --- a/docs/brain/08-Guides/Server-Configuration.md +++ /dev/null @@ -1,154 +0,0 @@ -# Server Configuration Reference - -## Overview - -OwnCord server reads configuration from `config.yaml` in the working directory. -On first run, if the file does not exist, a default `config.yaml` is created automatically. - -Configuration is loaded in three layers (later layers override earlier ones): - -1. **Built-in defaults** (compiled into the binary) -2. **YAML file** (`config.yaml`) -3. **Environment variables** (prefix: `OWNCORD_`) - -## Config Key Reference - -### Server (`server`) - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| `server.port` | int | `8443` | HTTP(S) listen port | -| `server.name` | string | `"OwnCord Server"` | Server display name (shown in `/api/v1/info` and admin panel) | -| `server.data_dir` | string | `"data"` | Directory for database, certs, uploads, backups | -| `server.allowed_origins` | string[] | `["*"]` | WebSocket CORS allowed origins; restrict in production | -| `server.trusted_proxies` | string[] | `[]` | CIDRs of trusted reverse proxies (for X-Forwarded-For) | -| `server.admin_allowed_cidrs` | string[] | private networks | CIDRs allowed to access `/admin` routes. Default: `127.0.0.0/8`, `::1/128`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7` | - -### TLS (`tls`) - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| `tls.mode` | string | `"self_signed"` | TLS mode: `self_signed`, `acme`, `manual`, `off` | -| `tls.cert_file` | string | `"data/cert.pem"` | Path to TLS certificate (used by `manual` and `self_signed`) | -| `tls.key_file` | string | `"data/key.pem"` | Path to TLS private key | -| `tls.domain` | string | `""` | Domain for ACME/Let's Encrypt (required when `mode: acme`) | -| `tls.acme_cache_dir` | string | `"data/acme_certs"` | Directory for cached Let's Encrypt certificates | - -### Database (`database`) - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| `database.path` | string | `"data/chatserver.db"` | Path to SQLite database file | - -### Uploads (`upload`) - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| `upload.max_size_mb` | int | `100` | Maximum file upload size in megabytes | -| `upload.storage_dir` | string | `"data/uploads"` | Directory where uploaded files are stored | - -### Voice / LiveKit (`voice`) - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| `voice.livekit_api_key` | string | *(random per run)* | LiveKit API key. Set a stable value in config for persistent voice tokens. | -| `voice.livekit_api_secret` | string | *(random per run)* | LiveKit API secret (min 32 chars). Set a stable value for persistent tokens. | -| `voice.livekit_url` | string | `"ws://localhost:7880"` | LiveKit server WebSocket URL | -| `voice.livekit_binary` | string | `""` | Path to `livekit-server` binary; empty = don't auto-start | -| `voice.node_ip` | string | `""` | Public IP for WebRTC ICE candidates; empty = auto-detect. Required for remote users behind NAT. | -| `voice.quality` | string | `"medium"` | Voice quality preset: `low`, `medium`, `high` | - -> **Warning:** If `livekit_api_key` or `livekit_api_secret` are left empty, random credentials are generated on each startup. This means voice tokens break on restart. Always set stable credentials in production. See [[08-Guides/LiveKit-Setup]] for details. - -### GitHub / Updates (`github`) - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| `github.token` | string | `""` | Optional GitHub API token for higher rate limits on update checks (5000 req/hr vs 60) | - -### Security / WAF (`server`) - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| `server.waf_enabled` | bool | `false` | Enable Coraza WAF (OWASP CRS middleware) | -| `server.waf_paranoia_level` | int | `2` | OWASP CRS paranoia level (1–4); higher = stricter rules but more false positives | - -## Environment Variable Overrides - -Every config key can be overridden via environment variables using the prefix `OWNCORD_`. - -**Format:** `OWNCORD_
_` - -The first underscore after `OWNCORD_` separates the section from the key. Underscores within key names are preserved. - -| Environment Variable | Config Path | -|---------------------|-------------| -| `OWNCORD_SERVER_PORT` | `server.port` | -| `OWNCORD_SERVER_NAME` | `server.name` | -| `OWNCORD_SERVER_DATA_DIR` | `server.data_dir` | -| `OWNCORD_DATABASE_PATH` | `database.path` | -| `OWNCORD_TLS_MODE` | `tls.mode` | -| `OWNCORD_TLS_CERT_FILE` | `tls.cert_file` | -| `OWNCORD_TLS_DOMAIN` | `tls.domain` | -| `OWNCORD_UPLOAD_MAX_SIZE_MB` | `upload.max_size_mb` | -| `OWNCORD_UPLOAD_STORAGE_DIR` | `upload.storage_dir` | -| `OWNCORD_VOICE_LIVEKIT_API_KEY` | `voice.livekit_api_key` | -| `OWNCORD_VOICE_LIVEKIT_API_SECRET` | `voice.livekit_api_secret` | -| `OWNCORD_VOICE_LIVEKIT_URL` | `voice.livekit_url` | -| `OWNCORD_VOICE_NODE_IP` | `voice.node_ip` | -| `OWNCORD_VOICE_QUALITY` | `voice.quality` | -| `OWNCORD_GITHUB_TOKEN` | `github.token` | -| `OWNCORD_SERVER_WAF_ENABLED` | `server.waf_enabled` | -| `OWNCORD_SERVER_WAF_PARANOIA_LEVEL` | `server.waf_paranoia_level` | - -## Example config.yaml - -```yaml -# OwnCord Server Configuration -server: - port: 8443 - name: "OwnCord Server" - data_dir: "data" - allowed_origins: ["*"] # restrict in production, e.g. ["https://chat.example.com"] - trusted_proxies: [] # e.g. ["10.0.0.0/8"] if behind a reverse proxy - admin_allowed_cidrs: # CIDRs allowed to reach /admin - - "127.0.0.0/8" - - "::1/128" - - "10.0.0.0/8" - - "172.16.0.0/12" - - "192.168.0.0/16" - -database: - path: "data/chatserver.db" - -tls: - mode: "self_signed" # self_signed | acme | manual | off - cert_file: "data/cert.pem" - key_file: "data/key.pem" - domain: "" # required for acme mode - acme_cache_dir: "data/acme_certs" - -upload: - max_size_mb: 100 - storage_dir: "data/uploads" - -voice: - livekit_api_key: "your-api-key" - livekit_api_secret: "your-secret-at-least-32-characters-long" - livekit_url: "ws://localhost:7880" - livekit_binary: "" # path to livekit-server binary; empty = don't auto-start - node_ip: "" # public IP for remote users behind NAT - quality: "medium" # low | medium | high - -github: - token: "" # optional GitHub PAT for update check rate limits -``` - -## See Also - -- [[08-Guides/Deployment]] -- production deployment guide -- [[08-Guides/LiveKit-Setup]] -- voice/video setup -- [[08-Guides/quick-start]] -- getting started - ---- -Last verified: 2026-03-29 diff --git a/docs/brain/08-Guides/Testing-Tools.md b/docs/brain/08-Guides/Testing-Tools.md deleted file mode 100644 index 8516f7d4..00000000 --- a/docs/brain/08-Guides/Testing-Tools.md +++ /dev/null @@ -1,316 +0,0 @@ -# Testing Tools Guide - -This guide covers the mutation testing, load testing, and chaos testing tools -installed in the OwnCord project. - -## Quick Reference - -| Tool | Purpose | Config | Command | -|------|---------|--------|---------| -| **Stryker** | Client mutation testing | `stryker.config.mjs` | `npm run test:mutate` | -| **go-gremlins** | Server mutation testing | `.gremlins.yaml` | `go-gremlins mutate ./...` | -| **k6** | WebSocket load testing | `scripts/k6/ws-load.js` | `k6 run scripts/k6/ws-load.js` | -| **toxiproxy** | Chaos/network testing | `scripts/toxiproxy/chaos-test.sh` | `bash scripts/toxiproxy/chaos-test.sh` | -| **Coraza WAF** | OWASP security middleware | `server.waf_enabled` config | Optional; enable in `config.yaml` | - -## Client: Stryker Mutation Testing - -**Purpose:** Verify that your unit/integration tests actually catch bugs. -Stryker introduces small mutations into your TypeScript code and checks -if tests fail. Tests that don't catch mutations are "survivors." - -### Installation - -Already in `package.json` as dev dependency: - -```bash -cd Client/tauri-client -npm install -``` - -### Configuration - -File: `Client/tauri-client/stryker.config.mjs` - -- Runs against all tests in `tests/unit/` and `tests/integration/` -- Uses vitest as the test runner -- Generates HTML and JSON reports - -### Running Mutations - -```bash -cd Client/tauri-client - -# Full mutation testing (will take several minutes) -npm run test:mutate - -# Dry run (checks what would be tested, no actual mutations) -npm run test:mutate:dry -``` - -### Understanding Results - -After a run, open the HTML report: - -```bash -# On Windows -start reports\mutation\index.html - -# On Linux/macOS -open reports/mutation/index.html -``` - -**Key metrics:** - -- **Killed**: Mutations that tests caught (good) -- **Survived**: Mutations that tests missed (investigate) -- **Timeout**: Mutation caused infinite loop -- **Compile error**: Mutation syntax was invalid (rare) - -**Kill rate interpretation:** - -- **>95%**: Excellent, tests are very thorough -- **80-95%**: Good, typical for mature projects -- **<80%**: Poor, tests may have gaps - -### Investigating Survivors - -A surviving mutation means tests don't cover that code path. Steps: - -1. Find the mutation in the HTML report (file + line number) -2. Read the mutation details (what changed?) -3. Determine if this path is reachable -4. If reachable, add a test that kills this mutation - -Example: - -```typescript -// Original code -if (count > 0) { - console.log("positive"); -} - -// Mutation: > becomes >= -// Surviving mutation means no test with count === 0 -``` - -## Server: go-gremlins Mutation Testing - -**Purpose:** Same as Stryker, but for Go code. Mutates Go source -and runs `go test ./...` to check if tests catch the mutations. - -### Installation - -```bash -go install github.com/go-gremlins/gremlins/cmd/go-gremlins@latest -``` - -### Configuration - -File: `Server/.gremlins.yaml` - -Specifies which files to mutate and mutation settings. - -### Running Mutations - -```bash -cd Server - -# Run full mutation testing -go-gremlins mutate ./... - -# View JSON report -cat gremlins-report.json -``` - -### Mutation Types - -go-gremlins applies mutations like: - -- Boundary changes: `<` → `<=`, `>` → `>=` -- Logical operators: `&&` → `||` -- Assignment mutations: `x = y` → `x = 0` -- Arithmetic: `x + y` → `x - y` - -See [go-gremlins docs](https://github.com/go-gremlins/gremlins) for full list. - -## Load Testing: k6 - -**Purpose:** Stress-test the WebSocket server and HTTP API. -Simulates many concurrent clients sending messages. - -### Installation - -Download from [k6.io](https://k6.io/docs/get-started/installation/): - -```bash -# On Windows (via chocolatey) -choco install k6 - -# On macOS -brew install k6 - -# On Linux -sudo apt-get install k6 -``` - -### Configuration - -File: `Server/scripts/k6/ws-load.js` - -- Configurable VU (virtual users), duration, ramp-up, etc. -- Measures: response time, throughput, errors, WebSocket latency - -### Running Load Tests - -```bash -cd Server - -# Run the default WebSocket load test -k6 run scripts/k6/ws-load.js - -# With custom options -k6 run --vus 100 --duration 30s scripts/k6/ws-load.js -``` - -### Interpreting Results - -Output shows: - -- **http_req_duration**: Response time (ms) -- **http_reqs**: Total requests per second -- **errors**: Failed requests -- **ws_connecting**: WebSocket connection latency - -Monitor for: - -- High error rates (server instability) -- Increasing latency (saturation) -- Resource exhaustion (check server logs) - -## Chaos Testing: toxiproxy - -**Purpose:** Inject network faults (latency, packet loss, timeouts) -to verify reconnection and error handling logic. - -### Installation - -Download from [shopify/toxiproxy](https://github.com/shopify/toxiproxy): - -```bash -# On Windows -# Download release from GitHub - -# On macOS -brew install shopify/shopify/toxiproxy - -# On Linux -# Download release from GitHub -``` - -### Configuration - -File: `Server/scripts/toxiproxy/chaos-test.sh` - -- Defines toxiproxy proxies (intercept server connections) -- Applies faults: latency, bandwidth limits, packet loss, timeouts -- Runs tests before/during/after fault injection - -### Running Chaos Tests - -```bash -cd Server - -bash scripts/toxiproxy/chaos-test.sh -``` - -### Fault Scenarios - -Common scenarios defined in the script: - -1. **Latency injection**: Add 500ms delay to all packets -2. **Packet loss**: Drop 10% of packets -3. **Bandwidth throttle**: Limit to 1 Mbps -4. **Connection timeout**: Kill connections after inactivity -5. **Periodic disconnects**: Force reconnect every 30s - -Watch for: - -- Client reconnects successfully -- No data loss (seq numbers correct) -- Graceful fallback (no app crashes) - -## Security: Coraza WAF - -**Purpose:** Protect against OWASP top 10 attacks (SQL injection, XSS, etc.) -using OWASP CRS (Core Rule Set) rules. - -### Configuration - -Enable in `config.yaml`: - -```yaml -server: - waf_enabled: true - waf_paranoia_level: 2 -``` - -Or via environment variable: - -```bash -OWNCORD_SERVER_WAF_ENABLED=true -OWNCORD_SERVER_WAF_PARANOIA_LEVEL=2 -``` - -### Paranoia Levels - -| Level | Coverage | False Positives | Recommended For | -|-------|----------|-----------------|-----------------| -| 1 | Basic | Very low | Production (default baseline) | -| 2 | Standard | Low | Production (balance) | -| 3 | Strict | Medium | Staging/QA | -| 4 | Paranoid | High | Testing only | - -### Behavior - -With WAF enabled: - -- All HTTP requests are scanned against OWASP CRS rules -- Suspicious requests are blocked with HTTP 403 -- Benign requests pass through -- Logs show rule violations - -### Testing the WAF - -Try a simple SQL injection attempt (should be blocked): - -```bash -curl -X POST http://localhost:8443/api/v1/auth/login \ - -H "Content-Type: application/json" \ - -d '{"username": "admin\" OR \"1\"=\"1", "password": "test"}' -# Response: 403 Forbidden (if WAF enabled) -``` - -## Integration with CI - -All mutation tests can run in CI pipelines: - -```bash -# Client -npm run test:mutate - -# Server -go-gremlins mutate ./... - -# Load testing (optional, longer duration) -k6 run --vus 50 --duration 60s scripts/k6/ws-load.js - -# Chaos testing (optional, requires toxiproxy) -bash scripts/toxiproxy/chaos-test.sh -``` - -## Next Steps - -- See [[06-Specs/TESTING-STRATEGY|TESTING-STRATEGY.md]] for detailed test patterns -- See [[06-Specs/CHATSERVER|CHATSERVER.md]] for security rules (WAF context) -- See [[08-Guides/Server-Configuration|Server-Configuration.md]] for config details