package migration import ( "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" ) func TestNormalizeAuthModeMicrosoftApp(t *testing.T) { if got := NormalizeAuthMode("microsoft", "microsoft_app"); got != AuthModeMicrosoftApp { t.Fatalf("got %q", got) } if got := NormalizeAuthMode("google", "microsoft_app"); got != AuthModeOAuth { t.Fatalf("google ignores ms app: got %q", got) } } func TestUsesUserOAuth(t *testing.T) { if UsesUserOAuth("google", AuthModeGoogleDWD) { t.Fatal("google dwd should skip user oauth") } if UsesUserOAuth("microsoft", AuthModeMicrosoftApp) { t.Fatal("microsoft app should skip user oauth") } if !UsesUserOAuth("microsoft", "oauth") { t.Fatal("microsoft oauth needs user oauth") } } func TestGraphUserBase(t *testing.T) { if got := graphUserBase(""); got != "/v1.0/me" { t.Fatalf("empty upn: %q", got) } if got := graphUserBase("alice@contoso.com"); got != "/v1.0/users/alice@contoso.com" { t.Fatalf("encoded upn: %q", got) } } func TestNewMicrosoftAppEmpty(t *testing.T) { app, err := NewMicrosoftApp(MicrosoftAppConfig{}) if err != nil || app != nil { t.Fatalf("empty config: app=%v err=%v", app, err) } } func TestMicrosoftAppAccessToken(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Fatalf("method %s", r.Method) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("grant_type") != "client_credentials" { t.Fatalf("grant_type=%q", r.Form.Get("grant_type")) } auth := r.Header.Get("Authorization") if !strings.HasPrefix(auth, "Basic ") { t.Fatalf("expected basic auth, got %q", auth) } if !strings.Contains(r.Form.Get("scope"), "graph.microsoft.com/.default") { t.Fatalf("scope=%q", r.Form.Get("scope")) } w.Header().Set("Content-Type", "application/json") _, _ = io.WriteString(w, `{"access_token":"app-token","token_type":"Bearer","expires_in":3600}`) })) t.Cleanup(srv.Close) app, err := NewMicrosoftApp(MicrosoftAppConfig{ ClientID: "client-id", ClientSecret: "client-secret", TokenURL: srv.URL, }) if err != nil { t.Fatal(err) } token, err := app.AccessToken(t.Context(), "tenant-123") if err != nil { t.Fatal(err) } if token != "app-token" { t.Fatalf("token=%q", token) } } func TestMicrosoftAppRequiresTenant(t *testing.T) { app, err := NewMicrosoftApp(MicrosoftAppConfig{ ClientID: "client-id", ClientSecret: "client-secret", TokenURL: "http://example.invalid/token", }) if err != nil { t.Fatal(err) } if _, err := app.AccessToken(t.Context(), ""); err == nil { t.Fatal("expected tenant required error") } } func TestWorkerAuthPathSelection(t *testing.T) { cases := []struct { provider string authMode string userOAuth bool }{ {"google", AuthModeOAuth, true}, {"google", AuthModeGoogleDWD, false}, {"microsoft", AuthModeOAuth, true}, {"microsoft", AuthModeMicrosoftApp, false}, {"microsoft", "google_dwd", true}, } for _, tc := range cases { got := UsesUserOAuth(tc.provider, tc.authMode) if got != tc.userOAuth { t.Fatalf("%s/%s: got userOAuth=%v want %v", tc.provider, tc.authMode, got, tc.userOAuth) } } } func TestMicrosoftAppTokenError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) _ = json.NewEncoder(w).Encode(map[string]string{"error": "invalid_client"}) })) t.Cleanup(srv.Close) app, err := NewMicrosoftApp(MicrosoftAppConfig{ ClientID: "bad", ClientSecret: "bad", TokenURL: srv.URL, }) if err != nil { t.Fatal(err) } if _, err := app.AccessToken(t.Context(), "tenant"); err == nil { t.Fatal("expected token error") } }