mirror of
https://github.com/anand34577/ferrum.git
synced 2026-10-03 20:20:24 +00:00
d9b2519b75
- 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
109 lines
4.4 KiB
Go
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)
|
|
}
|
|
}
|