Files
synapbus/internal/attachments/sqlite_store_test.go
T
Algis DumbrisandClaude Opus 4.6 cad0138337 chore: migrate to github.com/synapbus org and add logo assets
Move module path from github.com/smart-mcp-proxy/synapbus to
github.com/synapbus/synapbus across all Go imports (47 files).
Add constellation logo options generated via FLUX 1.1 Pro.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 05:46:46 +02:00

227 lines
6.7 KiB
Go

package attachments
import (
"context"
"database/sql"
"fmt"
"log/slog"
"testing"
_ "modernc.org/sqlite"
"github.com/synapbus/synapbus/internal/storage"
)
func newTestDB(t *testing.T) *sql.DB {
t.Helper()
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
db, err := sql.Open("sqlite", dsn)
if err != nil {
t.Fatalf("open database: %v", err)
}
t.Cleanup(func() { db.Close() })
if _, err := db.Exec("PRAGMA foreign_keys=ON"); err != nil {
t.Fatalf("enable foreign keys: %v", err)
}
ctx := context.Background()
if err := storage.RunMigrations(ctx, db); err != nil {
t.Fatalf("run migrations: %v", err)
}
// Seed test user and agent.
db.Exec(`INSERT OR IGNORE INTO users (id, username, password_hash, display_name) VALUES (1, 'testowner', 'hash', 'Test Owner')`)
return db
}
func newTestStore(t *testing.T) (*SQLiteStore, *sql.DB) {
t.Helper()
db := newTestDB(t)
store := NewSQLiteStore(db, slog.Default())
return store, db
}
// seedMessage inserts a test message so we can reference it via foreign key.
func seedMessage(t *testing.T, db *sql.DB) int64 {
t.Helper()
// Ensure user exists.
db.Exec(`INSERT OR IGNORE INTO users (id, username, password_hash, display_name) VALUES (1, 'testowner', 'hash', 'Test Owner')`)
// Ensure agents exist.
db.Exec(`INSERT OR IGNORE INTO agents (id, name, display_name, owner_id, api_key_hash) VALUES (1, 'agent-a', 'Agent A', 1, 'hash')`)
db.Exec(`INSERT OR IGNORE INTO agents (id, name, display_name, owner_id, api_key_hash) VALUES (2, 'agent-b', 'Agent B', 1, 'hash')`)
// Ensure conversation exists.
db.Exec(`INSERT OR IGNORE INTO conversations (id, subject, created_by) VALUES (1, 'test', 'agent-a')`)
// Insert message.
result, err := db.Exec(`INSERT INTO messages (conversation_id, from_agent, to_agent, body) VALUES (1, 'agent-a', 'agent-b', 'hello')`)
if err != nil {
t.Fatalf("seed message: %v", err)
}
id, _ := result.LastInsertId()
return id
}
func TestSQLiteStore_InsertMetadata(t *testing.T) {
store, _ := newTestStore(t)
ctx := context.Background()
a := &Attachment{
Hash: "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
OriginalFilename: "test.png",
Size: 1024,
MIMEType: "image/png",
UploadedBy: "agent-a",
}
if err := store.InsertMetadata(ctx, a); err != nil {
t.Fatalf("InsertMetadata: %v", err)
}
if a.ID == 0 {
t.Error("ID not populated after insert")
}
if a.CreatedAt.IsZero() {
t.Error("CreatedAt not populated after insert")
}
}
func TestSQLiteStore_GetByHash(t *testing.T) {
store, _ := newTestStore(t)
ctx := context.Background()
hash := "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
// Insert two rows with the same hash.
a1 := &Attachment{Hash: hash, OriginalFilename: "file1.png", Size: 100, MIMEType: "image/png", UploadedBy: "agent-a"}
a2 := &Attachment{Hash: hash, OriginalFilename: "file2.png", Size: 100, MIMEType: "image/png", UploadedBy: "agent-b"}
if err := store.InsertMetadata(ctx, a1); err != nil {
t.Fatalf("InsertMetadata a1: %v", err)
}
if err := store.InsertMetadata(ctx, a2); err != nil {
t.Fatalf("InsertMetadata a2: %v", err)
}
results, err := store.GetByHash(ctx, hash)
if err != nil {
t.Fatalf("GetByHash: %v", err)
}
if len(results) != 2 {
t.Fatalf("expected 2 results, got %d", len(results))
}
}
func TestSQLiteStore_GetByMessageID(t *testing.T) {
store, db := newTestStore(t)
ctx := context.Background()
msgID := seedMessage(t, db)
a := &Attachment{
Hash: "1111111111111111111111111111111111111111111111111111111111111111",
OriginalFilename: "report.pdf",
Size: 2048,
MIMEType: "application/pdf",
MessageID: &msgID,
UploadedBy: "agent-a",
}
if err := store.InsertMetadata(ctx, a); err != nil {
t.Fatalf("InsertMetadata: %v", err)
}
results, err := store.GetByMessageID(ctx, msgID)
if err != nil {
t.Fatalf("GetByMessageID: %v", err)
}
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d", len(results))
}
if results[0].OriginalFilename != "report.pdf" {
t.Errorf("filename = %s, want report.pdf", results[0].OriginalFilename)
}
}
func TestSQLiteStore_DeleteByHash(t *testing.T) {
store, _ := newTestStore(t)
ctx := context.Background()
hash := "2222222222222222222222222222222222222222222222222222222222222222"
a := &Attachment{Hash: hash, OriginalFilename: "del.txt", Size: 10, MIMEType: "text/plain", UploadedBy: "agent-a"}
if err := store.InsertMetadata(ctx, a); err != nil {
t.Fatalf("InsertMetadata: %v", err)
}
if err := store.DeleteByHash(ctx, hash); err != nil {
t.Fatalf("DeleteByHash: %v", err)
}
results, err := store.GetByHash(ctx, hash)
if err != nil {
t.Fatalf("GetByHash after delete: %v", err)
}
if len(results) != 0 {
t.Errorf("expected 0 results after delete, got %d", len(results))
}
}
func TestSQLiteStore_CountReferences(t *testing.T) {
store, _ := newTestStore(t)
ctx := context.Background()
hash := "3333333333333333333333333333333333333333333333333333333333333333"
count, err := store.CountReferences(ctx, hash)
if err != nil {
t.Fatalf("CountReferences: %v", err)
}
if count != 0 {
t.Errorf("expected 0 references, got %d", count)
}
a := &Attachment{Hash: hash, OriginalFilename: "ref.txt", Size: 5, MIMEType: "text/plain", UploadedBy: "agent-a"}
if err := store.InsertMetadata(ctx, a); err != nil {
t.Fatalf("InsertMetadata: %v", err)
}
count, err = store.CountReferences(ctx, hash)
if err != nil {
t.Fatalf("CountReferences: %v", err)
}
if count != 1 {
t.Errorf("expected 1 reference, got %d", count)
}
}
func TestSQLiteStore_FindOrphanHashes(t *testing.T) {
store, db := newTestStore(t)
ctx := context.Background()
// Create an orphan attachment (no message_id).
orphanHash := "4444444444444444444444444444444444444444444444444444444444444444"
a := &Attachment{Hash: orphanHash, OriginalFilename: "orphan.txt", Size: 5, MIMEType: "text/plain", UploadedBy: "agent-a"}
if err := store.InsertMetadata(ctx, a); err != nil {
t.Fatalf("InsertMetadata orphan: %v", err)
}
// Create a linked attachment.
msgID := seedMessage(t, db)
linkedHash := "5555555555555555555555555555555555555555555555555555555555555555"
aLinked := &Attachment{Hash: linkedHash, OriginalFilename: "linked.txt", Size: 5, MIMEType: "text/plain", MessageID: &msgID, UploadedBy: "agent-a"}
if err := store.InsertMetadata(ctx, aLinked); err != nil {
t.Fatalf("InsertMetadata linked: %v", err)
}
orphans, err := store.FindOrphanHashes(ctx)
if err != nil {
t.Fatalf("FindOrphanHashes: %v", err)
}
if len(orphans) != 1 {
t.Fatalf("expected 1 orphan, got %d", len(orphans))
}
if orphans[0] != orphanHash {
t.Errorf("orphan hash = %s, want %s", orphans[0], orphanHash)
}
}