Skip to content

fix: keep tests and one-off commands off other checkouts' Open WebUI - #97

Merged
SushantGautam merged 4 commits into
mainfrom
fix/hermetic-chat-tests
Nov 12, 2026
Merged

SushantGautam merged 4 commits into
mainfrom
fix/hermetic-chat-tests

Conversation

@SushantGautam

Copy link
Copy Markdown
Collaborator

Why

A full pytest -m "not embedded_hatchet" run overwrote the workspace model studio.agent-1 of the Open WebUI that a local manage.py dev serves on 127.0.0.1:8080 (name "Prompted Agent", base model 1.model-123, from AgentUITest::test_agent_prompt_picker_renders_as_list_and_resolves_version).

Root cause: .env is not loaded under pytest (only manage.py calls load_dotenv). 23 tests patch chat.config.ENABLED = True without mocking the HTTP layer, while chat.config.UPSTREAM keeps its default http://127.0.0.1:8080/chat. Agent/KB/tool syncs, ChatAPI.sign_in and trace_target._owui_get_jwt then hit whatever listens there. In CI nothing does, so the connection is refused and the tests pass, which is why it went unnoticed.

A related path: one-off manage.py commands in a worktree read the same .env but not the per-checkout SIMPLEAUDIT_CHAT_UPSTREAM that manage.py dev exports, so they fall back to :8080, which is the main checkout's Open WebUI.

What changed

  • simpleaudit_studio/no_live_chat.py: a socket guard that, during tests, refuses every TCP connect to the chat upstream or to loopback :8080. Ports the test process listens on itself (stub servers, e.g. chat/tests/test_proxy.py) stay reachable. conftest.py and manage.py test install it.
  • pytest.ini and manage.py test force SIMPLEAUDIT_CHAT=off, so neither a sourced nor a loaded .env can turn chat on for a whole run. Chat tests keep enabling it themselves.
  • lifecycle.route_one_off_chat (called from manage.py for non-dev, non-test commands): with chat on and no explicit upstream, it uses this checkout's live dev stack's chat port. When :8080 belongs to another checkout's live stack, it turns chat off for that command and prints a note. An explicit SIMPLEAUDIT_CHAT_UPSTREAM (compose, the dev stack's own children) is left alone.
  • Tests: simpleaudit_studio/tests/test_no_live_chat.py covers the guard, plus the original leak (an unmocked agent sync against a foreign listener receives no connection). tests/test_lifecycle.py covers the routing. There is also a short section in docs/chat.md.

Reviewer notes

  • The 23 leaky tests are not rewritten. They now get the same refused connection as in CI. Mocking them properly is a possible follow-up.
  • _CHAT_DISABLED_VALUES in lifecycle.py mirrors chat.config.DISABLED_VALUES and is not imported from it, because importing chat.config before django.setup() would freeze its settings. A test asserts the two sets match.

Checks

  • With SIMPLEAUDIT_CHAT=embedded in .env and exported in the shell, and SIMPLEAUDIT_CHAT_UPSTREAM pointed at a recording server: uv run pytest -m "not embedded_hatchet" -n auto gave 1822 passed and 3 skipped, and the recorder logged 0 requests.
  • uv run manage.py test simpleaudit_studio.tests.test_no_live_chat passes with chat enabled in .env.
  • uv run ruff check . is clean.
  • Not run: -m embedded_hatchet (infra/worker.py and infra/minimal_config.py are untouched).

Chat tests patch chat.config.ENABLED on while UPSTREAM keeps its default,
http://127.0.0.1:8080/chat, where a local manage.py dev serves Open WebUI.
23 tests pushed unmocked agent/KB/tool syncs there, overwriting the live
instance's workspace models (test agent pk 1 -> studio.agent-1).

- simpleaudit_studio/no_live_chat.py: socket guard refusing every test
  connection to the chat upstream or loopback :8080 (stub servers the test
  process listens on stay reachable). Installed by conftest.py and by
  manage.py test, so local runs behave like CI, where nothing listens there.
- pytest.ini and manage.py test force SIMPLEAUDIT_CHAT=off so a shell-sourced
  or loaded .env cannot enable chat for the whole run.
- manage.py one-off commands: with chat on and no explicit upstream, use this
  checkout's live dev stack's Open WebUI port, or turn chat off for the command
  when :8080 belongs to another checkout's live stack.
- Tests for the guard, the original agent-sync leak and the routing; docs.
Copilot AI balanced review requested due to automatic review settings November 12, 2026 10:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

test_private_connection_not_in_picker asserted no 'value="<pk>"' anywhere on
the New Experiment page, so a scenario set or prompt version with the same pk
failed it depending on test order (CI: value="6"). Assert on the model
pickers' name="<role>_model" checkbox instead.
ChatPrecedenceTests asserted chat-mode defaults while pytest.ini sets
SIMPLEAUDIT_CHAT=off, and two of them popped the variable in a finally,
deleting the guard for every later test in the worker; the outcome
depended on test order (CI: 'off' != 'docker').

- ChatPrecedenceTests clear the variable inside mock.patch.dict(os.environ)
  per test, so it is restored afterwards.
- conftest.py: after each test's teardown, a test that changed
  SIMPLEAUDIT_CHAT fails and the value is put back.
- test_ports: listen backlog 8, so the probe connection cannot fill the
  queue and get the next connect reset under parallel load.
@SushantGautam
SushantGautam merged commit 023fb80 into main Nov 12, 2026
2 checks passed
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.

2 participants