Commit Graph
4 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 303c47fe2a fix(test-e2e-playwright): retry transient onion transport errors for every API verb, not only GET
0a52433dcb gave the standalone request fixture a Tor-resilient GET,
because the bundled socks client caps the CONNECT at 30s and a cold
onion circuit fails a request regardless of its timeout. POSTs and calls
made through context.request still went out once, and a circuit Tor
rejected ("Socks5 proxy rejected connection") was not recognised as
transient at all.

apiFetchOnion now takes the verb from opts.method and calls
request.fetch, so it serves both APIRequestContexts; apiGetOnion becomes
its GET shorthand. The rejection joins the transient patterns. Retries
stay onion-only and transport-only: clearnet keeps a single attempt and
HTTP or other errors re-throw at once.

The Mattermost LiteLLM spec routes its login, users/me, users/username
and channels/direct calls through it. The /posts call that sends the
prompt stays a single attempt, because a replayed post would message the
bot twice. The Hermes administrator spec reads /v1/models through
apiGetOnion.

env.test.js pins a POST surviving a rejected circuit, the single
clearnet attempt, no retry of a non-transport error over Tor, and the
GET shorthand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 12:05:02 +02:00
kevinveenbirkenbachandClaude Opus 5 347904c04a test(mattermost): keep polling for the bot reply through a transport error
firstBotReply already treats an HTTP error as "no reply yet" and returns an
empty string, but a transport failure throws out of context.request.get, and
expect.poll does not catch what its callback throws, so the attempt ends
there instead of at the poll's deadline. Over tor that is routine. Job
102705652088 of run 34421944328 lost three retries to it, each thrown at
test-litellm-agent.js:54 from inside the poll (:142):

    TimeoutError: apiRequestContext.get: Timeout 30000ms exceeded.   after 6.2 of 15 minutes
    Error: apiRequestContext.get: Socks5 proxy rejected connection - Failure   twice

A failed request now counts like a failed response: the poll asks again two
seconds later and only its deadline decides. A reply is still required, so a
transport that never recovers still fails the test, with the poll's message
rather than the last transport error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 11:08:26 +02:00
kevinveenbirkenbachandClaude Opus 5 45a138d1a6 fix(mattermost): stop the session cookie defeating header auth in the specs
Mattermost classifies a request by the session cookie it carries, not by the
Authorization header, and enforces CSRF on non-GET requests for a
cookie-authenticated session.

test-litellm-agent.js took its token out of the MMAUTHTOKEN cookie on the
OIDC path, so every later context.request sent cookie and bearer together.
GET /users/me and the bot lookup returned 200 while POST /channels/direct
came back 401, and the spec died before it could prompt anything. Clear the
cookies once the token is in hand so only header auth remains.

mattermostLogout only navigated to /logout with waitUntil "commit" and
returned immediately, so the session survived and the spec still landed on
/main/channels/town-square afterwards. Perform a real server-side logout
first, passing the X-CSRF-Token from the MMCSRF cookie, then wait for the
session cookie to disappear. The wait removes the race only; the assertion
stays in the spec.

test-sso-login.js was already failing before any of this, so this repairs an
existing weakness rather than one introduced here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 12:11:50 +02:00
kevinveenbirkenbachandClaude Opus 5 19407bad1f feat(mattermost): make the Agents gateway binding converge and prove itself
The previous contract could not fail. Its guard and its closing assertion both
keyed on apiURL -- the one field the merge itself writes -- so once any run had
set the URL, every later run skipped the merge on a false_condition and still
reported all assertions passed. The live row shows what that hides: an apiKey of
`sk-` followed by 48 literal `a` characters, which the gateway answers 401 to,
and defaultModel `llama3:latest` while the round's gateway serves only
`smollm2:135m`. The merge task is `skipping` in every deploy log on this host.

The read now compares the whole desired service and bot with jsonb equality and
answers `converged` or `stale`, so a drifted key or model is drift the guard can
see. jsonb equality is key-order independent, which keeps the check from
depending on how Jinja happened to serialise the dict. Both SQL templates render
from the same two role vars, so read and write cannot disagree about what
correct means. The `jsonb_typeof(...) = 'array'` guards stay: before the first
write those keys exist as JSON null, coalesce does not fire on JSON null, and
jsonb_array_elements then raises "cannot extract elements from a scalar".

A row edit cannot create the bot account, which is why the Playwright spec 404s
on user `agent`. In mattermost-plugin-agents v2.5.1 the config lives in an
in-memory atomic pointer; bots.EnsureBots() is the only path that calls
pluginAPI.Bot.Create, and it runs from OnActivate and from the config update
listener. Writing straight into agents_confighistory reaches neither. The role's
only reload lever was `notify: container recreate`, which never fired -- the
container is unchanged with RestartCount 0 while the config row was written
inside it. The plugin is now cycled explicitly through mmctl, and the enable step
moves into tasks/utils/agents/enable.yml so the MCP path and the AI path share
one definition.

The closing gate is deliberately not another read of what we wrote: it polls the
bots-to-users join until the agent account exists. That table is written by the
plugin and never by this role, so it cannot pass by echoing the deploy's own
work. The reload fires on stale config OR a missing bot, which covers the state
this stack is actually in, where the config matched but no account existed.

Verified: the stale row, the absent bot account and the always-skipping merge are
all measured on the live stack; the EnsureBots path was read from upstream v2.5.1
source rather than inferred; every changed task file parses.
Not verified: none of this has executed yet. Specifically open are whether
`MATTERMOST_LITELLM_SERVICE | to_json` renders nested Jinja inside a dict var,
whether the converged/stale SQL is valid against live Postgres, and whether a
disable/enable cycle triggers EnsureBots on this version.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 12:56:57 +02:00