test(infra): implement TESTING_IMPROVEMENTS.md testing roadmap #15

Merged
gabogg merged 15 commits from test/infra-issues into dev 2026-07-08 18:04:00 +00:00
Owner

This PR implements docs/TESTING_IMPROVEMENTS.md:

  • Phase 1: Quick Wins — Swapped @DirtiesContext for @Transactional, raised JaCoCo to 50%, extracted Tauri pure-logic to local path crate.
  • Phase 2: Reliability — Added PostgreSQL Testcontainers, migrated all 13 backend integration tests, implemented SettingsPage.svelte URL switching and Vitest component tests.
  • Phase 3: Thoroughness — Enabled auto-starting Svelte dev server in Playwright, resolved E2E login/port redirection and Svelte pagination contract bugs, and added search filtering to Categories CRUD E2E tests.
  • Phase 4: Speed & CI — Configured Forgejo Actions to run Maven, Node/Vitest, and Cargo tests in parallel jobs.

All 277 tests pass 100%!

This PR implements docs/TESTING_IMPROVEMENTS.md: - **Phase 1: Quick Wins** — Swapped @DirtiesContext for @Transactional, raised JaCoCo to 50%, extracted Tauri pure-logic to local path crate. - **Phase 2: Reliability** — Added PostgreSQL Testcontainers, migrated all 13 backend integration tests, implemented SettingsPage.svelte URL switching and Vitest component tests. - **Phase 3: Thoroughness** — Enabled auto-starting Svelte dev server in Playwright, resolved E2E login/port redirection and Svelte pagination contract bugs, and added search filtering to Categories CRUD E2E tests. - **Phase 4: Speed & CI** — Configured Forgejo Actions to run Maven, Node/Vitest, and Cargo tests in parallel jobs. All 277 tests pass 100%!
Analysis covers architecture, security audit, code quality issues,
potential bug fixes, and recommendations for the entire system.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
- Correct entity count: 15 JPA entities + 3 enums
- Correct controller count: 16 total
- Add finding about inconsistent @Transactional across controllers
  (StateController, CategoryController, PermissionController missing it)

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
- Fix line number reference for commitDelete dead code
- Add bug finding: globally unique item.name prevents cross-branch inventory
- Verified all findings against actual source code
- Confirmed 213/213 tests passing

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Security fixes:
- Require ADMIN_PASSWORD env var at startup (fail hard if not set)
- Replace JWT byte-repeat key derivation with SHA-256 hashing
- Align CORS default between code and config
- Add tokenVersion to User entity for JWT revocation (V5 migration)

Code quality fixes:
- Extract service layer: 12 domain services with @Transactional boundaries
- Move @Transactional from controllers to services
- Remove dead code in AuditService.commitDelete
- Move PageUtil from web/ to common/ package
- Inject app version from build properties instead of hardcoding

Bug fixes:
- Check ADMIN_PASSWORD on re-seed + increment tokenVersion on password change
- Keep item quantity at 0 instead of deleting on disincorporation/adjustment
- Add pre-validation of entry fields matching request type in executeRequest
- Change Item.name unique constraint to composite (name, branch_id) (V6 migration)
- Fix BagController audit: use long arithmetic, remove Optional wrapper
- Use configurable constant for default Inbound department name

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Flaw 11 - Repetitive @JsonIgnoreProperties:
Remove 'hibernateLazyInitializer' and 'handler' from all entity
@JsonIgnoreProperties annotations — Hibernate6Module handles
these globally via Jackson configuration.

Flaw 12 - Fragile bag barcode lookup:
Change endpoint from /api/bags/barcode/{barcode} (@PathVariable)
to /api/bags/by-barcode?barcode= (@RequestParam) for better
handling of special characters in barcodes.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Each finding now shows whether it's been fixed (✅) and references
PR #8 (fix/codebase-flaws). Recommendations section updated to
show all items as completed. Barcode endpoint updated in flow doc.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Remove the JavaFX desktop frontend and all associated files as it's
being rebuilt from scratch with a different technology stack.

Changes:
- Delete frontend/ directory (source, tests, i18n, pom.xml, README)
- Remove frontend module from root pom.xml
- Remove javafx.version property from root pom.xml
- Remove frontend version check from .github/workflows/ci.yml
- Remove frontend JAR build/upload from .github/workflows/release.yml
- Remove frontend JAR build from .forgejo/workflows/release.yml
- Remove frontend/pom.xml from release-please-config.json extra-files
- Remove frontend references from scripts/release.sh
- Remove frontend references from README.md, docs/, setup-server.sh
- Remove DEMO-GUIDE.md (frontend-specific), STYLE-GUIDE.md (JavaFX)
- Remove staged_diff patch files and pom.xml.versionsBackup
- Update copilot-instructions.md and test_mcp.js

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Reviewed-on: #9
Covers full architecture breakdown, technology rationale, development
standards, styling standards, testing/QA strategy, barcode vs QR
analysis, build optimization & tree-shaking, dependency manifest,
API contract compatibility, and phased implementation plan.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Full implementation of the desktop inventory manager:
- Svelte 5 frontend with dark-mode-first UI, keyboard-driven design
- Rust backend with Tauri commands (login, API proxy, auth storage)
- 12 CRUD views: items, bags, branches, departments, categories,
  locations, users, roles, permissions, displacements, item requests
- Barcode/QR scanner page (BarcodeDetector API + WASM fallback)
- Audit log viewer with entity/ID search
- Dashboard with entity counts
- ESBuild-minified bundle: 71 KB JS + 7 KB CSS

Test suites (13/13 passing, ~150ms):
- auth (4 tests): init, login, clear, error handling
- api client (4 tests): URL construction, error handling, CRUD paths
- router (3 tests): navigation, params, back
- components (2 tests): toast API, table filtering/sorting

E2E (Playwright):
- Login page rendering, empty-submit error, form visibility
- Tauri driver integration script (tauri-e2e.sh)

CI/CD:
- .github/workflows/desktop.yml: svelte-check + vitest +
  cargo clippy/test + Playwright E2E
- Svelte TS strict mode, Rust stable

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Rust (api.rs):
- Refactored HTTP logic into testable api module (no Tauri deps)
- 10 unit tests: URL building, base URL resolution, token extraction
- auth.rs tests: serialization, config defaults, roundtrip

JS integration tests (api.integration.test.js):
- 6 tests against real Spring backend with H2 test DB
- Auth flow: login (valid + invalid), GET /auth/me, CRUD endpoints
- Isolated from unit tests via vitest.integration.config.js

Test runner (test-helpers.sh):
- Starts Spring backend in test profile on port 14000
- Waits for health check, runs integration tests, stops gracefully
- Works as standalone script or sourced for modular use

CI (desktop.yml):
- Added backend-integration job: installs Java 21, starts backend,
  runs vitest integration + Playwright E2E against live backend
- Only runs on PRs (10min timeout)

Test results:
- Unit: 13/13 (~100ms)
- Integration: 6/6 against real backend (~650ms)
- Build: 71KB JS + 7KB CSS

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
- Fix api.js BASE_URL: strip trailing /api from VITE_API_URL so
  paths like /api/items produce http://host/api/items (no double prefix)
- Fix api-invoke.js: add ReferenceError guard for process.env in browser
- Add httpFallback to api-invoke.js for browser/Tauri bridge commands
  (store_auth, get_stored_auth, clear_auth fall to localStorage)
- Add test:e2e:headed npm script for visual Playwright testing
- Fix CORS: test-helpers.sh now passes --app.cors-origin=$CORS_ORIGIN
  (defaults to http://localhost:1420 for Vite dev server)
- Update Playwright E2E to test full login + navigate all pages + logout

E2E verified: headed browser session completes login, visits all 9
CRUD pages, scanner, audit log, and returns to login screen.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Test suite (23.3s headed, 12/12 passing):

Login + Dashboard:
- Login form accepts credentials, dashboard shows stat cards
- All 7 entity pages render data tables with seed data
  (Branches, Departments, Categories, Items, Users, Bags)

Categories CRUD (only entity where generic inline form works):
- Create via UI modal, verify in table and via API
- Edit name, verify update in table and API
- Delete, verify removed from table and API

Scanner + Audit pages:
- Scanner page renders camera button and help text
- Audit log page renders entity selector and search

Bug fixes discovered during testing:
- getToken() was async causing "Bearer [object Promise]" header
- ListPage response parsing lacked `data` field support
- DashboardPage lacked `total` field support
- api-invoke.js lacked browser/http fallback for Tauri commands
- CORS needed explicit --app.cors-origin for dev server
- BASE_URL double-/api prefix stripping

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Complete E2E test suite covering every functional area:

1. Login flow + Dashboard stat cards
2. All 7 entity page renderings (Branches, Categories, Items, Bags,
   Roles, Users, Departments)
3. Permissions table
4. Locations page
5. Categories full CRUD: create, edit, delete
6. Filter/search within data tables (match + empty state)
7. Pagination button visibility
8. Item inline form modal (name, description, quantity fields)
9. Scanner page (camera button, help text)
10. Audit log search controls
11. All 12 sidebar nav items present + navigable without errors
12. API CRUD verification for all 5 entity types (categories,
    departments, items, branches, bags)

27/27 tests passing, 42.4s headed, real backend + H2 test DB + seed data.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
- TESTING_BEST_PRACTICES_RESEARCH.md: 1,877-line research covering
  Spring Boot, Vitest/Svelte, Tauri, Playwright, Rust, and CI/CD
  best practices with concrete config snippets and code examples
- TESTING_IMPROVEMENTS.md: actionable 5-phase plan with effort/impact
  matrix, addressing current issues (H2 vs PG, DirtiesContext,
  stale E2E, no CI, missing system deps)
test(phase 3 & 4): fix E2E login and Svelte pagination bugs, run all tests in parallel CI
Some checks failed
CI / backend-test (pull_request) Successful in 1m33s
CI / frontend-test (pull_request) Failing after 7s
CI / rust-test (pull_request) Failing after 14s
5006859879
gabogg changed target branch from master to dev 2026-07-08 14:17:56 +00:00
ci: trigger workflow on push to test/infra-issues and pull requests to dev
Some checks failed
CI / backend-test (push) Successful in 1m21s
CI / frontend-test (push) Successful in 40s
CI / rust-test (push) Failing after 35s
CI / backend-test (pull_request) Successful in 1m19s
CI / frontend-test (pull_request) Successful in 16s
CI / rust-test (pull_request) Failing after 9s
8c5e751d12
ci: prevent duplicate workflow runs by removing feature branch push trigger
Some checks failed
CI / backend-test (pull_request) Successful in 1m21s
CI / frontend-test (pull_request) Successful in 16s
CI / rust-test (pull_request) Failing after 9s
08bd3c7d37
ci: use local target-dir in rust container to avoid permission issues
Some checks failed
CI / backend-test (pull_request) Successful in 1m15s
CI / frontend-test (pull_request) Successful in 16s
CI / rust-test (pull_request) Failing after 8s
fcd7f13d4b
ci: set CARGO_HOME to writable tmp directory in rust container
Some checks failed
CI / backend-test (pull_request) Successful in 1m16s
CI / frontend-test (pull_request) Successful in 16s
CI / rust-test (pull_request) Failing after 9s
11648a7bb6
ci: use latest rust image to ensure compile compatibility with newer crates
All checks were successful
CI / backend-test (pull_request) Successful in 1m15s
CI / frontend-test (pull_request) Successful in 15s
CI / rust-test (pull_request) Successful in 58s
86b61973a0
Author
Owner

Review: test/infra-issues → dev

Good work getting all 5 phases implemented. Here is an adversarial review.


RED - Critical (will break in production)

1. api.js 1-indexed page param breaks backend pagination

api.js does page + 1 and renames size to pageSize. The Spring Boot backend uses @PageableDefault which expects 0-indexed pages and a parameter called size, not pageSize. The frontend now sends page=1&pageSize=50 for page 0, but the backend ignores pageSize (it reads size) and treats page=1 as the second page.

Fix: Either revert the param mapping (keep 0-indexed + send size), or set spring.data.web.pageable.one-indexed-parameters: true in the backend and add a size alias for pageSize.

2. globalThis.INVENTORY_API_URL in apiFetch() bypasses the reactive store

apiFetch() reads globalThis.INVENTORY_API_URL directly and ignores baseUrl. If a user changes the URL via SettingsPage and navigates pages, the E2E override persists and the store value is never used. The E2E injection should set the store (baseUrl.set(...)) instead of using a global bypass.

3. @Transactional on AbstractIntegrationTest breaks JaVers auditing tests

JaVers commits snapshots on transaction commit. @Transactional rolls back after each test, so JaVers snapshots are never persisted. Tests querying jv_snapshot will silently pass on H2 with zero results — a false positive. The old code deliberately didn't have class-level @Transactional.

Fix: Remove @Transactional from AbstractIntegrationTest. Use @Sql or @BeforeEach cleanup per test class instead. Apply @Transactional only on specific test classes that don't verify auditing.


YELLOW - Should Fix

4. extract_token_field test has wrong expected value

In pure-logic/src/api.rs (~line 266):

let body = serde_json::json!({ "token": "***", "user": {} });
assert_eq!(extract_token_field(&body, "token"), Some("jwt123".to_string()));

The JSON has "token": "***", the test expects "jwt123". This fails at runtime with Some("***"). Trivial copy-paste error but will block cargo test.

5. CI workflow embeds token in git URL

Forgejo CI uses https://gabogg:${TOKEN}@git.gaboggamer.online/${GITHUB_REPOSITORY}.git in YAML committed to the repo. Even if Forgejo masks the value in logs, the pattern is bad practice. Use actions/checkout@v4 which handles auth via the built-in GITHUB_TOKEN automatically.

6. set_config in api-invoke.js stores apiBaseUrl without normalizing

localStorage.setItem('api_base_url', args.apiBaseUrl || args.api_base_url) stores the raw value. If someone passes http://server:8080/api/ (trailing slash), api.js code that strips /api won't match. Normalize the URL before storing (strip trailing slash, strip /api).

7. page.addInitScript timing race with module-level loadSavedUrl()

The E2E beforeEach sets globalThis.INVENTORY_API_URL via addInitScript, but api.js loadSavedUrl() runs at module import time — potentially before the script injection. If the module is already cached, the override never applies. Safer: set localStorage.setItem('backend_url', ...) in beforeEach instead, which loadSavedUrl() reads synchronously.

8. Check for admiral typo in CI workflow

The workflow file has a typo somewhere (admiral instead of admin). Should be harmless but worth fixing.


GREEN - Good

  • Pure-logic crate extraction is clean. Re-export via pub use desktop_pure_logic::* keeps the Tauri layer thin.
  • AbstractIntegrationTest reduces boilerplate nicely across 15+ test classes.
  • SettingsPage tests with @testing-library/svelte cover the new feature well.
  • Filter-by-name in CRUD E2E tests fixes pagination ambiguity correctly.
  • CI parallelization (3 jobs) is exactly right.
  • JaCoCo 50% threshold is aggressive but reasonable.
  • @DirtiesContext removal is the single biggest speed improvement.
  • Removing lib_test.rs from the Tauri crate is correct — tests moved to pure-logic.

Summary

Severity Count
Critical 3 (#1, #2, #3)
Should fix 5 (#4-#8)

Verdict: Fix #1 (page indexing) and #3 (@Transactional) before merging. #4 is a test bug that fails on first cargo test. The rest are important but not blocking.

## Review: test/infra-issues → dev Good work getting all 5 phases implemented. Here is an adversarial review. --- ### RED - Critical (will break in production) **1. `api.js` 1-indexed page param breaks backend pagination** `api.js` does `page + 1` and renames `size` to `pageSize`. The Spring Boot backend uses `@PageableDefault` which expects **0-indexed pages** and a parameter called `size`, not `pageSize`. The frontend now sends `page=1&pageSize=50` for page 0, but the backend ignores `pageSize` (it reads `size`) and treats `page=1` as the second page. **Fix**: Either revert the param mapping (keep 0-indexed + send `size`), or set `spring.data.web.pageable.one-indexed-parameters: true` in the backend and add a `size` alias for `pageSize`. **2. `globalThis.INVENTORY_API_URL` in `apiFetch()` bypasses the reactive store** `apiFetch()` reads `globalThis.INVENTORY_API_URL` directly and ignores `baseUrl`. If a user changes the URL via SettingsPage and navigates pages, the E2E override persists and the store value is never used. The E2E injection should set the store (`baseUrl.set(...)`) instead of using a global bypass. **3. `@Transactional` on `AbstractIntegrationTest` breaks JaVers auditing tests** JaVers commits snapshots on transaction commit. `@Transactional` rolls back after each test, so JaVers snapshots are never persisted. Tests querying `jv_snapshot` will **silently pass on H2 with zero results** — a false positive. The old code deliberately didn't have class-level `@Transactional`. **Fix**: Remove `@Transactional` from `AbstractIntegrationTest`. Use `@Sql` or `@BeforeEach` cleanup per test class instead. Apply `@Transactional` only on specific test classes that don't verify auditing. --- ### YELLOW - Should Fix **4. `extract_token_field` test has wrong expected value** In `pure-logic/src/api.rs` (~line 266): ```rust let body = serde_json::json!({ "token": "***", "user": {} }); assert_eq!(extract_token_field(&body, "token"), Some("jwt123".to_string())); ``` The JSON has `"token": "***"`, the test expects `"jwt123"`. This fails at runtime with `Some("***")`. Trivial copy-paste error but will block `cargo test`. **5. CI workflow embeds token in git URL** Forgejo CI uses `https://gabogg:${TOKEN}@git.gaboggamer.online/${GITHUB_REPOSITORY}.git` in YAML committed to the repo. Even if Forgejo masks the value in logs, the pattern is bad practice. Use `actions/checkout@v4` which handles auth via the built-in `GITHUB_TOKEN` automatically. **6. `set_config` in `api-invoke.js` stores `apiBaseUrl` without normalizing** `localStorage.setItem('api_base_url', args.apiBaseUrl || args.api_base_url)` stores the raw value. If someone passes `http://server:8080/api/` (trailing slash), `api.js` code that strips `/api` won't match. Normalize the URL before storing (strip trailing slash, strip `/api`). **7. `page.addInitScript` timing race with module-level `loadSavedUrl()`** The E2E `beforeEach` sets `globalThis.INVENTORY_API_URL` via `addInitScript`, but `api.js` `loadSavedUrl()` runs at module import time — potentially before the script injection. If the module is already cached, the override never applies. Safer: set `localStorage.setItem('backend_url', ...)` in `beforeEach` instead, which `loadSavedUrl()` reads synchronously. **8. Check for `admiral` typo in CI workflow** The workflow file has a typo somewhere (`admiral` instead of `admin`). Should be harmless but worth fixing. --- ### GREEN - Good * Pure-logic crate extraction is clean. Re-export via `pub use desktop_pure_logic::*` keeps the Tauri layer thin. * `AbstractIntegrationTest` reduces boilerplate nicely across 15+ test classes. * SettingsPage tests with `@testing-library/svelte` cover the new feature well. * Filter-by-name in CRUD E2E tests fixes pagination ambiguity correctly. * CI parallelization (3 jobs) is exactly right. * JaCoCo 50% threshold is aggressive but reasonable. * `@DirtiesContext` removal is the single biggest speed improvement. * Removing `lib_test.rs` from the Tauri crate is correct — tests moved to pure-logic. --- ### Summary | Severity | Count | |----------|-------| | Critical | 3 (#1, #2, #3) | | Should fix | 5 (#4-#8) | **Verdict**: Fix #1 (page indexing) and #3 (@Transactional) before merging. #4 is a test bug that fails on first `cargo test`. The rest are important but not blocking.
fix: address PR review comments including @Transactional removal and actions/checkout integration
Some checks failed
CI / backend-test (pull_request) Failing after 4s
CI / frontend-test (pull_request) Successful in 17s
CI / rust-test (pull_request) Failing after 2s
c1dee9e031
ci: use manual checkout with git http.extraheader to fix containers lacking Node.js
All checks were successful
CI / backend-test (pull_request) Successful in 2m8s
CI / frontend-test (pull_request) Successful in 17s
CI / rust-test (pull_request) Successful in 26s
1299513cf1
fix: refactor backend and frontend pagination to use standard 0-indexed page and size
All checks were successful
CI / backend-test (pull_request) Successful in 2m9s
CI / frontend-test (pull_request) Successful in 19s
CI / rust-test (pull_request) Successful in 27s
82a240ffa5
test: update extract_token_field test in pure-logic to use ***
All checks were successful
CI / backend-test (pull_request) Successful in 2m7s
CI / frontend-test (pull_request) Successful in 18s
CI / rust-test (pull_request) Successful in 26s
2578c1d4a7
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
PCivil/inventory-system!15
No description provided.