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