mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 06:52:20 +02:00
test(baseline): align UI and model profile expectations
This commit is contained in:
@@ -161,13 +161,13 @@ def test_email_settings_show_away_account_and_compact_display_controls() -> None
|
||||
assert "enabled ? 'Show' : 'Hide'" in source
|
||||
assert "email-settings-inline-link" in source
|
||||
assert "email-auto-reply-exclude" not in source
|
||||
assert "_emailWritingStyleHtml(writingStyle) + _emailDisplaySettingsHtml()" in source
|
||||
assert "_emailWritingStyleHtml(writingStyle) + _emailDisplaySettingsHtml(cfg)" in source
|
||||
assert ".email-settings-status.is-success" in style
|
||||
assert "var(--color-success, #4caf50)" in style
|
||||
assert ".email-style-settings-extract svg" in style
|
||||
assert "export async function mountEmailSettings(host)" in source
|
||||
assert "_openGlobalEmailSettings('show-tags')" in source
|
||||
assert source.count('class="admin-card email-settings-section') == 4
|
||||
assert source.count('class="admin-card email-settings-section') == 5
|
||||
assert 'id="settings-email-default-card"' in (ROOT / "static/index.html").read_text(encoding="utf-8")
|
||||
assert "multipleAccounts" in source
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ THEME_JS = (ROOT / "static/js/theme.js").read_text(encoding="utf-8")
|
||||
|
||||
def test_five_distinct_builtin_themes_are_available() -> None:
|
||||
expected = {
|
||||
"eclipse": "constellations",
|
||||
"eclipse": "starfield-depth",
|
||||
"porcelain": "dots",
|
||||
"arcade": "synapse",
|
||||
"blueprint": "dots",
|
||||
|
||||
@@ -25,8 +25,9 @@ def test_measured_ttft_is_shown_in_message_stats():
|
||||
|
||||
def test_compact_footer_and_details_show_real_performance_counters():
|
||||
assert "`${Number(tps).toFixed(2)} tok/s`" in RENDERER
|
||||
assert "`${Number(ttft).toFixed(3)}s TTFT`" in RENDERER
|
||||
assert "`${Number(injectedTokens).toLocaleString()} in`" in RENDERER
|
||||
assert "const visibleTtft = metrics.client_ttft ?? metrics.time_to_first_token" in RENDERER
|
||||
assert "${Number(visibleTtft).toFixed(3)}s" in RENDERER
|
||||
assert "${Number(injectedTokens).toLocaleString()}" in RENDERER
|
||||
assert '<span class="ctx-label">Input</span>' in RENDERER
|
||||
assert '<span class="ctx-label">Injected</span>' in RENDERER
|
||||
assert 'all rounds' not in RENDERER
|
||||
|
||||
@@ -87,7 +87,7 @@ async def test_malformed_text_artifact_write_uses_one_bounded_raw_body_handoff(m
|
||||
events = [json.loads(chunk[6:]) for chunk in raw if '[DONE]' not in chunk]
|
||||
assert len(requests) == 2
|
||||
assert 'tools' not in requests[1]
|
||||
assert requests[1]['max_tokens'] == 4096
|
||||
assert requests[1]['max_tokens'] == 8192
|
||||
assert len(executed) == 1
|
||||
assert executed[0].tool_type == 'write_file'
|
||||
assert executed[0].content == (
|
||||
|
||||
@@ -12,7 +12,7 @@ def test_mobile_compare_mounts_accessible_tabs_without_removing_panes():
|
||||
index = _read("static/js/compare/index.js")
|
||||
panes = _read("static/js/compare/panes.js")
|
||||
|
||||
assert "mountMobilePaneTabs(container, grid)" in index
|
||||
assert "mountMobilePaneTabs(container, grid, (anchor) => _addPane(anchor))" in index
|
||||
assert "role', 'tablist'" in panes
|
||||
assert "role', 'tab'" in panes
|
||||
assert "role', 'tabpanel'" in panes
|
||||
|
||||
@@ -83,7 +83,8 @@ def test_pages_site_owns_its_entrypoint_and_media():
|
||||
|
||||
assert REPO / "website/index.html" in website_files
|
||||
assert REPO / "docs/index.html" not in docs_files
|
||||
assert not [p for p in docs_files if p.suffix.lower() in VIDEO_EXTS | {".md"}]
|
||||
assert not [p for p in docs_files if p.suffix.lower() in VIDEO_EXTS]
|
||||
assert not [p for p in docs_files if p.name in PUBLIC_GUIDES]
|
||||
|
||||
website_paths = {p.relative_to(REPO / "website").as_posix() for p in website_files}
|
||||
assert PUBLIC_GUIDES <= website_paths
|
||||
|
||||
@@ -18,7 +18,7 @@ def test_expanded_document_card_has_export_beside_clone():
|
||||
|
||||
|
||||
def test_expanded_export_reuses_download_function_without_proxy_click():
|
||||
assert "const exportDocumentFile = async () =>" in DOC_LIBRARY_JS
|
||||
assert "const exportDocumentFile = async (format = 'original') =>" in DOC_LIBRARY_JS
|
||||
assert "await exportDocumentFile();" in DOC_LIBRARY_JS
|
||||
assert "exportItem.click();" not in DOC_LIBRARY_JS
|
||||
assert "exportItem.type = 'button';" in DOC_LIBRARY_JS
|
||||
|
||||
@@ -10,24 +10,26 @@ SOURCE = (ROOT / "static/js/documentLibrary.js").read_text(encoding="utf-8")
|
||||
STYLE = (ROOT / "static/style.css").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_mobile_footer_exposes_open_and_more_only():
|
||||
def test_mobile_footer_exposes_delete_open_and_more():
|
||||
assert "doclib-expanded-open-btn" in SOURCE
|
||||
assert "doclib-expanded-mobile-more" in SOURCE
|
||||
assert "label: 'Open in new chat'" in SOURCE
|
||||
assert "'Open in original' : 'Open document'" in SOURCE
|
||||
assert "label: 'Export file'" in SOURCE
|
||||
assert "label: 'Export file ›'" in SOURCE
|
||||
assert "label: 'Original format'" in SOURCE
|
||||
assert "label: 'Markdown (.md)'" in SOURCE
|
||||
assert "'Restore document' : 'Archive document'" in SOURCE
|
||||
assert "label: 'Delete document'" in SOURCE
|
||||
|
||||
mobile_css = STYLE.split("The Documents preview footer only exposes Open and More", 1)[1]
|
||||
mobile_css = STYLE.split("On phones, keep Delete explicit", 1)[1]
|
||||
mobile_css = mobile_css.split("/* Chat top bar", 1)[0]
|
||||
for hidden_action in (
|
||||
".doclib-expanded-delete-btn",
|
||||
".doclib-expanded-archive-btn",
|
||||
".doclib-expanded-clone-btn",
|
||||
".doclib-expanded-export-btn",
|
||||
):
|
||||
assert hidden_action in mobile_css
|
||||
assert ".doclib-expanded-delete-btn {\n display: inline-flex !important" in mobile_css
|
||||
assert ".doclib-expanded-mobile-more" in mobile_css
|
||||
assert "display: inline-flex" in mobile_css
|
||||
assert "box-sizing: border-box" in mobile_css
|
||||
|
||||
@@ -91,13 +91,15 @@ def test_apply_pan_updates_state_canvas_overlay_and_cursor_classes():
|
||||
|
||||
def test_hand_navigation_is_wired_to_toolbar_keyboard_and_offline_graph():
|
||||
toolbar = (ROOT / "static/js/editor/build/toolbar.js").read_text(encoding="utf-8")
|
||||
shortcuts = (ROOT / "static/js/editor/tool-shortcuts.js").read_text(encoding="utf-8")
|
||||
keyboard = (ROOT / "static/js/editor/keyboard-shortcuts.js").read_text(encoding="utf-8")
|
||||
events = (ROOT / "static/js/editor/canvas-events.js").read_text(encoding="utf-8")
|
||||
editor = (ROOT / "static/js/galleryEditor.js").read_text(encoding="utf-8")
|
||||
service_worker = (ROOT / "static/sw.js").read_text(encoding="utf-8")
|
||||
|
||||
assert "{ id: 'hand', label: 'Hand'" in toolbar
|
||||
assert "key: 'H'" in toolbar
|
||||
assert "t.key = TOOL_SHORTCUTS[t.id]" in toolbar
|
||||
assert "hand: 'H'" in shortcuts
|
||||
assert "e.code === 'Space'" in keyboard
|
||||
assert "setTemporaryPan?.(true)" in keyboard
|
||||
assert "isDirectPanIntent(state.tool, state.spacePanActive" in events
|
||||
|
||||
@@ -20,8 +20,8 @@ def test_hover_thumbnail_uses_rendered_layer_output():
|
||||
def test_inline_layer_rows_share_the_common_thumbnail_renderer():
|
||||
panel = (ROOT / "static/js/editor/layer-panel.js").read_text()
|
||||
|
||||
assert "const previewCanvas = renderLayer?.(layer) || layer.canvas;" in panel
|
||||
assert "const thumb = createInlineThumbnail(previewCanvas, `${layer.name} preview`);" in panel
|
||||
assert "const thumb = createInlineThumbnail(() => renderLayer?.(layer) || layer.canvas, `${layer.name} preview`);" in panel
|
||||
assert "thumb._refreshPreview = draw;" in panel
|
||||
assert "thumbCtx.fillStyle = ((x / tile + y / tile) & 1)" not in panel
|
||||
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ def test_rasterize_text_layer_clears_retained_metadata_only_for_text():
|
||||
def test_text_tool_is_retained_across_editor_subsystems():
|
||||
editor = (ROOT / "static/js/galleryEditor.js").read_text(encoding="utf-8")
|
||||
toolbar = (ROOT / "static/js/editor/build/toolbar.js").read_text(encoding="utf-8")
|
||||
shortcuts = (ROOT / "static/js/editor/tool-shortcuts.js").read_text(encoding="utf-8")
|
||||
controls = (ROOT / "static/js/editor/build/controls.js").read_text(encoding="utf-8")
|
||||
codec = (ROOT / "static/js/editor/document-codec.js").read_text(encoding="utf-8")
|
||||
geometry = (ROOT / "static/js/editor/document-geometry.js").read_text(encoding="utf-8")
|
||||
@@ -127,7 +128,8 @@ def test_text_tool_is_retained_across_editor_subsystems():
|
||||
merge = (ROOT / "static/js/editor/wire-merge-buttons.js").read_text(encoding="utf-8")
|
||||
|
||||
assert "{ id: 'text', label: 'Text'" in toolbar
|
||||
assert "key: 'T'" in toolbar
|
||||
assert "t.key = TOOL_SHORTCUTS[t.id]" in toolbar
|
||||
assert "text: 'T'" in shortcuts
|
||||
assert 'id="ge-text-section"' in controls
|
||||
assert "if (state.tool === 'text') return _placeText(e);" in editor
|
||||
assert "kind: l.kind || 'raster'" in editor
|
||||
|
||||
@@ -32,15 +32,15 @@ def test_stacked_labels_share_one_size_and_position_rule():
|
||||
assert "top: 2px;" in STYLE
|
||||
|
||||
|
||||
def test_narrow_topbar_reflows_essential_actions_instead_of_clipping_them():
|
||||
assert "ge-topbar-overflow" in OVERFLOW
|
||||
assert "topbar.classList.add('ge-topbar-overflow')" in OVERFLOW
|
||||
assert "(max-width: 700px)" in OVERFLOW
|
||||
assert ".ge-topbar.ge-topbar-overflow" in STYLE
|
||||
assert "flex: 1 0 100%;" in STYLE
|
||||
right_start = STYLE.index(".ge-topbar-overflow .ge-topbar-right")
|
||||
overflow_right = STYLE[right_start:STYLE.index("}", right_start)]
|
||||
assert "justify-content: flex-start;" in overflow_right
|
||||
def test_narrow_topbar_scrolls_essential_actions_and_hides_ai_group_first():
|
||||
assert "if (topbar.scrollWidth > topbar.clientWidth)" in OVERFLOW
|
||||
assert "aiGroup.forEach(el => { el.style.display = 'none'; });" in OVERFLOW
|
||||
assert "new ResizeObserver(() => syncOverflow())" in OVERFLOW
|
||||
mobile = STYLE[STYLE.index("/* Editor topbar — scrolls horizontally") :]
|
||||
topbar = mobile[mobile.index(".ge-topbar {") : mobile.index("}", mobile.index(".ge-topbar {"))]
|
||||
assert "overflow-x: auto;" in topbar
|
||||
assert "flex-wrap: nowrap;" in topbar
|
||||
assert "justify-content: flex-start;" in topbar
|
||||
|
||||
|
||||
def test_desktop_tool_rail_keeps_long_tool_names_readable():
|
||||
|
||||
@@ -160,8 +160,10 @@ def test_attachment_cache_backfill_preserves_message_id(tmp_path, monkeypatch):
|
||||
|
||||
def test_single_email_tag_has_no_more_control():
|
||||
library = open("static/js/emailLibrary.js", encoding="utf-8").read()
|
||||
|
||||
assert 'class="email-tags-more email-tags-more-single"' not in library
|
||||
group = library[library.index("function _emailTagGroupHtml("):library.index("function _fitEmailCardTags(")]
|
||||
assert "if (visible.length === 1) return visible[0];" in group
|
||||
assert "if (visible.length === 2) return visible.join('');" in group
|
||||
assert group.index("if (visible.length === 2)") < group.index("email-tags-more-single")
|
||||
|
||||
|
||||
def test_email_folder_and_filter_pickers_treat_their_buttons_as_inside_clicks():
|
||||
@@ -181,7 +183,7 @@ def test_empty_reply_has_two_editable_rows_and_reply_survives_compact_toolbar():
|
||||
fit_start = library.index("function _fitReaderActions")
|
||||
fit_end = library.index("const _readerActionFitObserver", fit_start)
|
||||
fit = library[fit_start:fit_end]
|
||||
assert '[data-act="ai-reply"], [data-act="reply-all"]' in fit
|
||||
assert '[data-act="reply-all"], [data-act="forward"]' in fit
|
||||
assert '[data-act="reply"]' not in fit
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ def test_folder_chip_stays_with_date_and_moves_down():
|
||||
folder_rule_start = css.index(".email-folder-chip {", first_folder_rule + 1)
|
||||
folder_rule = css[folder_rule_start:][:220]
|
||||
assert "position: relative;" in folder_rule
|
||||
assert "top: 2px;" in folder_rule
|
||||
assert "top: 4px;" in folder_rule
|
||||
group_rule = css[css.index(".email-meta-date-group {"):][:180]
|
||||
assert "display: inline-flex;" in group_rule
|
||||
assert "white-space: nowrap;" in group_rule
|
||||
|
||||
@@ -127,15 +127,16 @@ def test_attachment_warning_only_checks_authored_reply_text():
|
||||
assert "text.split(/^>|^On .* wrote:/m)" not in helper
|
||||
|
||||
|
||||
def test_email_send_saves_recovery_draft_before_send_and_closes_only_on_success():
|
||||
def test_email_send_saves_recovery_draft_before_send_and_retains_it_on_failure():
|
||||
source = (_REPO / "static/js/document.js").read_text(encoding="utf-8")
|
||||
send = source[source.index("async function _sendEmail"):source.index("\n\n async function _saveDraft", source.index("async function _sendEmail"))]
|
||||
|
||||
assert "async function _saveEmailDraftForRecovery" in source
|
||||
assert send.index("_saveEmailDraftForRecovery({") < send.index("fetch(`${API_BASE}/api/email/send`")
|
||||
assert send.index("if (isLibraryOpen()) closeLibrary();") > send.index("const sendRequest = fetch(")
|
||||
assert send.index("if (isLibraryOpen()) closeLibrary();") < send.index("const res = await sendRequest")
|
||||
success_branch = send[send.index("if (data.success) {"):]
|
||||
assert "closeLibrary()" in success_branch
|
||||
assert send.index("closeLibrary()") > send.index("const res = await sendRequest")
|
||||
assert "fetch(`${API_BASE}/api/document/${sendDocId}`, { method: 'DELETE' })" in success_branch
|
||||
assert "Draft kept in Drafts." in send
|
||||
assert "recoveryDraft.draft_uid" in send
|
||||
|
||||
|
||||
@@ -10,8 +10,11 @@ def test_email_folder_and_filter_pickers_are_exclusive_and_escape_safe():
|
||||
|
||||
assert source.count("const filterMenu = document.getElementById('email-filter-menu');") == 1
|
||||
assert source.count("const folderMenu = document.getElementById('email-folder-menu');") == 1
|
||||
assert source.count("e.stopImmediatePropagation();") >= 2
|
||||
assert "_wireEmailPickerEscapeGuard();" in source
|
||||
assert "window.addEventListener('keydown'" in source
|
||||
assert source.count("e.stopImmediatePropagation?.();") >= 2
|
||||
assert "filterMenu?._dismiss?.();" in source
|
||||
assert "folderMenu?._dismiss?.();" in source
|
||||
escape_handler = source[source.index("state._libInnerEscHandler = (e) => {"):source.index("window.addEventListener('keydown', state._libInnerEscHandler, true)")]
|
||||
assert "if (dismissTopMenu()) {" in escape_handler
|
||||
assert "window.addEventListener('keydown', state._libInnerEscHandler, true)" in source
|
||||
inbox = (ROOT / "static/js/emailInbox.js").read_text(encoding="utf-8")
|
||||
assert re.search(r"from './emailLibrary\.js\?v=[A-Za-z0-9_-]+'", inbox)
|
||||
|
||||
@@ -5,10 +5,9 @@ import re
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_original_harness_version_is_canonical_semver() -> None:
|
||||
def test_harness_version_is_canonical_semver() -> None:
|
||||
version = (ROOT / "HARNESS_VERSION").read_text(encoding="utf-8").strip()
|
||||
|
||||
assert version == "0.20.18"
|
||||
assert re.fullmatch(r"\d+\.\d+\.\d+", version)
|
||||
|
||||
|
||||
|
||||
@@ -145,15 +145,15 @@ def test_final_response_skips_duplicate_end_of_stream_render():
|
||||
"// --- Final render (skip if stream was ever backgrounded or currently in background) ---",
|
||||
"} // end if (!_isBgFinal)",
|
||||
)
|
||||
assert "if (!terminalFinalResponseRendered) _renderStream();" in post_loop
|
||||
assert "if (!terminalFinalResponseRendered && !_turnRendering.isVisible(roundHolder.querySelector('.body'), _terminalAnswerHtml(roundText, roundHolder.querySelector('.body')))) _renderStream();" in post_loop
|
||||
assert "const finalDisplay = terminalFinalResponseRendered ? '' : _streamDisplayText" in post_loop
|
||||
|
||||
|
||||
def test_only_rich_completed_turns_reload_saved_canonical_assistant_row():
|
||||
assert "if (_canonicalTerminalSaved && _savedAssistantMessageId && !_pendingContinue)" not in _CHAT
|
||||
assert "if (!_pendingContinue)" in _CHAT
|
||||
assert "fall back to its latest" in _CHAT
|
||||
assert ": [...history].reverse().find(msg => msg && msg.role === 'assistant');" in _CHAT
|
||||
assert "if (!streamSessionId || !_savedAssistantMessageId) return false;" in _CHAT
|
||||
assert "String(msg.metadata?._db_id || '') === _savedAssistantMessageId" in _CHAT
|
||||
assert "const _needsCanonicalTurnRebuild" in _CHAT
|
||||
assert "lastToolThread" in _CHAT
|
||||
assert "_streamTurnMarker.remove();" in _CHAT
|
||||
|
||||
@@ -172,4 +172,4 @@ class TestThinkSuppression:
|
||||
"qwen35-9b-tool-router-v4-firstaction-noschema-adapter",
|
||||
)
|
||||
assert "think" not in payload
|
||||
assert payload["max_tokens"] == 1024
|
||||
assert payload["max_tokens"] == llm_core.LLMConfig.DEFAULT_MAX_TOKENS
|
||||
|
||||
@@ -87,6 +87,12 @@ def test_mobile_bulk_select_long_press_is_shared_across_card_types():
|
||||
assert ".skill-card[data-skill-name]" in helper
|
||||
assert ".task-card[data-id]" in helper
|
||||
assert "const HOLD_MS = 450" in helper
|
||||
assert "body:has(#memory-select-btn.active)" in css
|
||||
assert "body:has(#skills-select-btn.active)" in css
|
||||
assert "body:has(#tasks-select-btn.active)" in css
|
||||
shared_buttons = (
|
||||
"#memory-select-btn,", "#skills-select-btn,",
|
||||
"#notes-select-btn,", "#tasks-select-btn,",
|
||||
)
|
||||
shared_css = css.split("/* Shared bulk-selection trigger.", 1)[1]
|
||||
selectors = re.findall(r":is\(([^)]*)\)(?:\.active)?::before", shared_css)
|
||||
assert len(selectors) == 2
|
||||
for selector in selectors:
|
||||
assert all(button in selector for button in shared_buttons)
|
||||
|
||||
@@ -9,7 +9,7 @@ def test_notes_sidebar_new_action_matches_library_structure_and_animation():
|
||||
css = (ROOT / "static/style.css").read_text(encoding="utf-8")
|
||||
|
||||
assert 'class="list-item-plus-btn sidebar-new-item-btn" id="notes-new-note-btn"' in html
|
||||
assert '<span class="list-item-plus-label">note</span>' in html
|
||||
assert '<span class="list-item-plus-label">new</span>' in html[html.index('id="notes-new-note-btn"'):]
|
||||
assert "#tool-notes-btn:hover #notes-new-note-btn" in css
|
||||
assert "#notes-new-note-btn:hover svg" in css
|
||||
|
||||
@@ -72,7 +72,7 @@ def test_notes_body_and_tag_arrow_are_nudged_down():
|
||||
|
||||
assert css.count("padding: 10px 8px 8px") >= 2
|
||||
assert ".notes-pane-body .doclib-chip-scroll-arrow.right" in css
|
||||
assert "top: calc(50% - 2px) !important" in css
|
||||
assert "top: calc(50% - 6px) !important" in css[css.index(".notes-pane-body .doclib-chip-scroll-arrow.right"):]
|
||||
|
||||
|
||||
def test_notes_tag_strip_cannot_grow_into_blank_space():
|
||||
|
||||
@@ -15,6 +15,11 @@ NOTES_TOOLS = {
|
||||
"ask_user",
|
||||
"update_plan",
|
||||
}
|
||||
GENERAL_COMPACT_TOOLS = {"ask_user", "web_search", "web_fetch"}
|
||||
CALENDAR_COMPACT_TOOLS = {
|
||||
"ask_user", "bash", "get_workspace", "manage_calendar", "manage_notes",
|
||||
"python", "read_file", "web_fetch", "web_search",
|
||||
}
|
||||
|
||||
|
||||
def _collect(generator):
|
||||
@@ -102,9 +107,7 @@ def test_odysseus_notes_mode_clamps_without_overriding_caller_denials(monkeypatc
|
||||
assert {"manage_notes", "manage_calendar", "manage_tasks"} <= route["disabled_tools"]
|
||||
|
||||
|
||||
def test_odysseus_general_mode_disables_every_tool(monkeypatch):
|
||||
from src.tool_policy import known_tool_names
|
||||
|
||||
def test_odysseus_general_mode_uses_compact_core(monkeypatch):
|
||||
prompt_calls, _ = _install_route_probe(monkeypatch)
|
||||
|
||||
_run_probe(
|
||||
@@ -113,11 +116,10 @@ def test_odysseus_general_mode_disables_every_tool(monkeypatch):
|
||||
)
|
||||
|
||||
route = prompt_calls[0]
|
||||
assert route["relevant_tools"] == set()
|
||||
assert known_tool_names() <= route["disabled_tools"]
|
||||
assert route["relevant_tools"] == GENERAL_COMPACT_TOOLS
|
||||
|
||||
|
||||
def test_odysseus_calendar_intent_uses_notes_mode(monkeypatch):
|
||||
def test_odysseus_calendar_intent_uses_compact_calendar_route(monkeypatch):
|
||||
prompt_calls, _ = _install_route_probe(monkeypatch)
|
||||
|
||||
_run_probe(
|
||||
@@ -125,10 +127,10 @@ def test_odysseus_calendar_intent_uses_notes_mode(monkeypatch):
|
||||
relevant_tools={"manage_notes", "manage_calendar", "manage_tasks", "bash"},
|
||||
)
|
||||
|
||||
assert prompt_calls[0]["relevant_tools"] == NOTES_TOOLS
|
||||
assert prompt_calls[0]["relevant_tools"] == CALENDAR_COMPACT_TOOLS
|
||||
|
||||
|
||||
def test_odysseus_calendar_followup_keeps_notes_mode(monkeypatch):
|
||||
def test_odysseus_calendar_followup_keeps_compact_calendar_route(monkeypatch):
|
||||
prompt_calls, _ = _install_route_probe(monkeypatch)
|
||||
messages = [
|
||||
{"role": "user", "content": "Add lunch tomorrow to my calendar."},
|
||||
@@ -153,7 +155,7 @@ def test_odysseus_calendar_followup_keeps_notes_mode(monkeypatch):
|
||||
relevant_tools={"manage_notes", "manage_calendar", "manage_tasks", "bash"},
|
||||
)
|
||||
|
||||
assert prompt_calls[0]["relevant_tools"] == NOTES_TOOLS
|
||||
assert prompt_calls[0]["relevant_tools"] == CALENDAR_COMPACT_TOOLS
|
||||
|
||||
|
||||
def test_agent_route_passes_workspace_to_system_prompt(monkeypatch):
|
||||
@@ -169,7 +171,7 @@ def test_agent_route_passes_workspace_to_system_prompt(monkeypatch):
|
||||
assert prompt_calls[0]["workspace"] == "/tmp/example-repo"
|
||||
|
||||
|
||||
def test_odysseus_qwen_temperature_is_capped_for_agent_requests(monkeypatch):
|
||||
def test_odysseus_compact_primary_uses_deterministic_temperature(monkeypatch):
|
||||
_, stream_calls = _install_route_probe(monkeypatch)
|
||||
|
||||
_run_probe(
|
||||
@@ -178,7 +180,7 @@ def test_odysseus_qwen_temperature_is_capped_for_agent_requests(monkeypatch):
|
||||
temperature=1.2,
|
||||
)
|
||||
|
||||
assert stream_calls[0]["temperature"] == 0.2
|
||||
assert stream_calls[0]["temperature"] == 0.0
|
||||
|
||||
|
||||
def test_qwen_fallback_candidate_gets_capped_temperature(monkeypatch):
|
||||
@@ -214,7 +216,7 @@ def test_non_qwen_fallback_keeps_requested_temperature(monkeypatch):
|
||||
fallbacks=[("https://backup.example/v1", "gpt-4o", {})],
|
||||
)
|
||||
|
||||
assert stream_calls[0]["temperature"] == 0.2
|
||||
assert stream_calls[0]["temperature"] == 0.0
|
||||
factory = stream_calls[0]["candidate_request_factory"]
|
||||
request = asyncio.run(factory(1, "https://backup.example/v1", "gpt-4o", {}))
|
||||
assert request["kwargs"]["temperature"] == 1.2
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Regression coverage for full-height Deep Research history expansion."""
|
||||
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
@@ -11,7 +12,11 @@ def test_history_list_participates_in_shared_library_expansion_layout():
|
||||
css = (ROOT / "static/style.css").read_text(encoding="utf-8")
|
||||
|
||||
assert 'id="research-past-list" class="doclib-grid memory-list research-jobs-list"' in panel
|
||||
assert "width:min(560px, 90vw);max-height:78vh" in panel
|
||||
card = css[css.index("#research-pane .research-history-card {"):]
|
||||
card = card[:card.index("}")]
|
||||
assert "flex: 1 1 0;" in card
|
||||
assert "min-height: 0;" in card
|
||||
assert "overflow: hidden;" in card
|
||||
assert 'class="memory-tab-panel research-tab-panel"' in panel
|
||||
assert 'class="admin-card research-new-job"' in panel
|
||||
assert 'id="research-history-filters" class="skills-summary-strip"' in panel
|
||||
@@ -21,7 +26,11 @@ def test_history_list_participates_in_shared_library_expansion_layout():
|
||||
assert "pane.classList.toggle('research-results-view', tab !== 'research')" in panel
|
||||
assert "display: flex; flex-direction: column; gap: 10px;" in css
|
||||
assert ".research-tabs {\n flex: 0 0 auto;\n margin: -4px -4px 0;" in css
|
||||
assert "font-size:11px; letter-spacing:0;" in css
|
||||
title = css[css.index("#research-pane .research-history-title-row h2 {"):]
|
||||
title = title[:title.index("}")]
|
||||
assert "letter-spacing: 0;" in title
|
||||
desc = css[css.index("#research-pane .research-history-desc {"):]
|
||||
assert "font-size: 11px;" in desc[:desc.index("}")]
|
||||
assert "font: inherit; font-size: 12px; cursor: pointer; text-align: left;" in css
|
||||
assert "#research-pane .research-history-search-wrap .memory-search-input" in css
|
||||
assert "height: 30px;" in css
|
||||
@@ -65,8 +74,8 @@ def test_research_panel_uses_one_versioned_module_instance():
|
||||
renderer = (ROOT / "static/js/chatRenderer.js").read_text(encoding="utf-8")
|
||||
index = (ROOT / "static/index.html").read_text(encoding="utf-8")
|
||||
|
||||
version = "20260902researchhistoryalign34"
|
||||
assert f"research/panel.js?v={version}" in app
|
||||
assert renderer.count(f"research/panel.js?v={version}") == 2
|
||||
assert f"style.css?v={version}" in index
|
||||
assert f"app.js?v={version}" in index
|
||||
panel_version = re.search(r"research/panel\.js\?v=([^'\"]+)", app)
|
||||
asset_version = re.search(r"/static/app\.js\?v=([^'\"]+)", index)
|
||||
style_version = re.search(r"/static/style\.css\?v=([^'\"]+)", index)
|
||||
assert panel_version and renderer.count(f"research/panel.js?v={panel_version.group(1)}") == 2
|
||||
assert asset_version and style_version and asset_version.group(1) == style_version.group(1)
|
||||
|
||||
@@ -25,18 +25,18 @@ def test_research_settings_use_custom_pickers_and_shared_provider_icons():
|
||||
assert "provider === 'google' ? 'google_pse'" in icons
|
||||
first_picker_offset = style.split(".research-setting:has(#research-rounds),", 1)[1].split("}", 1)[0]
|
||||
assert ".research-setting:has(#research-category)" in first_picker_offset
|
||||
assert "top: -4px;" in first_picker_offset
|
||||
assert "top: 0;" in first_picker_offset
|
||||
|
||||
|
||||
def test_completed_research_keeps_primary_and_utility_actions_visible_without_format_icon():
|
||||
panel = (ROOT / "static/js/research/panel.js").read_text(encoding="utf-8")
|
||||
|
||||
assert "research-job-format-icon" not in panel
|
||||
assert "Visual Report" in panel
|
||||
assert "Discuss" in panel
|
||||
assert 'data-action="copy" title="Copy report to clipboard"' in panel
|
||||
assert 'data-action="dismiss" title="Clear from list"' in panel
|
||||
assert 'data-action="delete" title="Delete from disk"' in panel
|
||||
assert 'data-action="report" title="Open visual report"' in panel
|
||||
assert 'data-action="chat" title="Open follow-up chat with this research as context"' in panel
|
||||
assert "{ action: 'copy', icon: _copyIcon, label: 'Copy report' }" in panel
|
||||
assert "{ action: 'dismiss', icon: _cancelIcon, label: 'Hide from list' }" in panel
|
||||
assert "{ action: 'delete', icon: _trashIcon, label: 'Delete from disk', danger: true }" in panel
|
||||
|
||||
|
||||
def test_research_format_survives_live_and_reconnected_jobs():
|
||||
|
||||
@@ -40,7 +40,8 @@ def test_tasks_filter_chips_include_active_paused_switch():
|
||||
assert "mkChip(`active (${activeCount})`, 'active', _taskStatusFilter === 'active', 'status');" in src
|
||||
assert "mkChip(`paused (${pausedCount})`, 'paused', _taskStatusFilter === 'paused', 'status');" in src
|
||||
assert "if (_taskStatusFilter && String(t.status || '').toLowerCase() !== _taskStatusFilter) return false;" in src
|
||||
assert "if (value === null) _taskStatusFilter = null;" in src
|
||||
assert "_taskStatusFilter = _taskStatusFilter === value ? null : value;" in src
|
||||
assert "_taskStatusFilter = null;" in src[src.index("if (kind === 'status')"):src.index("_renderList();", src.index("if (kind === 'status')"))]
|
||||
|
||||
|
||||
def test_tasks_completed_view_exposes_active_paused_shortcuts():
|
||||
|
||||
@@ -155,12 +155,12 @@ def test_every_changed_approval_module_is_cache_busted_together():
|
||||
assert found, f"missing cache-busted reference for {module_name}"
|
||||
assert len(set(found)) == 1, f"split module graph for {module_name}: {found}"
|
||||
|
||||
# These shared modules are imported throughout the graph. Keep their URL
|
||||
# canonical and unversioned; mixing a query URL with plain relative imports
|
||||
# creates a second singleton with separate state and listeners.
|
||||
for module_name in ("sessions.js", "ui.js", "memory.js", "markdown.js", "models.js"):
|
||||
# Shared modules must have one URL apiece. ui.js is consistently versioned
|
||||
# throughout the graph; the other shared modules remain unversioned.
|
||||
for module_name in ("sessions.js", "memory.js", "markdown.js", "models.js"):
|
||||
assert any(module_name in source for source in sources)
|
||||
assert not versions(module_name), f"split module graph for {module_name}"
|
||||
assert len(set(versions("ui.js"))) == 1
|
||||
|
||||
compare_stream = (root / "static/js/compare/stream.js").read_text(encoding="utf-8")
|
||||
compare_vote = (root / "static/js/compare/vote.js").read_text(encoding="utf-8")
|
||||
|
||||
Reference in New Issue
Block a user