|
016a29f…
|
lmata
|
1 |
package main |
|
016a29f…
|
lmata
|
2 |
|
|
016a29f…
|
lmata
|
3 |
import ( |
|
016a29f…
|
lmata
|
4 |
"bytes" |
|
016a29f…
|
lmata
|
5 |
"path/filepath" |
|
016a29f…
|
lmata
|
6 |
"testing" |
|
016a29f…
|
lmata
|
7 |
"time" |
|
016a29f…
|
lmata
|
8 |
|
|
016a29f…
|
lmata
|
9 |
"github.com/conflicthq/scuttlebot/pkg/sessionrelay" |
|
016a29f…
|
lmata
|
10 |
) |
|
016a29f…
|
lmata
|
11 |
|
|
016a29f…
|
lmata
|
12 |
func TestFilterMessages(t *testing.T) { |
|
016a29f…
|
lmata
|
13 |
now := time.Now() |
|
016a29f…
|
lmata
|
14 |
nick := "gemini-test" |
|
016a29f…
|
lmata
|
15 |
messages := []message{ |
|
016a29f…
|
lmata
|
16 |
{Nick: "operator", Text: "gemini-test: hello", At: now}, |
|
016a29f…
|
lmata
|
17 |
{Nick: "gemini-test", Text: "i am gemini", At: now}, // self |
|
016a29f…
|
lmata
|
18 |
{Nick: "other", Text: "not for me", At: now}, // no mention |
|
016a29f…
|
lmata
|
19 |
{Nick: "bridge", Text: "system message", At: now}, // service bot |
|
016a29f…
|
lmata
|
20 |
} |
|
016a29f…
|
lmata
|
21 |
|
|
cefe27d…
|
lmata
|
22 |
filtered, _ := filterMessages(messages, now.Add(-time.Minute), nick, "worker") |
|
016a29f…
|
lmata
|
23 |
if len(filtered) != 1 { |
|
016a29f…
|
lmata
|
24 |
t.Errorf("expected 1 filtered message, got %d", len(filtered)) |
|
016a29f…
|
lmata
|
25 |
} |
|
016a29f…
|
lmata
|
26 |
if filtered[0].Nick != "operator" { |
|
016a29f…
|
lmata
|
27 |
t.Errorf("expected operator message, got %s", filtered[0].Nick) |
|
016a29f…
|
lmata
|
28 |
} |
|
016a29f…
|
lmata
|
29 |
} |
|
016a29f…
|
lmata
|
30 |
|
|
016a29f…
|
lmata
|
31 |
func TestLoadConfig(t *testing.T) { |
|
016a29f…
|
lmata
|
32 |
t.Setenv("SCUTTLEBOT_CONFIG_FILE", filepath.Join(t.TempDir(), "scuttlebot-relay.env")) |
|
016a29f…
|
lmata
|
33 |
t.Setenv("SCUTTLEBOT_URL", "http://test:8080") |
|
016a29f…
|
lmata
|
34 |
t.Setenv("SCUTTLEBOT_TOKEN", "test-token") |
|
016a29f…
|
lmata
|
35 |
t.Setenv("GEMINI_SESSION_ID", "abc") |
|
016a29f…
|
lmata
|
36 |
t.Setenv("SCUTTLEBOT_TRANSPORT", "irc") |
|
016a29f…
|
lmata
|
37 |
t.Setenv("SCUTTLEBOT_IRC_ADDR", "127.0.0.1:7667") |
|
016a29f…
|
lmata
|
38 |
t.Setenv("SCUTTLEBOT_SESSION_ID", "") |
|
016a29f…
|
lmata
|
39 |
t.Setenv("SCUTTLEBOT_NICK", "") |
|
016a29f…
|
lmata
|
40 |
|
|
016a29f…
|
lmata
|
41 |
cfg, err := loadConfig([]string{"--cd", "../.."}) |
|
016a29f…
|
lmata
|
42 |
if err != nil { |
|
016a29f…
|
lmata
|
43 |
t.Fatal(err) |
|
016a29f…
|
lmata
|
44 |
} |
|
016a29f…
|
lmata
|
45 |
|
|
016a29f…
|
lmata
|
46 |
if cfg.URL != "http://test:8080" { |
|
016a29f…
|
lmata
|
47 |
t.Errorf("expected URL http://test:8080, got %s", cfg.URL) |
|
016a29f…
|
lmata
|
48 |
} |
|
016a29f…
|
lmata
|
49 |
if cfg.Token != "test-token" { |
|
016a29f…
|
lmata
|
50 |
t.Errorf("expected token test-token, got %s", cfg.Token) |
|
016a29f…
|
lmata
|
51 |
} |
|
016a29f…
|
lmata
|
52 |
if cfg.SessionID != "abc" { |
|
016a29f…
|
lmata
|
53 |
t.Errorf("expected session ID abc, got %s", cfg.SessionID) |
|
016a29f…
|
lmata
|
54 |
} |
|
016a29f…
|
lmata
|
55 |
if cfg.Nick != "gemini-scuttlebot-abc" { |
|
016a29f…
|
lmata
|
56 |
t.Errorf("expected nick gemini-scuttlebot-abc, got %s", cfg.Nick) |
|
016a29f…
|
lmata
|
57 |
} |
|
016a29f…
|
lmata
|
58 |
if cfg.Transport != sessionrelay.TransportIRC { |
|
016a29f…
|
lmata
|
59 |
t.Errorf("expected transport irc, got %s", cfg.Transport) |
|
016a29f…
|
lmata
|
60 |
} |
|
016a29f…
|
lmata
|
61 |
if cfg.IRCAddr != "127.0.0.1:7667" { |
|
016a29f…
|
lmata
|
62 |
t.Errorf("expected irc addr 127.0.0.1:7667, got %s", cfg.IRCAddr) |
|
016a29f…
|
lmata
|
63 |
} |
|
016a29f…
|
lmata
|
64 |
} |
|
016a29f…
|
lmata
|
65 |
|
|
016a29f…
|
lmata
|
66 |
func TestRelayStateShouldInterruptOnlyWhenRecentlyBusy(t *testing.T) { |
|
016a29f…
|
lmata
|
67 |
t.Helper() |
|
016a29f…
|
lmata
|
68 |
|
|
016a29f…
|
lmata
|
69 |
var state relayState |
|
016a29f…
|
lmata
|
70 |
now := time.Date(2026, 3, 31, 21, 47, 0, 0, time.UTC) |
|
016a29f…
|
lmata
|
71 |
state.observeOutput([]byte("Working (1s • esc to interrupt)"), now) |
|
016a29f…
|
lmata
|
72 |
|
|
016a29f…
|
lmata
|
73 |
if !state.shouldInterrupt(now.Add(defaultBusyWindow / 2)) { |
|
016a29f…
|
lmata
|
74 |
t.Fatal("shouldInterrupt = false, want true for recent busy session") |
|
016a29f…
|
lmata
|
75 |
} |
|
016a29f…
|
lmata
|
76 |
if state.shouldInterrupt(now.Add(defaultBusyWindow + time.Millisecond)) { |
|
016a29f…
|
lmata
|
77 |
t.Fatal("shouldInterrupt = true, want false after busy window expires") |
|
016a29f…
|
lmata
|
78 |
} |
|
016a29f…
|
lmata
|
79 |
} |
|
016a29f…
|
lmata
|
80 |
|
|
016a29f…
|
lmata
|
81 |
func TestInjectMessagesIdleSkipsCtrlCAndSubmits(t *testing.T) { |
|
016a29f…
|
lmata
|
82 |
t.Helper() |
|
016a29f…
|
lmata
|
83 |
|
|
016a29f…
|
lmata
|
84 |
var writer bytes.Buffer |
|
016a29f…
|
lmata
|
85 |
cfg := config{ |
|
016a29f…
|
lmata
|
86 |
Nick: "gemini-scuttlebot-1234", |
|
016a29f…
|
lmata
|
87 |
InterruptOnMessage: true, |
|
016a29f…
|
lmata
|
88 |
} |
|
016a29f…
|
lmata
|
89 |
state := &relayState{} |
|
016a29f…
|
lmata
|
90 |
batch := []message{{ |
|
016a29f…
|
lmata
|
91 |
Nick: "glengoolie", |
|
016a29f…
|
lmata
|
92 |
Text: "gemini-scuttlebot-1234: check README.md", |
|
016a29f…
|
lmata
|
93 |
}} |
|
016a29f…
|
lmata
|
94 |
|
|
1d3caa2…
|
lmata
|
95 |
if err := injectMessages(&writer, cfg, state, "#general", batch); err != nil { |
|
016a29f…
|
lmata
|
96 |
t.Fatal(err) |
|
016a29f…
|
lmata
|
97 |
} |
|
016a29f…
|
lmata
|
98 |
|
|
1d3caa2…
|
lmata
|
99 |
want := bracketedPasteStart + "[IRC operator messages]\n[general] glengoolie: check README.md\n" + bracketedPasteEnd + "\r" |
|
016a29f…
|
lmata
|
100 |
if writer.String() != want { |
|
016a29f…
|
lmata
|
101 |
t.Fatalf("injectMessages idle = %q, want %q", writer.String(), want) |
|
016a29f…
|
lmata
|
102 |
} |
|
016a29f…
|
lmata
|
103 |
} |
|
016a29f…
|
lmata
|
104 |
|
|
016a29f…
|
lmata
|
105 |
func TestInjectMessagesBusySendsCtrlCBeforeSubmit(t *testing.T) { |
|
016a29f…
|
lmata
|
106 |
t.Helper() |
|
016a29f…
|
lmata
|
107 |
|
|
016a29f…
|
lmata
|
108 |
var writer bytes.Buffer |
|
016a29f…
|
lmata
|
109 |
cfg := config{ |
|
016a29f…
|
lmata
|
110 |
Nick: "gemini-scuttlebot-1234", |
|
016a29f…
|
lmata
|
111 |
InterruptOnMessage: true, |
|
016a29f…
|
lmata
|
112 |
} |
|
016a29f…
|
lmata
|
113 |
state := &relayState{} |
|
016a29f…
|
lmata
|
114 |
state.observeOutput([]byte("Working (2s • esc to interrupt)"), time.Now()) |
|
016a29f…
|
lmata
|
115 |
batch := []message{{ |
|
016a29f…
|
lmata
|
116 |
Nick: "glengoolie", |
|
016a29f…
|
lmata
|
117 |
Text: "gemini-scuttlebot-1234: stop and re-read bridge.go", |
|
016a29f…
|
lmata
|
118 |
}} |
|
016a29f…
|
lmata
|
119 |
|
|
1d3caa2…
|
lmata
|
120 |
if err := injectMessages(&writer, cfg, state, "#general", batch); err != nil { |
|
016a29f…
|
lmata
|
121 |
t.Fatal(err) |
|
016a29f…
|
lmata
|
122 |
} |
|
016a29f…
|
lmata
|
123 |
|
|
1d3caa2…
|
lmata
|
124 |
want := string([]byte{3}) + bracketedPasteStart + "[IRC operator messages]\n[general] glengoolie: stop and re-read bridge.go\n" + bracketedPasteEnd + "\r" |
|
016a29f…
|
lmata
|
125 |
if writer.String() != want { |
|
016a29f…
|
lmata
|
126 |
t.Fatalf("injectMessages busy = %q, want %q", writer.String(), want) |
|
016a29f…
|
lmata
|
127 |
} |
|
016a29f…
|
lmata
|
128 |
} |