package db import ( "context" "errors" "testing" "sourcecraft.dev/bigbes/sr-ht-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 }