Initial commit

This commit is contained in:
prosolis
2026-05-22 17:25:27 -07:00
commit 652d6dfa38
40 changed files with 5855 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
package poster
import (
"context"
"path/filepath"
"sync"
"testing"
"time"
"pete/internal/config"
"pete/internal/storage"
)
// mockMatrix implements the subset of matrix.Client that Queue needs.
// We test via the public Enqueue/Start interface and check storage state.
func setupTestEnv(t *testing.T) {
t.Helper()
dbPath := filepath.Join(t.TempDir(), "test.db")
if err := storage.Init(dbPath); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { storage.Close() })
}
func TestQueue_EnqueueAndDrain(t *testing.T) {
setupTestEnv(t)
q := &Queue{
queues: make(map[string][]QueueItem),
config: config.PostingConfig{
MinIntervalSeconds: 0,
BurstCapCount: 10,
BurstCapWindowSeconds: 1800,
},
done: make(chan struct{}),
}
// Enqueue directly (bypass ChannelRoomID check)
q.mu.Lock()
q.queues["tech"] = append(q.queues["tech"], QueueItem{
GUID: "g1",
Channel: "tech",
})
q.mu.Unlock()
// Verify item is in queue
q.mu.Lock()
if len(q.queues["tech"]) != 1 {
t.Fatalf("expected 1 item in queue, got %d", len(q.queues["tech"]))
}
q.mu.Unlock()
}
func TestQueueItem_MaxRetries(t *testing.T) {
// Verify the retry counter works
item := QueueItem{GUID: "g1", Channel: "tech", retries: 0}
item.retries++
if item.retries != 1 {
t.Errorf("expected retries=1, got %d", item.retries)
}
item.retries++
item.retries++
if item.retries < maxRetries {
t.Errorf("expected retries >= maxRetries after 3 increments, got %d", item.retries)
}
}
func TestQueue_StartAndWait(t *testing.T) {
setupTestEnv(t)
q := &Queue{
queues: make(map[string][]QueueItem),
config: config.PostingConfig{
MinIntervalSeconds: 0,
BurstCapCount: 10,
BurstCapWindowSeconds: 1800,
},
done: make(chan struct{}),
}
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
q.Start(ctx)
}()
// Cancel immediately — Start should return promptly
cancel()
done := make(chan struct{})
go func() {
q.Wait()
close(done)
}()
select {
case <-done:
// good
case <-time.After(5 * time.Second):
t.Fatal("queue.Wait() did not return after cancel")
}
}
func TestQueue_BurstCap(t *testing.T) {
setupTestEnv(t)
cfg := config.PostingConfig{
MinIntervalSeconds: 0,
BurstCapCount: 2,
BurstCapWindowSeconds: 3600,
}
q := &Queue{
queues: make(map[string][]QueueItem),
config: cfg,
done: make(chan struct{}),
}
// Simulate 2 posts already in window
now := time.Now().Unix()
storage.Get().Exec(`INSERT INTO post_log (guid, channel, event_id, posted_at) VALUES (?, ?, ?, ?)`,
"prev1", "tech", "$e1", now-100)
storage.Get().Exec(`INSERT INTO post_log (guid, channel, event_id, posted_at) VALUES (?, ?, ?, ?)`,
"prev2", "tech", "$e2", now-50)
// Queue a new item
q.mu.Lock()
q.queues["tech"] = []QueueItem{{GUID: "g3", Channel: "tech"}}
q.mu.Unlock()
// drainChannel should not post (burst cap reached)
q.drainChannel("tech")
q.mu.Lock()
remaining := len(q.queues["tech"])
q.mu.Unlock()
if remaining != 1 {
t.Errorf("expected item to remain in queue (burst cap), got %d remaining", remaining)
}
}