test(ui): align source pickers and mock responses with current contracts

This commit is contained in:
Ouroboros 2026-09-18 00:32:15 +03:00
parent 7f61c080bf
commit 358be285ee
9 changed files with 64 additions and 21 deletions

View file

@ -215,8 +215,10 @@ def test_partial_account_refresh_keeps_draft_and_only_failed_account_history(acc
assert "not in discovery" in suggestions(page, field)[DRAFT_MODEL]
account.select_option("work")
assert "not checked" in suggestions(page, field)[DRAFT_MODEL]
assert row.locator("[data-subagent-status]").inner_text() == "Draft · Not checked"
assert "model list could not be read" in row.locator("[data-subagent-meta]").inner_text()
status = row.locator("[data-subagent-status]")
assert status.inner_text() == "Draft · Not checked"
# The unread account catalog is disclosed on the row's own status sentence.
assert "model list could not be read" in status.get_attribute("title")
account.select_option("")
roles.capture(page, "account-catalog-partial-" + consumer.lower().replace(" ", "-"))
assert_saved(ui, consumer, DRAFT_MODEL, "")

View file

@ -20,6 +20,9 @@ def catalog(ui):
def select_model_field(ui, consumer):
roles.configure_mixed(ui)
# The catalog above is unprefixed, so every consumer is put on the same
# OpenRouter API lane the suggestions belong to.
lane = roles.api_lane(ui)
if consumer in ['Scope', 'Deep']:
slots = ui['fixture']['preview']['reviewer_slots']
if consumer == 'Scope': slots['scope'] = [{'slot_id': 'scope_1', 'route': {'kind': 'api_chat', 'target_id': 'owner/model'}}]
@ -33,15 +36,16 @@ def select_model_field(ui, consumer):
page.locator('[data-settings-tab="models"]').click()
if consumer == 'Fallback': page.locator('[data-model-add]').click()
group = page.locator('[data-model-role="main"]') if consumer == 'Models' else page.locator('[data-model-role-group="fallback"] .model-role-row').first
group.locator('[data-model-role-source]').select_option('openrouter')
group.locator('[data-model-role-source]').select_option(lane)
return page, group.locator('[data-model-role-model]')
if consumer == "Actor":
row = page.locator('[data-subagent-row]').nth(1)
row.locator('[data-subagent-field="route"]').select_option(lane)
return page, row.locator('[data-subagent-field="model"]')
if consumer == 'Scope': return page, page.locator('[data-slot-id="scope_1"] [data-slot-custom-api]')
if consumer == 'Deep': return page, page.locator('[data-deep-review-api-model]')
row = page.locator('[data-slot-id="triad_1"]') if consumer == "Triad" else page.locator('[data-advisory-row]')
row.locator('[data-slot-route], [data-advisory-route]').select_option('api')
row.locator('[data-slot-route], [data-advisory-route]').select_option(lane)
return page, row.locator('[data-slot-custom-api], [data-advisory-api-model]')

View file

@ -13,12 +13,15 @@ capture = setup_browser.capture
pytestmark = [pytest.mark.ui_browser, pytest.mark.serial]
TASK = "analysis-task"
# The wait picker offers an API lane per provider whose credential is stored.
API_LANE = "api:openai"
@pytest.fixture
def waiting_ui(subscription_ui):
ui = subscription_ui
page = ui["page"]
ui["settings"]["OPENAI_API_KEY"] = "***set***"
rows = {
key: {"wait_id": key, "revision": 1, "task_attempt": 1, "role": role,
"model": "claudexor::codex=gpt-test", "source": "codex",
@ -162,7 +165,7 @@ def test_multiple_waits_toggle_and_exact_role_switch_wait_for_application(waitin
page.wait_for_selector('[data-wait-id="light-wait"] [data-wait-change]:not([disabled])')
assert not light.locator('[data-wait-auto]').is_checked()
light.locator('[data-wait-change]').click()
light.locator('[data-model-role-source]').select_option('openai')
light.locator('[data-model-role-source]').select_option(API_LANE)
light.locator('[data-model-role-model]').fill('owner-model')
assert not light.locator('[data-wait-persist]').is_checked()
light.locator('[data-wait-apply]').click()
@ -320,7 +323,7 @@ def test_wait_reconnect_preserves_unsubmitted_form_without_context_or_animation(
field = row.locator('[data-model-role-model]')
field.wait_for()
assert row.locator('.model-role-details').count() == 0
row.locator('[data-model-role-source]').select_option('openai')
row.locator('[data-model-role-source]').select_option(API_LANE)
assert row.locator('.model-role-details').count() == 0
field.fill('unfinished-owner-model')
row.locator('[data-wait-persist]').check()

View file

@ -9,7 +9,7 @@ from ouroboros import model_wait, owner_mailbox
from ouroboros.task_results import load_task_result
from tests.test_llm_claudexor import setup as subscription_transport
from tests.test_model_wait import live_wait as wait_fixture
from tests.test_model_wait_browser import TASK, waiting_ui as waiting_fixture
from tests.test_model_wait_browser import API_LANE, TASK, waiting_ui as waiting_fixture
from tests.test_subscription_setup_browser import subscription_ui as ui_fixture, capture
setup = subscription_transport
@ -97,7 +97,7 @@ def test_fallback_local_change_discloses_task_only_and_keeps_other_roles(waiting
waiter = page.locator('[data-wait-id="light-wait"]')
waiter.locator('[data-wait-role]').filter(has_text="Fallback 2").wait_for()
waiter.locator('[data-wait-change]').click()
waiter.locator('[data-model-role-source]').select_option("openai")
waiter.locator('[data-model-role-source]').select_option(API_LANE)
waiter.locator('[data-model-role-model]').fill("replacement")
local = waiter.locator('[data-model-local]')
persist = waiter.locator('[data-wait-persist]')

View file

@ -80,7 +80,7 @@ def test_onboarding_processing_draft_returns_and_reaches_finish(subscription_ui)
main.locator('summary').click()
main.locator('[data-model-role-processing]').select_option('standard')
page.locator('#next-btn').click()
page.wait_for_selector('#reviewer-slots-section')
page.wait_for_selector('[data-collapse="reviewers"]')
page.locator('#back-btn').click()
page.wait_for_selector('[data-global-processing]')
assert page.locator('[data-global-processing]').input_value() == 'fast'

View file

@ -9,6 +9,15 @@ from tests import test_subscription_setup_browser as setup_browser
pytestmark = [pytest.mark.ui_browser, pytest.mark.serial]
subscription_ui = setup_browser.subscription_ui
capture = setup_browser.capture
# Every route picker offers an API lane per provider whose credential is
# stored, so a fixture that selects one advertises that provider's key first.
API_KEYS = {"openrouter": "OPENROUTER_API_KEY", "openai": "OPENAI_API_KEY"}
def api_lane(ui, provider="openrouter"):
"""Store `provider`'s key in the served settings and return its route choice."""
ui["settings"][API_KEYS[provider]] = "***set***"
return f"api:{provider}"
@pytest.fixture
@ -72,6 +81,7 @@ def open_agents(ui):
def test_reviewer_source_roundtrip_restores_its_own_model_and_account(role_ui):
configure_mixed(role_ui)
lane = api_lane(role_ui, 'openai')
page = open_agents(role_ui)
for selector, model_field, account_field in [
('[data-slot-id="triad_1"]', '[data-slot-custom-api]', '[data-slot-profile]'),
@ -79,13 +89,15 @@ def test_reviewer_source_roundtrip_restores_its_own_model_and_account(role_ui):
]:
row = page.locator(selector)
route = row.locator('[data-slot-route], [data-advisory-route]')
route.select_option('api')
row.locator(model_field).fill('openai::other-choice')
route.select_option(lane)
# The chooser holds the model alone; the source select names the provider.
row.locator(model_field).fill('other-choice')
route.select_option('subscription:opaque-source')
assert row.locator(model_field).input_value() == 'gpt-test'
assert row.locator(account_field).input_value() == 'personal'
route.select_option('api')
assert row.locator(model_field).input_value() == 'openai::other-choice'
route.select_option(lane)
assert row.locator(model_field).input_value() == 'other-choice'
assert route.input_value() == lane
route.select_option('subscription:opaque-source')
page.locator('[data-advisory-row]').scroll_into_view_if_needed()
capture(page, "reviewer-source-roundtrip-restored")
@ -93,7 +105,7 @@ def test_reviewer_source_roundtrip_restores_its_own_model_and_account(role_ui):
with page.expect_response('**/api/reviewer-slots'):
page.locator('#btn-reload-settings').click()
page.get_by_role('button', name='Discard and continue', exact=True).click()
page.locator('[data-advisory-route]').select_option('api')
page.locator('[data-advisory-route]').select_option(lane)
assert page.locator('[data-advisory-api-model]').input_value() == ''
@ -164,9 +176,10 @@ def test_subscription_accounts_roundtrip_existing_editors(role_ui, width):
def test_source_switch_and_catalog_refresh_keep_focus_and_draft(role_ui):
ui = role_ui
configure_mixed(ui)
lane = api_lane(ui, 'openai')
page = open_agents(ui)
triad = page.locator('[data-slot-id="triad_1"]')
triad.locator('[data-slot-route]').select_option("api")
triad.locator('[data-slot-route]').select_option(lane)
assert triad.locator('[data-slot-profile]').count() == 0
triad.locator('[data-slot-custom-api]').fill("openai::gpt-api")
triad.locator('[data-slot-route]').select_option("subscription:opaque-source")

View file

@ -63,6 +63,8 @@ def integrated_wait(subscription_ui, live_wait, monkeypatch):
config.SETTINGS_PATH.write_text(json.dumps(settings), encoding="utf-8")
original_settings = config.SETTINGS_PATH.read_bytes()
ui["settings"].update(settings)
# The wait picker offers an API lane per provider whose credential is stored.
ui["settings"]["OPENAI_API_KEY"] = "***set***"
source_entered, source_release = threading.Event(), threading.Event()
catalog_entered, catalog_release = threading.Event(), threading.Event()
@ -265,7 +267,7 @@ def test_browser_wait_controls_apply_once_and_only_to_light(integrated_wait, per
assert not row.locator("[data-wait-auto]").is_checked()
row.locator("[data-wait-change]").click()
row.locator("[data-model-role-source]").select_option("openai")
row.locator("[data-model-role-source]").select_option("api:openai")
row.locator("[data-model-role-model]").fill("owner-model")
assert not row.locator("[data-wait-persist]").is_checked()
if persist_role:

View file

@ -99,12 +99,29 @@ def test_large_attachment_returns_through_real_document_handler_and_download(
"name": "send_file", "arguments": json.dumps({"file_path": str(staged[0]), "caption": "Complete large dataset"}),
},
}]}
finish = "tool_calls" if message.get("tool_calls") else "stop"
payload = {"id": "mock-large-file", "object": "chat.completion",
"choices": [{"message": message, "finish_reason": "tool_calls" if message.get("tool_calls") else "stop"}],
"model": request.get("model") or "mock-model",
"choices": [{"index": 0, "message": message, "finish_reason": finish}],
"usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2}}
data = json.dumps(payload).encode()
content_type = "application/json"
if request.get("stream"):
# The main loop streams every completion: answer in SSE frames with
# the terminal framing the assembler requires.
content_type = "text/event-stream"
delta = dict(message)
if delta.get("tool_calls"):
delta["tool_calls"] = [dict(call, index=index)
for index, call in enumerate(delta["tool_calls"])]
common = {"id": payload["id"], "model": payload["model"], "object": "chat.completion.chunk"}
frames = [{**common, "choices": [{"index": 0, "delta": delta, "finish_reason": finish}]},
{**common, "choices": [], "usage": payload["usage"]}]
data = ("".join("data: " + json.dumps(frame) + "\n\n" for frame in frames)
+ "data: [DONE]\n\n").encode()
else:
data = json.dumps(payload).encode()
handler.send_response(200)
handler.send_header("Content-Type", "application/json")
handler.send_header("Content-Type", content_type)
handler.send_header("Content-Length", str(len(data)))
handler.end_headers()
handler.wfile.write(data)

View file

@ -211,6 +211,8 @@ def test_initial_settings_document_survives_early_edit_while_enrichment_waits(su
ui, pending = subscription_ui, {"reviewers": [], "status": [], "catalog": []}
page = ui["page"]
ui["settings"]["OUROBOROS_MODEL"] = "claudexor::opaque-source=gpt-test"
# An API lane is offered per provider whose credential is stored.
ui["settings"]["OPENROUTER_API_KEY"] = "***set***"
ui["fixture"]["catalog"]["model_sources"] = [
{"id": "opaque-source", "label": "Managed models", "credentialHarness": "codex"},
]
@ -225,7 +227,7 @@ def test_initial_settings_document_survives_early_edit_while_enrichment_waits(su
assert pending["status"], "the status read must still be pending"
assert page.locator('#btn-save-settings').is_enabled(), "the known document can be saved before enrichment"
source = main.locator('[data-model-role-source]')
source.select_option("openrouter")
source.select_option("api:openrouter")
model = main.locator('[data-model-role-model]')
model.fill("owner-kept-model")
model.evaluate("element => { window.__earlyModel = element; element.setSelectionRange(4, 4); }")
@ -245,7 +247,7 @@ def test_initial_settings_document_survives_early_edit_while_enrichment_waits(su
page.wait_for_function("""() => document.querySelector('[data-model-role="main"] [data-model-role-source]')
.querySelector('option[value="subscription:opaque-source"]')""")
expect(model).to_have_value("owner-kept-model")
assert source.input_value() == "openrouter"
assert source.input_value() == "api:openrouter"
assert model.evaluate("element => element === window.__earlyModel && element.selectionStart === 4")
expect(page.locator('#settings-unsaved-indicator')).to_have_class(re.compile('is-visible'))
expect(page.locator('#btn-save-settings')).to_be_enabled()