package handler import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/service" "bytes" "context" "errors" "io" "log/slog" "net/http/httptest" "testing" "github.com/gofiber/fiber/v2" "github.com/stretchr/testify/require" ) func TestWorksmobileHandlerRejectsNonHanmacTenant(t *testing.T) { h := NewWorksmobileHandler(&fakeWorksmobileAdminService{ overview: service.WorksmobileTenantOverview{ Tenant: domain.Tenant{ID: "tenant-1", Slug: "other"}, }, }) app := fiber.New() app.Get("/tenants/:tenantId/worksmobile", h.GetOverview) resp, err := app.Test(httptest.NewRequest("GET", "/tenants/tenant-1/worksmobile", nil)) require.NoError(t, err) require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } func TestWorksmobileHandlerReturnsOverviewForHanmacTenant(t *testing.T) { h := NewWorksmobileHandler(&fakeWorksmobileAdminService{ overview: service.WorksmobileTenantOverview{ Tenant: domain.Tenant{ID: "hanmac-id", Slug: "hanmac-family"}, Config: service.WorksmobileConfigSummary{ Enabled: true, }, }, }) app := fiber.New() app.Get("/tenants/:tenantId/worksmobile", h.GetOverview) resp, err := app.Test(httptest.NewRequest("GET", "/tenants/hanmac-id/worksmobile", nil)) require.NoError(t, err) require.Equal(t, fiber.StatusOK, resp.StatusCode) } func TestWorksmobileHandlerDownloadsInitialPasswordCSV(t *testing.T) { h := NewWorksmobileHandler(&fakeWorksmobileAdminService{ credentials: []service.WorksmobileInitialPasswordCredential{ {Email: "user@hanmaceng.co.kr", InitialPassword: "Aa1!Aa1!Aa1!Aa1!", Status: "processed"}, }, }) app := fiber.New() app.Get("/tenants/:tenantId/worksmobile/initial-passwords.csv", h.DownloadInitialPasswordsCSV) resp, err := app.Test(httptest.NewRequest("GET", "/tenants/hanmac-id/worksmobile/initial-passwords.csv", nil)) require.NoError(t, err) require.Equal(t, fiber.StatusOK, resp.StatusCode) require.Contains(t, resp.Header.Get("Content-Disposition"), "worksmobile_initial_passwords.csv") body, err := io.ReadAll(resp.Body) require.NoError(t, err) require.Contains(t, string(body), "email,initialPassword,status,lastError") require.Contains(t, string(body), "user@hanmaceng.co.kr,Aa1!Aa1!Aa1!Aa1!,processed,") } func TestWorksmobileHandlerLogsActionFailures(t *testing.T) { var logs bytes.Buffer previous := slog.Default() slog.SetDefault(slog.New(slog.NewJSONHandler(&logs, nil))) t.Cleanup(func() { slog.SetDefault(previous) }) h := NewWorksmobileHandler(&fakeWorksmobileAdminService{ syncUserErr: errors.New("works user sync failed"), }) app := fiber.New() app.Post("/tenants/:tenantId/worksmobile/users/:userId/sync", h.SyncUser) resp, err := app.Test(httptest.NewRequest("POST", "/tenants/hanmac-id/worksmobile/users/user-1/sync", nil)) require.NoError(t, err) require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) require.Contains(t, logs.String(), "worksmobile admin operation failed") require.Contains(t, logs.String(), "sync_user") require.Contains(t, logs.String(), "works user sync failed") } type fakeWorksmobileAdminService struct { overview service.WorksmobileTenantOverview credentials []service.WorksmobileInitialPasswordCredential syncUserErr error } func (f *fakeWorksmobileAdminService) GetTenantOverview(ctx context.Context, tenantID string) (service.WorksmobileTenantOverview, error) { return f.overview, nil } func (f *fakeWorksmobileAdminService) GetComparison(ctx context.Context, tenantID string, includeMatched bool) (service.WorksmobileComparison, error) { return service.WorksmobileComparison{}, nil } func (f *fakeWorksmobileAdminService) EnqueueBackfillDryRun(ctx context.Context, tenantID string) (service.WorksmobileBackfillDryRun, error) { return service.WorksmobileBackfillDryRun{}, nil } func (f *fakeWorksmobileAdminService) EnqueueOrgUnitSync(ctx context.Context, tenantID, orgUnitID string) (*domain.WorksmobileOutbox, error) { return &domain.WorksmobileOutbox{ID: "job-orgunit", ResourceID: orgUnitID}, nil } func (f *fakeWorksmobileAdminService) EnqueueOrgUnitDelete(ctx context.Context, tenantID, orgUnitID string) (*domain.WorksmobileOutbox, error) { return &domain.WorksmobileOutbox{ID: "job-orgunit-delete", ResourceID: orgUnitID, Action: domain.WorksmobileActionDelete}, nil } func (f *fakeWorksmobileAdminService) EnqueueUserSync(ctx context.Context, tenantID, userID string) (*domain.WorksmobileOutbox, error) { if f.syncUserErr != nil { return nil, f.syncUserErr } return &domain.WorksmobileOutbox{ID: "job-user", ResourceID: userID}, nil } func (f *fakeWorksmobileAdminService) RetryJob(ctx context.Context, tenantID, jobID string) (*domain.WorksmobileOutbox, error) { return &domain.WorksmobileOutbox{ID: jobID}, nil } func (f *fakeWorksmobileAdminService) ListInitialPasswordCredentials(ctx context.Context, tenantID string) ([]service.WorksmobileInitialPasswordCredential, error) { return f.credentials, nil }