test: MCP server tests, benchmarks, and nix build fix

- Add MCP server protocol tests (initialize, tools/list, errors)
- Add database benchmarks (batch inserts, search, children)
- Add sample options.json test fixture
- Fix flake.nix vendor hash for nix build

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 17:39:46 +01:00
parent f7112d4459
commit 939abc8d8e
5 changed files with 571 additions and 1 deletions

View File

@@ -0,0 +1,246 @@
package database
import (
"context"
"fmt"
"testing"
"time"
)
func BenchmarkCreateOptions(b *testing.B) {
store, err := NewSQLiteStore(":memory:")
if err != nil {
b.Fatalf("Failed to create store: %v", err)
}
defer store.Close()
ctx := context.Background()
if err := store.Initialize(ctx); err != nil {
b.Fatalf("Failed to initialize: %v", err)
}
rev := &Revision{GitHash: "bench123", ChannelName: "bench"}
if err := store.CreateRevision(ctx, rev); err != nil {
b.Fatalf("Failed to create revision: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
opt := &Option{
RevisionID: rev.ID,
Name: fmt.Sprintf("services.test%d.enable", i),
ParentPath: fmt.Sprintf("services.test%d", i),
Type: "boolean",
DefaultValue: "false",
Description: "Test option",
}
if err := store.CreateOption(ctx, opt); err != nil {
b.Fatalf("Failed to create option: %v", err)
}
}
}
func BenchmarkCreateOptionsBatch(b *testing.B) {
benchmarkBatch(b, 100)
}
func BenchmarkCreateOptionsBatch1000(b *testing.B) {
benchmarkBatch(b, 1000)
}
func benchmarkBatch(b *testing.B, batchSize int) {
store, err := NewSQLiteStore(":memory:")
if err != nil {
b.Fatalf("Failed to create store: %v", err)
}
defer store.Close()
ctx := context.Background()
if err := store.Initialize(ctx); err != nil {
b.Fatalf("Failed to initialize: %v", err)
}
rev := &Revision{GitHash: "batchbench", ChannelName: "bench"}
if err := store.CreateRevision(ctx, rev); err != nil {
b.Fatalf("Failed to create revision: %v", err)
}
opts := make([]*Option, batchSize)
for i := 0; i < batchSize; i++ {
opts[i] = &Option{
RevisionID: rev.ID,
Name: fmt.Sprintf("services.batch%d.enable", i),
ParentPath: fmt.Sprintf("services.batch%d", i),
Type: "boolean",
DefaultValue: "false",
Description: "Batch test option",
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Reset IDs for next iteration
for _, opt := range opts {
opt.ID = 0
}
if err := store.CreateOptionsBatch(ctx, opts); err != nil {
b.Fatalf("Failed to create batch: %v", err)
}
// Clean up for next iteration
store.DeleteRevision(ctx, rev.ID)
rev = &Revision{GitHash: fmt.Sprintf("batchbench%d", i), ChannelName: "bench"}
store.CreateRevision(ctx, rev)
for _, opt := range opts {
opt.RevisionID = rev.ID
}
}
}
func BenchmarkSearchOptions(b *testing.B) {
store, err := NewSQLiteStore(":memory:")
if err != nil {
b.Fatalf("Failed to create store: %v", err)
}
defer store.Close()
ctx := context.Background()
if err := store.Initialize(ctx); err != nil {
b.Fatalf("Failed to initialize: %v", err)
}
rev := &Revision{GitHash: "searchbench", ChannelName: "bench"}
if err := store.CreateRevision(ctx, rev); err != nil {
b.Fatalf("Failed to create revision: %v", err)
}
// Create 1000 options to search through
opts := make([]*Option, 1000)
for i := 0; i < 1000; i++ {
opts[i] = &Option{
RevisionID: rev.ID,
Name: fmt.Sprintf("services.service%d.enable", i),
ParentPath: fmt.Sprintf("services.service%d", i),
Type: "boolean",
DefaultValue: "false",
Description: fmt.Sprintf("Enable service %d for testing purposes", i),
}
}
if err := store.CreateOptionsBatch(ctx, opts); err != nil {
b.Fatalf("Failed to create options: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := store.SearchOptions(ctx, rev.ID, "enable service", SearchFilters{Limit: 50})
if err != nil {
b.Fatalf("Search failed: %v", err)
}
}
}
func BenchmarkGetChildren(b *testing.B) {
store, err := NewSQLiteStore(":memory:")
if err != nil {
b.Fatalf("Failed to create store: %v", err)
}
defer store.Close()
ctx := context.Background()
if err := store.Initialize(ctx); err != nil {
b.Fatalf("Failed to initialize: %v", err)
}
rev := &Revision{GitHash: "childrenbench", ChannelName: "bench"}
if err := store.CreateRevision(ctx, rev); err != nil {
b.Fatalf("Failed to create revision: %v", err)
}
// Create parent and 100 children
opts := make([]*Option, 101)
opts[0] = &Option{
RevisionID: rev.ID,
Name: "services",
ParentPath: "",
Type: "attrsOf",
}
for i := 1; i <= 100; i++ {
opts[i] = &Option{
RevisionID: rev.ID,
Name: fmt.Sprintf("services.service%d", i),
ParentPath: "services",
Type: "submodule",
}
}
if err := store.CreateOptionsBatch(ctx, opts); err != nil {
b.Fatalf("Failed to create options: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := store.GetChildren(ctx, rev.ID, "services")
if err != nil {
b.Fatalf("GetChildren failed: %v", err)
}
}
}
func BenchmarkSchemaInitialize(b *testing.B) {
for i := 0; i < b.N; i++ {
store, err := NewSQLiteStore(":memory:")
if err != nil {
b.Fatalf("Failed to create store: %v", err)
}
ctx := context.Background()
if err := store.Initialize(ctx); err != nil {
b.Fatalf("Failed to initialize: %v", err)
}
store.Close()
}
}
// BenchmarkRevisionCRUD benchmarks the full CRUD cycle for revisions.
func BenchmarkRevisionCRUD(b *testing.B) {
store, err := NewSQLiteStore(":memory:")
if err != nil {
b.Fatalf("Failed to create store: %v", err)
}
defer store.Close()
ctx := context.Background()
if err := store.Initialize(ctx); err != nil {
b.Fatalf("Failed to initialize: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
rev := &Revision{
GitHash: fmt.Sprintf("crud%d", i),
ChannelName: "test",
CommitDate: time.Now(),
}
// Create
if err := store.CreateRevision(ctx, rev); err != nil {
b.Fatalf("Create failed: %v", err)
}
// Read
_, err := store.GetRevision(ctx, rev.GitHash)
if err != nil {
b.Fatalf("Get failed: %v", err)
}
// Update
if err := store.UpdateRevisionOptionCount(ctx, rev.ID, 100); err != nil {
b.Fatalf("Update failed: %v", err)
}
// Delete
if err := store.DeleteRevision(ctx, rev.ID); err != nil {
b.Fatalf("Delete failed: %v", err)
}
}
}