Flaky under load: keycloak-admin user-list spec settles resources with a fixed hop count (reddens full-suite SonarQube scan); 9 sibling specs share the pattern #830

Open
opened 2026-07-24 19:44:19 +00:00 by spikerj · 4 comments
Owner

Symptom

Second failure reddening the SonarQube Scan job (the only job running the full nx run-many --target=test --coverage suite — see #829 for the first):

FAIL  keycloak-admin  libraries/keycloak-admin/src/lib/components/user-list/user-list.component.spec.ts
      > UserListComponent > stays idle until the admin permission check passes, then loads users and count
AssertionError: expected [] to deeply equal [ { id: 'u1', … }, … ]

Unlike #829 this one is a test-harness flake, not an app bug — the component is fine.

Root cause

The spec settles resources with a FIXED number of macrotask hops:

async function settle(): Promise<void> {
    TestBed.tick();
    await new Promise((r) => setTimeout(r, 0));
    TestBed.tick();
}
...
await settle(); // permission promise resolves -> resources fire
await settle(); // loader promises resolve

The component gates its httpResources on an async admin-permission check, so the chain is: permission promise → request computation updates → loader promise → value. Two hops is enough on an idle machine. Under the Sonar job (81 projects, --coverage, parallel) the chain does not complete within the allotted hops, so assertions run against an empty list. Passes in isolation and in a full single-project run locally — only the loaded full-suite run trips it.

Fix

Make settle() accept a predicate and keep hopping (bounded, then throw) until the awaited state actually arrives, e.g.:

await settle(() => fixture.componentInstance.users().length > 0);

Waiting on the observable outcome rather than a hop count removes the timing dependence entirely.

Related — latent, not yet failing

Nine sibling specs in libraries/keycloak-admin/src/lib/components/ carry the identical fixed-hop settle() helper (realm-events, realm-settings, clients-list, groups-list, roles-list, user-consents, user-federated-identity, user-detail, user-roles). They have the same latent flake; user-list is simply the one that lost the race first. Worth a follow-up sweep to the predicate form — I have deliberately NOT swept them in the fixing PR to keep it reviewable, so this ticket should stay open for that sweep even after user-list is fixed.

## Symptom Second failure reddening the `SonarQube Scan` job (the only job running the full `nx run-many --target=test --coverage` suite — see #829 for the first): ``` FAIL keycloak-admin libraries/keycloak-admin/src/lib/components/user-list/user-list.component.spec.ts > UserListComponent > stays idle until the admin permission check passes, then loads users and count AssertionError: expected [] to deeply equal [ { id: 'u1', … }, … ] ``` Unlike #829 this one is a **test-harness flake, not an app bug** — the component is fine. ## Root cause The spec settles resources with a FIXED number of macrotask hops: ```ts async function settle(): Promise<void> { TestBed.tick(); await new Promise((r) => setTimeout(r, 0)); TestBed.tick(); } ... await settle(); // permission promise resolves -> resources fire await settle(); // loader promises resolve ``` The component gates its `httpResource`s on an async admin-permission check, so the chain is: permission promise → request computation updates → loader promise → value. Two hops is enough on an idle machine. Under the Sonar job (81 projects, `--coverage`, parallel) the chain does not complete within the allotted hops, so assertions run against an empty list. Passes in isolation and in a full single-project run locally — only the loaded full-suite run trips it. ## Fix Make `settle()` accept a predicate and keep hopping (bounded, then throw) until the awaited state actually arrives, e.g.: ```ts await settle(() => fixture.componentInstance.users().length > 0); ``` Waiting on the observable outcome rather than a hop count removes the timing dependence entirely. ## Related — latent, not yet failing **Nine sibling specs in `libraries/keycloak-admin/src/lib/components/` carry the identical fixed-hop `settle()` helper** (realm-events, realm-settings, clients-list, groups-list, roles-list, user-consents, user-federated-identity, user-detail, user-roles). They have the same latent flake; user-list is simply the one that lost the race first. Worth a follow-up sweep to the predicate form — I have deliberately NOT swept them in the fixing PR to keep it reviewable, so this ticket should stay open for that sweep even after user-list is fixed.
Author
Owner

Fix for the user-list spec is up in spikersoft-angular PR #570 (fix/sonar-suite-red-jwt-race), alongside #829 since both were reddening the same job.

settle() now takes an optional predicate and hops (bounded at 50, then throws with a clear message) until the awaited state arrives — await settle(() => fixture.componentInstance.users().length > 0) instead of two blind hops. The negative/error cases wait on snackBar.open having been called, and the pagination/force-refresh cases wait on the specific follow-up call landing.

Leaving this ticket OPEN on purpose. PR #570 only fixes user-list.component.spec.ts — the one that actually lost the race. The nine sibling specs listed above still carry the identical fixed-hop helper and remain latent flakes; this ticket tracks that sweep. Closing it when #570 merges would lose that.

Full local nx run-many --target=test is green across all 81 projects, so user-list was the only sibling failing today.

— Opus 5 Agent

Fix for the user-list spec is up in spikersoft-angular **PR #570** (`fix/sonar-suite-red-jwt-race`), alongside #829 since both were reddening the same job. `settle()` now takes an optional predicate and hops (bounded at 50, then throws with a clear message) until the awaited state arrives — `await settle(() => fixture.componentInstance.users().length > 0)` instead of two blind hops. The negative/error cases wait on `snackBar.open` having been called, and the pagination/force-refresh cases wait on the specific follow-up call landing. **Leaving this ticket OPEN on purpose.** PR #570 only fixes `user-list.component.spec.ts` — the one that actually lost the race. The nine sibling specs listed above still carry the identical fixed-hop helper and remain latent flakes; this ticket tracks that sweep. Closing it when #570 merges would lose that. Full local `nx run-many --target=test` is green across all 81 projects, so user-list was the only sibling failing today. — Opus 5 Agent
Author
Owner

CI evidence: SonarQube Scan passed on PR #570 — first green scan today. That covers the user-list spec.

Reminder that this ticket stays open for the remaining sweep: nine sibling keycloak-admin specs still use the fixed-hop settle() and are latent flakes under a loaded full-suite run.

— Opus 5 Agent

CI evidence: `SonarQube Scan` passed on PR #570 — first green scan today. That covers the user-list spec. Reminder that this ticket stays open for the remaining sweep: nine sibling keycloak-admin specs still use the fixed-hop `settle()` and are latent flakes under a loaded full-suite run. — Opus 5 Agent
Author
Owner

Status: the user-list half is merged to master (PR #570, b3626e69) and master's full-suite SonarQube Scan is green again (run 18137 on 721d171b).

This ticket stays open for the remaining sweep. Nine sibling specs in libraries/keycloak-admin/src/lib/components/ still settle promise-loader resources with a fixed hop count and carry the same latent flake: realm-events, realm-settings, clients-list, groups-list, roles-list, user-consents, user-federated-identity, user-detail, user-roles.

They pass today, so this is not urgent — but the failure mode is nasty when it does hit: the only job running the full suite is the Sonar scan, which does not gate publish, so a flake there reddens master quietly and stales the Sonar metrics rather than blocking anything loudly. The fix is mechanical (predicate-based settle(), as in user-list) and would be one small PR.

— Opus 5 Agent

Status: the user-list half is merged to master (PR #570, `b3626e69`) and master's full-suite `SonarQube Scan` is green again (run 18137 on `721d171b`). **This ticket stays open for the remaining sweep.** Nine sibling specs in `libraries/keycloak-admin/src/lib/components/` still settle promise-loader resources with a fixed hop count and carry the same latent flake: realm-events, realm-settings, clients-list, groups-list, roles-list, user-consents, user-federated-identity, user-detail, user-roles. They pass today, so this is not urgent — but the failure mode is nasty when it does hit: the only job running the full suite is the Sonar scan, which does not gate `publish`, so a flake there reddens master quietly and stales the Sonar metrics rather than blocking anything loudly. The fix is mechanical (predicate-based `settle()`, as in user-list) and would be one small PR. — Opus 5 Agent
Author
Owner

Re-verified against origin/masteronly the named spec was fixed; all nine siblings still carry the blind two-hop helper. Staying open, which matches what you asked for in comments 12842/12874.

Fixed (angular PR #570, b3626e69): libraries/keycloak-admin/src/lib/components/user-list/user-list.component.spec.ts:36-44settle(until?) with a predicate, bounded at 50 hops, throwing on timeout.

Still on the fixed-hop helper, with line numbers so the sweep is mechanical:

  • realm-settings/realm-events.component.spec.ts:22-26
  • realm-settings/realm-settings.component.spec.ts:27-31
  • clients-list/clients-list.component.spec.ts:25-29
  • groups-list/groups-list.component.spec.ts:25-29
  • roles-list/roles-list.component.spec.ts:31-35
  • user-consents/user-consents.component.spec.ts:24-28
  • user-federated-identity/user-federated-identity.component.spec.ts:26-30
  • user-detail/user-detail.component.spec.ts:27-31
  • user-roles/user-roles.component.spec.ts:28-32

All nine are the same shape as the pre-fix user-list helper, so this is a copy of the same edit nine times rather than nine separate investigations. Until they're swept, the full-suite SonarQube scan stays exposed to the same load-dependent flake — the fix so far removes one of ten instances.

Tangentially: user-roles.component.spec.ts is also the spec for #762 (both role-search boxes wired to nothing), so whoever picks up either could reasonably do both in one pass.

Re-verified against `origin/master` — **only the named spec was fixed; all nine siblings still carry the blind two-hop helper.** Staying open, which matches what you asked for in comments 12842/12874. **Fixed** (angular PR #570, `b3626e69`): `libraries/keycloak-admin/src/lib/components/user-list/user-list.component.spec.ts:36-44` — `settle(until?)` with a predicate, bounded at 50 hops, throwing on timeout. **Still on the fixed-hop helper**, with line numbers so the sweep is mechanical: - `realm-settings/realm-events.component.spec.ts:22-26` - `realm-settings/realm-settings.component.spec.ts:27-31` - `clients-list/clients-list.component.spec.ts:25-29` - `groups-list/groups-list.component.spec.ts:25-29` - `roles-list/roles-list.component.spec.ts:31-35` - `user-consents/user-consents.component.spec.ts:24-28` - `user-federated-identity/user-federated-identity.component.spec.ts:26-30` - `user-detail/user-detail.component.spec.ts:27-31` - `user-roles/user-roles.component.spec.ts:28-32` All nine are the same shape as the pre-fix `user-list` helper, so this is a copy of the same edit nine times rather than nine separate investigations. Until they're swept, the full-suite SonarQube scan stays exposed to the same load-dependent flake — the fix so far removes one of ten instances. Tangentially: `user-roles.component.spec.ts` is also the spec for #762 (both role-search boxes wired to nothing), so whoever picks up either could reasonably do both in one pass.
Sign in to join this conversation.