package ca import ( "context" "net" "testing" "github.com/uopi/goca/proto" ) // ---- encodePut: every DBF branch + error paths ----------------------- func TestEncodePut(t *testing.T) { cases := []struct { name string dbf int v any wantDBR uint16 }{ {"double", proto.DBFDouble, 1.5, proto.DBRDouble}, {"float", proto.DBFFloat, float32(2.0), proto.DBRDouble}, {"long", proto.DBFLong, int(3), proto.DBRLong}, {"short", proto.DBFShort, int16(4), proto.DBRShort}, {"char", proto.DBFChar, int(5), proto.DBRShort}, {"enum", proto.DBFEnum, int(1), proto.DBRShort}, {"string", proto.DBFString, "hi", proto.DBRString}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { dbr, payload, err := encodePut(c.dbf, c.v) if err != nil { t.Fatalf("encodePut: %v", err) } if dbr != c.wantDBR { t.Errorf("dbr = %d, want %d", dbr, c.wantDBR) } if len(payload) == 0 { t.Error("empty payload") } }) } // Coercion failures propagate. if _, _, err := encodePut(proto.DBFDouble, "nope"); err == nil { t.Error("double from string: want error") } if _, _, err := encodePut(proto.DBFLong, "nope"); err == nil { t.Error("long from string: want error") } if _, _, err := encodePut(proto.DBFShort, "nope"); err == nil { t.Error("short from string: want error") } // Unsupported field type. if _, _, err := encodePut(9999, 1); err == nil { t.Error("unsupported DBF: want error") } } // ---- NewClient: error path + default name/host ----------------------- func TestNewClientNoAddrs(t *testing.T) { if _, err := NewClient(context.Background(), Config{AutoAddrList: false}); err == nil { t.Error("NewClient with no addresses: want error") } } func TestNewClientDefaultsNameHost(t *testing.T) { // AddrList present but ClientName/HostName empty → default branches run. cli, err := NewClient(context.Background(), Config{ AddrList: []string{"127.0.0.1:5064"}, AutoAddrList: false, }) if err != nil { t.Fatalf("NewClient: %v", err) } defer cli.Close() if cli.cfg.ClientName == "" { t.Error("ClientName should be defaulted") } } // ---- EncodeSearchReply with an explicit server IP -------------------- func TestEncodeSearchReplyExplicitIP(t *testing.T) { pkt := EncodeSearchReply(7, net.ParseIP("10.1.2.3"), 5064) h, _, err := proto.DecodeHeader(newBytesReader(pkt)) if err != nil { t.Fatalf("DecodeHeader: %v", err) } if h.Command != proto.CmdSearch { t.Errorf("command = %d, want CmdSearch", h.Command) } } // ---- parseReply robustness ------------------------------------------- func TestParseReplyTruncated(t *testing.T) { se := &searchEngine{waiters: make(map[uint32]*searchWaiter)} src := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5064} // Must not panic on a too-short datagram. se.parseReply([]byte{0x00, 0x01}, src) } func TestParseReplyUnknownSearchID(t *testing.T) { se := &searchEngine{waiters: make(map[uint32]*searchWaiter)} src := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5064} // Valid reply but no waiter is registered for this search ID → dropped. se.parseReply(EncodeSearchReply(12345, nil, 5064), src) }