package service import ( "baron-sso-backend/internal/domain" "context" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "io" "net/http" "net/url" "os" "strings" "testing" "time" "github.com/stretchr/testify/require" ) func TestWorksmobileHTTPClientCreateUserPostsDirectoryAdminPasswordPayload(t *testing.T) { transport := &captureRoundTripper{ statusCode: http.StatusCreated, body: `{}`, } client := &WorksmobileHTTPClient{ BaseURL: "https://works.example.test", DirectoryToken: "directory-token-1", SCIMToken: "scim-token-1", HTTPClient: &http.Client{Transport: transport}, } err := client.CreateUser(context.Background(), WorksmobileUserPayload{ DomainID: 300285955, Email: "tester@samaneng.com", UserExternalKey: "user-1", UserName: WorksmobileUserName{LastName: "Tester"}, AliasEmails: []string{"tester.alias@samaneng.com", "tester.alias2@samaneng.com"}, Locale: "ko_KR", PasswordConfig: WorksmobilePasswordConfig{ PasswordCreationType: "ADMIN", Password: GenerateWorksmobileInitialPassword(), }, Organizations: []WorksmobileUserOrganization{ {DomainID: 300285955, Primary: true, OrgUnits: []WorksmobileUserOrgUnit{{OrgUnitID: "externalKey:tenant-saman"}}}, }, }) require.NoError(t, err) require.NotNil(t, transport.request) require.Equal(t, "/v1.0/users", transport.request.URL.Path) require.Equal(t, http.MethodPost, transport.request.Method) require.Equal(t, "Bearer directory-token-1", transport.request.Header.Get("Authorization")) var payload map[string]any require.NoError(t, json.Unmarshal(transport.requestBody, &payload)) require.Equal(t, "tester@samaneng.com", payload["email"]) require.Equal(t, "user-1", payload["userExternalKey"]) require.NotContains(t, payload, "privateEmail") require.Equal(t, []any{"tester.alias@samaneng.com", "tester.alias2@samaneng.com"}, payload["aliasEmails"]) passwordConfig := payload["passwordConfig"].(map[string]any) require.Equal(t, "ADMIN", passwordConfig["passwordCreationType"]) require.Len(t, passwordConfig["password"], 16) } func TestWorksmobileHTTPClientUpsertUserPatchesOnCreateConflictWithoutPasswordOrPrivateEmail(t *testing.T) { transport := &captureRoundTripper{ responses: []captureResponse{ {statusCode: http.StatusConflict, body: `{"code":"ALREADY_EXISTS"}`}, {statusCode: http.StatusOK, body: `{}`}, }, } client := &WorksmobileHTTPClient{ BaseURL: "https://works.example.test", DirectoryToken: "directory-token-1", HTTPClient: &http.Client{Transport: transport}, } err := client.UpsertUser(context.Background(), WorksmobileUserPayload{ DomainID: 300285955, Email: "tester@samaneng.com", UserExternalKey: "user-1", UserName: WorksmobileUserName{LastName: "Tester"}, PrivateEmail: "private@example.com", PasswordConfig: WorksmobilePasswordConfig{ PasswordCreationType: "ADMIN", Password: GenerateWorksmobileInitialPassword(), }, Organizations: []WorksmobileUserOrganization{ { DomainID: 300285955, Primary: true, OrgUnits: []WorksmobileUserOrgUnit{ {OrgUnitID: "externalKey:tenant-saman", Primary: true}, }, }, }, }) require.NoError(t, err) require.Len(t, transport.requests, 2) require.Equal(t, http.MethodPost, transport.requests[0].Method) require.Equal(t, "/v1.0/users", transport.requests[0].URL.Path) require.Equal(t, http.MethodPatch, transport.requests[1].Method) require.Equal(t, "/v1.0/users/tester@samaneng.com", transport.requests[1].URL.Path) var patchPayload map[string]any require.NoError(t, json.Unmarshal(transport.requestBodies[1], &patchPayload)) require.NotContains(t, patchPayload, "passwordConfig") require.NotContains(t, patchPayload, "privateEmail") require.Equal(t, "tester@samaneng.com", patchPayload["email"]) require.Equal(t, "user-1", patchPayload["userExternalKey"]) } func TestWorksmobileHTTPClientCreateUserRequiresDirectoryToken(t *testing.T) { client := &WorksmobileHTTPClient{ BaseURL: "https://works.example.test", SCIMToken: "scim-token-1", HTTPClient: &http.Client{Transport: &captureRoundTripper{statusCode: http.StatusCreated, body: `{}`}}, } err := client.CreateUser(context.Background(), WorksmobileUserPayload{Email: "tester@samaneng.com"}) require.Error(t, err) require.Contains(t, err.Error(), "worksmobile directory token is not configured") } func TestWorksmobileHTTPClientRequestsJWTBearerAccessToken(t *testing.T) { privateKey := testRSAPrivateKeyPEM(t) transport := &captureRoundTripper{ responses: []captureResponse{ {statusCode: http.StatusOK, body: `{"access_token":"directory-token-from-jwt","token_type":"Bearer","expires_in":3600}`}, {statusCode: http.StatusCreated, body: `{}`}, }, } client := &WorksmobileHTTPClient{ BaseURL: "https://works.example.test", HTTPClient: &http.Client{Transport: transport}, now: func() time.Time { return time.Unix(1710000000, 0) }, OAuthConfig: WorksmobileOAuthConfig{ ClientID: "client-id-1", ClientSecret: "client-secret-1", ServiceAccount: "service-account-1", PrivateKey: privateKey, Scope: "directory", TokenURL: "https://auth.example.test/token", }, } err := client.CreateUser(context.Background(), WorksmobileUserPayload{ DomainID: 300285955, Email: "tester@samaneng.com", UserExternalKey: "user-1", UserName: WorksmobileUserName{LastName: "Tester"}, PasswordConfig: WorksmobilePasswordConfig{PasswordCreationType: "ADMIN", Password: "Aa1!Aa1!Aa1!Aa1!"}, }) require.NoError(t, err) require.Len(t, transport.requests, 2) require.Equal(t, "https://auth.example.test/token", transport.requests[0].URL.String()) require.Equal(t, "/v1.0/users", transport.requests[1].URL.Path) require.Equal(t, "Bearer directory-token-from-jwt", transport.requests[1].Header.Get("Authorization")) form, err := url.ParseQuery(string(transport.requestBodies[0])) require.NoError(t, err) require.Equal(t, "urn:ietf:params:oauth:grant-type:jwt-bearer", form.Get("grant_type")) require.Equal(t, "client-id-1", form.Get("client_id")) require.Equal(t, "client-secret-1", form.Get("client_secret")) require.Equal(t, "directory", form.Get("scope")) parts := strings.Split(form.Get("assertion"), ".") require.Len(t, parts, 3) payloadData, err := base64.RawURLEncoding.DecodeString(parts[1]) require.NoError(t, err) var payload map[string]any require.NoError(t, json.Unmarshal(payloadData, &payload)) require.Equal(t, "client-id-1", payload["iss"]) require.Equal(t, "service-account-1", payload["sub"]) require.Equal(t, float64(1710000000), payload["iat"]) require.Equal(t, float64(1710003600), payload["exp"]) } func TestWorksmobileHTTPClientListUsersUsesDirectoryAPIFirst(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "300285955") transport := &captureRoundTripper{ responses: []captureResponse{ {statusCode: http.StatusOK, body: `{"users":[{"userId":"works-user-1","userExternalKey":"user-1","email":"tester@samaneng.com","userName":{"lastName":"Tester"},"organizations":[{"primary":true,"orgUnits":[{"orgUnitId":"works-org-1","orgUnitName":"삼안"}]}]}],"responseMetaData":{}}`}, }, } client := &WorksmobileHTTPClient{ BaseURL: "https://works.example.test", DirectoryToken: "directory-token-1", SCIMToken: "scim-token-1", DomainIDs: []int64{300285955}, HTTPClient: &http.Client{Transport: transport}, } users, err := client.ListUsers(context.Background()) require.NoError(t, err) require.Len(t, users, 1) require.Equal(t, "user-1", users[0].ExternalID) require.Equal(t, "tester@samaneng.com", users[0].Email) require.Equal(t, int64(300285955), users[0].DomainID) require.Equal(t, "삼안", users[0].DomainName) require.Equal(t, "works-org-1", users[0].PrimaryOrgUnitID) require.Len(t, transport.requests, 1) require.Equal(t, "/v1.0/users", transport.requests[0].URL.Path) require.Equal(t, "300285955", transport.requests[0].URL.Query().Get("domainId")) } func TestWorksmobileHTTPClientListUsersFallsBackToSCIMWhenDirectoryFails(t *testing.T) { transport := &captureRoundTripper{ responses: []captureResponse{ {statusCode: http.StatusForbidden, body: `{"code":"FORBIDDEN"}`}, {statusCode: http.StatusOK, body: `{"totalResults":1,"Resources":[{"id":"scim-user-1","userName":"tester@samaneng.com","displayName":"Tester","emails":[]}]} `}, }, } client := &WorksmobileHTTPClient{ BaseURL: "https://works.example.test", DirectoryToken: "directory-token-1", SCIMToken: "scim-token-1", DomainIDs: []int64{300285955}, HTTPClient: &http.Client{Transport: transport}, } users, err := client.ListUsers(context.Background()) require.NoError(t, err) require.Len(t, users, 1) require.Equal(t, "scim-user-1", users[0].ID) require.Equal(t, "tester@samaneng.com", users[0].Email) require.Equal(t, "/v1.0/users", transport.requests[0].URL.Path) require.Equal(t, "/scim/v2/Users", transport.requests[1].URL.Path) } func TestWorksmobileHTTPClientListGroupsUsesDirectoryAPIFirst(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "300285955") transport := &captureRoundTripper{ responses: []captureResponse{ {statusCode: http.StatusOK, body: `{"orgUnits":[{"orgUnitId":"works-org-1","orgUnitExternalKey":"tenant-1","orgUnitName":"삼안","parentOrgUnitId":"parent-1","parentOrgUnitName":"상위"}],"responseMetaData":{}}`}, }, } client := &WorksmobileHTTPClient{ BaseURL: "https://works.example.test", DirectoryToken: "directory-token-1", SCIMToken: "scim-token-1", DomainIDs: []int64{300285955}, HTTPClient: &http.Client{Transport: transport}, } groups, err := client.ListGroups(context.Background()) require.NoError(t, err) require.Len(t, groups, 1) require.Equal(t, "tenant-1", groups[0].ExternalID) require.Equal(t, "삼안", groups[0].DisplayName) require.Equal(t, int64(300285955), groups[0].DomainID) require.Equal(t, "삼안", groups[0].DomainName) require.Equal(t, "parent-1", groups[0].ParentID) require.Equal(t, "/v1.0/orgunits", transport.requests[0].URL.Path) } func TestWorksmobileLiveJWTTokenExchange(t *testing.T) { if os.Getenv("WORKSMOBILE_LIVE_JWT_TOKEN_EXCHANGE") != "1" { t.Skip("live Worksmobile token exchange is disabled") } client := NewWorksmobileHTTPClientWithAuth("", os.Getenv("SAMAN_SCIM_LONGLIVE_TOKEN"), WorksmobileOAuthConfig{ ClientID: os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_ID"), ClientSecret: os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_SECRET"), ServiceAccount: os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_SERVICE_ACCOUNT"), PrivateKey: os.Getenv("WORKS_ADMIN_OAUTH_CLIENT_PRIVATE_KEY"), Scope: getenvDefault("WORKS_ADMIN_OAUTH_SCOPE", "directory"), }) token, err := client.directoryAccessToken(context.Background()) require.NoError(t, err) require.NotEmpty(t, token) } func TestWorksmobileRelayWorkerProcessesUserCreateAndMarksProcessed(t *testing.T) { repo := &fakeWorksmobileOutboxRepo{ ready: []domain.WorksmobileOutbox{ { ID: "job-1", ResourceType: domain.WorksmobileResourceUser, ResourceID: "user-1", Action: domain.WorksmobileActionUpsert, Status: domain.WorksmobileOutboxStatusPending, Payload: worksmobileUserOutboxPayload("root-1", WorksmobileUserPayload{ Email: "tester@samaneng.com", UserExternalKey: "user-1", PasswordConfig: WorksmobilePasswordConfig{ PasswordCreationType: "ADMIN", Password: "Aa1!Aa1!Aa1!Aa1!", }, }), }, }, } client := &fakeWorksmobileDirectoryClient{} worker := NewWorksmobileRelayWorker(repo, client) err := worker.ProcessOnce(context.Background()) require.NoError(t, err) require.Equal(t, []string{"job-1"}, repo.processingIDs) require.Equal(t, []string{"job-1"}, repo.processedIDs) require.Equal(t, "tester@samaneng.com", client.createdUsers[0].Email) } func TestRedactWorksmobileOutboxPayloadsRemovesInitialPasswordFromOverview(t *testing.T) { jobs := []domain.WorksmobileOutbox{ { ID: "job-1", Payload: domain.JSONMap{ "loginEmail": "tester@samaneng.com", "initialPassword": "Aa1!Aa1!Aa1!Aa1!", }, }, } redacted := redactWorksmobileOutboxPayloads(jobs) require.Nil(t, redacted[0].Payload) } func TestCompareWorksmobileUsersHidesMatchedByDefault(t *testing.T) { localUsers := []domain.User{ {ID: "user-1", Email: "matched@samaneng.com", Name: "Matched"}, {ID: "user-2", Email: "missing@samaneng.com", Name: "Missing"}, } remoteUsers := []WorksmobileRemoteUser{ {ID: "works-1", ExternalID: "user-1", Email: "matched@samaneng.com", DisplayName: "Matched"}, } diffOnly := compareWorksmobileUsers(localUsers, remoteUsers, false, nil) all := compareWorksmobileUsers(localUsers, remoteUsers, true, nil) require.Len(t, diffOnly, 1) require.Equal(t, "missing_in_worksmobile", diffOnly[0].Status) require.Len(t, all, 2) require.Equal(t, "matched", all[0].Status) } func TestCompareWorksmobileUsersIncludesBaronAndWorksPrimaryOrg(t *testing.T) { tenantID := "tenant-primary" localUsers := []domain.User{ {ID: "user-1", Email: "matched@samaneng.com", Name: "Matched", TenantID: &tenantID}, } localTenants := map[string]domain.Tenant{ tenantID: {ID: tenantID, Name: "기술기획", Slug: "tech-planning"}, } remoteUsers := []WorksmobileRemoteUser{ { ID: "works-1", ExternalID: "user-1", Email: "matched@samaneng.com", DisplayName: "Matched", DomainID: 300285955, DomainName: "삼안", PrimaryOrgUnitID: "works-org-1", PrimaryOrgUnitName: "WORKS 기술기획", }, } items := compareWorksmobileUsers(localUsers, remoteUsers, true, localTenants) require.Len(t, items, 1) require.Equal(t, tenantID, items[0].BaronPrimaryOrgID) require.Equal(t, "기술기획", items[0].BaronPrimaryOrgName) require.Equal(t, int64(300285955), items[0].WorksmobileDomainID) require.Equal(t, "삼안", items[0].WorksmobileDomainName) require.Equal(t, "works-org-1", items[0].WorksmobilePrimaryOrgID) require.Equal(t, "WORKS 기술기획", items[0].WorksmobilePrimaryOrgName) } func TestCompareWorksmobileUsersMatchesByEmailWhenDirectoryAPIOmitsExternalID(t *testing.T) { localUsers := []domain.User{ {ID: "user-1", Email: "tester@samaneng.com", Name: "Tester"}, } remoteUsers := []WorksmobileRemoteUser{ {ID: "works-1", ExternalID: "", Email: "tester@samaneng.com", DisplayName: "Tester"}, } diffOnly := compareWorksmobileUsers(localUsers, remoteUsers, false, nil) all := compareWorksmobileUsers(localUsers, remoteUsers, true, nil) require.Empty(t, diffOnly) require.Len(t, all, 1) require.Equal(t, "matched", all[0].Status) require.Equal(t, "works-1", all[0].WorksmobileID) require.Empty(t, all[0].ExternalKey) } func TestCompareWorksmobileUsersIncludesWorksOnlyRowsWithoutExternalIDWhenIncludingMatched(t *testing.T) { remoteUsers := []WorksmobileRemoteUser{ {ID: "works-1", ExternalID: "", Email: "works-only@samaneng.com", DisplayName: "Works Only"}, } items := compareWorksmobileUsers(nil, remoteUsers, true, nil) require.Len(t, items, 1) require.Equal(t, "missing_external_key", items[0].Status) require.Equal(t, "works-1", items[0].WorksmobileID) require.Equal(t, "works-only@samaneng.com", items[0].WorksmobileEmail) } func TestCompareWorksmobileGroupsIncludesBaronAndWorksParentOrg(t *testing.T) { parentID := "tenant-parent" childID := "tenant-child" localTenants := []domain.Tenant{ {ID: parentID, Name: "기술본부", Type: domain.TenantTypeOrganization}, {ID: childID, Name: "기술기획", Type: domain.TenantTypeOrganization, ParentID: &parentID}, } remoteGroups := []WorksmobileRemoteGroup{ { ID: "works-parent", ExternalID: parentID, DisplayName: "WORKS 기술본부", DomainID: 300286337, DomainName: "총괄기획&기술개발센터", }, { ID: "works-child", ExternalID: childID, DisplayName: "WORKS 기술기획", DomainID: 300286337, DomainName: "총괄기획&기술개발센터", ParentID: "works-parent", ParentName: "WORKS 기술본부", }, } items := compareWorksmobileGroups(localTenants, remoteGroups, true) require.Len(t, items, 2) require.Equal(t, parentID, items[1].BaronParentID) require.Equal(t, "기술본부", items[1].BaronParentName) require.Equal(t, int64(300286337), items[1].WorksmobileDomainID) require.Equal(t, "총괄기획&기술개발센터", items[1].WorksmobileDomainName) require.Equal(t, "works-parent", items[1].WorksmobileParentID) require.Equal(t, "WORKS 기술본부", items[1].WorksmobileParentName) } func TestCompareWorksmobileGroupsDoesNotMatchDomainCompanyByDomainID(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") parentID := "root-tenant" localTenants := []domain.Tenant{ { ID: "company-saman", Name: "삼안", Type: domain.TenantTypeCompany, ParentID: &parentID, Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, }, } remoteGroups := []WorksmobileRemoteGroup{ { ID: "works-org-1", DisplayName: "WORKS 전용 조직", DomainID: 1001, DomainName: "삼안", }, } items := compareWorksmobileGroups(localTenants, remoteGroups, true) require.Len(t, items, 1) require.Empty(t, items[0].BaronID) require.Equal(t, "missing_external_key", items[0].Status) } func TestCompareWorksmobileGroupsIncludesWorksOnlyRowsWithoutExternalIDWhenIncludingMatched(t *testing.T) { remoteGroups := []WorksmobileRemoteGroup{ {ID: "works-group-1", ExternalID: "", DisplayName: "WORKS 전용 조직"}, } items := compareWorksmobileGroups(nil, remoteGroups, true) require.Len(t, items, 1) require.Equal(t, "missing_external_key", items[0].Status) require.Equal(t, "works-group-1", items[0].WorksmobileID) require.Equal(t, "WORKS 전용 조직", items[0].WorksmobileName) } func TestParseWorksmobileRemoteUserUsesUserNameEmailWhenEmailsAreEmpty(t *testing.T) { user := parseWorksmobileRemoteUser(map[string]any{ "id": "works-1", "userName": "tester@samaneng.com", "displayName": "Tester", "emails": []any{}, }) require.Equal(t, "tester@samaneng.com", user.UserName) require.Equal(t, "tester@samaneng.com", user.Email) } func TestParseWorksmobileRemoteResourcesExtractsOrgFields(t *testing.T) { user := parseWorksmobileRemoteUser(map[string]any{ "id": "works-user", "externalId": "user-1", "organizations": []any{ map[string]any{ "primary": true, "orgUnitId": "works-org-1", "orgUnitName": "WORKS 기술기획", }, }, }) group := parseWorksmobileRemoteGroup(map[string]any{ "id": "works-group", "externalId": "group-1", "parent": map[string]any{ "id": "works-parent", "displayName": "WORKS 기술본부", }, }) require.Equal(t, "works-org-1", user.PrimaryOrgUnitID) require.Equal(t, "WORKS 기술기획", user.PrimaryOrgUnitName) require.Equal(t, "works-parent", group.ParentID) require.Equal(t, "WORKS 기술본부", group.ParentName) } func TestParseWorksmobileDirectoryUserIncludesFullNameLevelAndOrgRole(t *testing.T) { user := parseWorksmobileDirectoryUser(map[string]any{ "userId": "works-user", "email": "tester@samaneng.com", "userName": map[string]any{ "lastName": "홍", "firstName": "길동", }, "levelId": "level-1", "levelName": "책임", "task": "기술검토", "organizations": []any{ map[string]any{ "primary": true, "orgUnits": []any{ map[string]any{ "orgUnitId": "works-org-1", "orgUnitName": "기술기획", "positionId": "position-1", "positionName": "팀장", "isManager": true, }, }, }, }, }) require.Equal(t, "홍길동", user.DisplayName) require.Equal(t, "level-1", user.LevelID) require.Equal(t, "책임", user.LevelName) require.Equal(t, "기술검토", user.Task) require.Equal(t, "works-org-1", user.PrimaryOrgUnitID) require.Equal(t, "기술기획", user.PrimaryOrgUnitName) require.Equal(t, "position-1", user.PrimaryOrgUnitPositionID) require.Equal(t, "팀장", user.PrimaryOrgUnitPositionName) require.NotNil(t, user.PrimaryOrgUnitIsManager) require.True(t, *user.PrimaryOrgUnitIsManager) } type fakeWorksmobileOutboxRepo struct { ready []domain.WorksmobileOutbox created []domain.WorksmobileOutbox processingIDs []string processedIDs []string failedIDs []string } func (f *fakeWorksmobileOutboxRepo) Create(ctx context.Context, item *domain.WorksmobileOutbox) error { f.created = append(f.created, *item) return nil } func (f *fakeWorksmobileOutboxRepo) ListRecent(ctx context.Context, limit int) ([]domain.WorksmobileOutbox, error) { return nil, nil } func (f *fakeWorksmobileOutboxRepo) ListReady(ctx context.Context, limit int) ([]domain.WorksmobileOutbox, error) { return f.ready, nil } func (f *fakeWorksmobileOutboxRepo) FindByID(ctx context.Context, id string) (*domain.WorksmobileOutbox, error) { return nil, nil } func (f *fakeWorksmobileOutboxRepo) MarkRetry(ctx context.Context, id string) error { return nil } func (f *fakeWorksmobileOutboxRepo) MarkProcessing(ctx context.Context, id string) error { f.processingIDs = append(f.processingIDs, id) return nil } func (f *fakeWorksmobileOutboxRepo) MarkProcessed(ctx context.Context, id string) error { f.processedIDs = append(f.processedIDs, id) return nil } func (f *fakeWorksmobileOutboxRepo) MarkFailed(ctx context.Context, id string, message string, nextAttemptAt time.Time) error { f.failedIDs = append(f.failedIDs, id) return nil } type fakeWorksmobileDirectoryClient struct { createdOrgUnits []WorksmobileOrgUnitPayload createdUsers []WorksmobileUserPayload deletedUsers []string } type captureRoundTripper struct { request *http.Request requestBody []byte statusCode int body string responses []captureResponse requests []*http.Request requestBodies [][]byte } type captureResponse struct { statusCode int body string } func (t *captureRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { t.request = req t.requests = append(t.requests, req) if req.Body != nil { data, err := io.ReadAll(req.Body) if err != nil { return nil, err } t.requestBody = data t.requestBodies = append(t.requestBodies, data) } statusCode := t.statusCode body := t.body if len(t.responses) > 0 { response := t.responses[0] t.responses = t.responses[1:] statusCode = response.statusCode body = response.body } if statusCode == 0 { statusCode = http.StatusOK } return &http.Response{ StatusCode: statusCode, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(body)), Request: req, }, nil } func testRSAPrivateKeyPEM(t *testing.T) string { t.Helper() key, err := rsa.GenerateKey(rand.Reader, 2048) require.NoError(t, err) data := x509.MarshalPKCS1PrivateKey(key) return string(pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: data})) } func getenvDefault(key string, fallback string) string { if value := os.Getenv(key); value != "" { return value } return fallback } func (f *fakeWorksmobileDirectoryClient) CreateOrgUnit(ctx context.Context, payload WorksmobileOrgUnitPayload) error { f.createdOrgUnits = append(f.createdOrgUnits, payload) return nil } func (f *fakeWorksmobileDirectoryClient) CreateUser(ctx context.Context, payload WorksmobileUserPayload) error { f.createdUsers = append(f.createdUsers, payload) return nil } func (f *fakeWorksmobileDirectoryClient) UpsertUser(ctx context.Context, payload WorksmobileUserPayload) error { f.createdUsers = append(f.createdUsers, payload) return nil } func (f *fakeWorksmobileDirectoryClient) DeleteUser(ctx context.Context, userID string) error { f.deletedUsers = append(f.deletedUsers, userID) return nil } func (f *fakeWorksmobileDirectoryClient) ListUsers(ctx context.Context) ([]WorksmobileRemoteUser, error) { return nil, nil } func (f *fakeWorksmobileDirectoryClient) ListGroups(ctx context.Context) ([]WorksmobileRemoteGroup, error) { return nil, nil }