Files
pekoe/internal/server/server_test.go
2026-04-03 00:09:40 +03:00

1047 lines
23 KiB
Go

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")
}
}