Skip to content

Commit 8db50fd

Browse files
Add unit and E2E tests for connector operations
Add unit tests for the three token exchange styles and client credentials token caching. Add E2E tests covering API key connector creation, client credentials connector creation, deletion, provider info query, and RBAC. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
1 parent c5c6a43 commit 8db50fd

File tree

2 files changed

+588
-0
lines changed

2 files changed

+588
-0
lines changed

e2e/console/connector_test.go

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
2+
//
3+
// Permission to use, copy, modify, and/or distribute this software for any
4+
// purpose with or without fee is hereby granted, provided that the above
5+
// copyright notice and this permission notice appear in all copies.
6+
//
7+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8+
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9+
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10+
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11+
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
12+
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13+
// PERFORMANCE OF THIS SOFTWARE.
14+
15+
package console_test
16+
17+
import (
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
"github.com/stretchr/testify/require"
22+
"go.probo.inc/probo/e2e/internal/testutil"
23+
)
24+
25+
func TestConnectorProviderInfos(t *testing.T) {
26+
t.Parallel()
27+
owner := testutil.NewClient(t, testutil.RoleOwner)
28+
orgID := owner.GetOrganizationID().String()
29+
30+
t.Run("returns provider infos", func(t *testing.T) {
31+
t.Parallel()
32+
33+
const query = `
34+
query($id: ID!) {
35+
node(id: $id) {
36+
... on Organization {
37+
connectorProviderInfos {
38+
provider
39+
displayName
40+
oauthConfigured
41+
apiKeySupported
42+
clientCredentialsSupported
43+
extraSettings {
44+
key
45+
label
46+
required
47+
}
48+
}
49+
}
50+
}
51+
}
52+
`
53+
54+
var result struct {
55+
Node struct {
56+
ConnectorProviderInfos []struct {
57+
Provider string `json:"provider"`
58+
DisplayName string `json:"displayName"`
59+
OauthConfigured bool `json:"oauthConfigured"`
60+
APIKeySupported bool `json:"apiKeySupported"`
61+
ClientCredentialsSupported bool `json:"clientCredentialsSupported"`
62+
ExtraSettings []struct {
63+
Key string `json:"key"`
64+
Label string `json:"label"`
65+
Required bool `json:"required"`
66+
} `json:"extraSettings"`
67+
} `json:"connectorProviderInfos"`
68+
} `json:"node"`
69+
}
70+
71+
err := owner.Execute(query, map[string]any{"id": orgID}, &result)
72+
require.NoError(t, err)
73+
74+
infos := result.Node.ConnectorProviderInfos
75+
assert.NotEmpty(t, infos)
76+
77+
providerNames := make(map[string]bool)
78+
for _, info := range infos {
79+
assert.NotEmpty(t, info.Provider)
80+
assert.NotEmpty(t, info.DisplayName)
81+
assert.NotNil(t, info.ExtraSettings)
82+
providerNames[info.Provider] = true
83+
}
84+
85+
assert.True(t, providerNames["SLACK"], "expected SLACK provider to be present")
86+
assert.True(t, providerNames["HUBSPOT"], "expected HUBSPOT provider to be present")
87+
})
88+
89+
t.Run("viewer can list provider infos", func(t *testing.T) {
90+
t.Parallel()
91+
viewer := testutil.NewClientInOrg(t, testutil.RoleViewer, owner)
92+
93+
const query = `
94+
query($id: ID!) {
95+
node(id: $id) {
96+
... on Organization {
97+
connectorProviderInfos {
98+
provider
99+
displayName
100+
}
101+
}
102+
}
103+
}
104+
`
105+
106+
var result struct {
107+
Node struct {
108+
ConnectorProviderInfos []struct {
109+
Provider string `json:"provider"`
110+
DisplayName string `json:"displayName"`
111+
} `json:"connectorProviderInfos"`
112+
} `json:"node"`
113+
}
114+
115+
err := viewer.Execute(query, map[string]any{
116+
"id": viewer.GetOrganizationID().String(),
117+
}, &result)
118+
require.NoError(t, err)
119+
assert.NotEmpty(t, result.Node.ConnectorProviderInfos)
120+
})
121+
}
122+
123+
func TestCreateAPIKeyConnector(t *testing.T) {
124+
t.Parallel()
125+
owner := testutil.NewClient(t, testutil.RoleOwner)
126+
orgID := owner.GetOrganizationID().String()
127+
128+
const query = `
129+
mutation($input: CreateAPIKeyConnectorInput!) {
130+
createAPIKeyConnector(input: $input) {
131+
connector {
132+
id
133+
provider
134+
}
135+
}
136+
}
137+
`
138+
139+
var result struct {
140+
CreateAPIKeyConnector struct {
141+
Connector struct {
142+
ID string `json:"id"`
143+
Provider string `json:"provider"`
144+
} `json:"connector"`
145+
} `json:"createAPIKeyConnector"`
146+
}
147+
148+
err := owner.Execute(query, map[string]any{
149+
"input": map[string]any{
150+
"organizationId": orgID,
151+
"provider": "BREX",
152+
"apiKey": "test-key-123",
153+
},
154+
}, &result)
155+
require.NoError(t, err)
156+
157+
connector := result.CreateAPIKeyConnector.Connector
158+
assert.NotEmpty(t, connector.ID)
159+
assert.Equal(t, "BREX", connector.Provider)
160+
}
161+
162+
func TestCreateAPIKeyConnectorWithSettings(t *testing.T) {
163+
t.Parallel()
164+
owner := testutil.NewClient(t, testutil.RoleOwner)
165+
orgID := owner.GetOrganizationID().String()
166+
167+
const query = `
168+
mutation($input: CreateAPIKeyConnectorInput!) {
169+
createAPIKeyConnector(input: $input) {
170+
connector {
171+
id
172+
provider
173+
}
174+
}
175+
}
176+
`
177+
178+
var result struct {
179+
CreateAPIKeyConnector struct {
180+
Connector struct {
181+
ID string `json:"id"`
182+
Provider string `json:"provider"`
183+
} `json:"connector"`
184+
} `json:"createAPIKeyConnector"`
185+
}
186+
187+
err := owner.Execute(query, map[string]any{
188+
"input": map[string]any{
189+
"organizationId": orgID,
190+
"provider": "TALLY",
191+
"apiKey": "test-key",
192+
"tallyOrganizationId": "org-123",
193+
},
194+
}, &result)
195+
require.NoError(t, err)
196+
197+
connector := result.CreateAPIKeyConnector.Connector
198+
assert.NotEmpty(t, connector.ID)
199+
assert.Equal(t, "TALLY", connector.Provider)
200+
}
201+
202+
func TestCreateClientCredentialsConnector(t *testing.T) {
203+
t.Parallel()
204+
owner := testutil.NewClient(t, testutil.RoleOwner)
205+
orgID := owner.GetOrganizationID().String()
206+
207+
const query = `
208+
mutation($input: CreateClientCredentialsConnectorInput!) {
209+
createClientCredentialsConnector(input: $input) {
210+
connector {
211+
id
212+
provider
213+
}
214+
}
215+
}
216+
`
217+
218+
var result struct {
219+
CreateClientCredentialsConnector struct {
220+
Connector struct {
221+
ID string `json:"id"`
222+
Provider string `json:"provider"`
223+
} `json:"connector"`
224+
} `json:"createClientCredentialsConnector"`
225+
}
226+
227+
err := owner.Execute(query, map[string]any{
228+
"input": map[string]any{
229+
"organizationId": orgID,
230+
"provider": "ONE_PASSWORD",
231+
"clientId": "test-client",
232+
"clientSecret": "test-secret",
233+
"tokenUrl": "https://api.1password.com/v1beta1/users/oauth2/token",
234+
"onePasswordAccountId": "ACC123",
235+
"onePasswordRegion": "US",
236+
},
237+
}, &result)
238+
require.NoError(t, err)
239+
240+
connector := result.CreateClientCredentialsConnector.Connector
241+
assert.NotEmpty(t, connector.ID)
242+
assert.Equal(t, "ONE_PASSWORD", connector.Provider)
243+
}
244+
245+
func TestDeleteConnector(t *testing.T) {
246+
t.Parallel()
247+
owner := testutil.NewClient(t, testutil.RoleOwner)
248+
orgID := owner.GetOrganizationID().String()
249+
250+
// First, create a connector to delete.
251+
const createQuery = `
252+
mutation($input: CreateAPIKeyConnectorInput!) {
253+
createAPIKeyConnector(input: $input) {
254+
connector {
255+
id
256+
provider
257+
}
258+
}
259+
}
260+
`
261+
262+
var createResult struct {
263+
CreateAPIKeyConnector struct {
264+
Connector struct {
265+
ID string `json:"id"`
266+
Provider string `json:"provider"`
267+
} `json:"connector"`
268+
} `json:"createAPIKeyConnector"`
269+
}
270+
271+
err := owner.Execute(createQuery, map[string]any{
272+
"input": map[string]any{
273+
"organizationId": orgID,
274+
"provider": "BREX",
275+
"apiKey": "key-to-delete",
276+
},
277+
}, &createResult)
278+
require.NoError(t, err)
279+
280+
connectorID := createResult.CreateAPIKeyConnector.Connector.ID
281+
require.NotEmpty(t, connectorID)
282+
283+
// Now delete the connector.
284+
const deleteQuery = `
285+
mutation($input: DeleteConnectorInput!) {
286+
deleteConnector(input: $input) {
287+
deletedConnectorId
288+
}
289+
}
290+
`
291+
292+
var deleteResult struct {
293+
DeleteConnector struct {
294+
DeletedConnectorID string `json:"deletedConnectorId"`
295+
} `json:"deleteConnector"`
296+
}
297+
298+
err = owner.Execute(deleteQuery, map[string]any{
299+
"input": map[string]any{
300+
"connectorId": connectorID,
301+
},
302+
}, &deleteResult)
303+
require.NoError(t, err)
304+
assert.Equal(t, connectorID, deleteResult.DeleteConnector.DeletedConnectorID)
305+
}
306+
307+
func TestCreateAPIKeyConnector_RBAC(t *testing.T) {
308+
t.Parallel()
309+
owner := testutil.NewClient(t, testutil.RoleOwner)
310+
viewer := testutil.NewClientInOrg(t, testutil.RoleViewer, owner)
311+
312+
t.Run("viewer cannot create connector", func(t *testing.T) {
313+
t.Parallel()
314+
315+
_, err := viewer.Do(`
316+
mutation($input: CreateAPIKeyConnectorInput!) {
317+
createAPIKeyConnector(input: $input) {
318+
connector { id }
319+
}
320+
}
321+
`, map[string]any{
322+
"input": map[string]any{
323+
"organizationId": viewer.GetOrganizationID().String(),
324+
"provider": "BREX",
325+
"apiKey": "test-key",
326+
},
327+
})
328+
testutil.RequireForbiddenError(t, err, "viewer should not be able to create connector")
329+
})
330+
}

0 commit comments

Comments
 (0)