package cli import ( "context" "os" "path/filepath" "strings" "testing" "gitea.mynodes.xyz/ilker/llmgw/internal/config" "gitea.mynodes.xyz/ilker/llmgw/internal/store" ) func TestSetAuthStoreChangesOnlyAuthStore(t *testing.T) { p := filepath.Join(t.TempDir(), "server.yaml") body := `# my notes threads: store: /var/lib/llmgw/threads.db # not this one retain_hours: 24 auth: enabled: true # the store store: /var/lib/llmgw/llmgw.db signup_open: true landing: contact: "x" ` if err := os.WriteFile(p, []byte(body), 0o640); err != nil { t.Fatal(err) } if err := setAuthStore(p, "@/etc/llmgw/secrets/store-dsn"); err != nil { t.Fatal(err) } got, _ := os.ReadFile(p) want := strings.Replace(body, " store: /var/lib/llmgw/llmgw.db", ` store: "@/etc/llmgw/secrets/store-dsn"`, 1) if string(got) != want { t.Errorf("got:\n%s\nwant:\n%s", got, want) } cfg, err := config.Load(p) if err != nil { t.Fatal(err) } if cfg.Auth.Store != "@/etc/llmgw/secrets/store-dsn" || cfg.Threads.Store != "/var/lib/llmgw/threads.db" { t.Errorf("auth.store %q, threads.store %q", cfg.Auth.Store, cfg.Threads.Store) } backups, _ := filepath.Glob(p + ".bak-*") if len(backups) != 1 { t.Errorf("the old file should be kept beside it, found %v", backups) } } func TestSetAuthStoreAddsTheLineWhenAuthHasNone(t *testing.T) { p := filepath.Join(t.TempDir(), "server.yaml") if err := os.WriteFile(p, []byte("auth:\n enabled: true\nlanding:\n contact: x\n"), 0o640); err != nil { t.Fatal(err) } if err := setAuthStore(p, "@/x"); err != nil { t.Fatal(err) } cfg, err := config.Load(p) if err != nil { t.Fatal(err) } if cfg.Auth.Store != "@/x" || !cfg.Auth.Enabled { t.Errorf("auth = %+v", cfg.Auth) } } // fakePostgres stands in for setupPostgres the way it really answers: an // address file under /etc/llmgw/secrets, returned as "@file". What the file // holds is a SQLite path, so everything after the setup runs for real. func fakePostgres(t *testing.T, in *serverInit) (address, db string) { t.Helper() db = filepath.Join(t.TempDir(), "pg-standin.db") in.pgSetup = func(context.Context) (string, error) { if err := os.MkdirAll(filepath.Dir(in.dsnPath()), 0o750); err != nil { return "", err } return "@" + in.dsnPath(), os.WriteFile(in.dsnPath(), []byte(db+"\n"), 0o600) } return "@" + in.dsnPath(), db } // Moving an install that began on SQLite: the administrator it created must // be in the new store, the configuration must name it, and the SQLite file // must still be there. The new store is SQLite too here — what PostgreSQL // adds is the setup, which needs a machine to run on. func TestInitMovesAnExistingStore(t *testing.T) { in, out := newTestInit(t) if err := in.run(context.Background()); err != nil { t.Fatalf("%v\n%s", err, out) } old := filepath.Join(in.stateDir, "llmgw.db") in.postgres = true target, db := fakePostgres(t, in) out.Reset() if err := in.run(context.Background()); err != nil { t.Fatalf("%v\n%s", err, out) } if !strings.Contains(out.String(), "copied") { t.Errorf("nothing was copied:\n%s", out) } cfg, err := config.Load(in.configPath()) if err != nil { t.Fatal(err) } if cfg.Auth.Store != target { t.Fatalf("server.yaml still names %q", cfg.Auth.Store) } s, err := store.Open(context.Background(), db) if err != nil { t.Fatal(err) } u, err := s.UserByEmail(context.Background(), "admin@example.com") s.Close() if err != nil || !u.IsAdmin { t.Fatalf("the administrator did not move: %v %+v", err, u) } if _, err := os.Stat(old); err != nil { t.Error("the SQLite store should be kept") } // And once moved, again is nothing. in.changed = false out.Reset() if err := in.run(context.Background()); err != nil { t.Fatalf("%v\n%s", err, out) } if in.changed { t.Errorf("a run after the move changed something:\n%s", out) } } func TestInitWritesAPostgresConfigurationFromTheStart(t *testing.T) { in, out := newTestInit(t) in.postgres = true target, _ := fakePostgres(t, in) if err := in.run(context.Background()); err != nil { t.Fatalf("%v\n%s", err, out) } cfg, _ := config.Load(in.configPath()) if cfg.Auth.Store != target { t.Errorf("auth.store = %q", cfg.Auth.Store) } if _, err := os.Stat(filepath.Join(in.stateDir, "llmgw.db")); err == nil { t.Error("a PostgreSQL install should not leave a SQLite store behind") } } func TestInitWillNotMoveOnePostgresIntoAnother(t *testing.T) { in, out := newTestInit(t) if err := os.MkdirAll(in.etcDir, 0o755); err != nil { t.Fatal(err) } cfg := "control:\n enabled: true\nauth:\n enabled: true\n store: postgres://elsewhere/llmgw\n" if err := os.WriteFile(in.configPath(), []byte(cfg), 0o640); err != nil { t.Fatal(err) } in.postgres = true in.pgSetup = func(context.Context) (string, error) { return "@/somewhere/else", nil } err := in.run(context.Background()) if err == nil || !strings.Contains(err.Error(), "already names a PostgreSQL store") { t.Fatalf("expected a refusal, got %v\n%s", err, out) } }