2019-06-09 00:14:59 +03:00
|
|
|
package github
|
|
|
|
|
2019-06-21 02:09:05 +03:00
|
|
|
import (
|
2019-06-23 01:52:30 +03:00
|
|
|
"bytes"
|
2019-08-13 20:51:14 +03:00
|
|
|
"context"
|
2019-06-23 01:52:30 +03:00
|
|
|
"encoding/json"
|
2019-06-21 02:09:05 +03:00
|
|
|
"fmt"
|
2019-06-23 01:52:30 +03:00
|
|
|
"math/rand"
|
|
|
|
"net/http"
|
2019-06-21 02:09:05 +03:00
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2019-06-23 01:52:30 +03:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2019-06-21 02:09:05 +03:00
|
|
|
"github.com/MichaelMure/git-bug/bridge/core"
|
2019-12-08 23:15:06 +03:00
|
|
|
"github.com/MichaelMure/git-bug/bridge/core/auth"
|
2019-06-23 16:18:56 +03:00
|
|
|
"github.com/MichaelMure/git-bug/bug"
|
2019-06-21 02:09:05 +03:00
|
|
|
"github.com/MichaelMure/git-bug/cache"
|
|
|
|
"github.com/MichaelMure/git-bug/repository"
|
|
|
|
"github.com/MichaelMure/git-bug/util/interrupt"
|
|
|
|
)
|
2019-06-09 00:14:59 +03:00
|
|
|
|
2019-06-23 01:52:30 +03:00
|
|
|
const (
|
|
|
|
testRepoBaseName = "git-bug-test-github-exporter"
|
|
|
|
)
|
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
type testCase struct {
|
|
|
|
name string
|
|
|
|
bug *cache.BugCache
|
|
|
|
numOrOp int // number of original operations
|
|
|
|
}
|
2019-06-23 01:52:30 +03:00
|
|
|
|
2019-12-08 23:15:06 +03:00
|
|
|
func testCases(t *testing.T, repo *cache.RepoCache) []*testCase {
|
2019-06-23 01:52:30 +03:00
|
|
|
// simple bug
|
2019-06-23 20:20:10 +03:00
|
|
|
simpleBug, _, err := repo.NewBug("simple bug", "new bug")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 01:52:30 +03:00
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
// bug with comments
|
2019-06-23 20:20:10 +03:00
|
|
|
bugWithComments, _, err := repo.NewBug("bug with comments", "new bug")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 01:52:30 +03:00
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
_, err = bugWithComments.AddComment("new comment")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 01:52:30 +03:00
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
// bug with label changes
|
2019-06-23 20:20:10 +03:00
|
|
|
bugLabelChange, _, err := repo.NewBug("bug label change", "new bug")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 01:52:30 +03:00
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
_, _, err = bugLabelChange.ChangeLabels([]string{"bug"}, nil)
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
_, _, err = bugLabelChange.ChangeLabels([]string{"core"}, nil)
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
_, _, err = bugLabelChange.ChangeLabels(nil, []string{"bug"})
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
2019-08-06 00:27:32 +03:00
|
|
|
_, _, err = bugLabelChange.ChangeLabels([]string{"InVaLiD"}, nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
_, _, err = bugLabelChange.ChangeLabels([]string{"bUG"}, nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
// bug with comments editions
|
2019-06-23 20:20:10 +03:00
|
|
|
bugWithCommentEditions, createOp, err := repo.NewBug("bug with comments editions", "new bug")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
2019-08-12 17:12:14 +03:00
|
|
|
_, err = bugWithCommentEditions.EditComment(createOp.Id(), "first comment edited")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
commentOp, err := bugWithCommentEditions.AddComment("first comment")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
2019-08-12 17:12:14 +03:00
|
|
|
_, err = bugWithCommentEditions.EditComment(commentOp.Id(), "first comment edited")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
// bug status changed
|
2019-06-23 20:20:10 +03:00
|
|
|
bugStatusChanged, _, err := repo.NewBug("bug status changed", "new bug")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
_, err = bugStatusChanged.Close()
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
_, err = bugStatusChanged.Open()
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
// bug title changed
|
2019-06-23 20:20:10 +03:00
|
|
|
bugTitleEdited, _, err := repo.NewBug("bug title edited", "new bug")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
_, err = bugTitleEdited.SetTitle("bug title edited again")
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
return []*testCase{
|
2019-12-08 23:15:06 +03:00
|
|
|
{
|
2019-06-23 14:50:45 +03:00
|
|
|
name: "simple bug",
|
|
|
|
bug: simpleBug,
|
|
|
|
numOrOp: 1,
|
|
|
|
},
|
2019-12-08 23:15:06 +03:00
|
|
|
{
|
2019-06-23 14:50:45 +03:00
|
|
|
name: "bug with comments",
|
|
|
|
bug: bugWithComments,
|
|
|
|
numOrOp: 2,
|
|
|
|
},
|
2019-12-08 23:15:06 +03:00
|
|
|
{
|
2019-06-23 14:50:45 +03:00
|
|
|
name: "bug label change",
|
|
|
|
bug: bugLabelChange,
|
2019-08-06 00:27:32 +03:00
|
|
|
numOrOp: 6,
|
2019-06-23 14:50:45 +03:00
|
|
|
},
|
2019-12-08 23:15:06 +03:00
|
|
|
{
|
2019-06-23 14:50:45 +03:00
|
|
|
name: "bug with comment editions",
|
|
|
|
bug: bugWithCommentEditions,
|
|
|
|
numOrOp: 4,
|
|
|
|
},
|
2019-12-08 23:15:06 +03:00
|
|
|
{
|
2019-06-23 14:50:45 +03:00
|
|
|
name: "bug changed status",
|
|
|
|
bug: bugStatusChanged,
|
|
|
|
numOrOp: 3,
|
|
|
|
},
|
2019-12-08 23:15:06 +03:00
|
|
|
{
|
2019-06-23 14:50:45 +03:00
|
|
|
name: "bug title edited",
|
|
|
|
bug: bugTitleEdited,
|
|
|
|
numOrOp: 2,
|
|
|
|
},
|
2019-07-05 19:32:51 +03:00
|
|
|
}
|
2019-06-23 01:52:30 +03:00
|
|
|
}
|
|
|
|
|
2020-11-09 01:56:32 +03:00
|
|
|
func TestGithubPushPull(t *testing.T) {
|
2019-06-23 18:54:38 +03:00
|
|
|
// repo owner
|
2019-12-08 23:15:06 +03:00
|
|
|
envUser := os.Getenv("GITHUB_TEST_USER")
|
2019-06-23 18:54:38 +03:00
|
|
|
|
|
|
|
// token must have 'repo' and 'delete_repo' scopes
|
2019-12-08 23:15:06 +03:00
|
|
|
envToken := os.Getenv("GITHUB_TOKEN_ADMIN")
|
|
|
|
if envToken == "" {
|
2019-06-23 16:18:56 +03:00
|
|
|
t.Skip("Env var GITHUB_TOKEN_ADMIN missing")
|
2019-06-21 02:09:05 +03:00
|
|
|
}
|
|
|
|
|
2019-06-23 18:54:38 +03:00
|
|
|
// create repo backend
|
2020-10-04 20:56:16 +03:00
|
|
|
repo := repository.CreateGoGitTestRepo(false)
|
2020-06-23 19:02:54 +03:00
|
|
|
defer repository.CleanupTestRepos(repo)
|
2019-06-21 02:09:05 +03:00
|
|
|
|
|
|
|
backend, err := cache.NewRepoCache(repo)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-06-23 18:54:38 +03:00
|
|
|
// set author identity
|
2020-02-04 02:25:27 +03:00
|
|
|
login := "identity-test"
|
2019-06-23 14:50:45 +03:00
|
|
|
author, err := backend.NewIdentity("test identity", "test@test.org")
|
2019-06-25 02:33:48 +03:00
|
|
|
require.NoError(t, err)
|
2020-02-04 02:25:27 +03:00
|
|
|
author.SetMetadata(metaKeyGithubLogin, login)
|
2020-02-09 00:05:13 +03:00
|
|
|
err = author.Commit()
|
|
|
|
require.NoError(t, err)
|
2019-06-23 01:52:30 +03:00
|
|
|
|
|
|
|
err = backend.SetUserIdentity(author)
|
2019-06-25 02:33:48 +03:00
|
|
|
require.NoError(t, err)
|
2019-06-23 01:52:30 +03:00
|
|
|
|
2019-06-21 02:09:05 +03:00
|
|
|
defer backend.Close()
|
|
|
|
interrupt.RegisterCleaner(backend.Close)
|
|
|
|
|
2020-02-12 20:32:01 +03:00
|
|
|
token := auth.NewToken(target, envToken)
|
2020-02-08 19:35:35 +03:00
|
|
|
token.SetMetadata(auth.MetaKeyLogin, login)
|
|
|
|
err = auth.Store(repo, token)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-12-08 23:15:06 +03:00
|
|
|
tests := testCases(t, backend)
|
2019-06-21 02:09:05 +03:00
|
|
|
|
2019-06-23 01:52:30 +03:00
|
|
|
// generate project name
|
|
|
|
projectName := generateRepoName()
|
2019-06-23 14:50:45 +03:00
|
|
|
|
2019-06-23 18:54:38 +03:00
|
|
|
// create target Github repository
|
2019-12-08 23:15:06 +03:00
|
|
|
err = createRepository(projectName, envToken)
|
2019-06-25 02:33:48 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
fmt.Println("created repository", projectName)
|
2019-06-23 01:52:30 +03:00
|
|
|
|
2019-06-23 18:54:38 +03:00
|
|
|
// Make sure to remove the Github repository when the test end
|
2019-06-23 01:52:30 +03:00
|
|
|
defer func(t *testing.T) {
|
2019-12-08 23:15:06 +03:00
|
|
|
if err := deleteRepository(projectName, envUser, envToken); err != nil {
|
2019-06-23 01:52:30 +03:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2019-06-23 14:50:45 +03:00
|
|
|
fmt.Println("deleted repository:", projectName)
|
2019-06-23 01:52:30 +03:00
|
|
|
}(t)
|
|
|
|
|
2019-06-23 20:20:10 +03:00
|
|
|
interrupt.RegisterCleaner(func() error {
|
2019-12-08 23:15:06 +03:00
|
|
|
return deleteRepository(projectName, envUser, envToken)
|
2019-06-23 20:20:10 +03:00
|
|
|
})
|
|
|
|
|
2020-02-15 17:39:49 +03:00
|
|
|
ctx := context.Background()
|
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
// initialize exporter
|
2019-06-21 02:09:05 +03:00
|
|
|
exporter := &githubExporter{}
|
2020-02-15 17:39:49 +03:00
|
|
|
err = exporter.Init(ctx, backend, core.Configuration{
|
2020-02-23 16:05:03 +03:00
|
|
|
confKeyOwner: envUser,
|
|
|
|
confKeyProject: projectName,
|
|
|
|
confKeyDefaultLogin: login,
|
2019-06-21 02:09:05 +03:00
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
start := time.Now()
|
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
// export all bugs
|
2019-08-13 20:51:14 +03:00
|
|
|
exportEvents, err := exporter.ExportAll(ctx, backend, time.Time{})
|
2019-07-05 19:32:51 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
for result := range exportEvents {
|
2019-06-25 02:33:48 +03:00
|
|
|
require.NoError(t, result.Err)
|
|
|
|
}
|
2019-06-21 02:09:05 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
fmt.Printf("test repository exported in %f seconds\n", time.Since(start).Seconds())
|
2019-06-23 14:50:45 +03:00
|
|
|
|
2020-10-04 20:56:16 +03:00
|
|
|
repoTwo := repository.CreateGoGitTestRepo(false)
|
2020-06-23 19:02:54 +03:00
|
|
|
defer repository.CleanupTestRepos(repoTwo)
|
2019-06-23 14:50:45 +03:00
|
|
|
|
|
|
|
// create a second backend
|
|
|
|
backendTwo, err := cache.NewRepoCache(repoTwo)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
importer := &githubImporter{}
|
2020-02-15 17:39:49 +03:00
|
|
|
err = importer.Init(ctx, backend, core.Configuration{
|
2020-02-23 16:05:03 +03:00
|
|
|
confKeyOwner: envUser,
|
|
|
|
confKeyProject: projectName,
|
|
|
|
confKeyDefaultLogin: login,
|
2019-06-23 14:50:45 +03:00
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// import all exported bugs to the second backend
|
2019-08-13 20:51:14 +03:00
|
|
|
importEvents, err := importer.ImportAll(ctx, backendTwo, time.Time{})
|
2019-06-23 14:50:45 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
for result := range importEvents {
|
|
|
|
require.NoError(t, result.Err)
|
|
|
|
}
|
|
|
|
|
2019-06-23 14:50:45 +03:00
|
|
|
require.Len(t, backendTwo.AllBugsIds(), len(tests))
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
// for each operation a SetMetadataOperation will be added
|
|
|
|
// so number of operations should double
|
|
|
|
require.Len(t, tt.bug.Snapshot().Operations, tt.numOrOp*2)
|
|
|
|
|
2019-07-05 19:32:51 +03:00
|
|
|
// verify operation have correct metadata
|
2019-06-23 16:18:56 +03:00
|
|
|
for _, op := range tt.bug.Snapshot().Operations {
|
2019-06-23 18:54:38 +03:00
|
|
|
// Check if the originals operations (*not* SetMetadata) are tagged properly
|
2019-06-23 16:18:56 +03:00
|
|
|
if _, ok := op.(*bug.SetMetadataOperation); !ok {
|
2019-11-10 19:48:13 +03:00
|
|
|
_, haveIDMetadata := op.GetMetadata(metaKeyGithubId)
|
2019-06-23 16:18:56 +03:00
|
|
|
require.True(t, haveIDMetadata)
|
|
|
|
|
2019-11-10 19:48:13 +03:00
|
|
|
_, haveURLMetada := op.GetMetadata(metaKeyGithubUrl)
|
2019-06-23 16:18:56 +03:00
|
|
|
require.True(t, haveURLMetada)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-23 18:54:38 +03:00
|
|
|
// get bug github ID
|
2019-11-10 19:48:13 +03:00
|
|
|
bugGithubID, ok := tt.bug.Snapshot().GetCreateMetadata(metaKeyGithubId)
|
2019-06-23 14:50:45 +03:00
|
|
|
require.True(t, ok)
|
|
|
|
|
2019-07-05 19:32:51 +03:00
|
|
|
// retrieve bug from backendTwo
|
2019-11-10 19:48:13 +03:00
|
|
|
importedBug, err := backendTwo.ResolveBugCreateMetadata(metaKeyGithubId, bugGithubID)
|
2019-06-23 14:50:45 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-06-23 18:54:38 +03:00
|
|
|
// verify bug have same number of original operations
|
2019-06-23 14:50:45 +03:00
|
|
|
require.Len(t, importedBug.Snapshot().Operations, tt.numOrOp)
|
|
|
|
|
2020-02-08 19:35:35 +03:00
|
|
|
// verify bugs are tagged with origin=github
|
2019-11-10 19:48:13 +03:00
|
|
|
issueOrigin, ok := importedBug.Snapshot().GetCreateMetadata(core.MetaKeyOrigin)
|
2019-06-23 16:18:56 +03:00
|
|
|
require.True(t, ok)
|
|
|
|
require.Equal(t, issueOrigin, target)
|
|
|
|
|
2021-04-09 14:07:45 +03:00
|
|
|
// TODO: maybe more tests to ensure bug final state
|
2019-06-23 01:52:30 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateRepoName() string {
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
|
|
b := make([]rune, 8)
|
|
|
|
for i := range b {
|
|
|
|
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s-%s", testRepoBaseName, string(b))
|
|
|
|
}
|
2019-06-21 02:09:05 +03:00
|
|
|
|
2019-06-23 18:54:38 +03:00
|
|
|
// create repository need a token with scope 'repo'
|
2019-06-23 01:52:30 +03:00
|
|
|
func createRepository(project, token string) error {
|
2019-06-23 20:20:10 +03:00
|
|
|
// This function use the V3 Github API because repository creation is not supported yet on the V4 API.
|
2019-06-23 01:52:30 +03:00
|
|
|
url := fmt.Sprintf("%s/user/repos", githubV3Url)
|
2019-06-21 02:09:05 +03:00
|
|
|
|
2019-06-23 01:52:30 +03:00
|
|
|
params := struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
Private bool `json:"private"`
|
|
|
|
HasIssues bool `json:"has_issues"`
|
|
|
|
}{
|
|
|
|
Name: project,
|
|
|
|
Description: "git-bug exporter temporary test repository",
|
|
|
|
Private: true,
|
|
|
|
HasIssues: true,
|
2019-06-21 02:09:05 +03:00
|
|
|
}
|
2019-06-23 01:52:30 +03:00
|
|
|
|
|
|
|
data, err := json.Marshal(params)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// need the token for private repositories
|
|
|
|
req.Header.Set("Authorization", fmt.Sprintf("token %s", token))
|
|
|
|
|
|
|
|
client := &http.Client{
|
|
|
|
Timeout: defaultTimeout,
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := client.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp.Body.Close()
|
2019-06-21 02:09:05 +03:00
|
|
|
}
|
|
|
|
|
2019-06-23 18:54:38 +03:00
|
|
|
// delete repository need a token with scope 'delete_repo'
|
2019-06-23 01:52:30 +03:00
|
|
|
func deleteRepository(project, owner, token string) error {
|
2019-06-23 20:20:10 +03:00
|
|
|
// This function use the V3 Github API because repository removal is not supported yet on the V4 API.
|
2019-06-23 01:52:30 +03:00
|
|
|
url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project)
|
2019-06-21 02:09:05 +03:00
|
|
|
|
2019-06-23 01:52:30 +03:00
|
|
|
req, err := http.NewRequest("DELETE", url, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-06-21 02:09:05 +03:00
|
|
|
|
2019-06-23 01:52:30 +03:00
|
|
|
// need the token for private repositories
|
|
|
|
req.Header.Set("Authorization", fmt.Sprintf("token %s", token))
|
2019-06-21 02:09:05 +03:00
|
|
|
|
2019-06-23 01:52:30 +03:00
|
|
|
client := &http.Client{
|
|
|
|
Timeout: defaultTimeout,
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := client.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusNoContent {
|
|
|
|
return fmt.Errorf("error deleting repository")
|
|
|
|
}
|
2019-06-21 02:09:05 +03:00
|
|
|
|
2019-06-23 01:52:30 +03:00
|
|
|
return nil
|
2019-06-09 00:14:59 +03:00
|
|
|
}
|