1
0
forked from baron/baron-sso

동기화 기초구조 마련

This commit is contained in:
2026-05-12 12:25:31 +09:00
parent 3063450ee0
commit 5e649c279f
33 changed files with 3364 additions and 408 deletions

View File

@@ -262,6 +262,68 @@ func TestWorksmobileHTTPClientListGroupsUsesDirectoryAPIFirst(t *testing.T) {
require.Equal(t, "/v1.0/orgunits", transport.requests[0].URL.Path)
}
func TestWorksmobileHTTPClientUpsertOrgUnitBackfillsExternalKeyByMailLocalPart(t *testing.T) {
transport := &captureRoundTripper{
responses: []captureResponse{
{statusCode: http.StatusConflict, body: `{"code":"CONFLICT"}`},
{statusCode: http.StatusOK, body: `{"orgUnits":[{"orgUnitId":"works-org-1","orgUnitName":"기술개발센터","email":"tech-dev-center@samaneng.com"}],"responseMetaData":{}}`},
{statusCode: http.StatusOK, body: `{}`},
},
}
client := &WorksmobileHTTPClient{
BaseURL: "https://works.example.test",
DirectoryToken: "directory-token-1",
DomainIDs: []int64{300285955},
HTTPClient: &http.Client{Transport: transport},
OrgUnitWriteDelay: -1,
}
err := client.UpsertOrgUnit(context.Background(), WorksmobileOrgUnitPayload{
DomainID: 300285955,
OrgUnitName: "기술개발센터",
OrgUnitExternalKey: "tenant-tech-dev-center",
DisplayOrder: 0,
}, "tech-dev-center")
require.NoError(t, err)
require.Len(t, transport.requests, 3)
require.Equal(t, http.MethodPost, transport.requests[0].Method)
require.Equal(t, "/v1.0/orgunits", transport.requests[0].URL.Path)
require.Equal(t, http.MethodGet, transport.requests[1].Method)
require.Equal(t, "/v1.0/orgunits", transport.requests[1].URL.Path)
require.Equal(t, http.MethodPatch, transport.requests[2].Method)
require.Equal(t, "/v1.0/orgunits/works-org-1", transport.requests[2].URL.Path)
require.Contains(t, string(transport.requestBodies[1]), `"orgUnitExternalKey":"tenant-tech-dev-center"`)
}
func TestWorksmobileHTTPClientUpsertOrgUnitTreatsExistingExternalKeyConflictAsSuccess(t *testing.T) {
transport := &captureRoundTripper{
responses: []captureResponse{
{statusCode: http.StatusConflict, body: `{"code":"CONFLICT"}`},
{statusCode: http.StatusOK, body: `{"orgUnits":[{"orgUnitId":"works-org-1","orgUnitExternalKey":"tenant-tech-dev-center","orgUnitName":"기술개발센터"}],"responseMetaData":{}}`},
},
}
client := &WorksmobileHTTPClient{
BaseURL: "https://works.example.test",
DirectoryToken: "directory-token-1",
DomainIDs: []int64{300285955},
HTTPClient: &http.Client{Transport: transport},
}
err := client.UpsertOrgUnit(context.Background(), WorksmobileOrgUnitPayload{
DomainID: 300285955,
OrgUnitName: "기술개발센터",
OrgUnitExternalKey: "tenant-tech-dev-center",
}, "")
require.NoError(t, err)
require.Len(t, transport.requests, 3)
require.Equal(t, http.MethodPost, transport.requests[0].Method)
require.Equal(t, http.MethodGet, transport.requests[1].Method)
require.Equal(t, http.MethodPatch, transport.requests[2].Method)
require.Contains(t, string(transport.requestBodies[1]), `"orgUnitExternalKey":"tenant-tech-dev-center"`)
}
func TestWorksmobileLiveJWTTokenExchange(t *testing.T) {
if os.Getenv("WORKSMOBILE_LIVE_JWT_TOKEN_EXCHANGE") != "1" {
t.Skip("live Worksmobile token exchange is disabled")
@@ -486,6 +548,35 @@ func TestCompareWorksmobileGroupsIncludesWorksOnlyRowsWithoutExternalIDWhenInclu
require.Equal(t, "WORKS 전용 조직", items[0].WorksmobileName)
}
func TestCompareWorksmobileGroupsMatchesBySlugLocalPartWhenExternalIDMissing(t *testing.T) {
localTenants := []domain.Tenant{
{
ID: "tenant-tech-dev-center",
Slug: "tech-dev-center",
Name: "기술개발센터",
Type: domain.TenantTypeOrganization,
},
}
remoteGroups := []WorksmobileRemoteGroup{
{
ID: "works-org-1",
DisplayName: "기술개발센터",
Email: "tech-dev-center@samaneng.com",
MailLocalPart: "tech-dev-center",
},
}
diffOnly := compareWorksmobileGroups(localTenants, remoteGroups, false)
all := compareWorksmobileGroups(localTenants, remoteGroups, true)
require.Empty(t, diffOnly)
require.Len(t, all, 1)
require.Equal(t, "matched", all[0].Status)
require.Equal(t, "tenant-tech-dev-center", all[0].BaronID)
require.Equal(t, "works-org-1", all[0].WorksmobileID)
require.Empty(t, all[0].ExternalKey)
}
func TestParseWorksmobileRemoteUserUsesUserNameEmailWhenEmailsAreEmpty(t *testing.T) {
user := parseWorksmobileRemoteUser(map[string]any{
"id": "works-1",
@@ -564,6 +655,17 @@ func TestParseWorksmobileDirectoryUserIncludesFullNameLevelAndOrgRole(t *testing
require.True(t, *user.PrimaryOrgUnitIsManager)
}
func TestParseWorksmobileDirectoryGroupExtractsMailLocalPart(t *testing.T) {
group := parseWorksmobileDirectoryGroup(map[string]any{
"orgUnitId": "works-org-1",
"orgUnitName": "기술개발센터",
"email": "tech-dev-center@samaneng.com",
})
require.Equal(t, "tech-dev-center@samaneng.com", group.Email)
require.Equal(t, "tech-dev-center", group.MailLocalPart)
}
type fakeWorksmobileOutboxRepo struct {
ready []domain.WorksmobileOutbox
created []domain.WorksmobileOutbox
@@ -609,9 +711,10 @@ func (f *fakeWorksmobileOutboxRepo) MarkFailed(ctx context.Context, id string, m
}
type fakeWorksmobileDirectoryClient struct {
createdOrgUnits []WorksmobileOrgUnitPayload
createdUsers []WorksmobileUserPayload
deletedUsers []string
createdOrgUnits []WorksmobileOrgUnitPayload
createdUsers []WorksmobileUserPayload
deletedUsers []string
orgUnitMatchKeys []string
}
type captureRoundTripper struct {
@@ -679,6 +782,12 @@ func (f *fakeWorksmobileDirectoryClient) CreateOrgUnit(ctx context.Context, payl
return nil
}
func (f *fakeWorksmobileDirectoryClient) UpsertOrgUnit(ctx context.Context, payload WorksmobileOrgUnitPayload, matchLocalPart string) error {
f.createdOrgUnits = append(f.createdOrgUnits, payload)
f.orgUnitMatchKeys = append(f.orgUnitMatchKeys, matchLocalPart)
return nil
}
func (f *fakeWorksmobileDirectoryClient) CreateUser(ctx context.Context, payload WorksmobileUserPayload) error {
f.createdUsers = append(f.createdUsers, payload)
return nil