diff --git a/docs/README.md b/docs/README.md index f9eae49b..b560f1e0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,6 +26,7 @@ This directory contains SeedPass project documentation. - `tui_v2_plan.md` - `tui_v2_parity_checklist.md` - `tui_v2_cutover_decision.md` +- `tui_v2_large_vault_validation.md` - `security.md` - `nostr_setup.md` - `nostr_namespace_reset.md` diff --git a/docs/tui_v2_large_vault_validation.md b/docs/tui_v2_large_vault_validation.md new file mode 100644 index 00000000..26730602 --- /dev/null +++ b/docs/tui_v2_large_vault_validation.md @@ -0,0 +1,48 @@ +# TUI v2 Large-Vault Validation + +Status: Added on `beta` (March 1, 2026). + +This document defines a deterministic, CI-like validation pass for TUI v2 behavior at large vault sizes. + +## What Is Validated + +- Pagination behavior across large deterministic datasets. +- Detail preview truncation behavior for large document content. + +## Test Module + +- `src/tests/test_tui_v2_large_vault_validation.py` + +## Deterministic Data Profile + +- Standard profile: `10,000` entries. +- Stress profile: `50,000` entries (`--stress`). +- Labels are deterministic (`Entry-000000` style). + +## Runtime Thresholds + +Pagination scan budget (wall-clock for scan loop): +- `10,000` rows: `<= 2.0s` +- `50,000` rows: `<= 8.0s` + +Detail truncation checks: +- Confirms bounded preview behavior at large content sizes. + +## Commands + +Standard CI-like run: + +```bash +.venv/bin/python -m pytest -q src/tests/test_tui_v2_large_vault_validation.py +``` + +Extended stress run: + +```bash +.venv/bin/python -m pytest -q --stress src/tests/test_tui_v2_large_vault_validation.py +``` + +## Notes + +- Stress tests are opt-in in this repository (require `--stress`). +- Thresholds are intentionally conservative to reduce environment flakiness while still catching regressions. diff --git a/docs/tui_v2_parity_checklist.md b/docs/tui_v2_parity_checklist.md index 250367d4..e68f9f43 100644 --- a/docs/tui_v2_parity_checklist.md +++ b/docs/tui_v2_parity_checklist.md @@ -39,7 +39,7 @@ This checklist tracks readiness to switch default interactive mode from legacy T - Incomplete parity for legacy-only interactive operations not yet mapped into TUI v2. - Textual runtime/version behavior can vary by environment. -- No dedicated Textual interaction test suite yet for pagination and graph traversal. +- Large-vault validation is bounded to deterministic pagination/preview checks and should be expanded with full end-to-end sync workloads. ## Validation Checklist Before Cutover @@ -48,5 +48,7 @@ This checklist tracks readiness to switch default interactive mode from legacy T - `src/tests/test_tui_v2_textual_interactions.py` (skips when `textual` is unavailable). - [x] Add non-UI unit tests for TUI v2 helper logic. - `src/tests/test_tui_v2_helpers.py` covers command parsing, pagination, and detail truncation. -- [ ] Verify large vault behavior with realistic data volume in CI-like environment. +- [x] Verify large vault behavior with realistic data volume in CI-like environment. + - `src/tests/test_tui_v2_large_vault_validation.py` + - standard: `10,000` rows, stress: `50,000` rows (`--stress`) - [ ] Confirm help text and docs match shipped keybindings and palette commands. diff --git a/landing/docs.html b/landing/docs.html index 1b5a5d52..ea4343a9 100644 --- a/landing/docs.html +++ b/landing/docs.html @@ -59,6 +59,7 @@
  • > TUI v2 Plan
  • > TUI v2 Parity
  • > TUI v2 Cutover
  • +
  • > TUI v2 Large Vault
  • > Security
  • > Security Readiness
  • > Agent Security Plan
  • diff --git a/memory-update.md b/memory-update.md index 98d419c8..7c0bac65 100644 --- a/memory-update.md +++ b/memory-update.md @@ -194,3 +194,5 @@ - Added `src/tests/test_tui_v2_helpers.py` with coverage for palette parsing edge cases, pagination normalization, and large-vault pagination smoke sizes up to 50k rows. - 2026-03-01: Added Textual interaction tests in `src/tests/test_tui_v2_textual_interactions.py` for pagination/search, document edit+save, link add/remove + neighbor open, and retry-after-failure. - Introduced a test hook in `launch_tui2(..., app_hook=...)` so tests can capture and pilot the Textual app instance without starting a live terminal run. +- 2026-03-01: Added CI-like large-vault validation with explicit thresholds in `src/tests/test_tui_v2_large_vault_validation.py` (10k rows standard, 50k rows under `--stress`). +- Added docs `docs/tui_v2_large_vault_validation.md` with exact commands and budgets; parity checklist now marks large-vault CI-like validation complete. diff --git a/src/tests/test_tui_v2_large_vault_validation.py b/src/tests/test_tui_v2_large_vault_validation.py new file mode 100644 index 00000000..02d49c6a --- /dev/null +++ b/src/tests/test_tui_v2_large_vault_validation.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from time import perf_counter + +import pytest + +from seedpass.tui_v2.app import pagination_window, truncate_entry_for_display + + +def _deterministic_labels(rows: int) -> list[str]: + return [f"Entry-{i:06d}" for i in range(rows)] + + +@pytest.mark.parametrize( + ("rows", "max_seconds"), + [ + (10000, 2.0), + pytest.param(50000, 8.0, marks=pytest.mark.stress), + ], +) +def test_large_vault_pagination_validation(rows: int, max_seconds: float) -> None: + """CI-like pagination scan over deterministic large-vault datasets.""" + labels = _deterministic_labels(rows) + + start = perf_counter() + total_seen = 0 + page = 0 + seen_first = None + seen_last = None + while True: + page, start_idx, end_idx, total_pages = pagination_window(rows, 200, page) + if start_idx >= end_idx: + break + batch = labels[start_idx:end_idx] + assert len(batch) <= 200 + if seen_first is None: + seen_first = batch[0] + seen_last = batch[-1] + total_seen += len(batch) + if page >= total_pages - 1: + break + page += 1 + + elapsed = perf_counter() - start + + assert total_seen == rows + assert seen_first == "Entry-000000" + assert seen_last == f"Entry-{rows - 1:06d}" + assert ( + elapsed <= max_seconds + ), f"Large-vault pagination scan exceeded budget: {elapsed:.3f}s > {max_seconds:.3f}s" + + +@pytest.mark.parametrize( + ("content_size", "limit", "expected_truncated"), + [ + (4096, 4000, True), + (1000, 4000, False), + pytest.param(200000, 4000, True, marks=pytest.mark.stress), + ], +) +def test_large_document_detail_truncation_validation( + content_size: int, limit: int, expected_truncated: bool +) -> None: + """Ensure large document previews remain bounded and deterministic.""" + payload = truncate_entry_for_display( + { + "kind": "document", + "label": "Big Doc", + "content": "X" * content_size, + }, + limit, + ) + + assert payload["label"] == "Big Doc" + assert payload["kind"] == "document" + assert ("content_truncated" in payload) is expected_truncated + if expected_truncated: + assert payload["content"].startswith("X" * limit) + assert "truncated" in payload["content"] + else: + assert payload["content"] == "X" * content_size