package opencode import ( "context" "fmt" "net" "os" "path/filepath" "strings" "testing" "time" "banger/internal/vsockagent" ) func TestServiceUnitContainsExpectedExecStart(t *testing.T) { unit := ServiceUnit() for _, snippet := range []string{ "RequiresMountsFor=/root", "WorkingDirectory=/root", "Environment=HOME=/root", "ExecStart=/usr/local/bin/opencode serve --hostname 0.0.0.0 --port 4096", "WantedBy=multi-user.target", } { if !strings.Contains(unit, snippet) { t.Fatalf("service unit missing snippet %q\nunit:\n%s", snippet, unit) } } } func TestRunitRunScriptContainsExpectedExec(t *testing.T) { script := RunitRunScript() for _, snippet := range []string{ "export HOME=/root", "cd /root", "exec /usr/local/bin/opencode serve --hostname 0.0.0.0 --port 4096", } { if !strings.Contains(script, snippet) { t.Fatalf("runit script missing snippet %q\nscript:\n%s", snippet, script) } } } func TestReadyMatchesTCPPort(t *testing.T) { if Ready([]vsockagent.PortListener{{Proto: "udp", Port: Port}}) { t.Fatal("udp listener should not satisfy readiness") } if Ready([]vsockagent.PortListener{{Proto: "tcp", Port: 8080}}) { t.Fatal("wrong tcp port should not satisfy readiness") } if !Ready([]vsockagent.PortListener{{Proto: "tcp", Port: Port}}) { t.Fatal("tcp listener on opencode port should satisfy readiness") } } func TestWaitReadyReturnsWhenPortIsListening(t *testing.T) { socketPath := filepath.Join(t.TempDir(), "opencode.vsock") listener, err := net.Listen("unix", socketPath) if err != nil { t.Fatalf("listen: %v", err) } t.Cleanup(func() { _ = listener.Close() _ = os.Remove(socketPath) }) serverDone := make(chan error, 1) go func() { conn, err := listener.Accept() if err != nil { serverDone <- err return } defer conn.Close() buf := make([]byte, 512) n, err := conn.Read(buf) if err != nil { serverDone <- err return } if got := string(buf[:n]); got != "CONNECT 42070\n" { serverDone <- fmt.Errorf("unexpected connect message %q", got) return } if _, err := conn.Write([]byte("OK 1\n")); err != nil { serverDone <- err return } reqBuf := make([]byte, 0, 512) for { n, err = conn.Read(buf) if err != nil { serverDone <- err return } reqBuf = append(reqBuf, buf[:n]...) if strings.Contains(string(reqBuf), "\r\n\r\n") { break } } if !strings.Contains(string(reqBuf), "GET /ports HTTP/1.1\r\n") { serverDone <- fmt.Errorf("unexpected ports payload %q", string(reqBuf)) return } body := []byte(`{"listeners":[{"proto":"tcp","bind_address":"0.0.0.0","port":4096}]}`) _, err = conn.Write([]byte(fmt.Sprintf("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n%s", len(body), body))) serverDone <- err }() if err := waitReady(context.Background(), nil, socketPath, time.Second, nil); err != nil { t.Fatalf("waitReady: %v", err) } if err := <-serverDone; err != nil { t.Fatalf("server: %v", err) } }