Share the ADMIN text and KILL reason, and test the review's gaps
check / check (push) Waiting to run

ADMIN's location and email lines come from service.AdminLines, and
KillUser applies the default reason, so both transports send the same
reply. The Disconnect test now allows a quarter of one write window,
so a Disconnect that writes before returning fails it. QueryUserMode
and UserhostReply are tested with a read that fails. The handler tests
again cover USERHOST for a nick with no session, MODE +o on your own
nick, and the status of a successful KILL. handleMode's comment says
what it does.

Model: opus-5-5
This commit is contained in:
2026-10-08 03:01:13 +00:00
parent a7faabb68f
commit c0eef92d23
8 changed files with 70 additions and 18 deletions
+15 -1
View File
@@ -119,6 +119,15 @@ func (s *Service) InfoLines() []string {
}
}
// AdminLines returns the texts of the RPL_ADMINLOC1,
// RPL_ADMINLOC2 and RPL_ADMINEMAIL replies, in that order,
// that ADMIN sends on both transports.
func (s *Service) AdminLines(
serverName string,
) (string, string, string) {
return "neoirc server", "IRC over HTTP", "admin@" + serverName
}
// UserhostReply returns the RPL_USERHOST text for the first
// five of the given nicks: "nick=+user@host" entries joined
// by spaces, with * after the nick of an operator and - in
@@ -199,7 +208,8 @@ func (s *Service) UnregisterWireConn(
// KillUser carries out an operator's KILL: the target's
// channel peers see it quit, its session is deleted, and
// its IRC connection, if it has one, is closed.
// its IRC connection, if it has one, is closed. An empty
// reason is given as "KILLed".
func (s *Service) KillUser(
ctx context.Context,
sessionID int64,
@@ -231,6 +241,10 @@ func (s *Service) KillUser(
}
}
if reason == "" {
reason = "KILLed"
}
quitReason := "Killed (" + nick + " (" + reason + "))"
s.BroadcastQuit(ctx, targetSID, targetNick, quitReason)
+20
View File
@@ -406,6 +406,16 @@ func TestQueryUserMode(t *testing.T) {
if modes != "+ow" {
t.Errorf("expected +ow, got %s", modes)
}
// A flag that cannot be read is an error, not a flag
// that is unset.
cancelled, cancel := context.WithCancel(ctx)
cancel()
_, err = env.svc.QueryUserMode(cancelled, sid)
if !errors.Is(err, context.Canceled) {
t.Errorf("expected the read's error, got %v", err)
}
}
func TestUserhostReply(t *testing.T) {
@@ -453,6 +463,16 @@ func TestUserhostReply(t *testing.T) {
if strings.Count(reply, "bob=") != 5 {
t.Errorf("want 5 entries for 6 nicks, got %q", reply)
}
// A nick that cannot be looked up is an error, not a nick
// with no session.
cancelled, cancel := context.WithCancel(ctx)
cancel()
_, err = env.svc.UserhostReply(cancelled, []string{"alice"}, "srv")
if !errors.Is(err, context.Canceled) {
t.Errorf("want the lookup's error, got %v", err)
}
}
// userModes is the stored state of a session's user mode