package v1 import ( "encoding/json" "net/http" "net/http/httptest" "net/url" "strings" "testing" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func setupSocialAuthorizationRouter() *gin.Engine { gin.SetMode(gin.TestMode) r := gin.New() r.RedirectTrailingSlash = false instagramHandler := NewInstagramChannelHandler(nil, nil, nil, nil) tiktokHandler := NewTikTokChannelHandler(nil, nil, nil, nil) r.POST("/api/v1/accounts/:account_id/instagram/authorization", instagramHandler.ChatwootAuthorization) r.POST("/api/v1/accounts/:account_id/tiktok/authorization", tiktokHandler.ChatwootAuthorization) return r } func TestInstagramAuthorization_ReturnsChatwootPayload(t *testing.T) { t.Setenv("INSTAGRAM_APP_ID", "instagram-client") t.Setenv("INSTAGRAM_APP_SECRET", "instagram-secret") t.Setenv("FRONTEND_URL", "https://app.example.test/") r := setupSocialAuthorizationRouter() w := httptest.NewRecorder() req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/instagram/authorization", strings.NewReader(`{"return_to":"onboarding"}`)) req.Header.Set("Content-Type", "application/json") r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) var resp map[string]interface{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp)) assert.Equal(t, true, resp["success"]) parsed, err := url.Parse(resp["url"].(string)) require.NoError(t, err) assert.Equal(t, "https://api.instagram.com/oauth/authorize", parsed.Scheme+"://"+parsed.Host+parsed.Path) query := parsed.Query() assert.Equal(t, "instagram-client", query.Get("client_id")) assert.Equal(t, "https://app.example.test/instagram/callback", query.Get("redirect_uri")) assert.Equal(t, "instagram_business_basic,instagram_business_manage_messages", query.Get("scope")) assert.Equal(t, "0", query.Get("enable_fb_login")) assert.Equal(t, "1", query.Get("force_authentication")) assert.Equal(t, "code", query.Get("response_type")) assertSocialSignedState(t, query.Get("state"), "instagram-secret", 42, "onboarding") } func TestTikTokAuthorization_ReturnsChatwootPayload(t *testing.T) { t.Setenv("TIKTOK_APP_ID", "tiktok-client") t.Setenv("TIKTOK_APP_SECRET", "tiktok-secret") t.Setenv("FRONTEND_URL", "https://app.example.test") r := setupSocialAuthorizationRouter() w := httptest.NewRecorder() req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/tiktok/authorization?return_to=onboarding", nil) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) var resp map[string]interface{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp)) assert.Equal(t, true, resp["success"]) parsed, err := url.Parse(resp["url"].(string)) require.NoError(t, err) assert.Equal(t, "https://www.tiktok.com/v2/auth/authorize", parsed.Scheme+"://"+parsed.Host+parsed.Path) query := parsed.Query() assert.Equal(t, "tiktok-client", query.Get("client_id")) assert.Equal(t, "tiktok-client", query.Get("client_key")) assert.Equal(t, "https://app.example.test/tiktok/callback", query.Get("redirect_uri")) assert.Equal(t, tiktokAuthorizationScope, query.Get("scope")) assert.Equal(t, "code", query.Get("response_type")) assertSocialSignedState(t, query.Get("state"), "tiktok-secret", 42, "onboarding") } func TestSocialAuthorization_BadAccountID(t *testing.T) { r := setupSocialAuthorizationRouter() w := httptest.NewRecorder() req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/abc/instagram/authorization", nil) r.ServeHTTP(w, req) assert.Equal(t, http.StatusBadRequest, w.Code) var resp map[string]interface{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp)) errBody := resp["error"].(map[string]interface{}) assert.Contains(t, errBody["message"], "invalid account_id") } func TestSocialAuthorization_NotConfigured(t *testing.T) { t.Setenv("INSTAGRAM_APP_ID", "") t.Setenv("INSTAGRAM_APP_SECRET", "") r := setupSocialAuthorizationRouter() w := httptest.NewRecorder() req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/instagram/authorization", nil) r.ServeHTTP(w, req) assert.Equal(t, http.StatusUnprocessableEntity, w.Code) var resp map[string]interface{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp)) assert.Equal(t, false, resp["success"]) } func assertSocialSignedState(t *testing.T, state string, secret string, accountID float64, returnTo string) { t.Helper() claims := jwt.MapClaims{} token, err := jwt.ParseWithClaims(state, claims, func(token *jwt.Token) (any, error) { return []byte(secret), nil }) require.NoError(t, err) require.True(t, token.Valid) assert.Equal(t, accountID, claims["sub"]) assert.Equal(t, returnTo, claims["return_to"]) }