import json from datetime import date import pytest from mka.application.meeting_service import ParticipantInput from mka.application.run_inputs import ( RunInputJsonError, RunInputState, export_run_inputs, import_run_inputs, run_input_filename, ) def populated_state() -> RunInputState: return RunInputState( title="F&E Technik Abteilungs Jour Fixe", description="Review the production trial.", language="de", meeting_date=date(2026, 8, 25), participants=( ParticipantInput( participant_id="martin", display_name="Martin Tazl", role="Project lead", organization="Engineering", ), ParticipantInput( participant_id="alex", display_name="Alexander Funk", attendance_status="mentioned_only", ), ), audio_normalization=False, diarization_enabled=True, source_file_name="2026-08-25_jour_fixe.wav", ) def test_current_form_state_serializes_with_schema_and_all_supported_values() -> None: document = json.loads(export_run_inputs(populated_state())) assert document["schema_version"] == 1 assert document["meeting"]["title"] == "F&E Technik Abteilungs Jour Fixe" assert document["meeting"]["description"] == "Review the production trial." assert document["meeting"]["language"] == "de" assert document["meeting"]["date"] == "2026-08-25" assert document["meeting"]["participants"][1] == { "participant_id": "alex", "display_name": "Alexander Funk", "role": "", "organization": "", "attendance_status": "mentioned_only", } assert document["processing"] == { "audio_normalization": False, "diarization_enabled": True, } def test_export_import_round_trip_restores_form_and_participants() -> None: original = populated_state() restored = import_run_inputs(export_run_inputs(original)) assert restored == original assert restored.participants[0].participant_id == "martin" assert restored.participants[0].display_name == "Martin Tazl" def test_missing_optional_fields_use_current_defaults() -> None: restored = import_run_inputs('{"schema_version": 1}') defaults = RunInputState.defaults() assert restored == defaults def test_unknown_safe_fields_are_ignored() -> None: restored = import_run_inputs( json.dumps( { "schema_version": 1, "meeting": {"title": "Known", "future_field": {"value": 1}}, "processing": {"future_toggle": True}, "future_section": [1, 2, 3], } ) ) assert restored.title == "Known" @pytest.mark.parametrize("content", ["not-json", "[]", b"\xff"]) def test_malformed_json_is_rejected(content: str | bytes) -> None: with pytest.raises(RunInputJsonError, match="Malformed|top-level"): import_run_inputs(content) def test_unsupported_future_schema_is_rejected() -> None: with pytest.raises(RunInputJsonError, match="Unsupported.*schema_version"): import_run_inputs('{"schema_version": 2}') def test_media_contents_are_never_serialized() -> None: exported = export_run_inputs(populated_state()) assert "2026-08-25_jour_fixe.wav" in exported assert "audio_bytes" not in exported assert "base64" not in exported def test_source_filename_must_not_be_a_machine_specific_path() -> None: with pytest.raises(RunInputJsonError, match="filename, not a path"): import_run_inputs('{"schema_version": 1, "source_file_name": "/tmp/meeting.wav"}') def test_invalid_or_duplicate_participants_are_not_silently_reinterpreted() -> None: document = { "schema_version": 1, "meeting": { "participants": [ {"participant_id": "martin", "display_name": "Martin"}, {"participant_id": "martin", "display_name": "Someone else"}, ] }, } with pytest.raises(RunInputJsonError, match="Duplicate participant_id"): import_run_inputs(json.dumps(document)) def test_export_filename_is_readable_and_machine_independent() -> None: assert run_input_filename("F&E Technik Jour Fixe") == ( "meeting-inputs-f-e-technik-jour-fixe.json" )