aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/tests/test_cross_agent_send.py
blob: f716e959ffd6d04b5c6a675a61a241acbe78bb0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
"""Tests for cross-agent-send.

Subprocess-based: treat the script as a black-box CLI and assert on its
exit codes, stdout, and the files it produces.
"""

from __future__ import annotations

import os
import subprocess
import textwrap
from pathlib import Path

import pytest

SCRIPT = Path(__file__).resolve().parent.parent / "cross-agent-comms" / "cross-agent-send"


def _make_message(tmp_path: Path, conv_id: str = "test-conv", seq: int = 1, msg_type: str = "request",
                  proto_version: str = "5") -> Path:
    msg = tmp_path / "msg.org"
    msg.write_text(textwrap.dedent(f"""\
        #+TITLE: Test message
        #+CONVERSATION_ID: {conv_id}
        #+MESSAGE_TYPE: {msg_type}
        #+SEQUENCE: {seq}
        #+TIMESTAMP: 2026-04-27T05:00:00-05:00
        #+PROTOCOL_VERSION: {proto_version}

        Body.
        """))
    return msg


def _run(args: list[str], env: dict | None = None, cwd: Path | None = None) -> subprocess.CompletedProcess:
    return subprocess.run(
        [str(SCRIPT), *args],
        capture_output=True,
        text=True,
        env=env,
        cwd=cwd,
    )


@pytest.fixture
def isolated_env(tmp_path, monkeypatch):
    """Redirect HOME so peers.toml, HALT, marker files are scoped to the test."""
    fake_home = tmp_path / "home"
    fake_home.mkdir()
    monkeypatch.setenv("HOME", str(fake_home))
    # Pre-create projects/ so derive_sender_project has somewhere to look.
    (fake_home / "projects" / "homelab").mkdir(parents=True)
    return fake_home


def test_send_help(isolated_env):
    """--help works without side effects."""
    result = _run(["--help"], env={**os.environ, "HOME": str(isolated_env)})
    assert result.returncode == 0
    assert "Send a cross-agent message" in result.stdout


def test_send_missing_message_file(isolated_env):
    """Nonexistent message file returns general error."""
    import socket
    machine = socket.gethostname().split(".")[0]
    result = _run(
        [f"{machine}.homelab", str(isolated_env / "nonexistent.org")],
        env={**os.environ, "HOME": str(isolated_env)},
    )
    assert result.returncode == 1
    assert "not found" in result.stderr.lower()


def test_send_invalid_destination_format(isolated_env, tmp_path):
    """Destination without . returns dest-not-found exit code."""
    msg = _make_message(tmp_path)
    result = _run(
        ["bogus", str(msg)],
        env={**os.environ, "HOME": str(isolated_env)},
    )
    assert result.returncode == 2
    assert "<machine>.<project>" in result.stderr or "destination" in result.stderr.lower()


def test_send_dest_not_in_peers(isolated_env, tmp_path):
    """Cross-machine destination with no peers.toml entry exits 2."""
    msg = _make_message(tmp_path)
    result = _run(
        ["unknownmachine.homelab", str(msg)],
        env={**os.environ, "HOME": str(isolated_env)},
    )
    assert result.returncode == 2
    assert "not found in peers" in result.stderr


def test_send_frontmatter_missing_required(isolated_env, tmp_path):
    """Message missing required fields exits 4."""
    bad = tmp_path / "bad.org"
    bad.write_text("#+TITLE: nope\n\nBody.\n")
    import socket
    machine = socket.gethostname().split(".")[0]
    result = _run(
        [f"{machine}.homelab", str(bad)],
        env={**os.environ, "HOME": str(isolated_env)},
    )
    assert result.returncode == 4
    assert "missing required fields" in result.stderr


def test_send_invalid_message_type(isolated_env, tmp_path):
    """Unknown MESSAGE_TYPE exits 4."""
    msg = _make_message(tmp_path, msg_type="frobnicate")
    import socket
    machine = socket.gethostname().split(".")[0]
    result = _run(
        [f"{machine}.homelab", str(msg)],
        env={**os.environ, "HOME": str(isolated_env)},
    )
    assert result.returncode == 4
    assert "MESSAGE_TYPE" in result.stderr


def test_send_halt_blocks(isolated_env, tmp_path):
    """When HALT exists, send refuses with exit 5."""
    halt = isolated_env / ".config" / "cross-agent-comms" / "HALT"
    halt.parent.mkdir(parents=True)
    halt.write_text("test halt\n")
    msg = _make_message(tmp_path)
    import socket
    machine = socket.gethostname().split(".")[0]
    result = _run(
        [f"{machine}.homelab", str(msg)],
        env={**os.environ, "HOME": str(isolated_env)},
    )
    assert result.returncode == 5
    assert "halt active" in result.stderr.lower()


def test_send_same_machine_no_sign_delivers(isolated_env, tmp_path):
    """Same-machine delivery with --no-sign produces a canonically named file."""
    msg = _make_message(tmp_path, conv_id="my-conv")
    import socket
    machine = socket.gethostname().split(".")[0]
    # Sender is derived from CWD walking up to ~/projects/<name>/
    cwd = isolated_env / "projects" / "homelab"
    result = _run(
        [f"{machine}.homelab", str(msg), "--no-sign"],
        env={**os.environ, "HOME": str(isolated_env)},
        cwd=cwd,
    )
    assert result.returncode == 0, f"stderr={result.stderr}"
    inbox = isolated_env / "projects" / "homelab" / "inbox" / "from-agents"
    files = list(inbox.glob("*-from-homelab-my-conv.org"))
    assert len(files) == 1
    # No sig file with --no-sign.
    assert not list(inbox.glob("*.asc"))
    # Canonical filename pattern.
    assert files[0].name.startswith("2026") and files[0].name.endswith("-from-homelab-my-conv.org")


def test_send_same_machine_signed_writes_asc(isolated_env, tmp_path):
    """Signed delivery writes both .org and .asc."""
    msg = _make_message(tmp_path, conv_id="signed-conv")
    import socket
    machine = socket.gethostname().split(".")[0]
    cwd = isolated_env / "projects" / "homelab"
    # Use the real GPG keyring (not isolating GPG — Craig's existing keys are fine for tests).
    real_env = {**os.environ, "HOME": str(isolated_env), "GNUPGHOME": str(Path.home() / ".gnupg")}
    result = _run(
        [f"{machine}.homelab", str(msg)],
        env=real_env,
        cwd=cwd,
    )
    if result.returncode != 0:
        pytest.skip(f"GPG signing unavailable in this environment: {result.stderr}")
    inbox = isolated_env / "projects" / "homelab" / "inbox" / "from-agents"
    org_files = list(inbox.glob("*-from-homelab-signed-conv.org"))
    asc_files = list(inbox.glob("*-from-homelab-signed-conv.org.asc"))
    assert len(org_files) == 1
    assert len(asc_files) == 1


def test_send_filename_ignores_input_basename(isolated_env, tmp_path):
    """User's input filename is ignored; canonical filename is generated."""
    weird = tmp_path / "weird-user-name.org"
    weird.write_text(textwrap.dedent("""\
        #+TITLE: Title
        #+CONVERSATION_ID: ignored-input
        #+MESSAGE_TYPE: request
        #+SEQUENCE: 1
        #+TIMESTAMP: 2026-04-27T05:00:00-05:00
        #+PROTOCOL_VERSION: 5

        Body.
        """))
    import socket
    machine = socket.gethostname().split(".")[0]
    cwd = isolated_env / "projects" / "homelab"
    result = _run(
        [f"{machine}.homelab", str(weird), "--no-sign"],
        env={**os.environ, "HOME": str(isolated_env)},
        cwd=cwd,
    )
    assert result.returncode == 0
    inbox = isolated_env / "projects" / "homelab" / "inbox" / "from-agents"
    # No file named after the user's input.
    assert not (inbox / "weird-user-name.org").exists()
    # Canonical naming used.
    assert list(inbox.glob("*-from-homelab-ignored-input.org"))