Skip to content

Commit 7b53dbf

Browse files
committed
refactor(dashboard-api): flatten wrappers, fold sso handler tests into team tests
- identity is now a required value param of bootstrapUser (production always has one since the OIDC-only path); the nil-identity guards, the setOIDCIdentityExternalID wrapper, and the test-only nil-identity concurrency test go away — s.idp.SetIdentityExternalID is called directly - inline the one-expression creatorContextFromSignupMetadata - drop zero-value test constructors (newTestIdentityProvider et al) - rename provisioning_error.go -> utils_provisioning.go per handlers convention - move the two PostTeamsTeamIDMembers SSO-guard tests from sso_test.go into team_handlers_test.go where the other member tests live
1 parent 70e4c58 commit 7b53dbf

8 files changed

Lines changed: 141 additions & 276 deletions

File tree

packages/dashboard-api/internal/handlers/sso_test.go

Lines changed: 0 additions & 104 deletions
This file was deleted.

packages/dashboard-api/internal/handlers/team_handlers_test.go

Lines changed: 94 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func TestPostTeamsTeamIDMembers_DuplicateMemberReturnsBadRequest(t *testing.T) {
127127
store := &APIStore{
128128
db: testDB.SqlcClient,
129129
authDB: testDB.AuthDB,
130-
idp: newHandlerTestIdentityProvider(),
130+
idp: handlerTestIdentityProvider{},
131131
}
132132
store.PostTeamsTeamIDMembers(ginCtx, teamID)
133133

@@ -166,7 +166,7 @@ func TestPostTeamsTeamIDMembers_CreatesPublicUserAnchorForInvitee(t *testing.T)
166166
db: testDB.SqlcClient,
167167
authDB: testDB.AuthDB,
168168
authService: noopAuthService{},
169-
idp: newHandlerTestIdentityProvider(),
169+
idp: handlerTestIdentityProvider{},
170170
}
171171
store.PostTeamsTeamIDMembers(ginCtx, teamID)
172172

@@ -186,6 +186,92 @@ func TestPostTeamsTeamIDMembers_CreatesPublicUserAnchorForInvitee(t *testing.T)
186186
}
187187
}
188188

189+
// ssoIdentityProvider is a Provider whose organization lookups are configurable, so
190+
// tests can simulate identities that belong to an Ory organization.
191+
type ssoIdentityProvider struct {
192+
handlerTestIdentityProvider
193+
194+
orgByUser map[uuid.UUID]uuid.UUID
195+
}
196+
197+
func (p ssoIdentityProvider) GetUserOrganizationID(_ context.Context, userID uuid.UUID) (uuid.UUID, error) {
198+
return p.orgByUser[userID], nil
199+
}
200+
201+
func TestPostTeamsTeamIDMembers_RejectsInviteOutsideSSOOrg(t *testing.T) {
202+
t.Parallel()
203+
204+
ctx := t.Context()
205+
teamID := uuid.New()
206+
orgID := uuid.New()
207+
actingUserID := uuid.New()
208+
inviteeID := uuid.New()
209+
210+
recorder := httptest.NewRecorder()
211+
ginCtx, _ := gin.CreateTestContext(recorder)
212+
auth.SetUserIDForTest(t, ginCtx, actingUserID)
213+
auth.SetTeamInfoForTest(t, ginCtx, &authtypes.Team{
214+
Team: &authqueries.Team{ID: teamID, SsoOrganizationID: &orgID},
215+
})
216+
ginCtx.Request = httptest.NewRequestWithContext(ctx, http.MethodPost, "/", strings.NewReader(`{"email":"`+handlerTestUserEmail(inviteeID)+`"}`))
217+
ginCtx.Request.Header.Set("Content-Type", "application/json")
218+
219+
// invitee belongs to no org (orgByUser empty) → outside the team's org.
220+
store := &APIStore{
221+
idp: ssoIdentityProvider{},
222+
provisioning: provisioning.New(nil, ssoIdentityProvider{}, nil, ""),
223+
}
224+
store.PostTeamsTeamIDMembers(ginCtx, teamID)
225+
226+
if recorder.Code != http.StatusForbidden {
227+
t.Fatalf("expected 403 for an invite outside the SSO org, got %d: %s", recorder.Code, recorder.Body.String())
228+
}
229+
}
230+
231+
func TestPostTeamsTeamIDMembers_AllowsInviteFromSSOOrg(t *testing.T) {
232+
t.Parallel()
233+
234+
testDB := testutils.SetupDatabase(t)
235+
ctx := t.Context()
236+
237+
orgID := uuid.New()
238+
teamID := testutils.CreateTestTeam(t, testDB)
239+
actingUserID := createHandlerTestUser(t, testDB)
240+
inviteeID := uuid.New()
241+
242+
recorder := httptest.NewRecorder()
243+
ginCtx, _ := gin.CreateTestContext(recorder)
244+
auth.SetUserIDForTest(t, ginCtx, actingUserID)
245+
auth.SetTeamInfoForTest(t, ginCtx, &authtypes.Team{
246+
Team: &authqueries.Team{ID: teamID, SsoOrganizationID: &orgID},
247+
})
248+
ginCtx.Request = httptest.NewRequestWithContext(ctx, http.MethodPost, "/", strings.NewReader(`{"email":"`+handlerTestUserEmail(inviteeID)+`"}`))
249+
ginCtx.Request.Header.Set("Content-Type", "application/json")
250+
251+
// invitee belongs to the same org as the team → allowed.
252+
profiles := ssoIdentityProvider{orgByUser: map[uuid.UUID]uuid.UUID{inviteeID: orgID}}
253+
store := &APIStore{
254+
db: testDB.SqlcClient,
255+
authDB: testDB.AuthDB,
256+
authService: noopAuthService{},
257+
idp: profiles,
258+
provisioning: provisioning.New(testDB.AuthDB, profiles, nil, ""),
259+
}
260+
store.PostTeamsTeamIDMembers(ginCtx, teamID)
261+
262+
if ginCtx.Writer.Status() != http.StatusCreated {
263+
t.Fatalf("expected 201 for an in-org invite, got %d: %s", ginCtx.Writer.Status(), recorder.Body.String())
264+
}
265+
266+
memberships, err := testDB.AuthDB.Read.GetTeamsWithUsersTeams(ctx, inviteeID)
267+
if err != nil {
268+
t.Fatalf("failed to read memberships: %v", err)
269+
}
270+
if len(memberships) != 1 || memberships[0].Team.ID != teamID {
271+
t.Fatalf("expected invitee to be a member of %s, got %v", teamID, memberships)
272+
}
273+
}
274+
189275
func TestDeleteTeamsTeamIDMembersUserId_NonMemberReturnsBadRequest(t *testing.T) {
190276
t.Parallel()
191277

@@ -357,10 +443,6 @@ func handlerTestUserEmail(userID uuid.UUID) string {
357443

358444
type handlerTestIdentityProvider struct{}
359445

360-
func newHandlerTestIdentityProvider() identity.Provider {
361-
return handlerTestIdentityProvider{}
362-
}
363-
364446
func (handlerTestIdentityProvider) GetProfilesByUserID(_ context.Context, userIDs []uuid.UUID) (map[uuid.UUID]identity.Profile, error) {
365447
profiles := make(map[uuid.UUID]identity.Profile, len(userIDs))
366448
for _, userID := range userIDs {
@@ -483,7 +565,7 @@ func TestPostTeams_LocalPolicyDeniedReturnsBadRequestWithoutCreatingTeam(t *test
483565
store := &APIStore{
484566
db: testDB.SqlcClient,
485567
authDB: testDB.AuthDB,
486-
provisioning: provisioning.New(testDB.AuthDB, newHandlerTestIdentityProvider(), sink, ""),
568+
provisioning: provisioning.New(testDB.AuthDB, handlerTestIdentityProvider{}, sink, ""),
487569
}
488570
store.PostTeams(ginCtx)
489571

@@ -521,7 +603,7 @@ func TestPostTeams_InvalidNameReturnsBadRequest(t *testing.T) {
521603
store := &APIStore{
522604
db: testDB.SqlcClient,
523605
authDB: testDB.AuthDB,
524-
provisioning: provisioning.New(testDB.AuthDB, newHandlerTestIdentityProvider(), sink, ""),
606+
provisioning: provisioning.New(testDB.AuthDB, handlerTestIdentityProvider{}, sink, ""),
525607
}
526608
store.PostTeams(ginCtx)
527609

@@ -551,7 +633,7 @@ func TestPostTeams_InvalidRequestBodyReturnsBadRequest(t *testing.T) {
551633
store := &APIStore{
552634
db: testDB.SqlcClient,
553635
authDB: testDB.AuthDB,
554-
provisioning: provisioning.New(testDB.AuthDB, newHandlerTestIdentityProvider(), sink, ""),
636+
provisioning: provisioning.New(testDB.AuthDB, handlerTestIdentityProvider{}, sink, ""),
555637
}
556638
store.PostTeams(ginCtx)
557639

@@ -583,7 +665,7 @@ func TestPostTeams_TrimsNameBeforeCreate(t *testing.T) {
583665
store := &APIStore{
584666
db: testDB.SqlcClient,
585667
authDB: testDB.AuthDB,
586-
provisioning: provisioning.New(testDB.AuthDB, newHandlerTestIdentityProvider(), sink, ""),
668+
provisioning: provisioning.New(testDB.AuthDB, handlerTestIdentityProvider{}, sink, ""),
587669
}
588670
store.PostTeams(ginCtx)
589671

@@ -634,7 +716,7 @@ func TestPostTeams_ProvisioningFailureRollsBackCreatedTeam(t *testing.T) {
634716
store := &APIStore{
635717
db: testDB.SqlcClient,
636718
authDB: testDB.AuthDB,
637-
provisioning: provisioning.New(testDB.AuthDB, newHandlerTestIdentityProvider(), sink, ""),
719+
provisioning: provisioning.New(testDB.AuthDB, handlerTestIdentityProvider{}, sink, ""),
638720
}
639721
store.PostTeams(ginCtx)
640722

@@ -692,7 +774,7 @@ func TestPostTeams_ProvisioningFailurePreservesProvisionErrorStatus(t *testing.T
692774
store := &APIStore{
693775
db: testDB.SqlcClient,
694776
authDB: testDB.AuthDB,
695-
provisioning: provisioning.New(testDB.AuthDB, newHandlerTestIdentityProvider(), sink, ""),
777+
provisioning: provisioning.New(testDB.AuthDB, handlerTestIdentityProvider{}, sink, ""),
696778
}
697779
store.PostTeams(ginCtx)
698780

packages/dashboard-api/internal/handlers/provisioning_error.go renamed to packages/dashboard-api/internal/handlers/utils_provisioning.go

File renamed without changes.

0 commit comments

Comments
 (0)