Skip to content

Commit 389b190

Browse files
authored
test: Integration test with mocked GitHub (#37)
* Extract organizationResource() to a function * Test provisioning team membership * Build out CRUD operations * Mock graphQL * Add a suite of tests for every resource * lint * fix tests * go mod
1 parent 6378263 commit 389b190

File tree

227 files changed

+82658
-25
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

227 files changed

+82658
-25
lines changed

go.mod

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ go 1.21
55
toolchain go1.22.3
66

77
require (
8-
github.com/conductorone/baton-sdk v0.2.7
8+
github.com/conductorone/baton-sdk v0.2.8
9+
github.com/deckarep/golang-set/v2 v2.6.0
910
github.com/google/go-github/v63 v63.0.0
1011
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
12+
github.com/migueleliasweb/go-github-mock v0.0.23
1113
github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc
1214
github.com/spf13/viper v1.18.2
15+
github.com/stretchr/testify v1.9.0
1316
go.uber.org/zap v1.27.0
1417
golang.org/x/oauth2 v0.20.0
1518
golang.org/x/text v0.16.0
@@ -40,7 +43,7 @@ require (
4043
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
4144
github.com/aws/smithy-go v1.20.2 // indirect
4245
github.com/benbjohnson/clock v1.3.5 // indirect
43-
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
46+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
4447
github.com/doug-martin/goqu/v9 v9.19.0 // indirect
4548
github.com/dustin/go-humanize v1.0.1 // indirect
4649
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
@@ -49,8 +52,10 @@ require (
4952
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
5053
github.com/go-ole/go-ole v1.3.0 // indirect
5154
github.com/golang/protobuf v1.5.4 // indirect
55+
github.com/google/go-github/v59 v59.0.0 // indirect
5256
github.com/google/go-querystring v1.1.0 // indirect
5357
github.com/google/uuid v1.6.0 // indirect
58+
github.com/gorilla/mux v1.8.0 // indirect
5459
github.com/hashicorp/hcl v1.0.0 // indirect
5560
github.com/inconshreveable/mousetrap v1.1.0 // indirect
5661
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -61,6 +66,7 @@ require (
6166
github.com/mitchellh/mapstructure v1.5.0 // indirect
6267
github.com/ncruces/go-strftime v0.1.9 // indirect
6368
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
69+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
6470
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
6571
github.com/pquerna/xjwt v0.2.0 // indirect
6672
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
@@ -88,6 +94,7 @@ require (
8894
golang.org/x/net v0.26.0 // indirect
8995
golang.org/x/sync v0.7.0 // indirect
9096
golang.org/x/sys v0.21.0 // indirect
97+
golang.org/x/time v0.5.0 // indirect
9198
google.golang.org/genproto/googleapis/rpc v0.0.0-20240506185236-b8a5c65736ae // indirect
9299
gopkg.in/ini.v1 v1.67.0 // indirect
93100
gopkg.in/square/go-jose.v2 v2.6.0 // indirect

go.sum

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx
5050
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
5151
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
5252
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
53-
github.com/conductorone/baton-sdk v0.2.7 h1:mzp7H0zVeZLMvdGj3Yx31mpzM6ytNKI6QmpzRuiPlVE=
54-
github.com/conductorone/baton-sdk v0.2.7/go.mod h1:cg5FyUcJnD7xK5SPbHe/KNpwUVVlpHJ9rnmd3UwxSkU=
53+
github.com/conductorone/baton-sdk v0.2.8 h1:DLHTQXhl50A5AB0cbSoMOmzrUwwbpP7poCXI69Lcdtw=
54+
github.com/conductorone/baton-sdk v0.2.8/go.mod h1:cg5FyUcJnD7xK5SPbHe/KNpwUVVlpHJ9rnmd3UwxSkU=
5555
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
5656
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5757
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -105,6 +105,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
105105
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
106106
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
107107
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
108+
github.com/google/go-github/v59 v59.0.0 h1:7h6bgpF5as0YQLLkEiVqpgtJqjimMYhBkD4jT5aN3VA=
109+
github.com/google/go-github/v59 v59.0.0/go.mod h1:rJU4R0rQHFVFDOkqGWxfLNo6vEk4dv40oDjhV/gH6wM=
108110
github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE=
109111
github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA=
110112
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@@ -113,6 +115,8 @@ github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlG
113115
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
114116
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
115117
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
118+
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
119+
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
116120
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
117121
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
118122
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -146,6 +150,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
146150
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
147151
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
148152
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
153+
github.com/migueleliasweb/go-github-mock v0.0.23 h1:GOi9oX/+Seu9JQ19V8bPDLqDI7M9iEOjo3g8v1k6L2c=
154+
github.com/migueleliasweb/go-github-mock v0.0.23/go.mod h1:NsT8FGbkvIZQtDu38+295sZEX8snaUiiQgsGxi6GUxk=
149155
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
150156
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
151157
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
@@ -335,6 +341,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
335341
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
336342
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
337343
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
344+
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
345+
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
338346
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
339347
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
340348
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=

pkg/connector/org.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,26 @@ type orgResourceType struct {
3636
orgCache *orgNameCache
3737
}
3838

39+
func organizationResource(
40+
ctx context.Context,
41+
org *github.Organization,
42+
parentResourceID *v2.ResourceId,
43+
) (*v2.Resource, error) {
44+
return resource.NewResource(
45+
org.GetLogin(),
46+
resourceTypeOrg,
47+
org.GetID(),
48+
resource.WithParentResourceID(parentResourceID),
49+
resource.WithAnnotation(
50+
&v2.ExternalLink{Url: org.GetHTMLURL()},
51+
&v2.V1Identifier{Id: fmt.Sprintf("org:%d", org.GetID())},
52+
&v2.ChildResourceType{ResourceTypeId: resourceTypeUser.Id},
53+
&v2.ChildResourceType{ResourceTypeId: resourceTypeTeam.Id},
54+
&v2.ChildResourceType{ResourceTypeId: resourceTypeRepository.Id},
55+
),
56+
)
57+
}
58+
3959
func (o *orgResourceType) ResourceType(_ context.Context) *v2.ResourceType {
4060
return o.resourceType
4161
}
@@ -91,19 +111,7 @@ func (o *orgResourceType) List(
91111
continue
92112
}
93113

94-
orgResource, err := resource.NewResource(
95-
org.GetLogin(),
96-
resourceTypeOrg,
97-
org.GetID(),
98-
resource.WithParentResourceID(parentResourceID),
99-
resource.WithAnnotation(
100-
&v2.ExternalLink{Url: org.GetHTMLURL()},
101-
&v2.V1Identifier{Id: fmt.Sprintf("org:%d", org.GetID())},
102-
&v2.ChildResourceType{ResourceTypeId: resourceTypeUser.Id},
103-
&v2.ChildResourceType{ResourceTypeId: resourceTypeTeam.Id},
104-
&v2.ChildResourceType{ResourceTypeId: resourceTypeRepository.Id},
105-
),
106-
)
114+
orgResource, err := organizationResource(ctx, org, parentResourceID)
107115
if err != nil {
108116
return nil, "", nil, err
109117
}

pkg/connector/org_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package connector
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/conductorone/baton-github/test"
8+
"github.com/conductorone/baton-github/test/mocks"
9+
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
10+
"github.com/conductorone/baton-sdk/pkg/pagination"
11+
"github.com/conductorone/baton-sdk/pkg/types/entitlement"
12+
"github.com/google/go-github/v63/github"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
func TestOrganization(t *testing.T) {
17+
ctx := context.Background()
18+
19+
t.Run("should grant and revoke entitlements", func(t *testing.T) {
20+
mgh := mocks.NewMockGitHub()
21+
22+
githubOrganization, _, _, githubUser, _ := mgh.Seed()
23+
24+
githubClient := github.NewClient(mgh.Server())
25+
cache := newOrgNameCache(githubClient)
26+
client := orgBuilder(githubClient, cache, nil)
27+
28+
organization, _ := organizationResource(ctx, githubOrganization, nil)
29+
user, _ := userResource(ctx, githubUser, *githubUser.Email, nil)
30+
31+
entitlement := v2.Entitlement{
32+
Id: entitlement.NewEntitlementID(organization, orgRoleMember),
33+
Resource: organization,
34+
}
35+
36+
grantAnnotations, err := client.Grant(ctx, user, &entitlement)
37+
require.Nil(t, err)
38+
require.Empty(t, grantAnnotations)
39+
40+
grants, nextToken, grantsAnnotations, err := client.Grants(ctx, organization, &pagination.Token{})
41+
require.Nil(t, err)
42+
test.AssertNoRatelimitAnnotations(t, grantsAnnotations)
43+
require.Equal(t, "", nextToken)
44+
require.Len(t, grants, 2)
45+
46+
grant := v2.Grant{
47+
Entitlement: &entitlement,
48+
Principal: user,
49+
}
50+
51+
revokeAnnotations, err := client.Revoke(ctx, &grant)
52+
require.Nil(t, err)
53+
require.Empty(t, revokeAnnotations)
54+
})
55+
}

pkg/connector/repository_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package connector
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/conductorone/baton-github/test"
8+
"github.com/conductorone/baton-github/test/mocks"
9+
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
10+
"github.com/conductorone/baton-sdk/pkg/pagination"
11+
"github.com/google/go-github/v63/github"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestRepository(t *testing.T) {
16+
ctx := context.Background()
17+
18+
t.Run("should grant and revoke entitlements", func(t *testing.T) {
19+
mgh := mocks.NewMockGitHub()
20+
21+
githubOrganization, githubRepository, _, githubUser, _ := mgh.Seed()
22+
23+
githubClient := github.NewClient(mgh.Server())
24+
cache := newOrgNameCache(githubClient)
25+
client := repositoryBuilder(githubClient, cache)
26+
27+
organization, _ := organizationResource(ctx, githubOrganization, nil)
28+
repository, _ := repositoryResource(ctx, githubRepository, organization.Id)
29+
user, _ := userResource(ctx, githubUser, *githubUser.Email, nil)
30+
31+
entitlement := v2.Entitlement{Resource: repository}
32+
33+
grantAnnotations, err := client.Grant(ctx, user, &entitlement)
34+
require.Nil(t, err)
35+
require.Empty(t, grantAnnotations)
36+
37+
grants := make([]*v2.Grant, 0)
38+
bag := &pagination.Bag{}
39+
for {
40+
pToken := pagination.Token{}
41+
state := bag.Current()
42+
if state != nil {
43+
token, _ := bag.Marshal()
44+
pToken.Token = token
45+
}
46+
47+
nextGrants, nextToken, grantsAnnotations, err := client.Grants(ctx, repository, &pToken)
48+
grants = append(grants, nextGrants...)
49+
50+
require.Nil(t, err)
51+
test.AssertNoRatelimitAnnotations(t, grantsAnnotations)
52+
if nextToken == "" {
53+
break
54+
}
55+
56+
err = bag.Unmarshal(nextToken)
57+
if err != nil {
58+
t.Error(err)
59+
}
60+
}
61+
62+
require.Len(t, grants, 1)
63+
64+
grant := v2.Grant{
65+
Entitlement: &entitlement,
66+
Principal: user,
67+
}
68+
69+
revokeAnnotations, err := client.Revoke(ctx, &grant)
70+
require.Nil(t, err)
71+
require.Empty(t, revokeAnnotations)
72+
})
73+
}

pkg/connector/team_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package connector
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/conductorone/baton-github/test"
8+
"github.com/conductorone/baton-github/test/mocks"
9+
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
10+
"github.com/conductorone/baton-sdk/pkg/pagination"
11+
"github.com/google/go-github/v63/github"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestTeam(t *testing.T) {
16+
ctx := context.Background()
17+
18+
t.Run("should grant and revoke entitlements", func(t *testing.T) {
19+
mgh := mocks.NewMockGitHub()
20+
21+
githubOrganization, _, githubTeam, githubUser, _ := mgh.Seed()
22+
23+
githubClient := github.NewClient(mgh.Server())
24+
cache := newOrgNameCache(githubClient)
25+
client := teamBuilder(githubClient, cache)
26+
27+
organization, _ := organizationResource(ctx, githubOrganization, nil)
28+
team, _ := teamResource(githubTeam, organization.Id)
29+
user, _ := userResource(ctx, githubUser, *githubUser.Email, nil)
30+
31+
entitlement := v2.Entitlement{Resource: team}
32+
33+
grantAnnotations, err := client.Grant(ctx, user, &entitlement)
34+
require.Nil(t, err)
35+
require.Empty(t, grantAnnotations)
36+
37+
grants, nextToken, grantsAnnotations, err := client.Grants(ctx, team, &pagination.Token{})
38+
require.Nil(t, err)
39+
test.AssertNoRatelimitAnnotations(t, grantsAnnotations)
40+
require.Equal(t, "", nextToken)
41+
require.Len(t, grants, 1)
42+
43+
grant := v2.Grant{
44+
Entitlement: &entitlement,
45+
Principal: user,
46+
}
47+
48+
revokeAnnotations, err := client.Revoke(ctx, &grant)
49+
require.Nil(t, err)
50+
require.Empty(t, revokeAnnotations)
51+
})
52+
}

pkg/connector/user_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package connector
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/conductorone/baton-github/test"
9+
"github.com/conductorone/baton-github/test/mocks"
10+
"github.com/conductorone/baton-sdk/pkg/pagination"
11+
"github.com/google/go-github/v63/github"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestUsersList(t *testing.T) {
16+
ctx := context.Background()
17+
18+
trueBool, falseBool := true, false
19+
20+
testCases := []struct {
21+
hasSamlEnabled *bool
22+
message string
23+
}{
24+
{&trueBool, "true"},
25+
{&falseBool, "false"},
26+
{nil, "nil"},
27+
}
28+
for _, testCase := range testCases {
29+
t.Run(fmt.Sprintf("should get a list of users (SAML:%s)", testCase.message), func(t *testing.T) {
30+
mgh := mocks.NewMockGitHub()
31+
32+
githubOrganization, _, _, githubUser, _ := mgh.Seed()
33+
34+
organization, err := organizationResource(
35+
ctx,
36+
githubOrganization,
37+
nil,
38+
)
39+
if err != nil {
40+
t.Error(err)
41+
}
42+
43+
githubClient := github.NewClient(mgh.Server())
44+
graphQLClient := mocks.MockGraphQL()
45+
cache := newOrgNameCache(githubClient)
46+
client := userBuilder(
47+
githubClient,
48+
testCase.hasSamlEnabled,
49+
graphQLClient,
50+
cache,
51+
)
52+
53+
users, nextToken, annotations, err := client.List(
54+
ctx,
55+
organization.Id,
56+
&pagination.Token{},
57+
)
58+
require.Nil(t, err)
59+
test.AssertNoRatelimitAnnotations(t, annotations)
60+
require.Equal(t, "", nextToken)
61+
require.Len(t, users, 1)
62+
require.Equal(t, *githubUser.Login, users[0].Id.Resource)
63+
})
64+
}
65+
}

0 commit comments

Comments
 (0)