Files
fleet/ee/server/googleworkspace/google_workspace_test.go
T
2026-08-04 11:36:02 -04:00

200 lines
6.0 KiB
Go

package googleworkspace
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
directory "google.golang.org/api/admin/directory/v1"
)
type fakeAPI struct {
users []*directory.User
groups []*directory.Group
members map[string][]*directory.Member
usersErr error
groupsErr error
memberErr error
}
// ListUsers delivers the fake's users in two pages when there is more than one, so
// callers are exercised across page boundaries.
func (f *fakeAPI) ListUsers(_ context.Context, _ string, forEachPage func([]*directory.User) error) error {
if f.usersErr != nil {
return f.usersErr
}
if len(f.users) < 2 {
return forEachPage(f.users)
}
split := len(f.users) / 2
if err := forEachPage(f.users[:split]); err != nil {
return err
}
return forEachPage(f.users[split:])
}
func (f *fakeAPI) ListGroups(_ context.Context, _ string) ([]*directory.Group, error) {
return f.groups, f.groupsErr
}
func (f *fakeAPI) ListGroupMembers(_ context.Context, groupKey string) ([]*directory.Member, error) {
if f.memberErr != nil {
return nil, f.memberErr
}
return f.members[groupKey], nil
}
func TestMapUser(t *testing.T) {
t.Run("active user with names, department, emails", func(t *testing.T) {
u := &directory.User{
Id: "123",
PrimaryEmail: "alice@example.com",
Name: &directory.UserName{GivenName: "Alice", FamilyName: "Smith"},
Organizations: []map[string]any{
{"department": "Sales", "primary": false},
{"department": "Engineering", "primary": true},
},
Emails: []map[string]any{
{"address": "alice@example.com", "primary": true, "type": "work"},
{"address": "alice.alt@example.com", "type": "home"},
},
}
su := mapUser(u)
require.NotNil(t, su.ExternalID)
assert.Equal(t, "123", *su.ExternalID)
assert.Equal(t, "alice@example.com", su.UserName)
require.NotNil(t, su.GivenName)
assert.Equal(t, "Alice", *su.GivenName)
require.NotNil(t, su.FamilyName)
assert.Equal(t, "Smith", *su.FamilyName)
require.NotNil(t, su.Active)
assert.True(t, *su.Active)
require.NotNil(t, su.Department)
assert.Equal(t, "Engineering", *su.Department, "primary org's department wins")
require.Len(t, su.Emails, 2)
assert.Equal(t, "alice@example.com", su.Emails[0].Email)
require.NotNil(t, su.Emails[0].Primary)
assert.True(t, *su.Emails[0].Primary)
})
t.Run("suspended user is inactive", func(t *testing.T) {
su := mapUser(&directory.User{Id: "1", PrimaryEmail: "x@example.com", Suspended: true})
require.NotNil(t, su.Active)
assert.False(t, *su.Active)
})
t.Run("archived user is inactive", func(t *testing.T) {
su := mapUser(&directory.User{Id: "1", PrimaryEmail: "x@example.com", Archived: true})
require.NotNil(t, su.Active)
assert.False(t, *su.Active)
})
t.Run("department falls back to first non-empty when no primary org", func(t *testing.T) {
su := mapUser(&directory.User{
Id: "1",
PrimaryEmail: "x@example.com",
Organizations: []map[string]any{
{"department": ""},
{"department": "Support"},
},
})
require.NotNil(t, su.Department)
assert.Equal(t, "Support", *su.Department)
})
t.Run("primary email synthesized when absent from emails array", func(t *testing.T) {
su := mapUser(&directory.User{
Id: "1",
PrimaryEmail: "primary@example.com",
Emails: []map[string]any{{"address": "other@example.com", "type": "home"}},
})
require.Len(t, su.Emails, 2)
assert.Equal(t, "primary@example.com", su.Emails[0].Email)
require.NotNil(t, su.Emails[0].Primary)
assert.True(t, *su.Emails[0].Primary)
})
t.Run("duplicate email addresses are de-duplicated", func(t *testing.T) {
su := mapUser(&directory.User{
Id: "1",
PrimaryEmail: "dup@example.com",
Emails: []map[string]any{
{"address": "dup@example.com", "type": "work"},
{"address": "DUP@example.com", "type": "home"},
},
})
require.Len(t, su.Emails, 1)
assert.Equal(t, "dup@example.com", su.Emails[0].Email)
})
}
func TestDirectoryListUsers(t *testing.T) {
dir := &Directory{
domain: "example.com",
api: &fakeAPI{users: []*directory.User{
{Id: "1", PrimaryEmail: "a@example.com"},
{Id: "", PrimaryEmail: "noid@example.com"}, // skipped: no ID
{Id: "2", PrimaryEmail: ""}, // skipped: no email
{Id: "3", PrimaryEmail: "c@example.com"},
}},
}
users, err := dir.ListUsers(t.Context())
require.NoError(t, err)
require.Len(t, users, 2)
assert.Equal(t, "a@example.com", users[0].UserName)
assert.Equal(t, "c@example.com", users[1].UserName)
}
func TestDirectoryListUsersError(t *testing.T) {
dir := &Directory{domain: "example.com", api: &fakeAPI{usersErr: errors.New("boom")}}
_, err := dir.ListUsers(t.Context())
require.Error(t, err)
}
func TestDirectoryListGroups(t *testing.T) {
dir := &Directory{
domain: "example.com",
api: &fakeAPI{
groups: []*directory.Group{
{Id: "g1", Name: "Engineering", Email: "eng@example.com"},
{Id: "g2", Email: "ops@example.com"}, // no Name -> display name from email
{Id: ""}, // skipped
},
members: map[string][]*directory.Member{
"g1": {
{Id: "u1", Type: "USER"},
{Id: "u2"}, // empty type treated as user
{Id: "g9", Type: "GROUP"}, // nested group skipped
{Id: ""}, // skipped
},
"g2": {{Id: "u3", Type: "USER"}},
},
},
}
groups, err := dir.ListGroups(t.Context())
require.NoError(t, err)
require.Len(t, groups, 2)
assert.Equal(t, "g1", groups[0].ExternalID)
assert.Equal(t, "Engineering", groups[0].DisplayName)
assert.Equal(t, []string{"u1", "u2"}, groups[0].MemberExternalIDs)
assert.Equal(t, "ops@example.com", groups[1].DisplayName, "falls back to email when no name")
assert.Equal(t, []string{"u3"}, groups[1].MemberExternalIDs)
}
func TestDirectoryListGroupsMemberError(t *testing.T) {
dir := &Directory{
domain: "example.com",
api: &fakeAPI{
groups: []*directory.Group{{Id: "g1", Name: "Eng"}},
memberErr: errors.New("members boom"),
},
}
_, err := dir.ListGroups(t.Context())
require.Error(t, err)
}