- 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>
247 lines
5.9 KiB
Go
247 lines
5.9 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|