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:
246
internal/database/benchmark_test.go
Normal file
246
internal/database/benchmark_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user