repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

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"`