2 Commits
Author SHA1 Message Date
clawbot a054bf828d Give each IRC listener test its own database (closes #129)
check / check (push) Canceled after 0s
Each test environment in internal/ircserver named its shared in-memory
database after the test. A repeat run of a test in the same process
could open the database that an earlier run's server was still
holding, and start on that run's users. The database now gets a random
name, and a new test checks that two environments open at once do not
share one.

The database close and the server stop are now registered as soon as
each is opened, so a setup that fails part-way still closes its
database.

Model: opus-5-5
2026-10-08 12:01:48 +02:00
clawbot e80c9552eb Bring README.md in line with the modes and the schema (closes #119)
check / check (push) Canceled after 0s
The MODE section described a query-only command. It now covers channel
and user mode queries and changes on the HTTP API and the IRC listener,
the letters each accepts, what is broadcast, and the error numerics.
The schema section now lists every table and column in
001_initial.sql.

Every other statement this change touches was checked against the code:
polled numerics carry their name in command and their number in code;
command errors are numerics with HTTP 200; the broker is keyed by
session; WAL is never on; a hostname falls back to the IP and is
public; and the IRC listener handles modes, NOTICE, INVITE, +s and +H
more narrowly, with gaps in its bridge to the HTTP API.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
2026-10-08 11:30:48 +02:00
2 changed files with 741 additions and 440 deletions
+616 -334
View File
File diff suppressed because it is too large Load Diff
+28 -9
View File
@@ -2,6 +2,7 @@ package ircserver_test
import ( import (
"bufio" "bufio"
"crypto/rand"
"database/sql" "database/sql"
"fmt" "fmt"
"log/slog" "log/slog"
@@ -69,9 +70,11 @@ func newTestEnvWithConfig(
) *testEnv { ) *testEnv {
t.Helper() t.Helper()
// A random name, so that no other test environment, not
// even an earlier run of the same test, can share it.
dsn := fmt.Sprintf( dsn := fmt.Sprintf(
"file:%s?mode=memory&cache=shared&_journal_mode=WAL", "file:%s?mode=memory&cache=shared&_journal_mode=WAL",
t.Name(), rand.Text(),
) )
conn, err := sql.Open("sqlite", dsn) conn, err := sql.Open("sqlite", dsn)
@@ -79,6 +82,13 @@ func newTestEnvWithConfig(
t.Fatalf("open db: %v", err) t.Fatalf("open db: %v", err)
} }
t.Cleanup(func() {
err := conn.Close()
if err != nil {
t.Logf("close db: %v", err)
}
})
conn.SetMaxOpenConns(1) conn.SetMaxOpenConns(1)
_, err = conn.ExecContext( _, err = conn.ExecContext(
@@ -123,14 +133,9 @@ func newTestEnvWithConfig(
t.Fatalf("start irc server: %v", err) t.Fatalf("start irc server: %v", err)
} }
t.Cleanup(func() { // Cleanups run last registered first, so the server stops
srv.Stop() // before its database is closed.
t.Cleanup(srv.Stop)
err := conn.Close()
if err != nil {
t.Logf("close db: %v", err)
}
})
return &testEnv{ return &testEnv{
database: database, database: database,
@@ -342,6 +347,20 @@ func TestRegistration(t *testing.T) {
assertContains(t, lines, " 001 ", "RPL_WELCOME") assertContains(t, lines, " 001 ", "RPL_WELCOME")
} }
// TestEachTestEnvHasItsOwnDatabase checks that a test
// environment starts on an empty database while another one
// is still open.
func TestEachTestEnvHasItsOwnDatabase(t *testing.T) {
t.Parallel()
first := newTestEnv(t)
first.dial(t).register("samenick")
second := newTestEnv(t)
lines := second.dial(t).register("samenick")
assertContains(t, lines, " 001 ", "RPL_WELCOME")
}
func TestWelcomeContainsNick(t *testing.T) { func TestWelcomeContainsNick(t *testing.T) {
t.Parallel() t.Parallel()