Commit graph

3 commits

Author SHA1 Message Date
le king fu
b8e67c3d4f fix(license): apply the second review of #359
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m43s
PR Check — Rust / rust (pull_request) Successful in 8m54s
- The idempotent-deactivation test now pins the route's other 404:
  license_not_found stays an error. The previous negative case was a
  403 that the deactivate route never returns, so swallowing
  license_not_found (an unknown key shown as deactivated, the local
  token deleted) went unnoticed.
- A test deactivates this machine and checks that the re-read activation
  status brings the Activate button back. Removing that re-read used to
  leave the suite green.
- The Activate button stays disabled while the list loads
  (selectCanActivate), the counterpart of selectCanDeactivate: a verify
  answer landing after a successful activation can no longer mark as
  fresh a list that misses this machine.
- CHANGELOG: the old /simpl-resultat address is redirected by the site
  since la-compagnie-maximus#153, so the "Buy" bullet now says the
  button opens the product page directly, in the app's language. The
  test comment is moved to the past tense.

Proven red on the fixed tree: idempotence swallowing license_not_found
1, no status re-read after deactivation 1, Activate live during a list
load 1.

Refs #270

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 08:09:01 -04:00
le king fu
a051b96c87 fix(license): apply the review of #359
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m41s
PR Check — Rust / rust (pull_request) Successful in 8m48s
- A 404 without a known code (missing route, proxy error page) is now
  pinned as a server error by two table rows, so "key not found" can
  never be shown for it.
- The message-key test spells out the expected key of every code
  instead of comparing the map with itself, and checks the keys are
  distinct: swapping two entries now fails.
- Idempotent deactivation moves into deactivate_idempotent, without
  AppHandle, tested against a mock server (404 no_active_activation ->
  done, 403 revoked -> error).
- Deactivate buttons stay disabled while the list reloads
  (selectCanDeactivate), so a machine deactivated during a reload
  cannot reappear.
- The rate-limit message uses i18next plural forms (rateLimit_one /
  rateLimit_other with count): no more "1 seconds".
- CHANGELOG: drop the bullet about the edition updating after an
  activation. The edition does not change with or without an
  activation token, so nothing is observable; refresh() stays.

Proven red on the fixed tree: 404 without a known code read as
LicenseNotFound 1, license_expired mapped to the revoked key 2,
idempotence removed 1, buttons live during a reload 1, {seconds}
instead of {count} 2.

Refs #270

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 20:27:43 -04:00
le king fu
7d667f7ca3 fix(license): call /v1 routes with product, name server refusals, localize purchase URL
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m40s
PR Check — Rust / rust (pull_request) Successful in 8m52s
- Activation, deactivation and verify POST to /v1/licenses/* with
  "product":"simpl-resultat" instead of the legacy /licenses/* paths,
  which cost a 308 on every call.
- New LicenseError enum, serialized as {code} like FetchPriceError and
  read from error.code of the /v1 envelope. A 200 {valid:false} from
  verify is now a coded refusal instead of an empty machine list, and an
  HTML error page on a non-2xx maps to server instead of
  invalid_response. Deactivation is idempotent: no_active_activation
  counts as done.
- The three calls live in *_inner functions without AppHandle, tested
  against a mockito server that only declares the /v1 routes.
- useLicenseMachines hook with an exported reducer. The activation error
  survives the list load it triggers (machine_limit_reached opens the
  list), a deactivation removes the machine locally without re-running
  verify (4 requests on the machine-limit path, under the server's
  5/min/IP limit), and refresh() follows every successful activation or
  deactivation.
- license.errors.* in FR and EN; purchaseUrl() opens
  /{fr|en}/produits/simpl-resultat.

test: reproduces #270. Each original defect, reintroduced on the final
tree, turns tests red: legacy activate path 5, missing product 2,
envelope read as a string 3, verify valid:false as an empty list 2,
strict body parse on a non-2xx 2, untrimmed key 1, list load clearing
the activation error 4, verify re-run after deactivation 2, empty state
despite a list error 1, no refresh after activation 3, old purchase
URL 4.

Fixes #270

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 09:15:52 -04:00