export_test.go

  1package github
  2
  3import (
  4	"bytes"
  5	"encoding/json"
  6	"fmt"
  7	"math/rand"
  8	"net/http"
  9	"os"
 10	"testing"
 11	"time"
 12
 13	"github.com/stretchr/testify/require"
 14
 15	"github.com/MichaelMure/git-bug/bridge/core"
 16	"github.com/MichaelMure/git-bug/cache"
 17	"github.com/MichaelMure/git-bug/repository"
 18	"github.com/MichaelMure/git-bug/util/interrupt"
 19)
 20
 21const (
 22	testRepoBaseName = "git-bug-test-github-exporter"
 23)
 24
 25type testCase struct {
 26	name    string
 27	bug     *cache.BugCache
 28	numOrOp int // number of original operations
 29}
 30
 31func testCases(repo *cache.RepoCache, identity *cache.IdentityCache) ([]*testCase, error) {
 32	// simple bug
 33	simpleBug, err := repo.NewBug("simple bug", "new bug")
 34	if err != nil {
 35		return nil, err
 36	}
 37
 38	// bug with comments
 39	bugWithComments, err := repo.NewBug("bug with comments", "new bug")
 40	if err != nil {
 41		return nil, err
 42	}
 43
 44	_, err = bugWithComments.AddComment("new comment")
 45	if err != nil {
 46		return nil, err
 47	}
 48
 49	// bug with label changes
 50	bugLabelChange, err := repo.NewBug("bug label change", "new bug")
 51	if err != nil {
 52		return nil, err
 53	}
 54
 55	_, _, err = bugLabelChange.ChangeLabels([]string{"bug"}, nil)
 56	if err != nil {
 57		return nil, err
 58	}
 59
 60	_, _, err = bugLabelChange.ChangeLabels([]string{"core"}, nil)
 61	if err != nil {
 62		return nil, err
 63	}
 64
 65	_, _, err = bugLabelChange.ChangeLabels(nil, []string{"bug"})
 66	if err != nil {
 67		return nil, err
 68	}
 69
 70	// bug with comments editions
 71	bugWithCommentEditions, err := repo.NewBug("bug with comments editions", "new bug")
 72	if err != nil {
 73		return nil, err
 74	}
 75
 76	createOpHash, err := bugWithCommentEditions.Snapshot().Operations[0].Hash()
 77	if err != nil {
 78		return nil, err
 79	}
 80
 81	_, err = bugWithCommentEditions.EditComment(createOpHash, "first comment edited")
 82	if err != nil {
 83		return nil, err
 84	}
 85
 86	commentOp, err := bugWithCommentEditions.AddComment("first comment")
 87	if err != nil {
 88		return nil, err
 89	}
 90
 91	commentOpHash, err := commentOp.Hash()
 92	if err != nil {
 93		return nil, err
 94	}
 95
 96	_, err = bugWithCommentEditions.EditComment(commentOpHash, "first comment edited")
 97	if err != nil {
 98		return nil, err
 99	}
100
101	// bug status changed
102	bugStatusChanged, err := repo.NewBug("bug status changed", "new bug")
103	if err != nil {
104		return nil, err
105	}
106
107	_, err = bugStatusChanged.Close()
108	if err != nil {
109		return nil, err
110	}
111
112	_, err = bugStatusChanged.Open()
113	if err != nil {
114		return nil, err
115	}
116
117	// bug title changed
118	bugTitleEdited, err := repo.NewBug("bug title edited", "new bug")
119	if err != nil {
120		return nil, err
121	}
122
123	_, err = bugTitleEdited.SetTitle("bug title edited again")
124	if err != nil {
125		return nil, err
126	}
127
128	return []*testCase{
129		&testCase{
130			name:    "simple bug",
131			bug:     simpleBug,
132			numOrOp: 1,
133		},
134		&testCase{
135			name:    "bug with comments",
136			bug:     bugWithComments,
137			numOrOp: 2,
138		},
139		&testCase{
140			name:    "bug label change",
141			bug:     bugLabelChange,
142			numOrOp: 4,
143		},
144		&testCase{
145			name:    "bug with comment editions",
146			bug:     bugWithCommentEditions,
147			numOrOp: 4,
148		},
149		&testCase{
150			name:    "bug changed status",
151			bug:     bugStatusChanged,
152			numOrOp: 3,
153		},
154		&testCase{
155			name:    "bug title edited",
156			bug:     bugTitleEdited,
157			numOrOp: 2,
158		},
159	}, nil
160
161}
162
163func TestExporter(t *testing.T) {
164	user := os.Getenv("TEST_USER")
165	token := os.Getenv("BOT_TOKEN")
166	if token == "" {
167		t.Skip("Env var GITHUB_TOKEN_PRIVATE missing")
168	}
169
170	repo := repository.CreateTestRepo(false)
171	defer repository.CleanupTestRepos(t, repo)
172
173	backend, err := cache.NewRepoCache(repo)
174	require.NoError(t, err)
175
176	author, err := backend.NewIdentity("test identity", "test@test.org")
177	if err != nil {
178		t.Fatal(err)
179	}
180
181	err = backend.SetUserIdentity(author)
182	if err != nil {
183		t.Fatal(err)
184	}
185
186	defer backend.Close()
187	interrupt.RegisterCleaner(backend.Close)
188
189	tests, err := testCases(backend, author)
190	if err != nil {
191		t.Fatal(err)
192	}
193
194	// generate project name
195	projectName := generateRepoName()
196
197	// create repository
198	if err := createRepository(projectName, token); err != nil {
199		t.Fatal(err)
200	}
201	fmt.Println("created repository", projectName)
202
203	// delete repository before ending tests
204	defer func(t *testing.T) {
205		if err := deleteRepository(projectName, user, token); err != nil {
206			t.Fatal(err)
207		}
208		fmt.Println("deleted repository:", projectName)
209	}(t)
210
211	// initialize exporter
212	exporter := &githubExporter{}
213	err = exporter.Init(core.Configuration{
214		keyOwner:   user,
215		keyProject: projectName,
216		keyToken:   token,
217	})
218	require.NoError(t, err)
219
220	start := time.Now()
221
222	// export all bugs
223	err = exporter.ExportAll(backend, time.Time{})
224	require.NoError(t, err)
225
226	fmt.Printf("test repository exported in %f seconds\n", time.Since(start).Seconds())
227
228	repoTwo := repository.CreateTestRepo(false)
229	defer repository.CleanupTestRepos(t, repoTwo)
230
231	// create a second backend
232	backendTwo, err := cache.NewRepoCache(repoTwo)
233	require.NoError(t, err)
234
235	importer := &githubImporter{}
236	err = importer.Init(core.Configuration{
237		keyOwner:   user,
238		keyProject: projectName,
239		keyToken:   token,
240	})
241	require.NoError(t, err)
242
243	// import all exported bugs to the second backend
244	err = importer.ImportAll(backendTwo, time.Time{})
245	require.NoError(t, err)
246
247	require.Len(t, backendTwo.AllBugsIds(), len(tests))
248
249	for _, tt := range tests {
250		t.Run(tt.name, func(t *testing.T) {
251			// for each operation a SetMetadataOperation will be added
252			// so number of operations should double
253			require.Len(t, tt.bug.Snapshot().Operations, tt.numOrOp*2)
254
255			bugGithubID, ok := tt.bug.Snapshot().Operations[0].GetMetadata(keyGithubId)
256			require.True(t, ok)
257
258			importedBug, err := backendTwo.ResolveBugCreateMetadata(keyGithubId, bugGithubID)
259			require.NoError(t, err)
260
261			require.Len(t, importedBug.Snapshot().Operations, tt.numOrOp)
262
263			for _, _ = range importedBug.Snapshot().Operations {
264				// test operations or last bug state ?
265			}
266		})
267	}
268}
269
270func generateRepoName() string {
271	rand.Seed(time.Now().UnixNano())
272	var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
273	b := make([]rune, 8)
274	for i := range b {
275		b[i] = letterRunes[rand.Intn(len(letterRunes))]
276	}
277	return fmt.Sprintf("%s-%s", testRepoBaseName, string(b))
278}
279
280func createRepository(project, token string) error {
281	url := fmt.Sprintf("%s/user/repos", githubV3Url)
282
283	params := struct {
284		Name        string `json:"name"`
285		Description string `json:"description"`
286		Private     bool   `json:"private"`
287		HasIssues   bool   `json:"has_issues"`
288	}{
289		Name:        project,
290		Description: "git-bug exporter temporary test repository",
291		Private:     true,
292		HasIssues:   true,
293	}
294
295	data, err := json.Marshal(params)
296	if err != nil {
297		return err
298	}
299
300	req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
301	if err != nil {
302		return err
303	}
304
305	// need the token for private repositories
306	req.Header.Set("Authorization", fmt.Sprintf("token %s", token))
307
308	client := &http.Client{
309		Timeout: defaultTimeout,
310	}
311
312	resp, err := client.Do(req)
313	if err != nil {
314		return err
315	}
316
317	return resp.Body.Close()
318}
319
320func deleteRepository(project, owner, token string) error {
321	url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project)
322
323	req, err := http.NewRequest("DELETE", url, nil)
324	if err != nil {
325		return err
326	}
327
328	// need the token for private repositories
329	req.Header.Set("Authorization", fmt.Sprintf("token %s", token))
330
331	client := &http.Client{
332		Timeout: defaultTimeout,
333	}
334
335	resp, err := client.Do(req)
336	if err != nil {
337		return err
338	}
339
340	defer resp.Body.Close()
341
342	if resp.StatusCode != http.StatusNoContent {
343		return fmt.Errorf("error deleting repository")
344	}
345
346	return nil
347}