Go · 5912 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package orgs_test
4
5 import (
6 "context"
7 "errors"
8 "testing"
9
10 "github.com/jackc/pgx/v5/pgtype"
11
12 "github.com/tenseleyFlow/shithub/internal/entitlements"
13 "github.com/tenseleyFlow/shithub/internal/orgs"
14 reposdb "github.com/tenseleyFlow/shithub/internal/repos/sqlc"
15 )
16
17 func TestOwnerExpansionRespectsPrivateCollaborationLimit(t *testing.T) {
18 pool, deps, alice := setup(t)
19 ctx := context.Background()
20 org, err := orgs.Create(ctx, deps, orgs.CreateParams{Slug: "acme", CreatedByUserID: alice})
21 if err != nil {
22 t.Fatalf("create org: %v", err)
23 }
24 repo := mustOrgRepo(t, pool, org.ID, "secret", "private")
25 insertDirectCollab(t, pool, repo.ID, mustUser(t, pool, "bob"))
26 insertDirectCollab(t, pool, repo.ID, mustUser(t, pool, "carol"))
27
28 dave := mustUser(t, pool, "dave")
29 if err := orgs.AddMember(ctx, deps, org.ID, dave, alice, "owner"); !errors.Is(err, entitlements.ErrPrivateCollaborationLimitExceeded) {
30 t.Fatalf("AddMember owner err=%v, want private collaboration limit", err)
31 }
32 if err := orgs.AddMember(ctx, deps, org.ID, dave, alice, "member"); err != nil {
33 t.Fatalf("plain member add should not expand private collaboration: %v", err)
34 }
35 if err := orgs.ChangeRole(ctx, deps, org.ID, dave, "owner"); !errors.Is(err, entitlements.ErrPrivateCollaborationLimitExceeded) {
36 t.Fatalf("ChangeRole owner err=%v, want private collaboration limit", err)
37 }
38 }
39
40 func TestTeamExpansionRespectsPrivateCollaborationLimit(t *testing.T) {
41 pool, deps, alice := setup(t)
42 ctx := context.Background()
43 org, err := orgs.Create(ctx, deps, orgs.CreateParams{Slug: "acme", CreatedByUserID: alice})
44 if err != nil {
45 t.Fatalf("create org: %v", err)
46 }
47 repo := mustOrgRepo(t, pool, org.ID, "secret", "private")
48 insertDirectCollab(t, pool, repo.ID, mustUser(t, pool, "bob"))
49 insertDirectCollab(t, pool, repo.ID, mustUser(t, pool, "carol"))
50
51 team, err := orgs.CreateTeam(ctx, deps, orgs.CreateTeamParams{OrgID: org.ID, Slug: "security", CreatedByUserID: alice})
52 if err != nil {
53 t.Fatalf("create team: %v", err)
54 }
55 if err := orgs.GrantTeamRepoAccess(ctx, deps, team.ID, repo.ID, alice, "read"); err != nil {
56 t.Fatalf("empty-team private grant should not expand private collaboration: %v", err)
57 }
58 dave := mustUser(t, pool, "dave")
59 if err := orgs.AddTeamMember(ctx, deps, team.ID, dave, alice, "member"); !errors.Is(err, entitlements.ErrPrivateCollaborationLimitExceeded) {
60 t.Fatalf("AddTeamMember err=%v, want private collaboration limit", err)
61 }
62
63 team2, err := orgs.CreateTeam(ctx, deps, orgs.CreateTeamParams{OrgID: org.ID, Slug: "ops", CreatedByUserID: alice})
64 if err != nil {
65 t.Fatalf("create second team: %v", err)
66 }
67 insertTeamMemberRaw(t, pool, team2.ID, dave)
68 if err := orgs.GrantTeamRepoAccess(ctx, deps, team2.ID, repo.ID, alice, "read"); !errors.Is(err, entitlements.ErrPrivateCollaborationLimitExceeded) {
69 t.Fatalf("GrantTeamRepoAccess err=%v, want private collaboration limit", err)
70 }
71 if err := orgs.RemoveTeamMember(ctx, deps, team2.ID, dave); err != nil {
72 t.Fatalf("cleanup remove team member should remain allowed: %v", err)
73 }
74 }
75
76 func TestOwnerInvitationsRespectPrivateCollaborationLimitAtSendAndAccept(t *testing.T) {
77 pool, deps, alice := setup(t)
78 ctx := context.Background()
79 org, err := orgs.Create(ctx, deps, orgs.CreateParams{Slug: "acme", CreatedByUserID: alice})
80 if err != nil {
81 t.Fatalf("create org: %v", err)
82 }
83 repo := mustOrgRepo(t, pool, org.ID, "secret", "private")
84 insertDirectCollab(t, pool, repo.ID, mustUser(t, pool, "bob"))
85 insertDirectCollab(t, pool, repo.ID, mustUser(t, pool, "carol"))
86
87 dave := mustUser(t, pool, "dave")
88 if _, err := orgs.Invite(ctx, deps, orgs.InviteParams{
89 OrgID: org.ID,
90 InvitedByUserID: alice,
91 TargetUsername: "dave",
92 Role: "owner",
93 }); !errors.Is(err, entitlements.ErrPrivateCollaborationLimitExceeded) {
94 t.Fatalf("Invite owner err=%v, want private collaboration limit", err)
95 }
96
97 if _, err := pool.Exec(ctx, `DELETE FROM repo_collaborators WHERE repo_id = $1 AND user_id = $2`, repo.ID, dave); err != nil {
98 t.Fatalf("cleanup accidental dave collab: %v", err)
99 }
100 if _, err := pool.Exec(ctx, `DELETE FROM repo_collaborators WHERE repo_id = $1 AND user_id = (SELECT id FROM users WHERE username = 'carol')`, repo.ID); err != nil {
101 t.Fatalf("remove carol collab: %v", err)
102 }
103 res, err := orgs.Invite(ctx, deps, orgs.InviteParams{
104 OrgID: org.ID,
105 InvitedByUserID: alice,
106 TargetUsername: "dave",
107 Role: "owner",
108 })
109 if err != nil {
110 t.Fatalf("Invite owner under limit: %v", err)
111 }
112 insertDirectCollab(t, pool, repo.ID, mustUser(t, pool, "erin"))
113 if err := orgs.AcceptInvitation(ctx, deps, res.Invitation, dave); !errors.Is(err, entitlements.ErrPrivateCollaborationLimitExceeded) {
114 t.Fatalf("AcceptInvitation err=%v, want private collaboration limit", err)
115 }
116 }
117
118 func mustOrgRepo(t *testing.T, db reposdb.DBTX, orgID int64, name, visibility string) reposdb.Repo {
119 t.Helper()
120 repo, err := reposdb.New().CreateRepo(context.Background(), db, reposdb.CreateRepoParams{
121 OwnerOrgID: pgtype.Int8{Int64: orgID, Valid: true},
122 Name: name,
123 Visibility: reposdb.RepoVisibility(visibility),
124 DefaultBranch: "trunk",
125 })
126 if err != nil {
127 t.Fatalf("create org repo %s: %v", name, err)
128 }
129 return repo
130 }
131
132 func insertDirectCollab(t *testing.T, db reposdb.DBTX, repoID, userID int64) {
133 t.Helper()
134 if _, err := db.Exec(context.Background(), `INSERT INTO repo_collaborators (repo_id, user_id, role) VALUES ($1, $2, 'read')`, repoID, userID); err != nil {
135 t.Fatalf("insert direct collaborator: %v", err)
136 }
137 }
138
139 func insertTeamMemberRaw(t *testing.T, db reposdb.DBTX, teamID, userID int64) {
140 t.Helper()
141 if _, err := db.Exec(context.Background(), `INSERT INTO team_members (team_id, user_id, role) VALUES ($1, $2, 'member')`, teamID, userID); err != nil {
142 t.Fatalf("insert team member: %v", err)
143 }
144 }
145