Commit Graph
9 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 801923eb31 fix(funkwhale): let the log-out request finish before leaving the page
b6e3368a2 added the click on Funkwhale's own confirmation ("Yes, log me out!"), which fires POST /api/v2/users/logout. c7e5b0a27 then added the return to appBaseUrl so the assertion lands on Funkwhale rather than on the logout page. Each is right on its own; together the navigation aborts the request 9ms after it starts.

Measured in all three traces of job 95883448598: the POST carries status -1 with send/wait/receive all -1 - never answered, aborted. Click at 59744ms, POST at 59876ms, goto at 59885ms. The WebSocket that follows still carries sessionid=bam0ska8z0qkbtegsp10iot028cbcsr9, and openresty logs the matching 499. Only swarm+tor is slow enough to lose the race; the other seven Funkwhale variants of that run are green.

The catch keeps those seven safe: whether their confirm click fires the same XHR is unverified, and without it a missing POST would turn them red. Promise.all rather than sequential awaits because waitForResponse has to register before the click.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 16:41:09 +02:00
kevinveenbirkenbachandClaude Opus 5 553da9bb65 fix(funkwhale): match the username field it actually renders
Funkwhale's sign-in input carries id=username-field, name=username and autocomplete=username -- and no type attribute at all. An attribute selector does not match an element whose attribute is absent, so input[type='text'], input[type='email'] found zero nodes and locator.fill burned its full 30s timeout, three times, for both personas. The password field beside it does carry type=password, which is why the assertion one line above passed and only this one failed.

Adding an autocomplete=username arm matches the field without widening into input:not([type]): Funkwhale's header search box is typeless too, and .first() would then be decided by DOM order alone.

Transport does not enter into it: a selector that matches no node matches none over any route, and the run was not starved either -- the trace's slowest request is 360ms with the /login navigation at 107ms. The locator has been iterated twice before, scoped to the form and unscoped again, without anyone reaching the missing attribute.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 03:38:39 +02:00
kevinveenbirkenbachandClaude Opus 5 b6e3368a21 fix(funkwhale): click through Funkwhale's own log-out confirmation
Variant 2 of the swarm matrix fails the sign-out assertion for both personas,
and variant 0 fails the same assertion for a different reason. The artefacts of
run 31658241728 separate the two states. Variant 0's snapshot at failure is the
universal-logout terminal page (`- text: You have been logged out.`): the
session is gone and the test only had to return to the app, which f683d18b8
does.

Variant 2 stops one step earlier, for administrator and for biber alike:

  - heading "Are you sure you want to log out?" [level=2]
  - paragraph: You are currently logged in as administrator
  - button "Yes, log me out!"

Clicking the sidebar control routes Funkwhale to its own confirmation view, and
the session survives until that button is pressed. Returning to the app base and
asserting a sign-in offer therefore fails, and it would keep failing with
f683d18b8 alone -- that commit navigates away from the dialog without answering
it.

The new block presses the confirmation when it appears. One click covers both
personas, and the artefact lists no other failing test in the job.

Variant 0 is unaffected: its terminal page carries no button at all, so the probe
expires and the flow continues as before, at the cost of ten idle seconds.

The name regex is measured against the observed strings rather than assumed. It
matches "Yes, log me out!", "Log out", "Sign out" and "Logout", and rejects
"Log in" and "Sign in", so it cannot re-select the sign-in affordance that the
next assertion looks for.

Not verified: the click has not been run against a live instance. What is proven
is that the button is there and that the selector selects it, not that Funkwhale
ends the session afterwards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 02:02:30 +02:00
kevinveenbirkenbachandClaude Opus 5 c7e5b0a274 fix(funkwhale): assert the sign-in offer on Funkwhale, not on the logout page
The spec signed out and then asserted, on whatever page it had landed on, that
Funkwhale offers sign-in again. That page is never Funkwhale.

Both exits leave the app by design. The fallback navigates to
${appBaseUrl}/logout, but on every app vhost that exact path belongs to the
universal-logout service: roles/web-svc-logout/templates/logout-proxy.conf.j2
defines `location = /logout` with a proxy_pass, included through
sys-front-inj-all into the shared vhost. The in-app control is no better --
initLogoutPatch rewrites it to Keycloak's end-session URL with redirect_uri set
to the primary domain.

Measured in the run's trace rather than inferred:

  https://audio.infinito.example/logout   200, content-length 25
  body                                    "You have been logged out."
  clear-site-data                         "cache","cookies","storage"
  set-cookie                              sessionid=, csrftoken=, _oauth2_proxy=,
                                          _pk_* -- all Expires 1970, across the
                                          bare, app and parent domain scopes

So the sign-out itself works; the session really is gone. Only the assertion sat
on the wrong page, where no sign-in affordance can appear at any timeout.

Returning to appBaseUrl before the expectation puts the claim where it can be
true or false: the app, freshly anonymous. The locator is untouched -- 8aaac027d
and bfe7741c6 already settled how signInAffordance matches Funkwhale's bare-div
control, and this change does not revisit it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 02:02:29 +02:00
kevinveenbirkenbachandClaude Opus 5 e96c752549 Merge fix/playwright/env-template-gate into feature/svc-net-tor
75 commits: the socialhome role reimplemented against the project it actually
deploys, native-login specs for funkwhale and shopware, a window.open IdP
handoff and a settings-link fallback in the shared persona helpers, a swarm
converge budget that gives up on a terminal task, and a long tail of role
fixes.

Eleven conflicts. The shared persona helpers (keycloak.js, logout.js) take the
incoming structure — they gained real behaviour this branch never had — with
this branch's resolveTimeout convention re-applied over it and
confirmKeycloakLogoutIfPrompted re-attached at every return point; without it
the Keycloak SSO session outlives the logout. playwright.config.js keeps this
branch's version: both sides set actionTimeout, but ours routes through an env
var and 15s is too short behind an onion. socialhome's spec takes the incoming
content — 2dbd6eb10 found the role described the wrong upstream project
entirely — and keeps gotoOnion and expectHstsWhenTls, since demanding HSTS over
a plaintext onion is a false failure. Both READMEs were regenerated rather than
merged by hand.

Three collisions arrived with no conflict marker at all, because each side
changed a different place in a different file:

- group_vars/all/18_swarm.yml declared swarm_stack_converge_retries twice.
  Same value, so nothing breaks today; editing the documented one would have
  silently changed nothing.
- svc-net-tor and web-app-socialhome both claimed 192.168.74.0/24. Socialhome,
  the newer role, moves to 192.168.75.0/24.
- web-app-socialhome/meta/schema.yml arrived as a new file, so ef5ea7cb5's
  rename never saw it. Its credential would not have resolved and the role
  would have deployed with an unusable administrator account.

The incoming specs also needed this branch's conventions: raw timeouts wrapped,
four page.goto calls routed through gotoOnion, a request.get given an
onion-aware timeout, and 21 roles marked playwright-service-flag over the tor
block this branch added to them.

make quality green on the merge result.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 14:20:19 +02:00
kevinveenbirkenbachandClaude Opus 5 ff8a16a345 fix(funkwhale): create the database schema and wire the session cache
The role had no migration step anywhere in its history
(git log --all -S migrate over the role is empty). The api container runs
'command: gunicorn', which the image entrypoint execs straight into
without touching the database, so the schema was never created and
POST /api/v2/users/login died with
django.db.utils.ProgrammingError: relation "users_user" does not exist.

Adds a one-shot migrate service using the image's own documented
bootstrap. Verified against funkwhale/api:2.0.6 on an empty postgres:
'migrate' runs funkwhale-manage migrate without exec, applies the full
graph and exits 0, so the container reaches Shutdown/Complete and the
swarm converge gate's completed-oneshot carve-out accepts it. The same
probe showed the api container boots and stays up against an empty
schema rather than exiting, so it cannot trip the gate's fail-fast path
while migrate is still running.

CACHE_URL is the second blocker on the same request path: funkwhale keeps
sessions in the cache, so without it auth.login() writes to the image
default redis://127.0.0.1:6379/0 where nothing listens - the celeryworker
already proved that with ECONNREFUSED against that exact URL. Migrate
alone would have moved the 500 from the database layer to the session
layer.

LDAP_USER_ATTR_MAP was single-quoted JSON while funkwhale parses it with
env.list() then split(":"), which yielded keys like '{"username' and
populated no profile field.

The native-login spec asserted on the canonical domain as a substring.
Keycloak's authorization URL carries that domain inside redirect_uri and
oauth2-proxy serves /oauth2/* on the canonical host, so the poll could
never become true; it now requires the host AND the absence of the auth
chain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 23:56:08 +02:00
kevinveenbirkenbachandClaude Opus 5 8aaac027d2 fix(web-app-funkwhale): stop scoping the sign-in fields to a form element
bfe7741c6 anchored both credential fields to form:has(input[type=password]) to keep the username out of a header search box. Funkwhale renders no form element at all: the login chunk of funkwhale/front:2.0.6 emits four div and two span tags and nothing else, the inputs coming from a shared component. The locator therefore matched nothing and the spec would have failed on its first assertion, blaming the application for the author's mistake.

Return to the visible-scoped selectors the shopware spec uses, which did run green against a live stack. The search box that motivated the scoping was never evidenced: the failure artifact shows navigation links on the landing page, no visible text input. The URL poll added by the same commit is unaffected and stays.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 20:17:08 +02:00
kevinveenbirkenbachandClaude Opus 5 bfe7741c6f fix(web-app-funkwhale): scope the sign-in fields to the login form
The spec picked its username field with a global input[type=text]:visible .first(). Funkwhale's header carries a search field, so whenever that renders before the login input in DOM order the credentials would go into the search box and the run would fail later, at the password field, with a misleading message. Anchor both fields to form:has(input[type=password]) instead, letting the password field define which form is meant.

Also wait for the URL to leave the canonical domain before driving the Keycloak form. Without it a proxy that fails to intercept /login surfaces as a bare 60 second timeout inside the shared Keycloak helper, with nothing pointing at the missing redirect. The pattern is taken from roles/web-app-n8n/files/playwright/_shared.js, which runs the same chain and is green in CI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 15:24:12 +02:00
kevinveenbirkenbachandClaude Opus 5 f9bbf73a59 fix(web-app-funkwhale): block both personas, cover them natively
The shared persona helper cannot drive this role's sign-in in any matrix variant. The oauth2 ACL blacklists only /login, and sys-svc-proxy renders a blacklist as expose-everything-then-protect-the-listed-paths, so the helper's landing on / is public and the proxy never intercepts it. Funkwhale renders its own sign-in control as a bare div, which the helper's link and button role query cannot select. And application-level authentication is Funkwhale's LDAP form rather than OIDC, so even a completed Keycloak round trip at the proxy leaves the Funkwhale session anonymous.

The previous gate derived both blocks from services.sso.enabled, which unblocked the personas in exactly the variant where the chain does not work. The sibling jobs of run 31341444049 show it without interpretation: Funkwhale 0 with sso on fails, Funkwhale 2 with sso off passes, and it passes only because the block fires there.

The journey is not dropped. files/playwright/test-native-login.js drives both personas through Funkwhale's own chain: /login, the Keycloak round trip whenever the proxy gates that route, then Funkwhale's own sign-in form, an authenticated-surface assertion and sign-out back to the anonymous state. It skips when services.ldap.enabled is false, because no persona holds an account in that variant. Stage one reuses performKeycloakLoginForm from the shared helper without modifying it.

The selectors are structural rather than Funkwhale-specific: the upstream front-end source is not reachable from this environment, and the CI traces only ever captured the landing page, so the spec keys on input types the way the shared Keycloak helper does. The anonymous-state assertion is evidence-backed: the failure artifact shows the sidebar offering sign-in while unauthenticated. The spec has not yet run against a live stack.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 14:16:50 +02:00