List the missing channel modes in the 004 reply (closes #238)
check / check (push) Canceled after 0s

The channel-modes parameter of RPL_MYINFO left out modes that each
transport's MODE accepts. The IRC listener now sends Himnostv, adding
H, i, o and v. The HTTP API now sends Hbiklmnostv, adding H, b and v.
Each transport keeps its own list, because they accept different
modes. The tests compare the whole parameter, so a missing or extra
mode fails them. The README and the schema notes show the new HTTP API
list.

Model: opus-5-5
This commit is contained in:
2026-11-02 03:20:25 +00:00
parent cc18397ee1
commit 1d846501f8
6 changed files with 21 additions and 18 deletions
+1 -1
View File
@@ -1236,7 +1236,7 @@ IRC listener sends the usual 3-digit code. Over the HTTP API, 005 arrives named
| `001` | RPL_WELCOME | After session creation or login, on MOTD | `{"command":"RPL_WELCOME","code":1,"to":"alice","body":["Welcome to the network, alice"]}` | | `001` | RPL_WELCOME | After session creation or login, on MOTD | `{"command":"RPL_WELCOME","code":1,"to":"alice","body":["Welcome to the network, alice"]}` |
| `002` | RPL_YOURHOST | After session creation or login, on MOTD | `{"command":"RPL_YOURHOST","code":2,"to":"alice","body":["Your host is neoirc, running version 0.1"]}` | | `002` | RPL_YOURHOST | After session creation or login, on MOTD | `{"command":"RPL_YOURHOST","code":2,"to":"alice","body":["Your host is neoirc, running version 0.1"]}` |
| `003` | RPL_CREATED | After session creation or login, on MOTD | `{"command":"RPL_CREATED","code":3,"to":"alice","body":["This server was created 2026-02-10"]}` | | `003` | RPL_CREATED | After session creation or login, on MOTD | `{"command":"RPL_CREATED","code":3,"to":"alice","body":["This server was created 2026-02-10"]}` |
| `004` | RPL_MYINFO | After session creation or login, on MOTD | `{"command":"RPL_MYINFO","code":4,"to":"alice","params":["neoirc","0.1","ow","ikmnostl"]}` | | `004` | RPL_MYINFO | After session creation or login, on MOTD | `{"command":"RPL_MYINFO","code":4,"to":"alice","params":["neoirc","0.1","ow","Hbiklmnostv"]}` |
| `005` | RPL_ISUPPORT | After session creation or login, on MOTD | `{"command":"RPL_BOUNCE","code":5,"to":"alice","params":["CHANTYPES=#","NICKLEN=32","PREFIX=(ov)@+","CHANMODES=b,k,Hl,imnst","NETWORK=neoirc","CASEMAPPING=ascii"],"body":["are supported by this server"]}` | | `005` | RPL_ISUPPORT | After session creation or login, on MOTD | `{"command":"RPL_BOUNCE","code":5,"to":"alice","params":["CHANTYPES=#","NICKLEN=32","PREFIX=(ov)@+","CHANMODES=b,k,Hl,imnst","NETWORK=neoirc","CASEMAPPING=ascii"],"body":["are supported by this server"]}` |
| `221` | RPL_UMODEIS | In response to user MODE | `{"command":"RPL_UMODEIS","code":221,"to":"alice","body":["+"]}` | | `221` | RPL_UMODEIS | In response to user MODE | `{"command":"RPL_UMODEIS","code":221,"to":"alice","body":["+"]}` |
| `251` | RPL_LUSERCLIENT | On connect, MOTD or LUSERS | `{"command":"RPL_LUSERCLIENT","code":251,"to":"alice","body":["There are 5 users and 0 invisible on 1 servers"]}` | | `251` | RPL_LUSERCLIENT | On connect, MOTD or LUSERS | `{"command":"RPL_LUSERCLIENT","code":251,"to":"alice","body":["There are 5 users and 0 invisible on 1 servers"]}` |
+1 -1
View File
@@ -412,7 +412,7 @@ func (hdlr *Handlers) deliverWelcome(
// 004 RPL_MYINFO // 004 RPL_MYINFO
hdlr.enqueueNumeric( hdlr.enqueueNumeric(
ctx, clientID, irc.RplMyInfo, nick, ctx, clientID, irc.RplMyInfo, nick,
[]string{srvName, version, service.UserModes, "ikmnostl"}, []string{srvName, version, service.UserModes, "Hbiklmnostv"},
"", "",
) )
+7 -5
View File
@@ -6797,9 +6797,10 @@ func TestISUPPORT(t *testing.T) {
} }
} }
// TestMyInfoListsUserModes verifies the 004 numeric lists // TestMyInfoListsModes verifies the 004 numeric lists the
// the user modes o and w as its third parameter. // user modes o and w as its third parameter, and the HTTP
func TestMyInfoListsUserModes(t *testing.T) { // API's channel modes Hbiklmnostv as its fourth.
func TestMyInfoListsModes(t *testing.T) {
t.Parallel() t.Parallel()
tserver := newTestServer(t) tserver := newTestServer(t)
@@ -6813,9 +6814,10 @@ func TestMyInfoListsUserModes(t *testing.T) {
} }
params := getNumericParams(myInfo) params := getNumericParams(myInfo)
if len(params) != 4 || params[2] != "ow" { if len(params) != 4 || params[2] != "ow" ||
params[3] != "Hbiklmnostv" {
t.Fatalf( t.Fatalf(
"004 params %v, want four with user modes ow third", "004 params %v, want four ending ow Hbiklmnostv",
params, params,
) )
} }
+1 -1
View File
@@ -605,7 +605,7 @@ func (c *Conn) deliverWelcome() {
) )
c.sendNumeric( c.sendNumeric(
irc.RplMyInfo, irc.RplMyInfo,
c.serverSfx, "neoirc", service.UserModes, "mnst", c.serverSfx, "neoirc", service.UserModes, "Himnostv",
) )
c.sendNumeric( c.sendNumeric(
irc.RplIsupport, irc.RplIsupport,
+10 -9
View File
@@ -10,6 +10,7 @@ import (
"net" "net"
"os" "os"
"runtime" "runtime"
"slices"
"strings" "strings"
"sync/atomic" "sync/atomic"
"testing" "testing"
@@ -486,20 +487,20 @@ func TestWelcomeContainsNick(t *testing.T) {
} }
} }
// TestMyInfoListsUserModes checks that RPL_MYINFO lists the // TestMyInfoListsModes checks that RPL_MYINFO lists the user
// user modes o and w, with no empty parameter before the // modes o and w, then the channel modes MODE accepts on the
// channel modes. // IRC listener. It compares the whole line, so a missing or
func TestMyInfoListsUserModes(t *testing.T) { // extra mode fails it.
func TestMyInfoListsModes(t *testing.T) {
t.Parallel() t.Parallel()
env := newTestEnv(t) env := newTestEnv(t)
lines := env.dial(t).register("carol") lines := env.dial(t).register("carol")
assertContains( want := ":test.irc 004 carol test.irc neoirc ow Himnostv"
t, lines, if !slices.Contains(lines, want) {
":test.irc 004 carol test.irc neoirc ow mnst", t.Errorf("no line %q in %v", want, lines)
"RPL_MYINFO with user modes", }
)
} }
func TestPingPong(t *testing.T) { func TestPingPong(t *testing.T) {
+1 -1
View File
@@ -145,7 +145,7 @@ Where a schema file and the HTTP API disagree, the HTTP API is right:
- `003.json` describes the server's creation date. The HTTP API sends the date - `003.json` describes the server's creation date. The HTTP API sends the date
the server last started. the server last started.
- `004.json` has example mode lists. The HTTP API sends `ow` as the user modes - `004.json` has example mode lists. The HTTP API sends `ow` as the user modes
and `ikmnostl` as the channel modes. and `Hbiklmnostv` as the channel modes.
- `322.json` says `LIST` gets one reply per channel. The server leaves out a - `322.json` says `LIST` gets one reply per channel. The server leaves out a
secret channel, one with mode `+s`, unless the user is on it. secret channel, one with mode `+s`, unless the user is on it.
- `332.json` says `RPL_TOPIC` answers a `TOPIC` query. The HTTP API has no such - `332.json` says `RPL_TOPIC` answers a `TOPIC` query. The HTTP API has no such