| import inspect |
|
|
| import app |
| from sovereign_bench.models import AgentTurn, EvidenceItem, JurorVote, TrialEvent |
|
|
|
|
| OLD_CARD_CLASSES = [ |
| "paper-panel", |
| "juror-panel", |
| "mind-panel", |
| "empty-state", |
| "trial-downloads", |
| ] |
|
|
|
|
| def _event_with_lower_tab_data() -> TrialEvent: |
| evidence = EvidenceItem( |
| id="E1", |
| title="Ledger entry", |
| source="Archive", |
| excerpt="A short exhibit excerpt.", |
| supports="claimant", |
| reliability=0.82, |
| note="Useful but incomplete.", |
| ) |
| vote = JurorVote( |
| juror="Karl Marx", |
| persona=app.JUROR_PERSONAS["Karl Marx"], |
| vote="liable", |
| reason="The exhibit supports the claim.", |
| evidence_ids=["E1"], |
| ) |
| return TrialEvent( |
| phase="deliberation", |
| title="Jury weighs the record", |
| body="The jury reviews the record.", |
| turns=[ |
| AgentTurn( |
| agent="Nemotron Jury", |
| role="juror panel", |
| content="The jurors compare E1 and state their votes.", |
| model="nvidia/Nemotron-Orchestrator-8B", |
| confidence=0.84, |
| input="SYSTEM:\nYou are the jury.\n\nUSER:\nWeigh E1 and explain the vote.", |
| ) |
| ], |
| evidence=[evidence], |
| votes=[vote], |
| trace={"mode": "test"}, |
| ) |
|
|
|
|
| def _speaker_event(agent: str, phase: str = "questions") -> TrialEvent: |
| return TrialEvent( |
| phase=phase, |
| title=f"{agent} speaks", |
| body="A single speaker takes the floor.", |
| turns=[ |
| AgentTurn( |
| agent=agent, |
| role="test speaker", |
| content=f"{agent} has the visible floor.", |
| model="test-model", |
| confidence=0.9, |
| input="SYSTEM:\nTest prompt.", |
| ) |
| ], |
| ) |
|
|
|
|
| def test_lower_tab_renderers_emit_plain_text_classes(): |
| event = _event_with_lower_tab_data() |
| html = "\n".join( |
| [ |
| app.render_evidence([]), |
| app.render_evidence([event]), |
| app.render_jurors([]), |
| app.render_jurors([event]), |
| app.render_mind([], True), |
| app.render_mind([event], True), |
| app.render_mind([event], False), |
| ] |
| ) |
|
|
| for class_name in OLD_CARD_CLASSES: |
| assert class_name not in html |
|
|
| assert "drawer-text-block" in html |
| assert "drawer-empty" in html |
| assert "mind-text" in html |
|
|
|
|
| def test_download_controls_are_not_wired_into_app(): |
| source = inspect.getsource(app.build_app) |
|
|
| assert "DownloadButton" not in source |
| assert "Download decree" not in source |
| assert "Download agent trace" not in source |
|
|
|
|
| def test_courtroom_splits_six_jurors_between_side_benches(): |
| html = app.render_court([_event_with_lower_tab_data()], started=True) |
|
|
| assert "jury-benches left" in html |
| assert "jury-benches right" in html |
| assert html.count("<a class='juror") == 6 |
| assert html.find("jury-benches left") < html.find("jury-benches right") |
|
|
|
|
| def test_courtroom_threads_show_model_input_output_on_hover_and_click(): |
| html = app.render_court([_event_with_lower_tab_data()], started=True) |
|
|
| assert "tooltip-io-label'>Input" in html |
| assert "tooltip-io-label'>Output" in html |
| assert "Click to open full thread" in html |
| assert "class='ai-thread-modal'" in html |
| assert "thread-block'>SYSTEM:" in html |
| assert "The jurors compare E1 and state their votes." in html |
| assert "href='#ai-thread-karl-marx'" in html |
|
|
|
|
| def test_courtroom_renders_historical_judge_and_juror_assets(): |
| html = app.render_court([_event_with_lower_tab_data()], started=True) |
|
|
| assert "Marcus Aurelius" in html |
| assert "assets/characters/marcus-aurelius.png" in html |
| for name, image in app.JUROR_IMAGES.items(): |
| assert name in html |
| assert image in html |
| assert html.count("class='juror-portrait'") == 6 |
|
|
|
|
| def test_courtroom_renders_foreground_fences_and_judge_table_above_characters(): |
| html = app.render_court([_event_with_lower_tab_data()], started=True) |
|
|
| assert html.count("assets/foreground/foregroundFence.png") == 2 |
| assert "assets/foreground/JudgeTable.png" in html |
| assert html.find("class='puppet judge") < html.find("class='foreground-props'") |
| assert ".foreground-props {\n position: absolute;\n inset: 0;\n z-index: 13;" in app.CSS |
| assert ".puppet {\n --skin: #c99257;" in app.CSS |
| assert "z-index: 8;" in app.CSS |
|
|
|
|
| def test_latest_speaker_sets_stage_class_and_speech_bubble(): |
| html = app.render_court([_speaker_event("Advocate Auric", phase="claims")], started=True) |
|
|
| assert "speaker-auric" in html |
| assert "class='speech-bubble'" in html |
| assert "Advocate Auric has the visible floor." in html |
| assert "puppet auric active walking" in html |
| assert "puppet sable active" not in html |
|
|
|
|
| def test_individual_juror_can_be_active_speaker(): |
| event = TrialEvent( |
| phase="deliberation", |
| title="Juror Karl Marx Votes", |
| body=app.JUROR_PERSONAS["Karl Marx"], |
| turns=[ |
| AgentTurn( |
| agent="Karl Marx", |
| role="juror", |
| content="Liable. E1 carries the record.", |
| model="nvidia/Nemotron-Orchestrator-8B", |
| confidence=0.86, |
| input="SYSTEM:\nJury JSON prompt.", |
| ) |
| ], |
| votes=[ |
| JurorVote( |
| juror="Karl Marx", |
| persona=app.JUROR_PERSONAS["Karl Marx"], |
| vote="liable", |
| reason="E1 carries the record.", |
| evidence_ids=["E1"], |
| ) |
| ], |
| ) |
| html = app.render_court([event], started=True) |
|
|
| assert "speaker-karl-marx" in html |
| assert "<a class='juror active'" in html |
| assert "Liable. E1 carries the record." in html |
|
|
|
|
| def test_lawyer_movement_css_is_speaker_specific_not_phase_wide(): |
| assert ".speaker-auric .puppet.auric" in app.CSS |
| assert ".speaker-sable .puppet.sable" in app.CSS |
| assert ".phase-claims .puppet.auric" not in app.CSS |
| assert ".phase-opening .puppet.sable" not in app.CSS |
|
|
|
|
| def test_run_ui_yields_five_outputs_without_download_status(monkeypatch): |
| event = _event_with_lower_tab_data() |
| monkeypatch.setattr(app, "get_events", lambda request: iter([event])) |
|
|
| outputs = list(app.run_ui("Trial of Socrates", "", "", "swift", True)) |
|
|
| assert outputs |
| assert all(len(output) == 5 for output in outputs) |
| assert outputs[1][-1] == "Step 1: Jury weighs the record" |
| assert outputs[-1][-1] == "Verdict sealed." |
| assert "download" not in outputs[-1][-1].lower() |
|
|
|
|
| def test_run_ui_stops_with_model_unavailable_error(monkeypatch): |
| def broken_events(request): |
| raise RuntimeError("Marcus Aurelius unavailable: offline") |
| yield |
|
|
| monkeypatch.setattr(app, "get_events", broken_events) |
|
|
| outputs = list(app.run_ui("Trial of Socrates", "", "", "swift", True)) |
|
|
| assert outputs[-1][-1] == "Model response required. Trial stopped: Marcus Aurelius unavailable: offline" |
| assert "Claimant score" not in outputs[-1][0] |
|
|
|
|
| def test_court_renders_sound_toggle(): |
| html = app.render_court([]) |
|
|
| assert "sound-toggle" in html |
| assert "aria-label='Toggle sound'" in html |
| assert "aria-pressed='false'" in html |
|
|
|
|
| def test_audio_controller_has_score_breathing_and_mute_toggle(): |
| assert "SCORE_BREATH_INTERVAL_MS = 20000" in app.APP_JS |
| assert "SCORE_BREATH_DURATION_MS = 5000" in app.APP_JS |
| assert "toggleMuted()" in app.APP_JS |
| assert "this.fadeScore(SCORE_QUIET_VOLUME, halfDuration" in app.APP_JS |
|
|
|
|
| def test_courtroom_background_has_no_overlay_or_character_shadow(): |
| assert "background: #141413 !important;" in app.CSS |
| assert "background-color: #141413 !important;" in app.CSS |
| assert "cover fixed no-repeat" not in app.CSS |
| assert ".court-episode-stage::before {\n content: \"\";\n display: none;" in app.CSS |
| assert ".court-episode-stage::after {\n content: \"\";\n display: none;" in app.CSS |
| assert "url('/gradio_api/file=assets/background/CourtRoom.png') center center / 100% 100% no-repeat" in app.CSS |
| assert "filter: drop-shadow(0 12px 14px" not in app.CSS |
| assert "filter: drop-shadow(0 8px 10px" not in app.CSS |
|
|
|
|
| def test_synthetic_stage_props_do_not_tint_background(): |
| assert ".bench-front {\n display: none;" in app.CSS |
| assert ".trial-floor-mark {\n display: none;" in app.CSS |
| assert ".gallery-benches {\n display: none;" in app.CSS |
| assert ".prop-label {\n display: none;" in app.CSS |
| assert ".counsel-table" in app.CSS |
| assert "background: transparent;\n box-shadow: none;" in app.CSS |
| assert ".witness-area" in app.CSS |
|
|