export_test.go

  1package gitlab
  2
  3import (
  4	"context"
  5	"fmt"
  6	"math/rand"
  7	"os"
  8	"strconv"
  9	"testing"
 10	"time"
 11
 12	"github.com/xanzy/go-gitlab"
 13
 14	"github.com/stretchr/testify/require"
 15
 16	"github.com/MichaelMure/git-bug/bridge/core"
 17	"github.com/MichaelMure/git-bug/bug"
 18	"github.com/MichaelMure/git-bug/cache"
 19	"github.com/MichaelMure/git-bug/repository"
 20	"github.com/MichaelMure/git-bug/util/interrupt"
 21)
 22
 23const (
 24	testRepoBaseName = "git-bug-test-gitlab-exporter"
 25)
 26
 27type testCase struct {
 28	name    string
 29	bug     *cache.BugCache
 30	numOrOp int // number of original operations
 31}
 32
 33func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCache) []*testCase {
 34	// simple bug
 35	simpleBug, _, err := repo.NewBug("simple bug", "new bug")
 36	require.NoError(t, err)
 37
 38	// bug with comments
 39	bugWithComments, _, err := repo.NewBug("bug with comments", "new bug")
 40	require.NoError(t, err)
 41
 42	_, err = bugWithComments.AddComment("new comment")
 43	require.NoError(t, err)
 44
 45	// bug with label changes
 46	bugLabelChange, _, err := repo.NewBug("bug label change", "new bug")
 47	require.NoError(t, err)
 48
 49	_, _, err = bugLabelChange.ChangeLabels([]string{"bug"}, nil)
 50	require.NoError(t, err)
 51
 52	_, _, err = bugLabelChange.ChangeLabels([]string{"core"}, nil)
 53	require.NoError(t, err)
 54
 55	_, _, err = bugLabelChange.ChangeLabels(nil, []string{"bug"})
 56	require.NoError(t, err)
 57
 58	// bug with comments editions
 59	bugWithCommentEditions, createOp, err := repo.NewBug("bug with comments editions", "new bug")
 60	require.NoError(t, err)
 61
 62	_, err = bugWithCommentEditions.EditComment(createOp.Id(), "first comment edited")
 63	require.NoError(t, err)
 64
 65	commentOp, err := bugWithCommentEditions.AddComment("first comment")
 66	require.NoError(t, err)
 67
 68	_, err = bugWithCommentEditions.EditComment(commentOp.Id(), "first comment edited")
 69	require.NoError(t, err)
 70
 71	// bug status changed
 72	bugStatusChanged, _, err := repo.NewBug("bug status changed", "new bug")
 73	require.NoError(t, err)
 74
 75	_, err = bugStatusChanged.Close()
 76	require.NoError(t, err)
 77
 78	_, err = bugStatusChanged.Open()
 79	require.NoError(t, err)
 80
 81	// bug title changed
 82	bugTitleEdited, _, err := repo.NewBug("bug title edited", "new bug")
 83	require.NoError(t, err)
 84
 85	_, err = bugTitleEdited.SetTitle("bug title edited again")
 86	require.NoError(t, err)
 87
 88	return []*testCase{
 89		&testCase{
 90			name:    "simple bug",
 91			bug:     simpleBug,
 92			numOrOp: 1,
 93		},
 94		&testCase{
 95			name:    "bug with comments",
 96			bug:     bugWithComments,
 97			numOrOp: 2,
 98		},
 99		&testCase{
100			name:    "bug label change",
101			bug:     bugLabelChange,
102			numOrOp: 4,
103		},
104		&testCase{
105			name:    "bug with comment editions",
106			bug:     bugWithCommentEditions,
107			numOrOp: 4,
108		},
109		&testCase{
110			name:    "bug changed status",
111			bug:     bugStatusChanged,
112			numOrOp: 3,
113		},
114		&testCase{
115			name:    "bug title edited",
116			bug:     bugTitleEdited,
117			numOrOp: 2,
118		},
119	}
120}
121
122func TestPushPull(t *testing.T) {
123	// token must have 'repo' and 'delete_repo' scopes
124	token := os.Getenv("GITLAB_API_TOKEN")
125	if token == "" {
126		t.Skip("Env var GITLAB_API_TOKEN missing")
127	}
128
129	// create repo backend
130	repo := repository.CreateTestRepo(false)
131	defer repository.CleanupTestRepos(t, repo)
132
133	backend, err := cache.NewRepoCache(repo)
134	require.NoError(t, err)
135
136	// set author identity
137	author, err := backend.NewIdentity("test identity", "test@test.org")
138	require.NoError(t, err)
139
140	err = backend.SetUserIdentity(author)
141	require.NoError(t, err)
142
143	defer backend.Close()
144	interrupt.RegisterCleaner(backend.Close)
145
146	tests := testCases(t, backend, author)
147
148	// generate project name
149	projectName := generateRepoName()
150
151	// create target Gitlab repository
152	projectID, err := createRepository(context.TODO(), projectName, token)
153	require.NoError(t, err)
154
155	fmt.Println("created repository", projectName)
156
157	// Make sure to remove the Gitlab repository when the test end
158	defer func(t *testing.T) {
159		if err := deleteRepository(context.TODO(), projectID, token); err != nil {
160			t.Fatal(err)
161		}
162		fmt.Println("deleted repository:", projectName)
163	}(t)
164
165	interrupt.RegisterCleaner(func() error {
166		return deleteRepository(context.TODO(), projectID, token)
167	})
168
169	// initialize exporter
170	exporter := &gitlabExporter{}
171	err = exporter.Init(core.Configuration{
172		keyProjectID: strconv.Itoa(projectID),
173		keyToken:     token,
174	})
175	require.NoError(t, err)
176
177	start := time.Now()
178
179	// export all bugs
180	events, err := exporter.ExportAll(backend, time.Time{})
181	require.NoError(t, err)
182
183	for result := range events {
184		require.NoError(t, result.Err)
185	}
186	require.NoError(t, err)
187
188	fmt.Printf("test repository exported in %f seconds\n", time.Since(start).Seconds())
189
190	repoTwo := repository.CreateTestRepo(false)
191	defer repository.CleanupTestRepos(t, repoTwo)
192
193	// create a second backend
194	backendTwo, err := cache.NewRepoCache(repoTwo)
195	require.NoError(t, err)
196
197	importer := &gitlabImporter{}
198	err = importer.Init(core.Configuration{
199		keyProjectID: strconv.Itoa(projectID),
200		keyToken:     token,
201	})
202	require.NoError(t, err)
203
204	// import all exported bugs to the second backend
205	err = importer.ImportAll(backendTwo, time.Time{})
206	require.NoError(t, err)
207
208	require.Len(t, backendTwo.AllBugsIds(), len(tests))
209
210	for _, tt := range tests {
211		t.Run(tt.name, func(t *testing.T) {
212			// for each operation a SetMetadataOperation will be added
213			// so number of operations should double
214			require.Len(t, tt.bug.Snapshot().Operations, tt.numOrOp*2)
215
216			// verify operation have correct metadata
217			for _, op := range tt.bug.Snapshot().Operations {
218				// Check if the originals operations (*not* SetMetadata) are tagged properly
219				if _, ok := op.(*bug.SetMetadataOperation); !ok {
220					_, haveIDMetadata := op.GetMetadata(keyGitlabId)
221					require.True(t, haveIDMetadata)
222
223					_, haveURLMetada := op.GetMetadata(keyGitlabUrl)
224					require.True(t, haveURLMetada)
225				}
226			}
227
228			// get bug gitlab ID
229			bugGitlabID, ok := tt.bug.Snapshot().GetCreateMetadata(keyGitlabId)
230			require.True(t, ok)
231
232			// retrieve bug from backendTwo
233			importedBug, err := backendTwo.ResolveBugCreateMetadata(keyGitlabId, bugGitlabID)
234			require.NoError(t, err)
235
236			// verify bug have same number of original operations
237			require.Len(t, importedBug.Snapshot().Operations, tt.numOrOp)
238
239			// verify bugs are taged with origin=gitlab
240			issueOrigin, ok := importedBug.Snapshot().GetCreateMetadata(core.KeyOrigin)
241			require.True(t, ok)
242			require.Equal(t, issueOrigin, target)
243
244			//TODO: maybe more tests to ensure bug final state
245		})
246	}
247}
248
249func generateRepoName() string {
250	rand.Seed(time.Now().UnixNano())
251	var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
252	b := make([]rune, 8)
253	for i := range b {
254		b[i] = letterRunes[rand.Intn(len(letterRunes))]
255	}
256	return fmt.Sprintf("%s-%s", testRepoBaseName, string(b))
257}
258
259// create repository need a token with scope 'repo'
260func createRepository(ctx context.Context, name, token string) (int, error) {
261	client := buildClient(token)
262	project, _, err := client.Projects.CreateProject(
263		&gitlab.CreateProjectOptions{
264			Name: gitlab.String(name),
265		},
266		gitlab.WithContext(ctx),
267	)
268
269	return project.ID, err
270}
271
272// delete repository need a token with scope 'delete_repo'
273func deleteRepository(ctx context.Context, project int, token string) error {
274	client := buildClient(token)
275	_, err := client.Projects.DeleteProject(project, gitlab.WithContext(ctx))
276	return err
277}