Files
Artemis-Eapil/tests/unit/test_recording_config.py

115 lines
4.8 KiB
Python

"""Per-task archive quality overrides outrank JSON and saved environment defaults."""
import json
from types import SimpleNamespace
import pytest
from apps.admin_console.services.task_import import TaskCase, TaskOptions
from artemis.config.agent import AgentGlobalConfig, load_agent_config
from artemis.config.settings import settings
@pytest.fixture(autouse=True)
def recording_defaults(monkeypatch):
monkeypatch.setattr(settings, "ARTEMIS_RECORDING_CONFIG", {"fps": 10, "h264_kbps": 900})
monkeypatch.setattr(settings, "ARTEMIS_TASK_RECORDING_CONFIG", {})
def test_json_recording_defaults_override_saved_defaults(tmp_path):
config = tmp_path / "artemis.jsonc"
config.write_text(json.dumps({"agent": {"recording": {"fps": 20}}}), encoding="utf-8")
result = load_agent_config(config).recording
assert result.fps == 20
assert result.h264_kbps == 900
assert result.max_long_edge == 1280
def test_task_recording_override_has_highest_priority_and_is_partial(tmp_path, monkeypatch):
monkeypatch.setattr(settings, "ARTEMIS_TASK_RECORDING_CONFIG", {"fps": 30})
config = tmp_path / "artemis.jsonc"
config.write_text(
json.dumps({"agent": {"recording": {"fps": 20, "max_long_edge": 1920}}}), encoding="utf-8"
)
result = load_agent_config(config).recording
assert result.fps == 30
assert result.max_long_edge == 1920
assert result.h264_kbps == 900
def test_task_recording_override_applies_without_json_recording_block(monkeypatch):
monkeypatch.setattr(settings, "ARTEMIS_TASK_RECORDING_CONFIG", {"fps": 30})
assert AgentGlobalConfig().recording.fps == 30
def test_invalid_task_recording_override_is_rejected(monkeypatch):
monkeypatch.setattr(settings, "ARTEMIS_TASK_RECORDING_CONFIG", {"fps": 500})
with pytest.raises(ValueError, match="fps"):
AgentGlobalConfig.model_validate({"recording": {"fps": 20}})
def test_task_and_batch_serialization_do_not_inject_unset_recording_defaults():
options = TaskOptions.model_validate({"recording": {"fps": 30}})
assert options.model_dump(exclude_none=True)["recording"] == {"fps": 30}
case = TaskCase(name="fixture", goal="fixture", task_config=options)
assert case.model_dump()["task_config"]["recording"] == {"fps": 30}
# Round trips through stored batches preserve the partial override.
restored = TaskCase.model_validate(case.model_dump())
assert restored.model_dump()["task_config"]["recording"] == {"fps": 30}
def test_worker_env_keeps_task_quality_separate_from_saved_defaults(monkeypatch):
from apps.admin_console.services.task_queue_service import TaskQueueService
from artemis.runtime.adb_endpoint import AdbEndpoint, AdbTarget
monkeypatch.setenv("ARTEMIS_RECORDING_CONFIG", '{"fps":10}')
monkeypatch.setenv("ARTEMIS_TASK_RECORDING_CONFIG", '{"fps":60}')
_, environment = TaskQueueService._build_worker_invocation(
{"runtime_overrides": {"recording": {"fps": 30}}},
"test-run",
None,
"fixture",
"flash",
AdbTarget(AdbEndpoint.local()),
)
assert json.loads(environment["ARTEMIS_RECORDING_CONFIG"]) == {"fps": 10}
assert json.loads(environment["ARTEMIS_TASK_RECORDING_CONFIG"]) == {"fps": 30}
_, clean_environment = TaskQueueService._build_worker_invocation(
{}, "test-run", None, "fixture", "flash", AdbTarget(AdbEndpoint.local())
)
assert "ARTEMIS_TASK_RECORDING_CONFIG" not in clean_environment
def test_sdk_preserves_task_recording_scope_through_recorder(tmp_path, monkeypatch):
from artemis.plugins.models import EvidenceSource
from artemis.plugins.recording import PluginRecordings, capture_rectangle
from artemis.sdk.builders.agent_config_builder import AgentConfigBuilder
monkeypatch.setattr(
settings,
"ARTEMIS_TASK_RECORDING_CONFIG",
{"computer_scope": "window", "codec": "h264", "fps": 7, "max_long_edge": 960},
)
builder = AgentConfigBuilder()
config = builder.build()
ctx = SimpleNamespace(
agent_config=config,
data_engine=SimpleNamespace(base_dir=tmp_path, session_start_time=100),
execution_setup=SimpleNamespace(record_video_enabled=True),
)
recorder = PluginRecordings(ctx)
source = EvidenceSource(
kind="computer",
target_id="fixture",
window={"left": 40, "top": 50, "width": 800, "height": 600},
screen={"left": 0, "top": 0, "width": 1920, "height": 1080},
)
assert recorder.config.computer_scope == "window"
assert capture_rectangle(source, recorder.config.computer_scope) == source.window
assert recorder.config.codec == "h264"
assert recorder.config.fps == 7
assert recorder.config.max_long_edge == 960
# Building another task must not share a mutable recording configuration.
config.recording.fps = 12
assert builder.build().recording.fps == 7