mirror of
https://github.com/MichaelMure/git-bug.git
synced 2025-01-06 01:44:27 +03:00
Tests for the github bridge (#706)
Add integration test for github bridge
This commit is contained in:
parent
247e1a865d
commit
d546cdeee1
384
bridge/github/import_integration_test.go
Normal file
384
bridge/github/import_integration_test.go
Normal file
@ -0,0 +1,384 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/MichaelMure/git-bug/bridge/github/mocks"
|
||||
"github.com/MichaelMure/git-bug/bug"
|
||||
"github.com/MichaelMure/git-bug/cache"
|
||||
"github.com/MichaelMure/git-bug/repository"
|
||||
"github.com/MichaelMure/git-bug/util/interrupt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/shurcooL/githubv4"
|
||||
m "github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// using testify/mock and mockery
|
||||
|
||||
var userName = githubv4.String("marcus")
|
||||
var userEmail = githubv4.String("marcus@rom.com")
|
||||
var unedited = githubv4.String("unedited")
|
||||
var edited = githubv4.String("edited")
|
||||
|
||||
func TestGithubImporterIntegration(t *testing.T) {
|
||||
// mock
|
||||
clientMock := &mocks.Client{}
|
||||
setupExpectations(t, clientMock)
|
||||
importer := githubImporter{}
|
||||
importer.client = &rateLimitHandlerClient{sc: clientMock}
|
||||
|
||||
// arrange
|
||||
repo := repository.CreateGoGitTestRepo(false)
|
||||
defer repository.CleanupTestRepos(repo)
|
||||
backend, err := cache.NewRepoCache(repo)
|
||||
require.NoError(t, err)
|
||||
defer backend.Close()
|
||||
interrupt.RegisterCleaner(backend.Close)
|
||||
require.NoError(t, err)
|
||||
|
||||
// act
|
||||
events, err := importer.ImportAll(context.Background(), backend, time.Time{})
|
||||
|
||||
// assert
|
||||
require.NoError(t, err)
|
||||
for e := range events {
|
||||
require.NoError(t, e.Err)
|
||||
}
|
||||
require.Len(t, backend.AllBugsIds(), 5)
|
||||
require.Len(t, backend.AllIdentityIds(), 2)
|
||||
|
||||
b1, err := backend.ResolveBugCreateMetadata(metaKeyGithubUrl, "https://github.com/marcus/to-himself/issues/1")
|
||||
require.NoError(t, err)
|
||||
ops1 := b1.Snapshot().Operations
|
||||
require.Equal(t, "marcus", ops1[0].Author().Name())
|
||||
require.Equal(t, "title 1", ops1[0].(*bug.CreateOperation).Title)
|
||||
require.Equal(t, "body text 1", ops1[0].(*bug.CreateOperation).Message)
|
||||
|
||||
b3, err := backend.ResolveBugCreateMetadata(metaKeyGithubUrl, "https://github.com/marcus/to-himself/issues/3")
|
||||
require.NoError(t, err)
|
||||
ops3 := b3.Snapshot().Operations
|
||||
require.Equal(t, "issue 3 comment 1", ops3[1].(*bug.AddCommentOperation).Message)
|
||||
require.Equal(t, "issue 3 comment 2", ops3[2].(*bug.AddCommentOperation).Message)
|
||||
require.Equal(t, []bug.Label{"bug"}, ops3[3].(*bug.LabelChangeOperation).Added)
|
||||
require.Equal(t, "title 3, edit 1", ops3[4].(*bug.SetTitleOperation).Title)
|
||||
|
||||
b4, err := backend.ResolveBugCreateMetadata(metaKeyGithubUrl, "https://github.com/marcus/to-himself/issues/4")
|
||||
require.NoError(t, err)
|
||||
ops4 := b4.Snapshot().Operations
|
||||
require.Equal(t, "edited", ops4[1].(*bug.EditCommentOperation).Message)
|
||||
|
||||
}
|
||||
|
||||
func setupExpectations(t *testing.T, mock *mocks.Client) {
|
||||
rateLimitingError(mock)
|
||||
expectIssueQuery1(mock)
|
||||
expectIssueQuery2(mock)
|
||||
expectIssueQuery3(mock)
|
||||
expectUserQuery(t, mock)
|
||||
}
|
||||
|
||||
func rateLimitingError(mock *mocks.Client) {
|
||||
mock.On("Query", m.Anything, m.AnythingOfType("*github.issueQuery"), m.Anything).Return(errors.New("API rate limit exceeded")).Once()
|
||||
mock.On("Query", m.Anything, m.AnythingOfType("*github.rateLimitQuery"), m.Anything).Return(nil).Run(
|
||||
func(args m.Arguments) {
|
||||
retVal := args.Get(1).(*rateLimitQuery)
|
||||
retVal.RateLimit.ResetAt.Time = time.Now().Add(time.Millisecond * 200)
|
||||
},
|
||||
).Once()
|
||||
}
|
||||
|
||||
func expectIssueQuery1(mock *mocks.Client) {
|
||||
mock.On("Query", m.Anything, m.AnythingOfType("*github.issueQuery"), m.Anything).Return(nil).Run(
|
||||
func(args m.Arguments) {
|
||||
retVal := args.Get(1).(*issueQuery)
|
||||
retVal.Repository.Issues.Nodes = []issueNode{
|
||||
{
|
||||
issue: issue{
|
||||
authorEvent: authorEvent{
|
||||
Id: 1,
|
||||
Author: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
Title: "title 1",
|
||||
Number: 1,
|
||||
Body: "body text 1",
|
||||
Url: githubv4.URI{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "github.com",
|
||||
Path: "marcus/to-himself/issues/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
UserContentEdits: userContentEditConnection{},
|
||||
TimelineItems: timelineItemsConnection{},
|
||||
},
|
||||
{
|
||||
issue: issue{
|
||||
authorEvent: authorEvent{
|
||||
Id: 2,
|
||||
Author: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
Title: "title 2",
|
||||
Number: 2,
|
||||
Body: "body text 2",
|
||||
Url: githubv4.URI{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "github.com",
|
||||
Path: "marcus/to-himself/issues/2",
|
||||
},
|
||||
},
|
||||
},
|
||||
UserContentEdits: userContentEditConnection{},
|
||||
TimelineItems: timelineItemsConnection{},
|
||||
},
|
||||
}
|
||||
retVal.Repository.Issues.PageInfo = pageInfo{
|
||||
EndCursor: "end-cursor-1",
|
||||
HasNextPage: true,
|
||||
}
|
||||
},
|
||||
).Once()
|
||||
}
|
||||
|
||||
func expectIssueQuery2(mock *mocks.Client) {
|
||||
mock.On("Query", m.Anything, m.AnythingOfType("*github.issueQuery"), m.Anything).Return(nil).Run(
|
||||
func(args m.Arguments) {
|
||||
retVal := args.Get(1).(*issueQuery)
|
||||
retVal.Repository.Issues.Nodes = []issueNode{
|
||||
{
|
||||
issue: issue{
|
||||
authorEvent: authorEvent{
|
||||
Id: 3,
|
||||
Author: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
Title: "title 3",
|
||||
Number: 3,
|
||||
Body: "body text 3",
|
||||
Url: githubv4.URI{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "github.com",
|
||||
Path: "marcus/to-himself/issues/3",
|
||||
},
|
||||
},
|
||||
},
|
||||
UserContentEdits: userContentEditConnection{},
|
||||
TimelineItems: timelineItemsConnection{
|
||||
Nodes: []timelineItem{
|
||||
{
|
||||
Typename: "IssueComment",
|
||||
IssueComment: issueComment{
|
||||
authorEvent: authorEvent{
|
||||
Id: 301,
|
||||
Author: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
Body: "issue 3 comment 1",
|
||||
Url: githubv4.URI{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "github.com",
|
||||
Path: "marcus/to-himself/issues/3#issuecomment-1",
|
||||
},
|
||||
},
|
||||
UserContentEdits: userContentEditConnection{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Typename: "IssueComment",
|
||||
IssueComment: issueComment{
|
||||
authorEvent: authorEvent{
|
||||
Id: 302,
|
||||
Author: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
Body: "issue 3 comment 2",
|
||||
Url: githubv4.URI{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "github.com",
|
||||
Path: "marcus/to-himself/issues/3#issuecomment-2",
|
||||
},
|
||||
},
|
||||
UserContentEdits: userContentEditConnection{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Typename: "LabeledEvent",
|
||||
LabeledEvent: labeledEvent{
|
||||
actorEvent: actorEvent{
|
||||
Id: 303,
|
||||
Actor: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
Label: label{
|
||||
Name: "bug",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Typename: "RenamedTitleEvent",
|
||||
RenamedTitleEvent: renamedTitleEvent{
|
||||
actorEvent: actorEvent{
|
||||
Id: 304,
|
||||
Actor: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
CurrentTitle: "title 3, edit 1",
|
||||
},
|
||||
},
|
||||
},
|
||||
PageInfo: pageInfo{},
|
||||
},
|
||||
},
|
||||
{
|
||||
issue: issue{
|
||||
authorEvent: authorEvent{
|
||||
Id: 4,
|
||||
Author: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
Title: "title 4",
|
||||
Number: 4,
|
||||
Body: unedited,
|
||||
Url: githubv4.URI{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "github.com",
|
||||
Path: "marcus/to-himself/issues/4",
|
||||
},
|
||||
},
|
||||
},
|
||||
UserContentEdits: userContentEditConnection{
|
||||
Nodes: []userContentEdit{
|
||||
// Github is weird: here the order is reversed chronological
|
||||
{
|
||||
Id: 402,
|
||||
Editor: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
Diff: &edited,
|
||||
},
|
||||
{
|
||||
Id: 401,
|
||||
Editor: &actor{
|
||||
Typename: "User",
|
||||
User: userActor{
|
||||
Name: &userName,
|
||||
Email: userEmail,
|
||||
},
|
||||
},
|
||||
// Github is weird: whenever an issue has issue edits, then the first item
|
||||
// (issue edit) holds the original (unedited) content and the second item
|
||||
// (issue edit) holds the (first) edited content.
|
||||
Diff: &unedited,
|
||||
},
|
||||
},
|
||||
PageInfo: pageInfo{},
|
||||
},
|
||||
TimelineItems: timelineItemsConnection{},
|
||||
},
|
||||
}
|
||||
retVal.Repository.Issues.PageInfo = pageInfo{
|
||||
EndCursor: "end-cursor-2",
|
||||
HasNextPage: true,
|
||||
}
|
||||
},
|
||||
).Once()
|
||||
}
|
||||
|
||||
func expectIssueQuery3(mock *mocks.Client) {
|
||||
mock.On("Query", m.Anything, m.AnythingOfType("*github.issueQuery"), m.Anything).Return(nil).Run(
|
||||
func(args m.Arguments) {
|
||||
retVal := args.Get(1).(*issueQuery)
|
||||
retVal.Repository.Issues.Nodes = []issueNode{
|
||||
{
|
||||
issue: issue{
|
||||
authorEvent: authorEvent{
|
||||
Author: nil,
|
||||
},
|
||||
Title: "title 5",
|
||||
Number: 5,
|
||||
Body: "body text 5",
|
||||
Url: githubv4.URI{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "github.com",
|
||||
Path: "marcus/to-himself/issues/5",
|
||||
},
|
||||
},
|
||||
},
|
||||
UserContentEdits: userContentEditConnection{},
|
||||
TimelineItems: timelineItemsConnection{},
|
||||
},
|
||||
}
|
||||
retVal.Repository.Issues.PageInfo = pageInfo{}
|
||||
},
|
||||
).Once()
|
||||
}
|
||||
|
||||
func expectUserQuery(t *testing.T, mock *mocks.Client) {
|
||||
mock.On("Query", m.Anything, m.AnythingOfType("*github.userQuery"), m.AnythingOfType("map[string]interface {}")).Return(nil).Run(
|
||||
func(args m.Arguments) {
|
||||
vars := args.Get(2).(map[string]interface{})
|
||||
ghost := githubv4.String("ghost")
|
||||
require.Equal(t, ghost, vars["login"])
|
||||
|
||||
retVal := args.Get(1).(*userQuery)
|
||||
retVal.User.Name = &ghost
|
||||
retVal.User.Login = "ghost-login"
|
||||
},
|
||||
).Once()
|
||||
}
|
@ -113,6 +113,26 @@ type userContentEdit struct {
|
||||
Diff *githubv4.String
|
||||
}
|
||||
|
||||
type label struct {
|
||||
Name githubv4.String
|
||||
}
|
||||
|
||||
type labeledEvent struct {
|
||||
actorEvent
|
||||
Label label
|
||||
}
|
||||
|
||||
type unlabeledEvent struct {
|
||||
actorEvent
|
||||
Label label
|
||||
}
|
||||
|
||||
type renamedTitleEvent struct {
|
||||
actorEvent
|
||||
CurrentTitle githubv4.String
|
||||
PreviousTitle githubv4.String
|
||||
}
|
||||
|
||||
type timelineItem struct {
|
||||
Typename githubv4.String `graphql:"__typename"`
|
||||
|
||||
@ -120,20 +140,8 @@ type timelineItem struct {
|
||||
IssueComment issueComment `graphql:"... on IssueComment"`
|
||||
|
||||
// Label
|
||||
LabeledEvent struct {
|
||||
actorEvent
|
||||
Label struct {
|
||||
// Color githubv4.String
|
||||
Name githubv4.String
|
||||
}
|
||||
} `graphql:"... on LabeledEvent"`
|
||||
UnlabeledEvent struct {
|
||||
actorEvent
|
||||
Label struct {
|
||||
// Color githubv4.String
|
||||
Name githubv4.String
|
||||
}
|
||||
} `graphql:"... on UnlabeledEvent"`
|
||||
LabeledEvent labeledEvent `graphql:"... on LabeledEvent"`
|
||||
UnlabeledEvent unlabeledEvent `graphql:"... on UnlabeledEvent"`
|
||||
|
||||
// Status
|
||||
ClosedEvent struct {
|
||||
@ -145,11 +153,7 @@ type timelineItem struct {
|
||||
} `graphql:"... on ReopenedEvent"`
|
||||
|
||||
// Title
|
||||
RenamedTitleEvent struct {
|
||||
actorEvent
|
||||
CurrentTitle githubv4.String
|
||||
PreviousTitle githubv4.String
|
||||
} `graphql:"... on RenamedTitleEvent"`
|
||||
RenamedTitleEvent renamedTitleEvent `graphql:"... on RenamedTitleEvent"`
|
||||
}
|
||||
|
||||
type issueComment struct {
|
||||
@ -160,14 +164,20 @@ type issueComment struct {
|
||||
UserContentEdits userContentEditConnection `graphql:"userContentEdits(last: $commentEditLast, before: $commentEditBefore)"`
|
||||
}
|
||||
|
||||
type userActor struct {
|
||||
Name *githubv4.String
|
||||
Email githubv4.String
|
||||
}
|
||||
|
||||
type actor struct {
|
||||
Typename githubv4.String `graphql:"__typename"`
|
||||
Login githubv4.String
|
||||
AvatarUrl githubv4.String
|
||||
User struct {
|
||||
Name *githubv4.String
|
||||
Email githubv4.String
|
||||
} `graphql:"... on User"`
|
||||
// User struct {
|
||||
// Name *githubv4.String
|
||||
// Email githubv4.String
|
||||
// } `graphql:"... on User"`
|
||||
User userActor `graphql:"... on User"`
|
||||
Organization struct {
|
||||
Name *githubv4.String
|
||||
Email *githubv4.String
|
||||
|
44
bridge/github/mocks/Client.go
Normal file
44
bridge/github/mocks/Client.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
githubv4 "github.com/shurcooL/githubv4"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Client is an autogenerated mock type for the Client type
|
||||
type Client struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Mutate provides a mock function with given fields: _a0, _a1, _a2, _a3
|
||||
func (_m *Client) Mutate(_a0 context.Context, _a1 interface{}, _a2 githubv4.Input, _a3 map[string]interface{}) error {
|
||||
ret := _m.Called(_a0, _a1, _a2, _a3)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, interface{}, githubv4.Input, map[string]interface{}) error); ok {
|
||||
r0 = rf(_a0, _a1, _a2, _a3)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Query provides a mock function with given fields: _a0, _a1, _a2
|
||||
func (_m *Client) Query(_a0 context.Context, _a1 interface{}, _a2 map[string]interface{}) error {
|
||||
ret := _m.Called(_a0, _a1, _a2)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, interface{}, map[string]interface{}) error); ok {
|
||||
r0 = rf(_a0, _a1, _a2)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
Loading…
Reference in New Issue
Block a user