Skip to content

fix(ui): reuse existing UserId when replacing alias entry (#114 followup)#120

Merged
iduartgomez merged 1 commit intomainfrom
fix/userid-reuse-on-replace-114-followup
May 6, 2026
Merged

fix(ui): reuse existing UserId when replacing alias entry (#114 followup)#120
iduartgomez merged 1 commit intomainfrom
fix/userid-reuse-on-replace-114-followup

Conversation

@iduartgomez
Copy link
Copy Markdown
Contributor

Summary

The #114 dedup fix replaced ALIASES + user.identities entries in place when the alias already existed, but generated a fresh `UserId::new()` each time.

`User::set_logged_id` asserts `id.0 < self.identities.len()` and `logged_id()` indexes by `id.0`, so the UserId must equal the slot index. Replacing slot 0 with an Identity carrying `UserId(N>=1)` panics on the next `set_logged_id` call:

```
assertion failed: id.0 < self.identities.len()
```

Surfaced by an FREENET_LIVE_E2E_SEND=1 iso harness run during #81 followup verification — test 2 panicked once the dedup path was exercised on a delegate-restored alias.

Fix

Reuse the existing slot's UserId on replace; generate a new one only when pushing into a fresh slot. Both `set_alias` (manual create) and `set_aliases` (delegate restore) updated.

Test plan

…wup)

#114 fix replaced ALIASES + user.identities entries in place when the
alias already existed, but generated a fresh UserId::new() each time.

`User::set_logged_id` asserts `id.0 < self.identities.len()` and
`logged_id()` indexes by `id.0`, so the UserId must equal the slot
index. With a fresh UserId(N) replacing a slot at index 0, the next
login asserts `1 < 1` and panics with `id.0 < self.identities.len()`.

Reuse the existing slot's UserId on replace; generate a new one only
when pushing into a fresh slot.

Surfaced by FREENET_LIVE_E2E_SEND=1 iso harness test 2:
  assertion failed: id.0 < self.identities.len()
@iduartgomez iduartgomez merged commit 04428de into main May 6, 2026
4 checks passed
iduartgomez added a commit that referenced this pull request May 6, 2026
…on (#81) (#123)

#114 + #115 + #120 fixes plus a spec correction (use page.reload()
instead of page.goBack(), since the SPA doesn't push history entries
on click — goBack landed on about:blank rather than the inbox)
make tests 2 + 3 deterministically green on the iso harness. Drop
the FREENET_LIVE_E2E_SEND gate so they run by default.

Two assertions stay gated as separate concerns:

- Test 3 round 3 (alice → bob retry) gated on
  FREENET_LIVE_E2E_AFT_CAP_RAISED — AFT day-1 cap is 1 slot, alice
  burned hers in round 1; needs #85 to make tier configurable.
- Test 3 round 2 (bob → alice reply) gated on FREENET_LIVE_E2E_REPLY
  — flaky ~33%, separate flake tracked in #122. Click-to-read +
  reload assertion (the original #113 target) still runs and passes
  deterministically.

Verified locally via 3 consecutive `cargo make test-e2e-real-node`
runs, all passing in ~12s each.
@iduartgomez iduartgomez deleted the fix/userid-reuse-on-replace-114-followup branch May 6, 2026 16:36
iduartgomez added a commit that referenced this pull request May 6, 2026
* test(e2e): flip FREENET_LIVE_E2E_SEND off + fix click-to-read assertion (#81)

#114 + #115 + #120 fixes plus a spec correction (use page.reload()
instead of page.goBack(), since the SPA doesn't push history entries
on click — goBack landed on about:blank rather than the inbox)
make tests 2 + 3 deterministically green on the iso harness. Drop
the FREENET_LIVE_E2E_SEND gate so they run by default.

Two assertions stay gated as separate concerns:

- Test 3 round 3 (alice → bob retry) gated on
  FREENET_LIVE_E2E_AFT_CAP_RAISED — AFT day-1 cap is 1 slot, alice
  burned hers in round 1; needs #85 to make tier configurable.
- Test 3 round 2 (bob → alice reply) gated on FREENET_LIVE_E2E_REPLY
  — flaky ~33%, separate flake tracked in #122. Click-to-read +
  reload assertion (the original #113 target) still runs and passes
  deterministically.

Verified locally via 3 consecutive `cargo make test-e2e-real-node`
runs, all passing in ~12s each.

* feat(ui): refuse-with-error on alias collision in Create/Restore (#117)

Before submitting a NodeAction::CreateIdentity for a new alias name,
check Identity::get_alias(name). If it returns Some, refuse with an
inline error banner instead of advancing through keygen → reveal →
delegate write.

Two paths are guarded:

  - CreateAliasForm::submit — Create new identity. Rejects before
    keygen (no burned keys for a refused name).
  - ImportForm::onclick (Restore) — refuses to overwrite an existing
    identity record from a backup file, since the backup's keys would
    silently replace the on-device delegate entry and orphan the
    previous inbox.

Mirrors the existing rename collision guard at
\`Identity::rename_in_place\`. The two flows are now consistent:
explicit two-step (delete then create/restore) is required to swap an
alias, and there is no UI path that silently destroys access to a
prior inbox.

Adds an offline-mode Playwright test that submits "address1" — already
seeded — and asserts the banner appears and the fingerprint reveal
does not.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant