Eric Bower
·
2026-02-25
e2e_test.go
1package git
2
3import (
4 "context"
5 "log/slog"
6 "os"
7 "testing"
8 "time"
9
10 "github.com/gkampitakis/go-snaps/snaps"
11 "github.com/picosh/git-pr/fixtures"
12 "github.com/picosh/git-pr/util"
13)
14
15func TestE2E(t *testing.T) {
16 testSingleTenantE2E(t)
17 testMultiTenantE2E(t)
18}
19
20func testSingleTenantE2E(t *testing.T) {
21 t.Log("single tenant end-to-end tests")
22 dataDir := util.CreateTmpDir()
23 defer func() {
24 _ = os.RemoveAll(dataDir)
25 }()
26 suite := setupTest(dataDir, cfgSingleTenantTmpl)
27 ctx, cancel := context.WithCancel(context.Background())
28 defer cancel()
29 s := GitSshServer(ctx, suite.cfg)
30 go func() {
31 _ = s.ListenAndServe()
32 }()
33 // Hack to wait for startup
34 time.Sleep(time.Millisecond * 100)
35
36 suite.userKey.MustCmd(suite.patch, "register")
37 suite.adminKey.MustCmd(suite.patch, "register")
38
39 t.Log("User cannot create repo")
40 _, err := suite.userKey.Cmd(suite.patch, "pr create test")
41 if err == nil {
42 t.Fatal("user should not be able to create a PR")
43 }
44 suite.adminKey.MustCmd(suite.patch, "pr create test")
45
46 t.Log("User should be able to create a patch")
47 suite.userKey.MustCmd(suite.patch, "pr create test")
48
49 t.Log("Snapshot test ls command")
50 actual, err := suite.userKey.Cmd(nil, "pr ls")
51 bail(err)
52 snaps.MatchSnapshot(t, actual)
53}
54
55func testMultiTenantE2E(t *testing.T) {
56 t.Log("multi tenant end-to-end tests")
57 dataDir := util.CreateTmpDir()
58 defer func() {
59 _ = os.RemoveAll(dataDir)
60 }()
61 suite := setupTest(dataDir, cfgMultiTenantTmpl)
62 ctx, cancel := context.WithCancel(context.Background())
63 defer cancel()
64 s := GitSshServer(ctx, suite.cfg)
65 go func() {
66 _ = s.ListenAndServe()
67 }()
68
69 time.Sleep(time.Millisecond * 100)
70
71 suite.userKey.MustCmd(suite.patch, "register")
72 suite.adminKey.MustCmd(suite.patch, "register")
73
74 t.Log("Admin should be able to create a repo")
75 suite.adminKey.MustCmd(nil, "repo create test")
76
77 t.Log("Accepted pr")
78 suite.userKey.MustCmd(suite.patch, "pr create admin/test")
79 suite.userKey.MustCmd(nil, "pr edit 1 Accepted patch")
80 _, err := suite.userKey.Cmd(nil, "pr accept 1")
81 if err == nil {
82 t.Fatal("contrib should not be able to accept their own PR")
83 }
84 suite.adminKey.MustCmd(nil, "pr accept 1")
85
86 t.Log("Closed pr (admin)")
87 suite.userKey.MustCmd(suite.patch, "pr create test")
88 suite.userKey.MustCmd(nil, "pr edit 2 Closed patch (admin)")
89 suite.adminKey.MustCmd(nil, "pr close 2")
90
91 t.Log("Closed pr (contributor)")
92 suite.userKey.MustCmd(suite.patch, "pr create test")
93 suite.userKey.MustCmd(nil, "pr edit 3 Closed patch (contributor)")
94 suite.userKey.MustCmd(nil, "pr close 3")
95
96 t.Log("Reviewed pr")
97 suite.userKey.MustCmd(suite.patch, "pr create test")
98 suite.userKey.MustCmd(nil, "pr edit 4 Reviewed patch")
99 suite.adminKey.MustCmd(suite.otherPatch, "pr add --review 4")
100
101 t.Log("Accepted pr with review")
102 suite.userKey.MustCmd(suite.patch, "pr create test")
103 suite.userKey.MustCmd(nil, "pr edit 5 Accepted patch with review")
104 suite.adminKey.MustCmd(suite.otherPatch, "pr add --accept 5")
105
106 t.Log("Closed pr with review")
107 suite.userKey.MustCmd(suite.patch, "pr create test")
108 suite.userKey.MustCmd(nil, "pr edit 6 Closed patch with review")
109 suite.adminKey.MustCmd(suite.otherPatch, "pr add --close 6")
110
111 t.Log("Create pr with user repo and user can accept")
112 suite.userKey.MustCmd(nil, "repo create ai")
113 suite.adminKey.MustCmd(suite.patch, "pr create contributor/ai")
114 suite.userKey.MustCmd(suite.otherPatch, "pr accept 7")
115
116 t.Log("Create pr with admin repo and admin can accept")
117 suite.adminKey.MustCmd(nil, "repo create ai")
118 suite.userKey.MustCmd(suite.patch, "pr create admin/ai")
119 suite.adminKey.MustCmd(suite.otherPatch, "pr add --accept 8")
120
121 t.Log("Create pr with admin repo and user can accept with comment")
122 suite.adminKey.MustCmd(nil, "repo create ai")
123 suite.userKey.MustCmd(suite.patch, "pr create admin/ai")
124 suite.adminKey.MustCmd([]byte("nice work"), "pr accept --comment 9")
125
126 t.Log("Create pr with default `bin` repo")
127 actual, err := suite.userKey.Cmd(suite.patch, "pr create")
128 bail(err)
129 snaps.MatchSnapshot(t, actual)
130
131 t.Log("Snapshot test ls command")
132 actual, err = suite.userKey.Cmd(nil, "pr ls")
133 bail(err)
134 snaps.MatchSnapshot(t, actual)
135
136 t.Log("Snapshot test logs command")
137 actual, err = suite.userKey.Cmd(nil, "logs --repo admin/ai")
138 bail(err)
139 snaps.MatchSnapshot(t, actual)
140
141 t.Log("Delete repo")
142 suite.userKey.MustCmd(nil, "repo rm --write ai")
143
144 t.Log("Snapshot test ls command with ai prs removed")
145 actual, err = suite.userKey.Cmd(nil, "pr ls")
146 bail(err)
147 snaps.MatchSnapshot(t, actual)
148}
149
150type TestSuite struct {
151 cfg *GitCfg
152 userKey util.UserSSH
153 adminKey util.UserSSH
154 patch []byte
155 otherPatch []byte
156}
157
158func setupTest(dataDir string, cfgTmpl string) TestSuite {
159 opts := &slog.HandlerOptions{
160 AddSource: true,
161 }
162 logger := slog.New(
163 slog.NewTextHandler(os.Stdout, opts),
164 )
165
166 adminKey, userKey := util.GenerateKeys()
167 cfgPath := util.CreateCfgFile(dataDir, cfgTmpl, adminKey)
168 LoadConfigFile(cfgPath, logger)
169 cfg := NewGitCfg(logger)
170
171 // so outputs dont show dates
172 cfg.TimeFormat = ""
173
174 patch, err := fixtures.Fixtures.ReadFile("single.patch")
175 if err != nil {
176 panic(err)
177 }
178 otherPatch, err := fixtures.Fixtures.ReadFile("with-cover.patch")
179 if err != nil {
180 panic(err)
181 }
182
183 return TestSuite{cfg, userKey, adminKey, patch, otherPatch}
184}
185
186var cfgSingleTenantTmpl = `
187url = "localhost"
188data_dir = %q
189admins = [%q]
190time_format = "01/02/2006 15:04:05 07:00"
191create_repo = "admin"`
192
193var cfgMultiTenantTmpl = `
194url = "localhost"
195data_dir = %q
196admins = [%q]
197time_format = "01/02/2006 15:04:05 07:00"
198create_repo = "user"`