Files
ragflow/internal/service/connector_box_oauth_test.go
Hz_ 3633d08495 feat(go-api): Migrate Box web OAuth connector APIs to Go (#16480)
This PR migrates the Box web OAuth flow from Python to Go for:

  - POST /api/v1/connectors/box/oauth/web/start
  - GET /api/v1/connectors/box/oauth/web/callback
  - POST /api/v1/connectors/box/oauth/web/result
2026-06-30 18:10:36 +08:00

172 lines
4.7 KiB
Go

package service
import (
"io"
"net/http"
"net/url"
"strings"
"testing"
"ragflow/internal/common"
redisengine "ragflow/internal/engine/redis"
)
type roundTripFunc func(*http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}
func forceNilConnectorRedis(t *testing.T) {
t.Helper()
previous := connectorRedisGet
connectorRedisGet = func() *redisengine.RedisClient { return nil }
t.Cleanup(func() {
connectorRedisGet = previous
})
}
func TestBuildBoxAuthorizationURL(t *testing.T) {
authURL, err := buildBoxAuthorizationURL("client-1", "http://localhost/callback", "flow-1")
if err != nil {
t.Fatalf("buildBoxAuthorizationURL: %v", err)
}
parsed, err := url.Parse(authURL)
if err != nil {
t.Fatalf("parse auth url: %v", err)
}
if got := parsed.Scheme + "://" + parsed.Host + parsed.Path; got != boxOAuthAuthorizeURL {
t.Fatalf("base url=%q want=%q", got, boxOAuthAuthorizeURL)
}
query := parsed.Query()
if query.Get("client_id") != "client-1" {
t.Fatalf("client_id=%q", query.Get("client_id"))
}
if query.Get("redirect_uri") != "http://localhost/callback" {
t.Fatalf("redirect_uri=%q", query.Get("redirect_uri"))
}
if query.Get("response_type") != "code" {
t.Fatalf("response_type=%q", query.Get("response_type"))
}
if query.Get("state") != "flow-1" {
t.Fatalf("state=%q", query.Get("state"))
}
}
func TestRenderWebOAuthPopupBoxPayload(t *testing.T) {
html := renderWebOAuthPopup("flow-1", true, "Authorization completed successfully.", "box")
for _, want := range []string{
"Box Authorization",
"Authorization complete",
"ragflow-box-oauth",
`"status":"success"`,
`"flowId":"flow-1"`,
"window.close();",
} {
if !strings.Contains(html, want) {
t.Fatalf("popup html missing %q:\n%s", want, html)
}
}
}
func TestDefaultBoxWebOAuthRedirectURI(t *testing.T) {
t.Setenv("BOX_WEB_OAUTH_REDIRECT_URI", "http://example.test/box/callback")
if got := defaultBoxWebOAuthRedirectURI(); got != "http://example.test/box/callback" {
t.Fatalf("redirect uri=%q", got)
}
}
func TestExchangeBoxAuthorizationCodeRequestBody(t *testing.T) {
oldClient := http.DefaultClient
t.Cleanup(func() { http.DefaultClient = oldClient })
var form url.Values
http.DefaultClient = &http.Client{
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
if req.Method != http.MethodPost {
t.Fatalf("method=%s", req.Method)
}
if req.URL.String() != boxOAuthTokenURL {
t.Fatalf("url=%s", req.URL.String())
}
if got := req.Header.Get("Content-Type"); got != "application/x-www-form-urlencoded" {
t.Fatalf("content-type=%q", got)
}
body, err := io.ReadAll(req.Body)
if err != nil {
t.Fatalf("read body: %v", err)
}
form, err = url.ParseQuery(string(body))
if err != nil {
t.Fatalf("parse body: %v", err)
}
return &http.Response{
StatusCode: http.StatusOK,
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader(`{"access_token":"access-1","refresh_token":"refresh-1"}`)),
}, nil
}),
}
token, err := exchangeBoxAuthorizationCode("client-1", "secret-1", "http://localhost/callback", "code-1")
if err != nil {
t.Fatalf("exchangeBoxAuthorizationCode: %v", err)
}
if token.AccessToken != "access-1" || token.RefreshToken != "refresh-1" {
t.Fatalf("token=%#v", token)
}
want := map[string]string{
"grant_type": "authorization_code",
"code": "code-1",
"client_id": "client-1",
"client_secret": "secret-1",
}
for key, value := range want {
if form.Get(key) != value {
t.Fatalf("%s=%q want=%q", key, form.Get(key), value)
}
}
if got := form.Get("redirect_uri"); got != "" {
t.Fatalf("redirect_uri should not be sent to Box token endpoint, got %q", got)
}
}
func TestStartBoxWebOAuthRequiresClientCredentials(t *testing.T) {
svc := NewConnectorService()
_, code, err := svc.StartBoxWebOAuth("user-1", &StartBoxWebOAuthRequest{ClientID: "client-1"})
if err == nil {
t.Fatal("expected error")
}
if code != common.CodeArgumentError {
t.Fatalf("code=%v want=%v", code, common.CodeArgumentError)
}
if err.Error() != "Box client_id and client_secret are required." {
t.Fatalf("error=%q", err.Error())
}
}
func TestPollBoxWebOAuthResultPendingWithoutRedis(t *testing.T) {
forceNilConnectorRedis(t)
svc := NewConnectorService()
_, code, err := svc.PollBoxWebOAuthResult("user-1", &PollBoxWebOAuthResultRequest{FlowID: "flow-1"})
if err == nil {
t.Fatal("expected pending error")
}
if code != common.CodeRunning {
t.Fatalf("code=%v want=%v", code, common.CodeRunning)
}
if err.Error() != "Authorization is still pending." {
t.Fatalf("error=%q", err.Error())
}
}