Files
Password-Manager/js/tests/README.md
T
r-zakarya ca8081987d refactor(js): extract crypto module from app.js monofile (§3.1 start)
First slice of the app.js split. Approach: ordered classic-script files
loaded via separate <script> tags (argon2.js → app.crypto.js → app.js),
NOT ES modules / a bundler. Classic scripts share one global lexical
environment, so consts/functions cross-reference across files exactly as
in the monofile — zero call-site rewrites, near-zero risk. Chosen over the
audit's esbuild/ES-module suggestion because the code is written entirely
in global scope (functions call each other by bare name everywhere).

- js/app.crypto.js: KDF (PBKDF2 + Argon2id), verifier, AES-GCM encrypt/
  decrypt, key persist/restore. Verified byte-for-byte identical to the
  original block before removal; no duplicate const across the two scripts.
- index.html + BuildAssets whitelist + test harness updated for the load
  order. Harness CONCATENATES app.crypto.js + app.js (node:vm doesn't share
  top-level const across separate runInContext calls the way browsers share
  it across <script> tags); argon2.js stays a separate IIFE.
- Runtime-validated: rebuilt exe unlocks via quick-unlock and loads/decrypts
  entries — the extracted crypto (restoreCryptoKey, verifierFromKeyHex,
  decryptPwd) works from the separate file. 42/42 tests green.
- Docs: CLAUDE.md "Découpage frontend" (pattern + rules), file map, tests
  README, CODE_AUDIT §3.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 15:44:06 +01:00

63 lines
3.2 KiB
Markdown

# Frontend unit tests
Regression net for the highest-risk pure/near-pure logic in `js/app.js`:
crypto round-trip, KDF/verifier derivation, CSV import parsing, and the
sync-merge / tombstone-resurrection arbitration. Addresses `CODE_AUDIT.md`
§3.2 (aucun test automatisé).
## Running
```
npm test
# or directly:
node --test "js/tests/**/*.test.js"
```
Zero dependencies — uses the Node built-in test runner (`node:test`) and
`webcrypto`. Requires Node ≥ 18 (developed on v24). Runs in ~1.7 s.
## How it works — `harness.js`
The frontend is a large browser script with **no module exports** and one
top-level side effect (a `DOMContentLoaded` listener). It's being split into
ordered classic-script files (§3.1); the harness **concatenates** the app
parts in load order (`APP_PARTS` = `app.crypto.js` + `app.js`) into one
source — node:vm doesn't share top-level `const`/`let` across separate
`runInContext` calls the way the browser shares them across `<script>` tags.
`argon2.js` is a self-contained IIFE and loads separately first. The
concatenated source runs in a `node:vm` context with browser globals stubbed
(`crypto`, `localStorage`, `document`, `location`, …) so `init()` never
fires, then an export epilogue surfaces the internals on `globalThis.__test`.
Two gotchas the harness works around, both documented inline:
- **`const`/`let` don't attach to the vm global.** Top-level `function`/`var`
declarations become properties of the context global, but `const state`,
`const HASH_ALGO_V2`, etc. do not — hence the explicit export epilogue.
- **Cross-realm prototypes.** Values returned from the sandbox carry the
sandbox realm's prototypes, so `assert.deepStrictEqual` trips on the
prototype check. Structural comparisons normalize through JSON first
(see `eqDeep` in `csv.test.js`).
The merge tests stub only the `api()` seam (a `function` declaration →
overridable global property) with an in-memory fake server; `loadEntries`,
`loadFolders`, and `encryptImportEntry` all run for real against it — so the
tests exercise the actual pull→merge path, not a re-implementation.
## Suites
| File | Covers |
|---|---|
| `crypto.test.js` | `deriveKeyAndVerifier` (AES key == raw PBKDF2, cross-checked vs Node's `pbkdf2Sync`), legacy vs `-v2` verifier decoupling, `encryptPwd`/`decryptPwd` round-trip, IV uniqueness, AEAD tamper/wrong-key → `[ERROR]` |
| `csv.test.js` | `parseCSV` tokenizer (quotes, escaped `""`, CRLF, trailing field), `findColumn` header heuristics, `parseEntriesFromCSV` for Bitwarden/KeePass shapes, note-vs-login classification |
| `merge.test.js` | `applyRemoteSnapshot`: add/update/skip (last-write-wins), tombstone delete, resurrection arbitration (both `NaN` branches), local-tombstone veto, additive folder merge |
## Notes on encoded behavior
`merge.test.js` locks in one deliberately-asymmetric behavior: an unparseable
**local** `updated_at` favours KEEP (resurrection wins), but an unparseable
**remote** `deleted_at` still applies the delete. In production `deleted_at`
is always server-formatted (parseable), so this only matters for a corrupted
remote snapshot. If that arbitration is ever changed, the two
`unparseable …` tests are where to update the expectation.