Compare commits
3
Commits
1aa77e1bad
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a054bf828d | ||
|
|
e80c9552eb | ||
|
|
ccb2bf7960 |
+24
-3
@@ -204,21 +204,42 @@ func (database *Database) GetSessionByToken(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = database.UpdateLastSeen(ctx, sessionID, clientID)
|
||||||
|
|
||||||
|
return sessionID, clientID, nick, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLastSeen sets last_seen to now on a session and on
|
||||||
|
// one of its clients, so that the idle cleanup keeps them.
|
||||||
|
func (database *Database) UpdateLastSeen(
|
||||||
|
ctx context.Context,
|
||||||
|
sessionID, clientID int64,
|
||||||
|
) error {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
_, _ = database.conn.ExecContext(
|
_, err := database.conn.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
"UPDATE sessions SET last_seen = ? WHERE id = ?",
|
"UPDATE sessions SET last_seen = ? WHERE id = ?",
|
||||||
now, sessionID,
|
now, sessionID,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"update session last_seen: %w", err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
_, _ = database.conn.ExecContext(
|
_, err = database.conn.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
"UPDATE clients SET last_seen = ? WHERE id = ?",
|
"UPDATE clients SET last_seen = ? WHERE id = ?",
|
||||||
now, clientID,
|
now, clientID,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"update client last_seen: %w", err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return sessionID, clientID, nick, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSessionByNick returns session id for a given nick.
|
// GetSessionByNick returns session id for a given nick.
|
||||||
|
|||||||
@@ -368,6 +368,17 @@ func (c *Conn) handleMessage(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Every command is activity, so the idle cleanup must not
|
||||||
|
// remove this user.
|
||||||
|
err := c.database.UpdateLastSeen(
|
||||||
|
ctx, c.sessionID, c.clientID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
c.log.Error(
|
||||||
|
"failed to update last_seen", "error", err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
handler, ok := c.commands[msg.Command]
|
handler, ok := c.commands[msg.Command]
|
||||||
if !ok {
|
if !ok {
|
||||||
c.sendNumeric(
|
c.sendNumeric(
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
@@ -369,6 +388,39 @@ func TestPingPong(t *testing.T) {
|
|||||||
assertContains(t, lines, "PONG", "PONG response")
|
assertContains(t, lines, "PONG", "PONG response")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestIdleCleanupKeepsActiveUser checks that a user who
|
||||||
|
// sends commands is kept by the idle cleanup, and a user who
|
||||||
|
// sends nothing is not.
|
||||||
|
func TestIdleCleanupKeepsActiveUser(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const idleTimeout = 500 * time.Millisecond
|
||||||
|
|
||||||
|
env := newTestEnv(t)
|
||||||
|
|
||||||
|
active := env.dial(t)
|
||||||
|
active.register("active")
|
||||||
|
|
||||||
|
idle := env.dial(t)
|
||||||
|
idle.register("idle")
|
||||||
|
|
||||||
|
time.Sleep(idleTimeout)
|
||||||
|
active.sendAndExpect("PING :still here", "PONG")
|
||||||
|
|
||||||
|
// The idle cleanup in internal/handlers removes exactly
|
||||||
|
// the users this returns.
|
||||||
|
stale, err := env.database.GetStaleOrphanSessions(
|
||||||
|
t.Context(), time.Now().Add(-idleTimeout),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get stale sessions: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stale) != 1 || stale[0].Nick != "idle" {
|
||||||
|
t.Errorf("cleanup removes %v, want only idle", stale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestJoinChannel(t *testing.T) {
|
func TestJoinChannel(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user