package db
import (
"context"
"errors"
"testing"
"go.bigb.es/sourcehut-dolt/core"
)
func TestCreateAndGetRepo(t *testing.T) {
s, db, cleanup := newTestStore(t)
defer cleanup()
ctx := context.Background()
insertUser(t, db, 1, "alice", core.UserTypeUser)
repo := mkRepo(t, s, ctx, 1, "alice", "widgets", core.VisibilityPublic)
if repo.ID == 0 {
t.Fatal("expected non-zero repo id")
}
got, err := s.GetRepoByOwnerAndName(ctx, "alice", "widgets")
if err != nil {
t.Fatalf("get by owner/name: %v", err)
}
if got.ID != repo.ID || got.OwnerName != "alice" || got.Visibility != core.VisibilityPublic {
t.Fatalf("unexpected repo: %+v", got)
}
byID, err := s.GetRepoByID(ctx, repo.ID)
if err != nil {
t.Fatalf("get by id: %v", err)
}
if byID.Name != "widgets" || byID.OwnerName != "alice" {
t.Fatalf("unexpected repo by id: %+v", byID)
}
}
func TestCreateRepoDuplicateName(t *testing.T) {
s, db, cleanup := newTestStore(t)
defer cleanup()
ctx := context.Background()
insertUser(t, db, 1, "alice", core.UserTypeUser)
mkRepo(t, s, ctx, 1, "alice", "widgets", core.VisibilityPublic)
_, err := s.CreateRepo(ctx, &core.Repo{
Name: "widgets", OwnerID: 1, OwnerName: "alice",
Path: "/var/lib/dolt/~alice/widgets2", Visibility: core.VisibilityPrivate,
})
if !errors.Is(err, ErrNameTaken) {
t.Fatalf("expected ErrNameTaken, got %v", err)
}
}
func TestGetRepoNotFound(t *testing.T) {
s, _, cleanup := newTestStore(t)
defer cleanup()
ctx := context.Background()
if _, err := s.GetRepoByOwnerAndName(ctx, "nobody", "nope"); !errors.Is(err, ErrNotFound) {
t.Fatalf("expected ErrNotFound, got %v", err)
}
if _, err := s.GetRepoByID(ctx, 999); !errors.Is(err, ErrNotFound) {
t.Fatalf("expected ErrNotFound, got %v", err)
}
}
func TestUpdateAndDeleteRepo(t *testing.T) {
s, db, cleanup := newTestStore(t)
defer cleanup()
ctx := context.Background()
insertUser(t, db, 1, "alice", core.UserTypeUser)
repo := mkRepo(t, s, ctx, 1, "alice", "widgets", core.VisibilityPublic)
if err := s.UpdateRepo(ctx, repo.ID, "now with docs", core.VisibilityPrivate); err != nil {
t.Fatalf("update: %v", err)
}
got, err := s.GetRepoByID(ctx, repo.ID)
if err != nil {
t.Fatalf("get: %v", err)
}
if got.Description != "now with docs" || got.Visibility != core.VisibilityPrivate {
t.Fatalf("update not reflected: %+v", got)
}
if err := s.UpdateRepo(ctx, 999, "x", core.VisibilityPublic); !errors.Is(err, ErrNotFound) {
t.Fatalf("expected ErrNotFound updating missing repo, got %v", err)
}
if err := s.DeleteRepo(ctx, repo.ID); err != nil {
t.Fatalf("delete: %v", err)
}
if _, err := s.GetRepoByID(ctx, repo.ID); !errors.Is(err, ErrNotFound) {
t.Fatalf("expected ErrNotFound after delete, got %v", err)
}
if err := s.DeleteRepo(ctx, repo.ID); !errors.Is(err, ErrNotFound) {
t.Fatalf("expected ErrNotFound deleting twice, got %v", err)
}
}
// TestListingVisibility exercises the listing rule: owners and ACL holders see
// everything; everyone else (incl. anonymous) sees only PUBLIC; UNLISTED is
// never listed to non-owners without an ACL.
func TestListingVisibility(t *testing.T) {
s, db, cleanup := newTestStore(t)
defer cleanup()
ctx := context.Background()
owner := insertUser(t, db, 1, "alice", core.UserTypeUser)
aclUser := insertUser(t, db, 2, "bob", core.UserTypeUser)
stranger := insertUser(t, db, 3, "carol", core.UserTypeUser)
mkRepo(t, s, ctx, owner, "alice", "pub", core.VisibilityPublic)
mkRepo(t, s, ctx, owner, "alice", "unl", core.VisibilityUnlisted)
priv := mkRepo(t, s, ctx, owner, "alice", "priv", core.VisibilityPrivate)
// bob gets RO on the private repo.
if err := s.UpsertACL(ctx, priv.ID, aclUser, core.AccessRO); err != nil {
t.Fatalf("grant acl: %v", err)
}
names := func(repos []*core.Repo) map[string]bool {
m := map[string]bool{}
for _, r := range repos {
m[r.Name] = true
}
return m
}
// Owner sees all three.
ownerCaller := &core.Caller{UserID: owner, Username: "alice", UserType: core.UserTypeUser}
got, err := s.ListReposByOwner(ctx, "alice", ownerCaller)
if err != nil {
t.Fatalf("list owner: %v", err)
}
if n := names(got); !n["pub"] || !n["unl"] || !n["priv"] || len(got) != 3 {
t.Fatalf("owner should see all three, got %v", n)
}
// Anonymous sees only PUBLIC.
got, err = s.ListReposByOwner(ctx, "alice", nil)
if err != nil {
t.Fatalf("list anon: %v", err)
}
if n := names(got); !n["pub"] || n["unl"] || n["priv"] || len(got) != 1 {
t.Fatalf("anon should see only pub, got %v", n)
}
// Stranger (no ACL) sees only PUBLIC.
strangerCaller := &core.Caller{UserID: stranger, Username: "carol", UserType: core.UserTypeUser}
got, err = s.ListReposByOwner(ctx, "alice", strangerCaller)
if err != nil {
t.Fatalf("list stranger: %v", err)
}
if n := names(got); !n["pub"] || n["unl"] || n["priv"] || len(got) != 1 {
t.Fatalf("stranger should see only pub, got %v", n)
}
// ACL holder sees PUBLIC plus the private repo they were granted (not UNLISTED).
aclCaller := &core.Caller{UserID: aclUser, Username: "bob", UserType: core.UserTypeUser}
got, err = s.ListReposByOwner(ctx, "alice", aclCaller)
if err != nil {
t.Fatalf("list acl user: %v", err)
}
if n := names(got); !n["pub"] || !n["priv"] || n["unl"] || len(got) != 2 {
t.Fatalf("acl user should see pub+priv, got %v", n)
}
}
func TestListReposForDashboard(t *testing.T) {
s, db, cleanup := newTestStore(t)
defer cleanup()
ctx := context.Background()
owner := insertUser(t, db, 1, "alice", core.UserTypeUser)
other := insertUser(t, db, 2, "bob", core.UserTypeUser)
own := mkRepo(t, s, ctx, owner, "alice", "mine", core.VisibilityPrivate)
shared := mkRepo(t, s, ctx, other, "bob", "shared", core.VisibilityPrivate)
mkRepo(t, s, ctx, other, "bob", "hidden", core.VisibilityPrivate)
if err := s.UpsertACL(ctx, shared.ID, owner, core.AccessRW); err != nil {
t.Fatalf("grant: %v", err)
}
got, err := s.ListReposForDashboard(ctx, owner)
if err != nil {
t.Fatalf("dashboard: %v", err)
}
found := map[string]bool{}
for _, r := range got {
found[r.Name] = true
}
if !found["mine"] || !found["shared"] || found["hidden"] || len(got) != 2 {
t.Fatalf("dashboard should list own+acl only, got %v", found)
}
_ = own
}