fix: include hostmask in NAMES replies (RPL_NAMREPLY)

This commit is contained in:
user
2026-03-17 09:05:16 -07:00
parent 0fef7929ad
commit 58f958c8d3
3 changed files with 139 additions and 7 deletions

View File

@@ -2795,3 +2795,135 @@ func TestChannelHashcashMissingBitsArg(t *testing.T) {
)
}
}
func TestNamesShowsHostmask(t *testing.T) {
tserver := newTestServer(t)
queryToken, lastID := setupChannelWithIdentMember(
tserver, "namesmember", "nmident",
"namesquery", "#namestest",
)
// Issue an explicit NAMES command.
tserver.sendCommand(queryToken, map[string]any{
commandKey: "NAMES",
toKey: "#namestest",
})
msgs, _ := tserver.pollMessages(queryToken, lastID)
assertNamesHostmask(
t, msgs, "namesmember", "nmident",
)
}
func TestNamesOnJoinShowsHostmask(t *testing.T) {
tserver := newTestServer(t)
// First user joins to populate the channel.
firstToken := tserver.createSessionWithUsername(
"joinmem", "jmident",
)
tserver.sendCommand(firstToken, map[string]any{
commandKey: joinCmd, toKey: "#joinnamestest",
})
// Second user joins; the JOIN triggers
// deliverNamesNumerics which should include
// hostmask data.
joinerToken := tserver.createSession("joiner")
tserver.sendCommand(joinerToken, map[string]any{
commandKey: joinCmd, toKey: "#joinnamestest",
})
msgs, _ := tserver.pollMessages(joinerToken, 0)
assertNamesHostmask(
t, msgs, "joinmem", "jmident",
)
}
// setupChannelWithIdentMember creates a member session
// with username, joins a channel, then creates a querier
// and joins the same channel. Returns the querier token
// and last message ID.
func setupChannelWithIdentMember(
tserver *testServer,
memberNick, memberUsername,
querierNick, channel string,
) (string, int64) {
tserver.t.Helper()
memberToken := tserver.createSessionWithUsername(
memberNick, memberUsername,
)
tserver.sendCommand(memberToken, map[string]any{
commandKey: joinCmd, toKey: channel,
})
queryToken := tserver.createSession(querierNick)
tserver.sendCommand(queryToken, map[string]any{
commandKey: joinCmd, toKey: channel,
})
_, lastID := tserver.pollMessages(queryToken, 0)
return queryToken, lastID
}
// assertNamesHostmask verifies that a RPL_NAMREPLY (353)
// message contains the expected nick with hostmask format
// (nick!user@host).
func assertNamesHostmask(
t *testing.T,
msgs []map[string]any,
targetNick, expectedUsername string,
) {
t.Helper()
for _, msg := range msgs {
code, ok := msg["code"].(float64)
if !ok || int(code) != 353 {
continue
}
raw, exists := msg["body"]
if !exists || raw == nil {
continue
}
arr, isArr := raw.([]any)
if !isArr || len(arr) == 0 {
continue
}
bodyStr, isStr := arr[0].(string)
if !isStr {
continue
}
// Look for the target nick's hostmask entry.
expected := targetNick + "!" +
expectedUsername + "@"
if !strings.Contains(bodyStr, expected) {
t.Fatalf(
"expected NAMES body to contain %q, "+
"got %q",
expected, bodyStr,
)
}
return
}
t.Fatalf(
"expected RPL_NAMREPLY (353) with hostmask "+
"for %s, msgs: %v",
targetNick, msgs,
)
}