package ircserver_test import ( "testing" "git.eeqj.de/sneak/neoirc/internal/ircserver" ) //nolint:funlen // table-driven test func TestParseMessage(t *testing.T) { t.Parallel() tests := []struct { name string input string want *ircserver.Message wantNil bool }{ { name: "empty", input: "", want: nil, wantNil: true, }, { name: "simple command", input: "PING", want: &ircserver.Message{ Prefix: "", Command: "PING", Params: nil, }, wantNil: false, }, { name: "command with one param", input: "NICK alice", want: &ircserver.Message{ Prefix: "", Command: "NICK", Params: []string{"alice"}, }, wantNil: false, }, { name: "command case insensitive", input: "nick Alice", want: &ircserver.Message{ Prefix: "", Command: "NICK", Params: []string{"Alice"}, }, wantNil: false, }, { name: "privmsg with trailing", input: "PRIVMSG #general :hello world", want: &ircserver.Message{ Prefix: "", Command: "PRIVMSG", Params: []string{"#general", "hello world"}, }, wantNil: false, }, { name: "with prefix", input: ":server.example.com 001 alice :Welcome to IRC", want: &ircserver.Message{ Prefix: "server.example.com", Command: "001", Params: []string{"alice", "Welcome to IRC"}, }, wantNil: false, }, { name: "user command", input: "USER alice 0 * :Alice Smith", want: &ircserver.Message{ Prefix: "", Command: "USER", Params: []string{ "alice", "0", "*", "Alice Smith", }, }, wantNil: false, }, { name: "join channel", input: "JOIN #general", want: &ircserver.Message{ Prefix: "", Command: "JOIN", Params: []string{"#general"}, }, wantNil: false, }, { name: "quit with trailing", input: "QUIT :leaving now", want: &ircserver.Message{ Prefix: "", Command: "QUIT", Params: []string{"leaving now"}, }, wantNil: false, }, { name: "quit without reason", input: "QUIT", want: &ircserver.Message{ Prefix: "", Command: "QUIT", Params: nil, }, wantNil: false, }, { name: "mode query", input: "MODE #general", want: &ircserver.Message{ Prefix: "", Command: "MODE", Params: []string{"#general"}, }, wantNil: false, }, { name: "kick with reason", input: "KICK #general bob :misbehaving", want: &ircserver.Message{ Prefix: "", Command: "KICK", Params: []string{ "#general", "bob", "misbehaving", }, }, wantNil: false, }, { name: "empty trailing", input: "PRIVMSG #general :", want: &ircserver.Message{ Prefix: "", Command: "PRIVMSG", Params: []string{"#general", ""}, }, wantNil: false, }, { name: "pass command", input: "PASS mysecret", want: &ircserver.Message{ Prefix: "", Command: "PASS", Params: []string{"mysecret"}, }, wantNil: false, }, { name: "ping with server", input: "PING :irc.example.com", want: &ircserver.Message{ Prefix: "", Command: "PING", Params: []string{"irc.example.com"}, }, wantNil: false, }, { name: "topic with trailing spaces", input: "TOPIC #general :Welcome to the channel!", want: &ircserver.Message{ Prefix: "", Command: "TOPIC", Params: []string{ "#general", "Welcome to the channel!", }, }, wantNil: false, }, } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { t.Parallel() got := ircserver.ParseMessage(testCase.input) if testCase.wantNil { if got != nil { t.Fatalf("expected nil, got %+v", got) } return } if got == nil { t.Fatal("expected non-nil message") } if got.Prefix != testCase.want.Prefix { t.Errorf( "prefix: got %q, want %q", got.Prefix, testCase.want.Prefix, ) } if got.Command != testCase.want.Command { t.Errorf( "command: got %q, want %q", got.Command, testCase.want.Command, ) } if len(got.Params) != len(testCase.want.Params) { t.Fatalf( "params length: got %d, want %d (%v vs %v)", len(got.Params), len(testCase.want.Params), got.Params, testCase.want.Params, ) } for i, p := range got.Params { if p != testCase.want.Params[i] { t.Errorf( "param[%d]: got %q, want %q", i, p, testCase.want.Params[i], ) } } }) } } func TestFormatMessage(t *testing.T) { t.Parallel() tests := []struct { name string prefix string command string params []string want string }{ { name: "simple command", prefix: "", command: "PING", params: nil, want: "PING", }, { name: "with prefix", prefix: "server", command: "PONG", params: []string{"server"}, want: ":server PONG server", }, { name: "privmsg with trailing", prefix: "alice!alice@host", command: "PRIVMSG", params: []string{"#general", "hello world"}, want: ":alice!alice@host PRIVMSG #general :hello world", }, { name: "numeric reply", prefix: "server", command: "001", params: []string{"alice", "Welcome to IRC"}, want: ":server 001 alice :Welcome to IRC", }, { name: "empty trailing", prefix: "server", command: "PRIVMSG", params: []string{"#chan", ""}, want: ":server PRIVMSG #chan :", }, } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { t.Parallel() got := ircserver.FormatMessage( testCase.prefix, testCase.command, testCase.params..., ) if got != testCase.want { t.Errorf("got %q, want %q", got, testCase.want) } }) } } func TestParseFormatRoundTrip(t *testing.T) { t.Parallel() // Round-trip only works for lines where the last // parameter either contains a space (gets ':' prefix // on format) or is a non-trailing single token. lines := []string{ "PING", "NICK alice", "PRIVMSG #general :hello world", "JOIN #general", "MODE #general", } for _, line := range lines { msg := ircserver.ParseMessage(line) if msg == nil { t.Fatalf("failed to parse: %q", line) } formatted := ircserver.FormatMessage( msg.Prefix, msg.Command, msg.Params..., ) if formatted != line { t.Errorf( "round-trip failed: input %q, got %q", line, formatted, ) } } }