17 KiB
TalkEdit — Testing & Robustness Plan
Tests are critical before launch to prevent regressions and ensure the app is stable. Below is every function in the codebase that needs test coverage, organized by module.
1. Rust backend (src-tauri/src/)
licensing.rs — already has partial coverage (4 tests)
verify_license_key— valid key, invalid format, invalid signature, expired key ✅ (exists)load_cached_license— file exists, file missing, malformed filecache_license— write and read backremove_license— removes file, no-ops if missingget_or_start_trial— creates new trial file, loads existing, handles corrupt fileget_trial_info— active trial (29 days), expired trial (0 days), exactly at boundaryget_trial_days_remaining— active returns Some, expired returns None,clear_trial— removes file, no-ops if missingget_app_status— licensed (cached license), trial active, expired (no license, no trial)has_feature— feature exists, feature missing, empty features list
models.rs — no existing tests
list_models— whisper models found, llm models found, mixed, empty dirsdelete_model— deletes file, deletes directory, path doesn't existhuggingface_cache_dir— HF_HOME set, XDG_CACHE_HOME set, defaults to ~/.cache
transcription.rs — no existing tests
ensure_model_downloaded— returns success (stub function)
paths.rs — no existing tests
project_root— dev layout, packaged (TAURI_RESOURCE_DIR), fallbackpython_exe— bundled path, venv paths, fallback to .venv312backend_script— joins project_root/backendroot_script— joins project_root
2. Frontend store (frontend/src/store/)
editorStore.ts (Zustand) — partially covered (2 tests)
reset✅ (in beforeEach)setGlobalGainDb— clamps to -24/+24 ✅ (exists)addGainRange— adds with correct start/end/gain ✅ (exists)addCutRange— adds with correct start/end, handles overlapping rangesaddMuteRange— adds with correct start/endaddSpeedRange— adds with correct start/end/speedremoveCutRange— removes existing, no-ops on missingremoveMuteRange— removes existing, no-ops on missingremoveGainRange— removes existing, no-ops on missingremoveSpeedRange— removes existing, no-ops on missingupdateCutRange— updates bounds, prevents negative durationupdateMuteRange— updates boundsupdateGainRangeBounds— updates boundsupdateSpeedRangeBounds— updates boundsupdateGainRange— updates gain valueupdateSpeedRange— updates speed valuesetSelectedWordIndices— single, multiple, empty, out of range handledreplaceWordRange— replaces words in middle, replaces at start, handles invalid indicesupdateWordText— updates word, preserves timing, no-ops on bad indexgetWordAtTime— exact match, between words, before first word, after last word, no wordsloadVideo— sets videoUrl, resets state, handles missing filesetCurrentTime— sets time, clamps to 0-durationsetTranscribing— toggles flag, sets statussetTranscription— sets words and segments, handles empty arrayssetMarkInTime/setMarkOutTime— sets and clearsclearMarkRange— clears both marksaddTimelineMarker— adds with label/color/timeremoveTimelineMarker— removes by idupdateTimelineMarker— updates label/colorsetZonePreviewPaddingSeconds— sets and clampssetBackgroundMusic/updateBackgroundMusic— sets and updatessetAdditionalClips— adds, removes, reorderssetSilenceTrimGroups— sets groups
licenseStore.ts — no existing tests
canEdit— true for Licensed, true for Trial, false for Expired, false for nullcheckStatus— calls getAppStatus, sets correct state, handles error (falls to Expired)activateLicense— valid key sets Licensed, invalid key returns falsedeactivateLicense— reverts to trial if valid, falls to Expired otherwisehasFeature— returns true for matching, false for missingsetShowDialog— toggles dialog visibility- Persist middleware — Licensed status persists, Trial/Expired does not
aiStore.ts — no existing tests
setProviderConfig— updates provider, encrypts API keyssetDefaultProvider— changes defaultsetCustomFillerWords— sets and clearssetFillerResult— sets and clearssetProcessing— toggles with message
3. Frontend hooks (frontend/src/hooks/)
useKeyboardShortcuts.ts — no existing tests
- Keyboard event dispatch — space plays/pauses, J/K/L speed controls, I/O marks, Delete cuts, Ctrl+Z undo
toggleCheatsheet— creates overlay with correct content, toggles off- Skip logic — skips correctly forward and back from current playhead
useVideoSync.ts — no existing tests
- Synchronization of store
isPlayingwith video element, audio element togglePlay— starts playing, pausesseekTo— seeks video to correct time, seeks audio to correct time- Handles video element ref being null (doesn't crash)
4. Frontend lib (frontend/src/lib/)
keybindings.ts — no existing tests
loadBindings— loads from localStorage, falls back to standard preset when missingsaveBindings— persists and reloads correctlyapplyPreset— 'standard' and 'left-hand' both apply all required bindingsdetectConflicts— detects duplicate keys, returns empty when no conflicts
5. Backend services (backend/services/)
video_editor.py — no existing tests
apply_cut_segments— keeps correct segments from transcript word list, FFmpeg concat cmdapply_mute_ranges— cuts audio for muted rangesapply_gain_ranges— adjusts volume (positive and negative) for FFmpeg filter chainsapply_speed_ranges— time-stretches or compresses segmentsmix_background_music— mixes with ducking enabled, mixes without ducking, handles no musicbuild_export_filters— stitches together all zone types into correct filter order
audio_cleaner.py — no existing tests
detect_silence— detects pauses above threshold, returns correct time rangesremove_silence— splits by silence, re-concatenates keep segments
ai_provider.py — no existing tests
complete— Ollama completion succeeds, OpenAI completion succeeds, Claude completion succeedscomplete— handles missing provider, timeout, bad JSON responselist_ollama_models— returns models list, handles connection error
transcription.py — no existing tests
transcribe_file— returns words with correct format (word, start, end, confidence)transcribe_segment— re-transcribes a range with offset-adjusted timestamps_load_model— caches model, returns existing from cache, handles GPU/CPU
caption_generator.py — no existing tests
generate_srt— correct SRT format, sequential numbering, proper timestampsgenerate_vtt— correct VTT format with header, chroma key tagsgenerate_ass— correct ASS subtitle format
gpu_utils.py — no existing tests
get_optimal_device— returns CUDA when available, returns CPU otherwise
audio_processing.py — no existing tests
extract_audio— extracts wav from video, handles audio-only input, temp file cleanup
6. Frontend components — integration tests
TranscriptEditor.tsx
- Word selection: click, shift+click range, drag select
- Ctrl+click seeks video to correct time
- Double-click enters edit mode, Enter commits, Escape cancels
- Zone mode drag creates correct zone type
- Search finds matches, navigates with Enter/Shift+Enter
- "Restore" button appears on hover over words in a zone, removes the zone
- Re-transcribe calls backend and updates words
- Selection toolbar buttons create correct zone types
- When
canEditis false, buttons are disabled and zone creation is blocked
WaveformTimeline.tsx
- Canvas renders waveform when audio data loads
- Click seeks to correct time
- Zone drag creates zone on mouse up
- Zone selection and resize with handles
- Delete key removes selected zone
- Zoom and scroll work correctly
- Zone toggle buttons show/hide overlay layers
- Loading spinner shows when no waveform data
- Error message with retry button on load failure
ExportDialog.tsx
- Fast mode card and re-encode card toggle correctly
- Resolution selector only visible in re-encode mode
- Format selector disables WAV for video files
- Export button triggers export with correct parameters
- Progress bar updates during export
- Loudness normalization checkbox shows LUFS target selector
AIPanel.tsx
- Filler words tab: detect button sends request, displays results
- Apply All creates cut ranges for all fillers
- Clips tab: find clips shows suggestions
- Reprocess tab: model selector + reprocess button
- Error state with retry on API failure
App.tsx — layout and toolbar
- Welcome screen shows when no video loaded
- Trial bar shows on welcome screen for Trial/Expired states
- Toolbar buttons toggle modes correctly (Cut, Mute, Gain, Speed)
- Toolbar buttons open correct panels (Zones, Silence, Markers, Music, Append, AI, Export, Settings, Help)
- File menu opens/closes, items work
- Split pane dividers are draggable and keyboard-accessible
- First-run welcome overlay shows once
- Hotkeys work: Escape clears modes, ? opens cheatsheet
7. Error handling regression tests
- Backend crash: show reconnect banner, not broken UI
- Transcription failure: show error, allow retry with different model
- Export failure: show FFmpeg stderr, allow copy
- Model download timeout: show error, allow retry
- File not found: handled gracefully on open/load
- Permission denied: handled gracefully on save/export
- Concurrent operations: block export during transcription, block transcription during export
8. Licensing & trial flow tests
- Fresh install: shows 30-day trial
- Day 29: still allows editing
- Day 31: shows expired, editing locked, export still works
- Activate valid license: switches to Licensed, clears trial
- Activate invalid license: shows error, stays on trial
- Deactivate license: returns to trial if valid, expires if trial over
- Expired banner shows correct message and activate link
canEditprop correctly gates all editing controls across all components
Test infrastructure
| Layer | Framework | Run command |
|---|---|---|
| Rust | cargo test (built-in) |
cd src-tauri && cargo test |
| Frontend (Vitest) | Vitest + jsdom | cd frontend && npx vitest run |
| Frontend (components) | Playwright or Vitest + testing-library | cd frontend && npx vitest run |
| Python backend | pytest | cd backend && python -m pytest |
Setup needed
- Frontend:
npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom(vitest likely already installed) - Frontend: add
"test": "vitest run"topackage.jsonif not present - Python: ensure pytest is installed (
pip install pytest pytest-asyncio) - CI: add GitHub Actions workflow for
cargo test && vitest run && pytest
Priority order
- store tests (editorStore, licenseStore, aiStore) — core data integrity
- Rust licensing tests — payment/trial logic must never break
- Rust models tests — filesystem operations must be safe
- Backend service unit tests — export pipeline, transcription, AI
- Component integration tests — user-facing behavior
- Error handling regression tests — robustness
Robustness beyond tests
React Error Boundary
The app has no error boundary — a single JS error in any component crashes the entire UI to a white screen. Wrap the app in a <ErrorBoundary> that catches render errors and shows a fallback with "Something went wrong" + a reload button.
- Create
ErrorBoundary.tsxcomponent (componentDidCatchpattern) - Wrap the entire
<App />inmain.tsx - Fallback shows: error message, stack trace (collapsed), "Reload" button, "Reset & Clear State" button
Global JS error logging
Uncaught errors in async code and event handlers silently break the app. Add a window.onerror and window.onunhandledrejection handler that logs to the Tauri backend and shows a toast notification.
- Add global error handler in
main.tsxthat intercepts all uncaught errors - Log to Rust backend via
invoke('log_error', { message, stack }) - Show a non-blocking toast notification (bottom-right) for non-fatal errors
- Fatal errors still go to the ErrorBoundary
Input validation layer
The app trusts user input too much. Invalid values in number inputs, empty file paths, or negative durations can cause crashes or silent failures. Add validation at the store level.
editorStore.ts— validate all setters: clamp numbers, reject empty strings for paths, enforce min/max on dB and speed valueslicenseStore.ts— validate license key format before sending to Rust (prefix check, base64 pattern)aiStore.ts— validate API key formatting, model name not empty- Export options — validate resolution, format, loudness target against allowed values before sending to backend
Frontend runtime assertions
The app makes assumptions about data shapes (e.g. words[sorted[0]].start assumes the index exists). Add assertion checks in critical paths that log a clear error instead of silently producing NaN or undefined.
- Add an
assertutility function:assert(condition, message)that throws a descriptive error in dev, warns in prod - Guard all array index access in TranscriptEditor, WaveformTimeline, ExportDialog
- Guard null/undefined checks on store actions that expect existing data
Auto-save crash recovery
If the app crashes or the system loses power during editing, current work is lost. Add periodic auto-save to a temp file that gets restored on next launch.
- Every 60 seconds, save the full editor state to
app_data_dir/autosave.json - On launch, check if autosave.json exists and is newer than the last manual save
- Show a "Recover unsaved work?" prompt with date/time of autosave
- Clean up autosave after a manual save or after recovery is accepted/dismissed
Backend health check & self-diagnostics
When the Python backend dies mid-session, the app doesn't know until a request fails. Add periodic health checks and a diagnostics panel.
- Poll
GET /healthevery 15 seconds from the frontend - If backend is unreachable: show a non-blocking banner "Backend disconnected — retrying..."
- When backend comes back online, dismiss the banner automatically
- Add a
/diagnosticsendpoint that reports: Python version, available FFmpeg, GPU detection, model cache sizes, disk space - Wire to a "System Info" section in Settings or DevPanel
CI pipeline with automated checks
Currently no CI exists. A GitHub Actions workflow would catch regressions on every push.
- Add
.github/workflows/ci.yml:cargo test— all Rust testscargo clippy -- -D warnings— enforce Rust lint rulesnpx vitest run— all frontend testsnpx tsc --noEmit— TypeScript type checkpython -m pytest backend/tests/— Python backend testscargo build --release— verify release build succeeds
- Run on push to
mainand on PRs - Add Rust
#[deny(clippy::all)]to catch common mistakes at build time
Fuzz testing for Rust deserialization
The app deserializes user-provided JSON (project files, API responses) — any malformed input could crash the Rust backend. Add fuzz tests for the critical deserialization paths.
- Fuzz
TranscriptionResultdeserialization — malformed word/segment JSON - Fuzz
.aiveproject file loading — corrupted JSON, missing fields, wrong types - Fuzz
LicensePayloaddeserialization — tampered license payloads - Use
cargo-fuzzorproptestcrate for property-based testing
Performance telemetry (opt-in)
Without real-world data, you can't know where the app is slow. Add lightweight timing around operations that users complain about.
- Log timing for: transcription time, export time, AI completion time, model download time
- Store in localStorage (not sent anywhere — privacy-first)
- Show in DevPanel: last N operation timings
- Use this data to identify slow paths before users report them
Recovery from bad project state
A corrupted .aive file or a partially-loaded project can leave the app in an unusable state. Add recovery paths.
- On project load failure: show error, offer "Load anyway with partial data" or "Cancel"
- Add "Reset Editor State" button in DevPanel (clears all state back to empty)
- Store action: validate all zone ranges are within video duration, auto-remove invalid ones on save