Wait for IRC listener cleanup and HTTP shutdown on stop (closes #159)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
Stopping the server returned before the IRC listener's connections had run their cleanup, and the database closed under them, so their sessions, clients and channel memberships stayed and their nicks stayed taken after a restart. The IRC listener's stop now waits up to five seconds for every connection's cleanup, which now runs on a context that stopping does not cancel. The HTTP server's stop hook now runs Shutdown itself and waits for it; httpServer is set before serving starts, and the server's own signal watcher, which raced fx's stop hooks, is gone. Model: opus-5-5
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
@@ -812,6 +813,79 @@ func TestQuit(t *testing.T) {
|
||||
assertContains(t, lines, "goodbye", "QUIT reason")
|
||||
}
|
||||
|
||||
// TestStopEndsSessions checks that stopping the server ends
|
||||
// a registered connection's session before Stop returns, as
|
||||
// closing the connection does: the session, its client and
|
||||
// its channel membership are gone from the database, and a
|
||||
// channel peer has been sent the QUIT.
|
||||
func TestStopEndsSessions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
env := newTestEnv(t)
|
||||
client := env.dial(t)
|
||||
|
||||
client.register("shutirc")
|
||||
client.joinAndDrain("#shut")
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
// A peer on the HTTP API, whose session outlives the stop.
|
||||
peerID, peerClientID, _, err := env.database.CreateSession(
|
||||
ctx, "peer", "peer", "", "",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("create peer session: %v", err)
|
||||
}
|
||||
|
||||
channelID, err := env.database.GetChannelByName(ctx, "#shut")
|
||||
if err != nil {
|
||||
t.Fatalf("get channel: %v", err)
|
||||
}
|
||||
|
||||
err = env.database.JoinChannel(ctx, channelID, peerID)
|
||||
if err != nil {
|
||||
t.Fatalf("join peer: %v", err)
|
||||
}
|
||||
|
||||
env.srv.Stop()
|
||||
|
||||
_, err = env.database.GetSessionByNick(ctx, "shutirc")
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
t.Errorf("look up shutirc after stop: %v, want no rows", err)
|
||||
}
|
||||
|
||||
clients, err := env.database.GetClientCount(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("count clients: %v", err)
|
||||
}
|
||||
|
||||
if clients != 1 {
|
||||
t.Errorf("%d clients after stop, want only the peer's", clients)
|
||||
}
|
||||
|
||||
members, err := env.database.CountChannelMembers(ctx, channelID)
|
||||
if err != nil {
|
||||
t.Fatalf("count members: %v", err)
|
||||
}
|
||||
|
||||
if members != 1 {
|
||||
t.Errorf("%d members of #shut after stop, want only the peer", members)
|
||||
}
|
||||
|
||||
msgs, _, err := env.database.PollMessages(ctx, peerClientID, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("poll peer: %v", err)
|
||||
}
|
||||
|
||||
for _, msg := range msgs {
|
||||
if msg.Command == "QUIT" && msg.From == "shutirc" {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Errorf("peer was not sent the QUIT of shutirc: %v", msgs)
|
||||
}
|
||||
|
||||
func TestTopicSetAndGet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user