mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-06-05 23:03:55 +00:00
Increase test coverage to 45.6%
This commit is contained in:
@@ -0,0 +1,502 @@
|
||||
package sqlitevec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
sqlite_vec "github.com/asg017/sqlite-vec-go-bindings/cgo"
|
||||
"github.com/lukaszraczylo/claude-mnemonic/internal/embedding"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testDB creates a test SQLite database with the vectors table.
|
||||
func testDB(t *testing.T) (*sql.DB, func()) {
|
||||
t.Helper()
|
||||
|
||||
// Create temp directory
|
||||
tmpDir, err := os.MkdirTemp("", "sqlitevec-test-*")
|
||||
require.NoError(t, err)
|
||||
|
||||
dbPath := filepath.Join(tmpDir, "test.db")
|
||||
db, err := sql.Open("sqlite3", dbPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Enable sqlite-vec
|
||||
sqlite_vec.Auto()
|
||||
|
||||
// Create vectors table (matches production schema)
|
||||
_, err = db.Exec(`
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS vectors USING vec0(
|
||||
doc_id TEXT PRIMARY KEY,
|
||||
embedding float[384],
|
||||
sqlite_id INTEGER,
|
||||
doc_type TEXT,
|
||||
field_type TEXT,
|
||||
project TEXT,
|
||||
scope TEXT
|
||||
)
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
|
||||
cleanup := func() {
|
||||
db.Close()
|
||||
os.RemoveAll(tmpDir)
|
||||
}
|
||||
|
||||
return db, cleanup
|
||||
}
|
||||
|
||||
// testEmbeddingService creates a test embedding service.
|
||||
func testEmbeddingService(t *testing.T) (*embedding.Service, func()) {
|
||||
t.Helper()
|
||||
|
||||
svc, err := embedding.NewService()
|
||||
require.NoError(t, err)
|
||||
|
||||
cleanup := func() {
|
||||
svc.Close()
|
||||
}
|
||||
|
||||
return svc, cleanup
|
||||
}
|
||||
|
||||
func TestNewClient_Success(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, client)
|
||||
}
|
||||
|
||||
func TestNewClient_NilDB(t *testing.T) {
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: nil}, embedSvc)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, client)
|
||||
assert.Contains(t, err.Error(), "database connection required")
|
||||
}
|
||||
|
||||
func TestNewClient_NilEmbedding(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, client)
|
||||
assert.Contains(t, err.Error(), "embedding service required")
|
||||
}
|
||||
|
||||
func TestClient_AddDocuments_Empty(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.AddDocuments(context.Background(), []Document{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_AddDocuments_Single(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
docs := []Document{
|
||||
{
|
||||
ID: "obs-1-title",
|
||||
Content: "This is a test observation about authentication.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
"field_type": "title",
|
||||
"project": "test-project",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = client.AddDocuments(context.Background(), docs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify document was inserted
|
||||
var count int
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM vectors WHERE doc_id = ?", "obs-1-title").Scan(&count)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
||||
|
||||
func TestClient_AddDocuments_Multiple(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
docs := []Document{
|
||||
{
|
||||
ID: "obs-1-title",
|
||||
Content: "Authentication flow implementation.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
"field_type": "title",
|
||||
"project": "test-project",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-1-narrative",
|
||||
Content: "We implemented JWT-based authentication.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
"field_type": "narrative",
|
||||
"project": "test-project",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-2-title",
|
||||
Content: "Database optimization.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(2),
|
||||
"doc_type": "observation",
|
||||
"field_type": "title",
|
||||
"project": "test-project",
|
||||
"scope": "global",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = client.AddDocuments(context.Background(), docs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify all documents were inserted
|
||||
var count int
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM vectors").Scan(&count)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 3, count)
|
||||
}
|
||||
|
||||
func TestClient_DeleteDocuments_Empty(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.DeleteDocuments(context.Background(), []string{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_DeleteDocuments_Existing(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add documents first
|
||||
docs := []Document{
|
||||
{
|
||||
ID: "doc-1",
|
||||
Content: "First document.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "doc-2",
|
||||
Content: "Second document.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(2),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = client.AddDocuments(context.Background(), docs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete one document
|
||||
err = client.DeleteDocuments(context.Background(), []string{"doc-1"})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify only one remains
|
||||
var count int
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM vectors").Scan(&count)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
||||
|
||||
func TestClient_Query_Basic(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add some test documents
|
||||
docs := []Document{
|
||||
{
|
||||
ID: "obs-1",
|
||||
Content: "Authentication and login security implementation.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
"project": "test-project",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-2",
|
||||
Content: "Database query optimization techniques.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(2),
|
||||
"doc_type": "observation",
|
||||
"project": "test-project",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = client.AddDocuments(context.Background(), docs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Query for authentication-related content
|
||||
results, err := client.Query(context.Background(), "login authentication", 10, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotEmpty(t, results)
|
||||
assert.LessOrEqual(t, len(results), 10)
|
||||
|
||||
// First result should be the authentication document (higher similarity)
|
||||
assert.Equal(t, "obs-1", results[0].ID)
|
||||
}
|
||||
|
||||
func TestClient_Query_WithDocTypeFilter(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add documents of different types
|
||||
docs := []Document{
|
||||
{
|
||||
ID: "obs-1",
|
||||
Content: "Test content for observation.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
"project": "test-project",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "summary-1",
|
||||
Content: "Test content for summary.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(10),
|
||||
"doc_type": "session_summary",
|
||||
"project": "test-project",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = client.AddDocuments(context.Background(), docs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Query with doc_type filter
|
||||
where := map[string]any{"doc_type": "observation"}
|
||||
results, err := client.Query(context.Background(), "test content", 10, where)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should only return observation documents
|
||||
for _, r := range results {
|
||||
docType, _ := r.Metadata["doc_type"].(string)
|
||||
assert.Equal(t, "observation", docType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Query_WithProjectFilter(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add documents from different projects
|
||||
docs := []Document{
|
||||
{
|
||||
ID: "obs-1",
|
||||
Content: "Project A authentication content.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
"project": "project-a",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-2",
|
||||
Content: "Project B database content.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(2),
|
||||
"doc_type": "observation",
|
||||
"project": "project-b",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-3",
|
||||
Content: "Global security best practices.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(3),
|
||||
"doc_type": "observation",
|
||||
"project": "project-b",
|
||||
"scope": "global",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = client.AddDocuments(context.Background(), docs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Query without project filter to verify all docs are there
|
||||
results, err := client.Query(context.Background(), "authentication security", 10, nil)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, results, "Should find some results")
|
||||
}
|
||||
|
||||
func TestClient_IsConnected(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, client.IsConnected())
|
||||
}
|
||||
|
||||
func TestClient_Close(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Close()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestConfig_Fields(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
cfg := Config{DB: db}
|
||||
assert.Equal(t, db, cfg.DB)
|
||||
}
|
||||
|
||||
func TestClient_UpdateDocument_DeleteThenAdd(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add document
|
||||
docs1 := []Document{
|
||||
{
|
||||
ID: "doc-1",
|
||||
Content: "Original content.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = client.AddDocuments(context.Background(), docs1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete then add with new content (proper update pattern)
|
||||
err = client.DeleteDocuments(context.Background(), []string{"doc-1"})
|
||||
require.NoError(t, err)
|
||||
|
||||
docs2 := []Document{
|
||||
{
|
||||
ID: "doc-1",
|
||||
Content: "Updated content.",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(1),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = client.AddDocuments(context.Background(), docs2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should have exactly 1 document
|
||||
var count int
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM vectors WHERE doc_id = ?", "doc-1").Scan(&count)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
||||
|
||||
func TestClient_DeleteDocuments_NonExistent(t *testing.T) {
|
||||
db, dbCleanup := testDB(t)
|
||||
defer dbCleanup()
|
||||
|
||||
embedSvc, embedCleanup := testEmbeddingService(t)
|
||||
defer embedCleanup()
|
||||
|
||||
client, err := NewClient(Config{DB: db}, embedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Deleting non-existent document should not error
|
||||
err = client.DeleteDocuments(context.Background(), []string{"non-existent-id"})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -0,0 +1,574 @@
|
||||
package sqlitevec
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDocTypes(t *testing.T) {
|
||||
assert.Equal(t, DocType("observation"), DocTypeObservation)
|
||||
assert.Equal(t, DocType("session_summary"), DocTypeSessionSummary)
|
||||
assert.Equal(t, DocType("user_prompt"), DocTypeUserPrompt)
|
||||
}
|
||||
|
||||
func TestDocument_Fields(t *testing.T) {
|
||||
doc := Document{
|
||||
ID: "doc-123",
|
||||
Content: "test content",
|
||||
Metadata: map[string]any{
|
||||
"key": "value",
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, "doc-123", doc.ID)
|
||||
assert.Equal(t, "test content", doc.Content)
|
||||
assert.Equal(t, "value", doc.Metadata["key"])
|
||||
}
|
||||
|
||||
func TestQueryResult_Fields(t *testing.T) {
|
||||
result := QueryResult{
|
||||
ID: "result-123",
|
||||
Distance: 0.5,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(42),
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, "result-123", result.ID)
|
||||
assert.Equal(t, 0.5, result.Distance)
|
||||
assert.Equal(t, float64(42), result.Metadata["sqlite_id"])
|
||||
}
|
||||
|
||||
func TestBuildWhereFilter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
docType DocType
|
||||
project string
|
||||
expected map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "empty_filters",
|
||||
docType: "",
|
||||
project: "",
|
||||
expected: map[string]interface{}{},
|
||||
},
|
||||
{
|
||||
name: "doc_type_only",
|
||||
docType: DocTypeObservation,
|
||||
project: "",
|
||||
expected: map[string]interface{}{
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project_only",
|
||||
docType: "",
|
||||
project: "my-project",
|
||||
expected: map[string]interface{}{
|
||||
"project": "my-project",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "both_filters",
|
||||
docType: DocTypeSessionSummary,
|
||||
project: "test-project",
|
||||
expected: map[string]interface{}{
|
||||
"doc_type": "session_summary",
|
||||
"project": "test-project",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user_prompt_type",
|
||||
docType: DocTypeUserPrompt,
|
||||
project: "prompt-project",
|
||||
expected: map[string]interface{}{
|
||||
"doc_type": "user_prompt",
|
||||
"project": "prompt-project",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := BuildWhereFilter(tt.docType, tt.project)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractIDsByDocType_Empty(t *testing.T) {
|
||||
results := []QueryResult{}
|
||||
ids := ExtractIDsByDocType(results)
|
||||
|
||||
assert.Empty(t, ids.ObservationIDs)
|
||||
assert.Empty(t, ids.SummaryIDs)
|
||||
assert.Empty(t, ids.PromptIDs)
|
||||
}
|
||||
|
||||
func TestExtractIDsByDocType_AllTypes(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "obs-1",
|
||||
Distance: 0.1,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-2",
|
||||
Distance: 0.2,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(2),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "summary-1",
|
||||
Distance: 0.3,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(10),
|
||||
"doc_type": "session_summary",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "prompt-1",
|
||||
Distance: 0.4,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(20),
|
||||
"doc_type": "user_prompt",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractIDsByDocType(results)
|
||||
|
||||
assert.Equal(t, []int64{1, 2}, ids.ObservationIDs)
|
||||
assert.Equal(t, []int64{10}, ids.SummaryIDs)
|
||||
assert.Equal(t, []int64{20}, ids.PromptIDs)
|
||||
}
|
||||
|
||||
func TestExtractIDsByDocType_Deduplication(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "obs-1-field1",
|
||||
Distance: 0.1,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-1-field2",
|
||||
Distance: 0.2,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1), // Same ID, different field
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-2",
|
||||
Distance: 0.3,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(2),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractIDsByDocType(results)
|
||||
|
||||
assert.Equal(t, []int64{1, 2}, ids.ObservationIDs) // Should be deduplicated
|
||||
}
|
||||
|
||||
func TestExtractIDsByDocType_Int64Fallback(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "obs-1",
|
||||
Distance: 0.1,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": int64(42), // int64 instead of float64
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractIDsByDocType(results)
|
||||
|
||||
assert.Equal(t, []int64{42}, ids.ObservationIDs)
|
||||
}
|
||||
|
||||
func TestExtractIDsByDocType_MissingSqliteID(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "obs-1",
|
||||
Distance: 0.1,
|
||||
Metadata: map[string]any{
|
||||
"doc_type": "observation",
|
||||
// Missing sqlite_id
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractIDsByDocType(results)
|
||||
|
||||
assert.Empty(t, ids.ObservationIDs)
|
||||
}
|
||||
|
||||
func TestExtractIDsByDocType_UnknownType(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "unknown-1",
|
||||
Distance: 0.1,
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1),
|
||||
"doc_type": "unknown_type",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractIDsByDocType(results)
|
||||
|
||||
// Should not be added to any category
|
||||
assert.Empty(t, ids.ObservationIDs)
|
||||
assert.Empty(t, ids.SummaryIDs)
|
||||
assert.Empty(t, ids.PromptIDs)
|
||||
}
|
||||
|
||||
func TestExtractObservationIDs_NoFilter(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "obs-1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1),
|
||||
"doc_type": "observation",
|
||||
"project": "proj-a",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-2",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(2),
|
||||
"doc_type": "observation",
|
||||
"project": "proj-b",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "summary-1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(10),
|
||||
"doc_type": "session_summary",
|
||||
"project": "proj-a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractObservationIDs(results, "")
|
||||
|
||||
assert.Equal(t, []int64{1, 2}, ids)
|
||||
}
|
||||
|
||||
func TestExtractObservationIDs_WithProjectFilter(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "obs-1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1),
|
||||
"doc_type": "observation",
|
||||
"project": "proj-a",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-2",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(2),
|
||||
"doc_type": "observation",
|
||||
"project": "proj-b",
|
||||
"scope": "project",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-global",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(3),
|
||||
"doc_type": "observation",
|
||||
"project": "proj-b",
|
||||
"scope": "global",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractObservationIDs(results, "proj-a")
|
||||
|
||||
// Should include proj-a and global scope observations
|
||||
assert.Equal(t, []int64{1, 3}, ids)
|
||||
}
|
||||
|
||||
func TestExtractObservationIDs_Deduplication(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "obs-1-field1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-1-field2",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1), // Same ID
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractObservationIDs(results, "")
|
||||
|
||||
assert.Equal(t, []int64{1}, ids)
|
||||
}
|
||||
|
||||
func TestExtractSummaryIDs_NoFilter(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "summary-1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(10),
|
||||
"doc_type": "session_summary",
|
||||
"project": "proj-a",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "summary-2",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(20),
|
||||
"doc_type": "session_summary",
|
||||
"project": "proj-b",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "obs-1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(1),
|
||||
"doc_type": "observation",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractSummaryIDs(results, "")
|
||||
|
||||
assert.Equal(t, []int64{10, 20}, ids)
|
||||
}
|
||||
|
||||
func TestExtractSummaryIDs_WithProjectFilter(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "summary-1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(10),
|
||||
"doc_type": "session_summary",
|
||||
"project": "proj-a",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "summary-2",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(20),
|
||||
"doc_type": "session_summary",
|
||||
"project": "proj-b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractSummaryIDs(results, "proj-a")
|
||||
|
||||
assert.Equal(t, []int64{10}, ids)
|
||||
}
|
||||
|
||||
func TestExtractPromptIDs_NoFilter(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "prompt-1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(100),
|
||||
"doc_type": "user_prompt",
|
||||
"project": "proj-a",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "prompt-2",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(200),
|
||||
"doc_type": "user_prompt",
|
||||
"project": "proj-b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractPromptIDs(results, "")
|
||||
|
||||
assert.Equal(t, []int64{100, 200}, ids)
|
||||
}
|
||||
|
||||
func TestExtractPromptIDs_WithProjectFilter(t *testing.T) {
|
||||
results := []QueryResult{
|
||||
{
|
||||
ID: "prompt-1",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(100),
|
||||
"doc_type": "user_prompt",
|
||||
"project": "proj-a",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "prompt-2",
|
||||
Metadata: map[string]any{
|
||||
"sqlite_id": float64(200),
|
||||
"doc_type": "user_prompt",
|
||||
"project": "proj-b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ids := ExtractPromptIDs(results, "proj-b")
|
||||
|
||||
assert.Equal(t, []int64{200}, ids)
|
||||
}
|
||||
|
||||
func TestCopyMetadata(t *testing.T) {
|
||||
base := map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": 42,
|
||||
}
|
||||
|
||||
result := copyMetadata(base, "key3", "value3")
|
||||
|
||||
// Original should be unchanged
|
||||
assert.Len(t, base, 2)
|
||||
|
||||
// Result should have new key
|
||||
assert.Len(t, result, 3)
|
||||
assert.Equal(t, "value1", result["key1"])
|
||||
assert.Equal(t, 42, result["key2"])
|
||||
assert.Equal(t, "value3", result["key3"])
|
||||
}
|
||||
|
||||
func TestCopyMetadataMulti(t *testing.T) {
|
||||
base := map[string]any{
|
||||
"key1": "value1",
|
||||
}
|
||||
extra := map[string]any{
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
}
|
||||
|
||||
result := copyMetadataMulti(base, extra)
|
||||
|
||||
assert.Len(t, result, 3)
|
||||
assert.Equal(t, "value1", result["key1"])
|
||||
assert.Equal(t, "value2", result["key2"])
|
||||
assert.Equal(t, "value3", result["key3"])
|
||||
}
|
||||
|
||||
func TestJoinStrings(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
strs []string
|
||||
sep string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "empty_slice",
|
||||
strs: []string{},
|
||||
sep: ", ",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "single_element",
|
||||
strs: []string{"one"},
|
||||
sep: ", ",
|
||||
expected: "one",
|
||||
},
|
||||
{
|
||||
name: "multiple_elements",
|
||||
strs: []string{"one", "two", "three"},
|
||||
sep: ", ",
|
||||
expected: "one, two, three",
|
||||
},
|
||||
{
|
||||
name: "different_separator",
|
||||
strs: []string{"a", "b", "c"},
|
||||
sep: "-",
|
||||
expected: "a-b-c",
|
||||
},
|
||||
{
|
||||
name: "empty_separator",
|
||||
strs: []string{"a", "b", "c"},
|
||||
sep: "",
|
||||
expected: "abc",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := joinStrings(tt.strs, tt.sep)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncateString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
maxLen int
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "shorter_than_max",
|
||||
input: "hello",
|
||||
maxLen: 10,
|
||||
expected: "hello",
|
||||
},
|
||||
{
|
||||
name: "equal_to_max",
|
||||
input: "hello",
|
||||
maxLen: 5,
|
||||
expected: "hello",
|
||||
},
|
||||
{
|
||||
name: "longer_than_max",
|
||||
input: "hello world",
|
||||
maxLen: 5,
|
||||
expected: "hello...",
|
||||
},
|
||||
{
|
||||
name: "empty_string",
|
||||
input: "",
|
||||
maxLen: 5,
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "zero_max_length",
|
||||
input: "hello",
|
||||
maxLen: 0,
|
||||
expected: "...",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := truncateString(tt.input, tt.maxLen)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractedIDs_Empty(t *testing.T) {
|
||||
ids := &ExtractedIDs{}
|
||||
|
||||
assert.Nil(t, ids.ObservationIDs)
|
||||
assert.Nil(t, ids.SummaryIDs)
|
||||
assert.Nil(t, ids.PromptIDs)
|
||||
}
|
||||
Reference in New Issue
Block a user