ShiErFeng/shierfeng-fastapi-backend/tests/cli/tui/test_tui_dashboard_search.py
2026-07-24 09:43:10 +08:00

193 lines
7.3 KiB
Python

from types import SimpleNamespace
import pytest
def test_build_filter_bar_text_includes_search_context(tui_modules: SimpleNamespace) -> None:
text = tui_modules.cli_tui_search.TUI_SEARCH_SERVICE.build_filter_bar_text(
tui_modules.cli_tui_search.JOB_FILTER_OPTIONS,
'failed',
search_query='sync',
search_placeholder='按任务名搜索',
search_suggestions=['sync-job', 'sync-user'],
)
assert '当前筛选 · 失败' in text
assert '搜索器 · [/] 按任务名搜索' in text
assert '当前搜索 · sync | [Backspace] 清空' in text
assert '候选建议 · sync-job sync-user' in text
def test_tui_search_service_builds_filter_context_and_section_filtering(
tui_modules: SimpleNamespace,
) -> None:
service = tui_modules.cli_tui_search.TuiSearchService(
tui_modules.cli_tui_search.TuiSearchSuggestionProviderRegistry(
providers={
'jobs': tui_modules.cli_tui_search.SearchSuggestionProviderSpec(
'按任务名搜索',
lambda incomplete: ['sync-user', 'sync-role', 'cleanup'],
),
'app': tui_modules.cli_tui_search.SearchSuggestionProviderSpec('按分区或内容搜索'),
}
)
)
option = service.resolve_filter_option(tui_modules.cli_tui_search.JOB_FILTER_OPTIONS, 'failed')
filter_bar = service.build_filter_bar_text(
tui_modules.cli_tui_search.JOB_FILTER_OPTIONS,
'failed',
search_query='sync',
search_placeholder='按任务名搜索',
search_suggestions=['sync-user', 'sync-role'],
)
search_context = service.resolve_search_context('jobs', 'sync')
default_context = service.resolve_search_context('app', '总览', default_suggestions=['总览判断', '应用配置'])
sections = service.filter_detail_sections(
[
tui_modules.cli_tui_adapters.DetailSectionSnapshot(title='总览判断', status='ok', lines=['应用正常']),
tui_modules.cli_tui_adapters.DetailSectionSnapshot(title='路由摘要', status='warn', lines=['同步任务接口']),
],
'同步',
)
assert option is not None
assert option.key == 'failed'
assert '当前筛选 · 失败' in filter_bar
assert search_context is not None
assert search_context.placeholder == '按任务名搜索'
assert search_context.suggestions[:2] == ['sync-user', 'sync-role']
assert default_context is not None
assert default_context.suggestions == ['总览判断']
assert [section.title for section in sections] == ['路由摘要']
def test_tui_search_service_skips_provider_when_query_is_empty(
tui_modules: SimpleNamespace,
) -> None:
provider_calls: list[str] = []
def record_provider(incomplete: str) -> list[str]:
provider_calls.append(incomplete)
return ['site_name']
service = tui_modules.cli_tui_search.TuiSearchService(
tui_modules.cli_tui_search.TuiSearchSuggestionProviderRegistry(
providers={
'configs': tui_modules.cli_tui_search.SearchSuggestionProviderSpec(
'按配置键搜索',
record_provider,
)
}
)
)
search_context = service.resolve_search_context('configs', '')
assert search_context is not None
assert search_context.placeholder == '按配置键搜索'
assert search_context.suggestions == []
assert provider_calls == []
def test_search_highlight_helper_wraps_matches(tui_modules: SimpleNamespace) -> None:
highlighted = tui_modules.cli_tui_search.TUI_SEARCH_HIGHLIGHTER.highlight('sync-user job', 'sync')
assert highlighted == '【sync】-user job'
def test_dashboard_screen_support_builds_summary_signal_and_status_track(
tui_modules: SimpleNamespace,
) -> None:
support = tui_modules.cli_tui_dashboard.DashboardScreenSupport()
snapshot = tui_modules.cli_tui_app.DashboardSnapshot(
env='dev',
metrics=[],
panels=[
tui_modules.cli_tui_adapters.DashboardPanelSnapshot(title='应用', status='ok', lines=['ok']),
tui_modules.cli_tui_adapters.DashboardPanelSnapshot(title='任务', status='warn', lines=['warn']),
tui_modules.cli_tui_adapters.DashboardPanelSnapshot(title='数据库', status='fail', lines=['fail']),
],
)
summary = support.build_summary_text(snapshot)
status_track = support.build_status_track(snapshot)
signal_lines = support.build_signal_lines(snapshot)
assert '共 3 个面板' in summary
assert status_track == 'o!x'
assert any('失败 01' in line for line in signal_lines)
def test_dashboard_screen_focus_service_moves_and_scrolls_between_focus_targets(
tui_modules: SimpleNamespace,
) -> None:
focus_service = tui_modules.cli_tui_dashboard.DashboardScreenFocusService()
focused: list[str] = []
sidebar = SimpleNamespace(focus=lambda: focused.append('sidebar'))
workspace_main = SimpleNamespace(focus=lambda: focused.append('main'))
fake_app = SimpleNamespace(focused=None)
fake_screen = SimpleNamespace(
app=fake_app,
query_one=lambda selector, widget_type=None: {
tui_modules.cli_tui_widgets.WorkspaceSidebar: sidebar,
'#workspace-main': workspace_main,
}[selector],
)
focus_service.move_focus(fake_screen, 1)
fake_app.focused = sidebar
focus_service.move_focus(fake_screen, 1)
fake_app.focused = object()
scroll_target = focus_service.get_scroll_target(fake_screen)
assert focused == ['sidebar', 'main']
assert scroll_target is workspace_main
@pytest.mark.asyncio
async def test_dashboard_grid_expands_to_fit_status_panels(
monkeypatch: pytest.MonkeyPatch,
tui_modules: SimpleNamespace,
) -> None:
expected_dashboard_panel_count = 8
min_expanded_dashboard_grid_height = 20
snapshot = tui_modules.cli_tui_app.DashboardSnapshot(
env='dev',
metrics=[],
panels=[
tui_modules.cli_tui_adapters.DashboardPanelSnapshot(
title=f'面板 {index + 1}',
status='ok' if index % 2 == 0 else 'warn',
lines=[
'## 巡检摘要',
f'当前面板: {index + 1}',
'状态说明: 这里需要展示多行内容',
'',
'## 建议动作',
'进入对应分区继续查看详情',
],
)
for index in range(8)
],
)
monkeypatch.setitem(
tui_modules.cli_tui_app.TUI_SNAPSHOT_COLLECTOR_REGISTRY.collectors, 'dashboard', lambda env: snapshot
)
app = tui_modules.cli_tui_app.ShierfengTuiApp('dev')
async with app.run_test(size=(160, 60)) as pilot:
await pilot.pause()
await pilot.pause()
grid = app.screen.query_one('#dashboard-grid')
panels = list(
app.screen.query(tui_modules.cli_tui_widgets.StatusPanel).results(tui_modules.cli_tui_widgets.StatusPanel)
)
assert len(panels) == expected_dashboard_panel_count
assert grid.virtual_size.height > min_expanded_dashboard_grid_height
assert all(panel.content_size.height > 0 for panel in panels)
assert len({panel.region.height for panel in panels}) == 1