package rcon import ( "encoding/binary" "errors" "io" "net" "testing" "time" ) func writeTestPacket(w io.Writer, id, typ int32, body string) { binary.Write(w, binary.LittleEndian, int32(headerSize+len(body)+paddingSize)) binary.Write(w, binary.LittleEndian, id) binary.Write(w, binary.LittleEndian, typ) io.WriteString(w, body) w.Write([]byte{0, 0}) } func readTestPacket(r io.Reader) (id, typ int32, body string) { var size int32 binary.Read(r, binary.LittleEndian, &size) payload := make([]byte, size) io.ReadFull(r, payload) id = int32(binary.LittleEndian.Uint32(payload[0:4])) typ = int32(binary.LittleEndian.Uint32(payload[4:8])) return id, typ, string(payload[headerSize : size-paddingSize]) } // fakeServer speaks just enough Source RCON to exercise the client. func fakeServer(t *testing.T, password string) string { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } t.Cleanup(func() { ln.Close() }) go func() { conn, err := ln.Accept() if err != nil { return } defer conn.Close() id, typ, body := readTestPacket(conn) if typ != typeAuth { return } respID := id if body != password { respID = -1 } writeTestPacket(conn, id, typeResponse, "") // empty value first writeTestPacket(conn, respID, typeAuthResp, "") if respID == -1 { return } cid, _, cbody := readTestPacket(conn) out := "unknown command" if cbody == "list" { out = "There are 1 of a max of 20 players online: Steve" } writeTestPacket(conn, cid, typeResponse, out) }() return ln.Addr().String() } func TestClientExecute(t *testing.T) { client, err := Dial(fakeServer(t, "secret"), "secret", time.Second) if err != nil { t.Fatal(err) } defer client.Close() out, err := client.Execute("list") if err != nil { t.Fatal(err) } if want := "There are 1 of a max of 20 players online: Steve"; out != want { t.Errorf("Execute = %q, want %q", out, want) } } func TestClientAuthFailure(t *testing.T) { _, err := Dial(fakeServer(t, "secret"), "wrong", time.Second) if !errors.Is(err, ErrAuthFailed) { t.Errorf("Dial err = %v, want ErrAuthFailed", err) } }