Files
openswarm/openswarm-runner/tests/test_run_spec.py
T

95 lines
3.5 KiB
Python

"""The run spec is the only thing the control plane can say to this container."""
import json
import os
import stat
import pytest
from runner.run_spec import InvalidRunSpec, RunSpec, load_run_spec
from runner.seed.data_root import seed_data_root, settings_for_run
VALID_CREDENTIAL = {"provider": "anthropic", "auth_type": "api_key", "api_key": "sk-test-not-real"}
def spec_body(**overrides) -> dict:
body = {
"run_id": "run-1",
"workflow": {
"id": "wf-1",
"title": "Daily digest",
"model": "opus-5",
"steps": [{"text": "summarize the inbox"}],
"schedule": {"enabled": True, "repeat_unit": "day", "hour": 9},
},
"credentials": [VALID_CREDENTIAL],
}
body.update(overrides)
return body
def test_a_missing_spec_names_both_env_vars(monkeypatch) -> None:
monkeypatch.delenv("OPENSWARM_RUN_SPEC", raising=False)
monkeypatch.delenv("OPENSWARM_RUN_SPEC_FILE", raising=False)
with pytest.raises(InvalidRunSpec, match="OPENSWARM_RUN_SPEC_FILE"):
load_run_spec()
def test_a_spec_file_is_accepted(tmp_path, monkeypatch) -> None:
path = tmp_path / "spec.json"
path.write_text(json.dumps(spec_body()), encoding="utf-8")
monkeypatch.delenv("OPENSWARM_RUN_SPEC", raising=False)
monkeypatch.setenv("OPENSWARM_RUN_SPEC_FILE", str(path))
assert load_run_spec().workflow.title == "Daily digest"
def test_unknown_top_level_fields_are_rejected(monkeypatch) -> None:
monkeypatch.setenv("OPENSWARM_RUN_SPEC", json.dumps(spec_body(surprise="hello")))
with pytest.raises(InvalidRunSpec, match="surprise"):
load_run_spec()
def test_a_run_needs_at_least_one_credential(monkeypatch) -> None:
monkeypatch.setenv("OPENSWARM_RUN_SPEC", json.dumps(spec_body(credentials=[])))
with pytest.raises(InvalidRunSpec):
load_run_spec()
def test_the_container_never_inherits_the_schedule() -> None:
spec = RunSpec.model_validate(spec_body())
assert spec.workflow.schedule.enabled is True
assert spec.workflow_for_disk().schedule.enabled is False
def test_seeding_writes_the_workflow_and_owner_only_settings(tmp_path) -> None:
spec = RunSpec.model_validate(spec_body())
workspace = str(tmp_path / "workspace")
seed_data_root(str(tmp_path / "data"), workspace, spec)
workflow_path = tmp_path / "data" / "workflows" / "wf-1.json"
settings_path = tmp_path / "data" / "settings" / "settings.json"
assert json.loads(workflow_path.read_text())["schedule"]["enabled"] is False
assert stat.S_IMODE(os.stat(settings_path).st_mode) == 0o600
settings = json.loads(settings_path.read_text())
assert settings["anthropic_api_key"] == "sk-test-not-real"
assert settings["default_model"] == "opus-5"
assert settings["analytics_opt_in"] is False
# The agent's folder is the deliverable folder, and it exists before the backend boots.
assert settings["default_folder"] == workspace
assert os.path.isdir(workspace)
def test_the_agent_is_told_its_files_only_survive_from_the_workspace(tmp_path) -> None:
spec = RunSpec.model_validate(spec_body())
prompt = settings_for_run(spec, str(tmp_path)).default_system_prompt or ""
assert "delivered back to the user" in prompt
def test_an_unmappable_api_key_provider_fails_loudly(tmp_path) -> None:
spec = RunSpec.model_validate(spec_body(
credentials=[{"provider": "wat", "auth_type": "api_key", "api_key": "x"}]
))
with pytest.raises(ValueError, match="no settings field"):
settings_for_run(spec, str(tmp_path))