| 1 | // SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | |
| 3 | package orgs_test |
| 4 | |
| 5 | import ( |
| 6 | "context" |
| 7 | "strconv" |
| 8 | "testing" |
| 9 | |
| 10 | "github.com/jackc/pgx/v5/pgxpool" |
| 11 | |
| 12 | "github.com/tenseleyFlow/shithub/internal/orgs" |
| 13 | "github.com/tenseleyFlow/shithub/internal/worker" |
| 14 | ) |
| 15 | |
| 16 | func TestCreateEnqueuesBillingSeatSync(t *testing.T) { |
| 17 | t.Parallel() |
| 18 | pool, deps, alice := setup(t) |
| 19 | |
| 20 | org, err := orgs.Create(context.Background(), deps, orgs.CreateParams{ |
| 21 | Slug: "acme", DisplayName: "Acme Inc", CreatedByUserID: alice, |
| 22 | }) |
| 23 | if err != nil { |
| 24 | t.Fatalf("create org: %v", err) |
| 25 | } |
| 26 | if got := countBillingSeatSyncJobs(t, pool, org.ID); got != 1 { |
| 27 | t.Fatalf("billing seat sync jobs=%d, want 1", got) |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | func TestMemberChangesEnqueueBillingSeatSync(t *testing.T) { |
| 32 | t.Parallel() |
| 33 | pool, deps, alice := setup(t) |
| 34 | bob := mustUser(t, pool, "bob") |
| 35 | |
| 36 | org, err := orgs.Create(context.Background(), deps, orgs.CreateParams{ |
| 37 | Slug: "acme", DisplayName: "Acme Inc", CreatedByUserID: alice, |
| 38 | }) |
| 39 | if err != nil { |
| 40 | t.Fatalf("create org: %v", err) |
| 41 | } |
| 42 | if err := orgs.AddMember(context.Background(), deps, org.ID, bob, alice, "member"); err != nil { |
| 43 | t.Fatalf("AddMember: %v", err) |
| 44 | } |
| 45 | if got := countBillingSeatSyncJobs(t, pool, org.ID); got != 2 { |
| 46 | t.Fatalf("billing seat sync jobs after add=%d, want 2", got) |
| 47 | } |
| 48 | if err := orgs.RemoveMember(context.Background(), deps, org.ID, bob); err != nil { |
| 49 | t.Fatalf("RemoveMember: %v", err) |
| 50 | } |
| 51 | if got := countBillingSeatSyncJobs(t, pool, org.ID); got != 3 { |
| 52 | t.Fatalf("billing seat sync jobs after remove=%d, want 3", got) |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | func TestAcceptInvitationEnqueuesBillingSeatSync(t *testing.T) { |
| 57 | t.Parallel() |
| 58 | pool, deps, alice := setup(t) |
| 59 | bob := mustUser(t, pool, "bob") |
| 60 | |
| 61 | org, err := orgs.Create(context.Background(), deps, orgs.CreateParams{ |
| 62 | Slug: "acme", DisplayName: "Acme Inc", CreatedByUserID: alice, |
| 63 | }) |
| 64 | if err != nil { |
| 65 | t.Fatalf("create org: %v", err) |
| 66 | } |
| 67 | res, err := orgs.Invite(context.Background(), deps, orgs.InviteParams{ |
| 68 | OrgID: org.ID, InvitedByUserID: alice, |
| 69 | TargetUsername: "bob", Role: "member", |
| 70 | }) |
| 71 | if err != nil { |
| 72 | t.Fatalf("Invite: %v", err) |
| 73 | } |
| 74 | if err := orgs.AcceptInvitation(context.Background(), deps, res.Invitation, bob); err != nil { |
| 75 | t.Fatalf("AcceptInvitation: %v", err) |
| 76 | } |
| 77 | if got := countBillingSeatSyncJobs(t, pool, org.ID); got != 2 { |
| 78 | t.Fatalf("billing seat sync jobs after accept=%d, want 2", got) |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | func countBillingSeatSyncJobs(t *testing.T, pool *pgxpool.Pool, orgID int64) int { |
| 83 | t.Helper() |
| 84 | var jobs int |
| 85 | if err := pool.QueryRow(context.Background(), |
| 86 | `SELECT count(*) FROM jobs WHERE kind = $1 AND payload->>'org_id' = $2`, |
| 87 | worker.KindOrgBillingSeatSync, strconv.FormatInt(orgID, 10), |
| 88 | ).Scan(&jobs); err != nil { |
| 89 | t.Fatalf("query billing seat sync jobs: %v", err) |
| 90 | } |
| 91 | return jobs |
| 92 | } |
| 93 |