All checks were successful
check / check (push) Successful in 2m19s
Security: - Add channel membership check before PRIVMSG (prevents non-members from sending) - Add membership check on history endpoint (channels require membership, DMs scoped to own nick) - Enforce MaxBytesReader on all POST request bodies - Fix rand.Read error being silently ignored in token generation Data integrity: - Fix TOCTOU race in GetOrCreateChannel using INSERT OR IGNORE + SELECT Build: - Add CGO_ENABLED=0 to golangci-lint install in Dockerfile (fixes alpine build) Linting: - Strict .golangci.yml: only wsl disabled (deprecated in v2) - Re-enable exhaustruct, depguard, godot, wrapcheck, varnamelen - Fix linters-settings -> linters.settings for v2 config format - Fix ALL lint findings in actual code (no linter config weakening) - Wrap all external package errors (wrapcheck) - Fill struct fields or add targeted nolint:exhaustruct where appropriate - Rename short variables (ts->timestamp, n->bufIndex, etc.) - Add depguard deny policy for io/ioutil and math/rand - Exclude G704 (SSRF) in gosec config (CLI client takes user-configured URLs) Tests: - Add security tests (TestNonMemberCannotSend, TestHistoryNonMember) - Split TestInsertAndPollMessages for reduced complexity - Fix parallel test safety (viper global state prevents parallelism) - Use t.Context() instead of context.Background() in tests Docker build verified passing locally.
122 lines
1.7 KiB
Go
122 lines
1.7 KiB
Go
package broker_test
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.eeqj.de/sneak/chat/internal/broker"
|
|
)
|
|
|
|
func TestNewBroker(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
brk := broker.New()
|
|
if brk == nil {
|
|
t.Fatal("expected non-nil broker")
|
|
}
|
|
}
|
|
|
|
func TestWaitAndNotify(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
brk := broker.New()
|
|
waitCh := brk.Wait(1)
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
brk.Notify(1)
|
|
}()
|
|
|
|
select {
|
|
case <-waitCh:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("timeout")
|
|
}
|
|
}
|
|
|
|
func TestNotifyWithoutWaiters(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
brk := broker.New()
|
|
brk.Notify(42) // should not panic.
|
|
}
|
|
|
|
func TestRemove(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
brk := broker.New()
|
|
waitCh := brk.Wait(1)
|
|
|
|
brk.Remove(1, waitCh)
|
|
|
|
brk.Notify(1)
|
|
|
|
select {
|
|
case <-waitCh:
|
|
t.Fatal("should not receive after remove")
|
|
case <-time.After(50 * time.Millisecond):
|
|
}
|
|
}
|
|
|
|
func TestMultipleWaiters(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
brk := broker.New()
|
|
waitCh1 := brk.Wait(1)
|
|
waitCh2 := brk.Wait(1)
|
|
|
|
brk.Notify(1)
|
|
|
|
select {
|
|
case <-waitCh1:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("ch1 timeout")
|
|
}
|
|
|
|
select {
|
|
case <-waitCh2:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("ch2 timeout")
|
|
}
|
|
}
|
|
|
|
func TestConcurrentWaitNotify(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
brk := broker.New()
|
|
|
|
var waitGroup sync.WaitGroup
|
|
|
|
const concurrency = 100
|
|
|
|
for idx := range concurrency {
|
|
waitGroup.Add(1)
|
|
|
|
go func(uid int64) {
|
|
defer waitGroup.Done()
|
|
|
|
waitCh := brk.Wait(uid)
|
|
|
|
brk.Notify(uid)
|
|
|
|
select {
|
|
case <-waitCh:
|
|
case <-time.After(time.Second):
|
|
t.Error("timeout")
|
|
}
|
|
}(int64(idx % 10))
|
|
}
|
|
|
|
waitGroup.Wait()
|
|
}
|
|
|
|
func TestRemoveNonexistent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
brk := broker.New()
|
|
waitCh := make(chan struct{}, 1)
|
|
|
|
brk.Remove(999, waitCh) // should not panic.
|
|
}
|