Files
ferrum/internal/api/ai_providers_test.go
Anand d9b2519b75 Fleet audit fixes: PBS page, shared SSE, safer ops, deep links + comment cleanup
- New PBS Backups page (/pbs): datastore usage, group browser, per-group
  prune, GC with live status, sync/verify jobs with admin-gated run
- Shared single-connection SSE store (lib/sse.ts) feeding NotificationBell
  and the telemetry pill; global offline banner with reconnect invalidation
- Correctness: CIFS storage credentials sent in JSON body; per-row alert
  silencing + unsilence endpoint; graceful stop-and-delete for running
  guests; accent/shadow token fixes for all look presets
- Safer operations: confirms for SDN apply and host upgrade-all; Refresh
  control on every page; 30s polling added to previously-static pages
- Wayfinding: guests deep-linkable via /inventory?focusGuest, scoped
  inventory views, topology guest click-through, Overview links
- Forms/consistency: node storage/bridge pick-lists in CreateGuestDialog,
  backup schedule validation, BulkOperationsPage on shared DataTable,
  progressive pagination on cluster access lists, shared Timestamp and
  chartToneFor helpers, editable alert rules and webhooks
- Security/tests: CSRF, authz-matrix, redaction and PBS test coverage;
  webhook outbox + connection TLS fingerprint migrations (00032/00033,
  sqlite + postgres)
- Comment audit: removed stale, duplicated and orphaned comments; corrected
  inaccurate doc comments (UpdateGuestConfig, InvalidateAll, breakpoint
  references); no behavior changes
2026-09-13 23:29:33 +05:30

109 lines
4.4 KiB
Go

package api
import (
"context"
"testing"
"ferrum/internal/needle"
)
// TestSeedBuiltinNeedleProviderIsDefaultOnlyUntilAdminChoosesOtherwise
// exercises the "built-in assistant is always there, and is the default
// until an admin picks something else" contract: New() already seeds it
// once (so a fresh install has a working assistant with zero setup); this
// checks that a competing provider explicitly made the default SURVIVES the
// next seed pass (e.g. a server restart) instead of being silently demoted
// back to Needle, and that re-seeding never creates a second built-in
// provider row.
func TestSeedBuiltinNeedleProviderIsDefaultOnlyUntilAdminChoosesOtherwise(t *testing.T) {
env := newTestEnv(t)
if !env.server.needle.Available() {
t.Skip("no needle binary bundled/configured for this platform")
}
ctx := context.Background()
var providerCount int
if err := env.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM ai_providers WHERE base_url = ?`, needle.BaseURL).Scan(&providerCount); err != nil {
t.Fatalf("counting needle providers: %v", err)
}
if providerCount != 1 {
t.Fatalf("expected exactly one built-in provider row after New(), got %d", providerCount)
}
// Simulate an admin adding another provider and explicitly making it the
// default — a later restart (another seed pass) must respect that choice.
if _, err := env.db.ExecContext(ctx, `
INSERT INTO ai_providers (id, name, base_url, model, is_enabled, is_default, created_at, updated_at)
VALUES ('other-provider', 'Other', 'https://example.com/v1', '', 1, 0, 'now', 'now')`); err != nil {
t.Fatalf("inserting competing provider: %v", err)
}
if _, err := env.db.ExecContext(ctx, `
INSERT INTO ai_provider_models (id, provider_id, label, model_id, is_default, created_at)
VALUES ('other-model', 'other-provider', 'Other model', 'other', 0, 'now')`); err != nil {
t.Fatalf("inserting competing model: %v", err)
}
if err := env.server.clearOtherDefaultModels(ctx, env.db, "other-model"); err != nil {
t.Fatalf("clearing other default models: %v", err)
}
if _, err := env.db.ExecContext(ctx, `UPDATE ai_provider_models SET is_default = 1 WHERE id = 'other-model'`); err != nil {
t.Fatalf("making the competing model the default: %v", err)
}
env.server.seedBuiltinNeedleProvider(ctx)
var defaultCount int
if err := env.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM ai_provider_models WHERE is_default = 1`).Scan(&defaultCount); err != nil {
t.Fatalf("counting default models: %v", err)
}
if defaultCount != 1 {
t.Fatalf("expected exactly one default model, got %d", defaultCount)
}
var defaultProviderBaseURL string
if err := env.db.QueryRowContext(ctx, `
SELECT p.base_url FROM ai_provider_models m
JOIN ai_providers p ON p.id = m.provider_id
WHERE m.is_default = 1`).Scan(&defaultProviderBaseURL); err != nil {
t.Fatalf("finding default provider: %v", err)
}
if defaultProviderBaseURL != "https://example.com/v1" {
t.Fatalf("expected the admin's chosen default to survive re-seeding, got %q", defaultProviderBaseURL)
}
if err := env.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM ai_providers WHERE base_url = ?`, needle.BaseURL).Scan(&providerCount); err != nil {
t.Fatalf("re-counting needle providers: %v", err)
}
if providerCount != 1 {
t.Fatalf("re-seeding must not duplicate the built-in provider row, got %d", providerCount)
}
}
// TestSeedBuiltinNeedleProviderBecomesDefaultWhenNoneIsSet covers the fresh-
// install path: if nothing holds the default slot (e.g. the previous
// default model row was deleted), Needle claims it so the assistant still
// works with zero setup.
func TestSeedBuiltinNeedleProviderBecomesDefaultWhenNoneIsSet(t *testing.T) {
env := newTestEnv(t)
if !env.server.needle.Available() {
t.Skip("no needle binary bundled/configured for this platform")
}
ctx := context.Background()
if _, err := env.db.ExecContext(ctx, `UPDATE ai_provider_models SET is_default = 0`); err != nil {
t.Fatalf("clearing the default: %v", err)
}
env.server.seedBuiltinNeedleProvider(ctx)
var defaultProviderBaseURL string
if err := env.db.QueryRowContext(ctx, `
SELECT p.base_url FROM ai_provider_models m
JOIN ai_providers p ON p.id = m.provider_id
WHERE m.is_default = 1`).Scan(&defaultProviderBaseURL); err != nil {
t.Fatalf("finding default provider: %v", err)
}
if defaultProviderBaseURL != needle.BaseURL {
t.Fatalf("expected Needle to claim the empty default slot, got %q", defaultProviderBaseURL)
}
}