Diff
1diff --git a/cmd/soft/serve/server_test.go b/cmd/soft/serve/server_test.go
2new file mode 100644
3index 0000000000000000000000000000000000000000..ce7c9745094e1fb1672f008181dfc3a79bca2172
4--- /dev/null
5+++ b/cmd/soft/serve/server_test.go
6@@ -0,0 +1,80 @@
7+package serve
8+
9+import (
10+ "bytes"
11+ "context"
12+ "strings"
13+ "testing"
14+
15+ "charm.land/log/v2"
16+ "github.com/charmbracelet/soft-serve/pkg/access"
17+ "github.com/charmbracelet/soft-serve/pkg/backend"
18+ "github.com/charmbracelet/soft-serve/pkg/config"
19+ "github.com/charmbracelet/soft-serve/pkg/db"
20+ "github.com/charmbracelet/soft-serve/pkg/db/migrate"
21+ "github.com/charmbracelet/soft-serve/pkg/store"
22+ "github.com/charmbracelet/soft-serve/pkg/store/database"
23+ "github.com/matryer/is"
24+ _ "modernc.org/sqlite"
25+)
26+
27+// newTestBackend returns a Backend backed by a real, freshly migrated
28+// SQLite database, along with the *config.Config it was constructed with,
29+// so tests can set AnonAccess/AllowKeyless overrides directly.
30+func newTestBackend(t *testing.T) (*backend.Backend, *config.Config) {
31+ t.Helper()
32+ is := is.New(t)
33+ ctx := context.Background()
34+
35+ dp := t.TempDir()
36+ cfg := config.DefaultConfig()
37+ cfg.DataPath = dp
38+ cfg.DB.Driver = "sqlite"
39+ cfg.DB.DataSource = dp + "/test.db"
40+
41+ ctx = config.WithContext(ctx, cfg)
42+ dbx, err := db.Open(ctx, cfg.DB.Driver, cfg.DB.DataSource)
43+ is.NoErr(err)
44+ t.Cleanup(func() { dbx.Close() }) //nolint:errcheck
45+
46+ is.NoErr(migrate.Migrate(ctx, dbx))
47+ dbstore := database.New(ctx, dbx)
48+ ctx = store.WithContext(ctx, dbstore)
49+ be := backend.New(ctx, cfg, dbx, dbstore)
50+
51+ return be, cfg
52+}
53+
54+func TestWarnIfAnonAdminAccess(t *testing.T) {
55+ cases := []struct {
56+ name string
57+ allowKeyless bool
58+ anonAccess access.AccessLevel
59+ wantWarning bool
60+ }{
61+ {"defaults: keyless disabled, read-only anon", false, access.ReadOnlyAccess, false},
62+ {"keyless allowed but anon access below admin", true, access.ReadWriteAccess, false},
63+ {"admin anon access but keyless disallowed", false, access.AdminAccess, false},
64+ {"keyless allowed with admin anon access: the dangerous combo", true, access.AdminAccess, true},
65+ }
66+
67+ for _, c := range cases {
68+ t.Run(c.name, func(t *testing.T) {
69+ is := is.New(t)
70+ be, cfg := newTestBackend(t)
71+ ctx := context.Background()
72+
73+ allow := c.allowKeyless
74+ cfg.AllowKeyless = &allow
75+ cfg.AnonAccess = c.anonAccess.String()
76+
77+ var buf bytes.Buffer
78+ logger := log.New(&buf)
79+
80+ warnIfAnonAdminAccess(ctx, be, logger)
81+
82+ gotWarning := strings.Contains(buf.String(), "WARNING")
83+ is.Equal(gotWarning, c.wantWarning)
84+ })
85+ }
86+}
87diff --git a/pkg/backend/repo_test.go b/pkg/backend/repo_test.go
88new file mode 100644
89index 0000000000000000000000000000000000000000..57851fab6f6794de1711ea4e50950af5609c0651
90--- /dev/null
91+++ b/pkg/backend/repo_test.go
92@@ -0,0 +1,45 @@
93+package backend
94+
95+import (
96+ "context"
97+ "testing"
98+
99+ "github.com/charmbracelet/soft-serve/pkg/db"
100+ "github.com/charmbracelet/soft-serve/pkg/proto"
101+ "github.com/matryer/is"
102+)
103+
104+// TestCreateRepositoryAnonymousOwner verifies that a repository created with
105+// a nil user (an anon-access/allow-keyless override) is owned by the
106+// lowest-ID admin rather than failing the NOT NULL repos.user_id constraint.
107+func TestCreateRepositoryAnonymousOwner(t *testing.T) {
108+ is := is.New(t)
109+ be, _ := newTestBackend(t)
110+ ctx := context.Background()
111+
112+ admin, err := be.User(ctx, "admin")
113+ is.NoErr(err)
114+
115+ repo, err := be.CreateRepository(ctx, "anon-repo", nil, proto.RepositoryOptions{})
116+ is.NoErr(err)
117+ is.Equal(repo.UserID(), admin.ID())
118+}
119+
120+// TestDefaultAdminUserIDNoAdmin verifies that defaultAdminUserID surfaces an
121+// error rather than silently returning a zero user ID (which would violate
122+// the NOT NULL repos.user_id constraint) when the database has no admin.
123+func TestDefaultAdminUserIDNoAdmin(t *testing.T) {
124+ is := is.New(t)
125+ be, _ := newTestBackend(t)
126+ ctx := context.Background()
127+
128+ err := be.db.TransactionContext(ctx, func(tx *db.Tx) error {
129+ if _, err := tx.ExecContext(ctx, "UPDATE users SET admin = false"); err != nil {
130+ return err
131+ }
132+
133+ _, err := be.defaultAdminUserID(ctx, tx)
134+ return err
135+ })
136+ is.True(err != nil)
137+}