Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go/internal/backends/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func Bootstrap(ctx context.Context, q ExecQuerier, in BootstrapInput) (int, erro
(name, base_url, protocol, provider_class, trust, locality,
roles, model_map, timeouts, num_ctx, priority, enabled)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,true)
ON CONFLICT (name) DO NOTHING`,
ON CONFLICT (scope, name) DO NOTHING`,
b.Name, b.Host, string(b.Protocol), b.ProviderClass, string(b.Trust),
b.Locality, b.Roles, marshalModelMap(b.ModelMap), timeouts,
nullableInt(b.NumCtx), b.Priority)
Expand Down
68 changes: 68 additions & 0 deletions go/internal/backends/bootstrap_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//go:build integration

// Integration regression for the first-boot backend-pool seeding path
// (Bootstrap): migration 062 swapped uq_backends_name (name) →
// uq_backends_scope_name (scope, name), but the seeding INSERT still
// inferred ON CONFLICT (name) — SQLSTATE 42P10 on every fresh database,
// context_backends stays empty and every LLM role answers 503. A populated
// pool returns before the INSERT, which is why live deployments never hit
// it: the path only runs on the empty table a fresh install has.
//
// External test package like backends_tenant_integration_test.go (testdb
// imports store which imports backends — an internal test would cycle).
//
// Run with:
//
// go test -tags=integration ./internal/backends/ -run TestBootstrapSeedsEmptyPool -count=1 -v
package backends_test

import (
"context"
"testing"

"github.com/GottZ/ctx/internal/backends"
"github.com/GottZ/ctx/internal/testdb"
)

func TestBootstrapSeedsEmptyPool(t *testing.T) {
pool := testdb.SetupTestDB(t)
ctx := context.Background()

in := backends.BootstrapInput{
Chat: backends.Backend{
Host: "http://chat.internal:11434",
Protocol: backends.ProtocolOllama,
Model: "chat-model",
},
Embed: backends.Backend{
Host: "http://embed.internal:11434",
Protocol: backends.ProtocolOllama,
Model: "embed-model",
},
}

inserted, err := backends.Bootstrap(ctx, pool, in)
if err != nil {
t.Fatalf("Bootstrap on empty context_backends: %v", err)
}
if inserted == 0 {
t.Fatal("Bootstrap inserted 0 rows on an empty pool")
}

var count int
if err := pool.QueryRow(ctx, `SELECT count(*) FROM context_backends`).Scan(&count); err != nil {
t.Fatalf("count context_backends: %v", err)
}
if count != inserted {
t.Fatalf("row count %d != inserted %d", count, inserted)
}

// Populated table: the guard must return without touching the pool.
again, err := backends.Bootstrap(ctx, pool, in)
if err != nil {
t.Fatalf("Bootstrap on populated pool: %v", err)
}
if again != 0 {
t.Fatalf("second Bootstrap inserted %d rows, want 0", again)
}
}
Loading