package server import ( "bytes" "net" "testing" "time" "github.com/eleksir/pekoe/internal/config" "github.com/eleksir/pekoe/internal/protocol" "github.com/eleksir/pekoe/internal/pubsub" ) type mockServerConn struct { reader *bytes.Buffer writer *bytes.Buffer remote string } func newMockServerConn() *mockServerConn { return &mockServerConn{ reader: bytes.NewBuffer([]byte{}), writer: &bytes.Buffer{}, remote: "127.0.0.1:12345", } } func (m *mockServerConn) Read(b []byte) (n int, err error) { return m.reader.Read(b) } func (m *mockServerConn) Write(b []byte) (n int, err error) { return m.writer.Write(b) } func (m *mockServerConn) Close() error { return nil } func (m *mockServerConn) LocalAddr() net.Addr { return &mockAddr{"127.0.0.1:6379"} } func (m *mockServerConn) RemoteAddr() net.Addr { return &mockAddr{m.remote} } func (m *mockServerConn) SetDeadline(time.Time) error { return nil } func (m *mockServerConn) SetReadDeadline(time.Time) error { return nil } func (m *mockServerConn) SetWriteDeadline(time.Time) error { return nil } type mockAddr struct { s string } func (m *mockAddr) Network() string { return "tcp" } func (m *mockAddr) String() string { return m.s } type mockHub struct { subs map[string]map[*pubsub.Client]struct{} patterns map[string]map[*pubsub.Client]struct{} clients map[uint64]*pubsub.Client } func newMockHub() *mockHub { return &mockHub{ subs: make(map[string]map[*pubsub.Client]struct{}), patterns: make(map[string]map[*pubsub.Client]struct{}), clients: make(map[uint64]*pubsub.Client), } } func (m *mockHub) Subscribe(client *pubsub.Client, channel string) bool { if m.subs[channel] == nil { m.subs[channel] = make(map[*pubsub.Client]struct{}) } m.subs[channel][client] = struct{}{} return true } func (m *mockHub) Unsubscribe(client *pubsub.Client, channel string) bool { if m.subs[channel] != nil { delete(m.subs[channel], client) if len(m.subs[channel]) == 0 { delete(m.subs, channel) } return true } return false } func (m *mockHub) UnsubscribeAll(client *pubsub.Client) { for ch := range m.subs { delete(m.subs[ch], client) } for p := range m.patterns { delete(m.patterns[p], client) } } func (m *mockHub) Publish(channel string, message []byte) int { if m.subs[channel] == nil { return 0 } return len(m.subs[channel]) } func (m *mockHub) PublishToPattern(channel string, message []byte) int { if m.patterns == nil { return 0 } return len(m.patterns) } func (m *mockHub) PatternSubscribe(client *pubsub.Client, pattern string) (bool, error) { if m.patterns[pattern] == nil { m.patterns[pattern] = make(map[*pubsub.Client]struct{}) } m.patterns[pattern][client] = struct{}{} return true, nil } func (m *mockHub) PatternUnsubscribe(client *pubsub.Client, pattern string) bool { if m.patterns[pattern] != nil { delete(m.patterns[pattern], client) if len(m.patterns[pattern]) == 0 { delete(m.patterns, pattern) } return true } return false } func (m *mockHub) PatternCount() int { if m.patterns == nil { return 0 } count := 0 for _, clients := range m.patterns { count += len(clients) } return count } func (m *mockHub) ChannelCount(channel string) int { if m.subs[channel] == nil { return 0 } return len(m.subs[channel]) } func (m *mockHub) ChannelList(pattern string) []string { channels := make([]string, 0, len(m.subs)) for ch := range m.subs { if pattern == "" { channels = append(channels, ch) } } return channels } func (m *mockHub) TotalChannels() int { return len(m.subs) } func (m *mockHub) RegisterClient(client *pubsub.Client) { m.clients[client.ID()] = client } func (m *mockHub) UnregisterClient(client *pubsub.Client) { delete(m.clients, client.ID()) } func (m *mockHub) GetClient(id uint64) *pubsub.Client { return m.clients[id] } func (m *mockHub) GetAllClients() []*pubsub.Client { clients := make([]*pubsub.Client, 0, len(m.clients)) for _, c := range m.clients { clients = append(clients, c) } return clients } func (m *mockHub) ClientCount() int { return len(m.clients) } func TestNewServer(t *testing.T) { cfg := &config.Config{ Bind: "127.0.0.1", Port: 6379, MaxClients: 100, } hub := newMockHub() srv := New(cfg, hub) if srv == nil { t.Fatal("New() returned nil") } if srv.cfg == nil { t.Error("srv.cfg is nil") } if srv.hub == nil { t.Error("srv.hub is nil") } } func TestServerAddr(t *testing.T) { cfg := &config.Config{ Bind: "127.0.0.1", Port: 6380, } hub := newMockHub() srv := New(cfg, hub) addr := srv.cfg.Addr() if addr != "127.0.0.1:6380" { t.Errorf("Addr() = %q, want 127.0.0.1:6380", addr) } } func TestConnReader(t *testing.T) { input := "*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n" conn := newMockServerConn() conn.reader = bytes.NewBufferString(input) r := NewConnReader(conn, 1024) cmd, err := r.ReadCommand() if err != nil { t.Fatalf("ReadCommand() error: %v", err) } if len(cmd.Args) != 2 { t.Errorf("len(Args) = %d, want 2", len(cmd.Args)) } if string(cmd.Args[0]) != "foo" { t.Errorf("Args[0] = %q, want foo", string(cmd.Args[0])) } if string(cmd.Args[1]) != "bar" { t.Errorf("Args[1] = %q, want bar", string(cmd.Args[1])) } } func TestConnReaderEmptyCommand(t *testing.T) { input := "*0\r\n" conn := newMockServerConn() conn.reader = bytes.NewBufferString(input) r := NewConnReader(conn, 1024) _, err := r.ReadCommand() if err == nil { t.Error("Expected error for empty command") } } func TestConnWriterWriteStatus(t *testing.T) { conn := newMockServerConn() w := NewConnWriter(conn) w.WriteStatus("OK") _ = w.Flush() if conn.writer.String() != "+OK\r\n" { t.Errorf("WriteStatus() = %q, want +OK\\r\\n", conn.writer.String()) } } func TestConnWriterWriteError(t *testing.T) { conn := newMockServerConn() w := NewConnWriter(conn) w.WriteError("ERR test error") _ = w.Flush() if conn.writer.String() != "-ERR test error\r\n" { t.Errorf("WriteError() = %q, want -ERR test error\\r\\n", conn.writer.String()) } } func TestConnWriterWriteInt(t *testing.T) { conn := newMockServerConn() w := NewConnWriter(conn) w.WriteInt(42) _ = w.Flush() if conn.writer.String() != ":42\r\n" { t.Errorf("WriteInt() = %q, want :42\\r\\n", conn.writer.String()) } } func TestConnWriterWriteBulk(t *testing.T) { conn := newMockServerConn() w := NewConnWriter(conn) w.WriteBulk([]byte("hello")) _ = w.Flush() if conn.writer.String() != "$5\r\nhello\r\n" { t.Errorf("WriteBulk() = %q, want $5\\r\\nhello\\r\\n", conn.writer.String()) } } func TestConnWriterWriteArray(t *testing.T) { conn := newMockServerConn() w := NewConnWriter(conn) w.WriteArray([]protocol.Value{ protocol.Bulk("message"), protocol.Bulk("news"), protocol.Bulk("hello"), }) _ = w.Flush() if conn.writer.Len() == 0 { t.Error("WriteArray() produced empty output") } } func TestProcessCommandEmpty(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) err := srv.processCommand(nil, &Command{Args: [][]byte{}}, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandSubscribe(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("subscribe"), []byte("news")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandUnsubscribe(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("unsubscribe"), []byte("news")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandPublish(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("publish"), []byte("news"), []byte("Hello")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandPublishWrongArgs(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("publish"), []byte("news")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandPubsubNumsub(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("pubsub"), []byte("numsub"), []byte("news")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandPubsubChannels(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("pubsub"), []byte("channels")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandPubsubNumpat(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("pubsub"), []byte("numpat")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandUnknown(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("unknowncmd")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestProcessCommandPsubscribeNotImplemented(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("psubscribe"), []byte("news*")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand() error: %v", err) } } func TestHandleSubscribe(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) err := srv.handleSubscribe(client, [][]byte{[]byte("news"), []byte("sports")}, w) if err != nil { t.Errorf("handleSubscribe() error: %v", err) } if hub.ChannelCount("news") != 1 { t.Errorf("news channel count = %d, want 1", hub.ChannelCount("news")) } if hub.ChannelCount("sports") != 1 { t.Errorf("sports channel count = %d, want 1", hub.ChannelCount("sports")) } } func TestHandleSubscribeNoChannels(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) err := srv.handleSubscribe(client, [][]byte{}, w) if err != nil { t.Errorf("handleSubscribe() error: %v", err) } _ = w.Flush() if conn.writer.Len() == 0 { t.Error("Expected error response written to connection") } } func TestHandleUnsubscribe(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) hub.Subscribe(client, "news") hub.Subscribe(client, "sports") conn := newMockServerConn() w := NewConnWriter(conn) err := srv.handleUnsubscribe(client, [][]byte{[]byte("news")}, w) if err != nil { t.Errorf("handleUnsubscribe() error: %v", err) } if hub.ChannelCount("news") != 0 { t.Errorf("news channel count = %d, want 0", hub.ChannelCount("news")) } if hub.ChannelCount("sports") != 1 { t.Errorf("sports channel count = %d, want 1", hub.ChannelCount("sports")) } } func TestHandleUnsubscribeAll(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) client.AddSubscription("news") client.AddSubscription("sports") conn := newMockServerConn() w := NewConnWriter(conn) err := srv.handleUnsubscribe(client, [][]byte{}, w) if err != nil { t.Errorf("handleUnsubscribe() error: %v", err) } if hub.TotalChannels() != 0 { t.Errorf("TotalChannels() = %d, want 0", hub.TotalChannels()) } } func TestHandlePublish(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) hub.Subscribe(client, "news") hub.Subscribe(client, "news") conn := newMockServerConn() w := NewConnWriter(conn) err := srv.handlePublish([][]byte{[]byte("news"), []byte("Hello")}, w) if err != nil { t.Errorf("handlePublish() error: %v", err) } } func TestHandlePublishWrongArgs(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) err := srv.handlePublish([][]byte{[]byte("news")}, w) if err != nil { t.Errorf("handlePublish() error: %v", err) } _ = w.Flush() if conn.writer.Len() == 0 { t.Error("Expected error response written to connection") } } func TestHandlePubsubNoArgs(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) err := srv.handlePubsub([][]byte{}, w) if err != nil { t.Errorf("handlePubsub() error: %v", err) } _ = w.Flush() if conn.writer.Len() == 0 { t.Error("Expected error response written to connection") } } func TestHandlePubsubUnknownSubcommand(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) err := srv.handlePubsub([][]byte{[]byte("unknown")}, w) if err != nil { t.Errorf("handlePubsub() error: %v", err) } } func TestCommandStruct(t *testing.T) { cmd := &Command{ Args: [][]byte{[]byte("subscribe"), []byte("channel1")}, } if len(cmd.Args) != 2 { t.Errorf("len(Args) = %d, want 2", len(cmd.Args)) } } func TestChannelList(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) hub.Subscribe(client, "news") hub.Subscribe(client, "sports") channels := srv.channelList("") if len(channels) != 2 { t.Errorf("channelList() = %v, want 2 channels", channels) } } func TestProcessCommandQuit(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("quit")}, } err := srv.processCommand(nil, cmd, w) if err != ErrQuit { t.Errorf("processCommand(QUIT) error = %v, want ErrQuit", err) } _ = w.Flush() // Verify OK response was written if conn.writer.String() != "+OK\r\n" { t.Errorf("QUIT response = %q, want +OK\\r\\n", conn.writer.String()) } } func TestProcessCommandQuitCaseInsensitive(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("QUIT")}, } err := srv.processCommand(nil, cmd, w) if err != ErrQuit { t.Errorf("processCommand(QUIT) error = %v, want ErrQuit", err) } } func TestProcessCommandClientId(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) hub.RegisterClient(client) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("id")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT ID) error = %v", err) } _ = w.Flush() // Should return the client's ID output := conn.writer.String() if output == "" { t.Error("CLIENT ID returned empty response") } } func TestProcessCommandClientSetname(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("setname"), []byte("myconn")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT SETNAME) error = %v", err) } _ = w.Flush() if conn.writer.String() != "+OK\r\n" { t.Errorf("CLIENT SETNAME response = %q, want +OK", conn.writer.String()) } if client.GetName() != "myconn" { t.Errorf("Client name = %q, want 'myconn'", client.GetName()) } } func TestProcessCommandClientGetname(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) client.SetName("testname") conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("getname")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT GETNAME) error = %v", err) } _ = w.Flush() // Should return the name output := conn.writer.String() if output == "" { t.Error("CLIENT GETNAME returned empty response") } } func TestProcessCommandClientGetnameNoName(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("getname")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT GETNAME) error = %v", err) } _ = w.Flush() // Should return nil (null bulk string) output := conn.writer.String() if output != "$-1\r\n" { t.Errorf("CLIENT GETNAME response = %q, want $-1", output) } } func TestProcessCommandClientList(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) hub.RegisterClient(client) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("list")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT LIST) error = %v", err) } _ = w.Flush() // Should contain client info output := conn.writer.String() if output == "" { t.Error("CLIENT LIST returned empty response") } } func TestProcessCommandClientInfo(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("info")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT INFO) error = %v", err) } _ = w.Flush() // Should contain client info output := conn.writer.String() if output == "" { t.Error("CLIENT INFO returned empty response") } } func TestProcessCommandClientPause(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("pause"), []byte("1000")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT PAUSE) error = %v", err) } _ = w.Flush() if conn.writer.String() != "+OK\r\n" { t.Errorf("CLIENT PAUSE response = %q, want +OK", conn.writer.String()) } } func TestProcessCommandClientUnpause(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("unpause")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT UNPAUSE) error = %v", err) } _ = w.Flush() if conn.writer.String() != "+OK\r\n" { t.Errorf("CLIENT UNPAUSE response = %q, want +OK", conn.writer.String()) } } func TestProcessCommandClientReply(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) client := pubsub.NewClient(newMockServerConn()) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("reply"), []byte("skip")}, } err := srv.processCommand(client, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT REPLY) error = %v", err) } _ = w.Flush() if conn.writer.String() != "+OK\r\n" { t.Errorf("CLIENT REPLY response = %q, want +OK", conn.writer.String()) } } func TestProcessCommandClientTracking(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("tracking"), []byte("on")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT TRACKING) error = %v", err) } _ = w.Flush() if conn.writer.String() != "+OK\r\n" { t.Errorf("CLIENT TRACKING response = %q, want +OK", conn.writer.String()) } } func TestProcessCommandClientNoArgs(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT) error = %v", err) } _ = w.Flush() // Should return error output := conn.writer.String() if output == "" { t.Error("CLIENT without args should return error") } } func TestProcessCommandClientUnknownSubcommand(t *testing.T) { cfg := &config.Config{Bind: "127.0.0.1", Port: 6379} hub := newMockHub() srv := New(cfg, hub) conn := newMockServerConn() w := NewConnWriter(conn) cmd := &Command{ Args: [][]byte{[]byte("client"), []byte("unknowncmd")}, } err := srv.processCommand(nil, cmd, w) if err != nil { t.Errorf("processCommand(CLIENT unknowncmd) error = %v", err) } _ = w.Flush() // Should return error output := conn.writer.String() if output == "" { t.Error("CLIENT unknown subcommand should return error") } }