repos / git-pr

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

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