Files
gochat/internal/handler/api/v1/twitter_authorization_handler_test.go
T

133 lines
5.1 KiB
Go

package v1
import (
"encoding/json"
"io"
"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 setupTwitterAuthorizationRouter() *gin.Engine {
gin.SetMode(gin.TestMode)
r := gin.New()
r.RedirectTrailingSlash = false
handler := NewTwitterChannelHandler(nil, nil, nil, nil)
r.POST("/api/v1/accounts/:account_id/twitter/authorization", handler.ChatwootAuthorization)
return r
}
func TestTwitterAuthorization_BadAccountID(t *testing.T) {
r := setupTwitterAuthorizationRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/abc/twitter/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 TestTwitterAuthorization_ReturnsChatwootPayload(t *testing.T) {
t.Setenv("TWITTER_CONSUMER_KEY", "twitter-key")
t.Setenv("TWITTER_CONSUMER_SECRET", "twitter-secret")
t.Setenv("TWITTER_OAUTH_REQUEST_TOKEN_URL", "https://oauth.example.test/request_token")
t.Setenv("TWITTER_API_BASE_URL", "https://api.twitter.test")
t.Setenv("FRONTEND_URL", "https://app.example.test/")
originalClient := twitterAuthorizationHTTPClient
twitterAuthorizationHTTPClient = &http.Client{Transport: twitterAuthorizationRoundTripFunc(func(req *http.Request) (*http.Response, error) {
assert.Equal(t, http.MethodPost, req.Method)
assert.Equal(t, "https://oauth.example.test/request_token", req.URL.String())
authHeader := req.Header.Get("Authorization")
assert.Contains(t, authHeader, "OAuth ")
assert.Contains(t, authHeader, `oauth_consumer_key="twitter-key"`)
assert.Contains(t, authHeader, "oauth_callback=")
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("oauth_token=request-token&oauth_token_secret=request-secret")), Header: http.Header{}}, nil
})}
t.Cleanup(func() { twitterAuthorizationHTTPClient = originalClient })
r := setupTwitterAuthorizationRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/twitter/authorization", 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"])
assert.Equal(t, "https://api.twitter.test/oauth/authorize?oauth_token=request-token", resp["url"])
}
func TestTwitterAuthorization_SignedStateMatchesCallback(t *testing.T) {
t.Setenv("TWITTER_CONSUMER_KEY", "twitter-key")
t.Setenv("TWITTER_CONSUMER_SECRET", "twitter-secret")
t.Setenv("TWITTER_OAUTH_REQUEST_TOKEN_URL", "https://oauth.example.test/request_token")
t.Setenv("FRONTEND_URL", "https://app.example.test")
originalClient := twitterAuthorizationHTTPClient
var callbackURL string
twitterAuthorizationHTTPClient = &http.Client{Transport: twitterAuthorizationRoundTripFunc(func(req *http.Request) (*http.Response, error) {
authHeader := req.Header.Get("Authorization")
for _, part := range strings.Split(strings.TrimPrefix(authHeader, "OAuth "), ",") {
part = strings.TrimSpace(part)
if strings.HasPrefix(part, "oauth_callback=") {
callbackURL = strings.Trim(strings.TrimPrefix(part, "oauth_callback="), `"`)
decoded, err := url.QueryUnescape(callbackURL)
require.NoError(t, err)
callbackURL = decoded
}
}
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("oauth_token=request-token")), Header: http.Header{}}, nil
})}
t.Cleanup(func() { twitterAuthorizationHTTPClient = originalClient })
r := setupTwitterAuthorizationRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/twitter/authorization", nil)
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
parsed, err := url.Parse(callbackURL)
require.NoError(t, err)
assert.Equal(t, "https://app.example.test/twitter/callback", parsed.Scheme+"://"+parsed.Host+parsed.Path)
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(parsed.Query().Get("state"), claims, func(token *jwt.Token) (any, error) {
return []byte("twitter-secret"), nil
})
require.NoError(t, err)
require.True(t, token.Valid)
assert.Equal(t, float64(42), claims["sub"])
}
func TestTwitterAuthorization_NotConfigured(t *testing.T) {
t.Setenv("TWITTER_CONSUMER_KEY", "")
t.Setenv("TWITTER_CONSUMER_SECRET", "")
r := setupTwitterAuthorizationRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/twitter/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"])
}
type twitterAuthorizationRoundTripFunc func(*http.Request) (*http.Response, error)
func (f twitterAuthorizationRoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}