Files
sub2api/backend/internal/handler/auth_dingtalk_client_test.go
T
李建琦 6d655c9903
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
2026-08-21 18:30:13 +08:00

144 lines
5.0 KiB
Go

package handler
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestDingTalkClient_ExchangeCodeForUserToken_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "POST", r.Method)
require.Equal(t, "/v1.0/oauth2/userAccessToken", r.URL.Path)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"accessToken":"USER_TOKEN_X","expireIn":7200,"refreshToken":"R","corpId":"dingABC"}`))
}))
defer server.Close()
cli := &DingTalkClient{
cfg: dingTalkClientConfig{
ClientID: "k", ClientSecret: "s",
TokenURL: server.URL + "/v1.0/oauth2/userAccessToken",
},
httpClient: server.Client(),
}
resp, err := cli.ExchangeCodeForUserToken(context.Background(), "AUTH_CODE")
require.NoError(t, err)
require.Equal(t, "USER_TOKEN_X", resp.AccessToken)
require.Equal(t, "dingABC", resp.CorpID)
}
func TestDingTalkClient_GetUnionIdByUserToken_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "USER_TOKEN_X", r.Header.Get("x-acs-dingtalk-access-token"))
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"nick":"张三","unionId":"UID_AAA","openId":"OPEN","avatarUrl":"http://x"}`))
}))
defer server.Close()
cli := &DingTalkClient{
cfg: dingTalkClientConfig{UserInfoURL: server.URL + "/v1.0/contact/users/me"},
httpClient: server.Client(),
}
unionID, nick, err := cli.GetUnionIdByUserToken(context.Background(), "USER_TOKEN_X")
require.NoError(t, err)
require.Equal(t, "UID_AAA", unionID)
require.Equal(t, "张三", nick)
}
func TestDingTalkClient_GetAppToken_Cached(t *testing.T) {
callCount := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
callCount++
_, _ = w.Write([]byte(`{"accessToken":"APP_TKN","expireIn":7200}`))
}))
defer server.Close()
cli := &DingTalkClient{
cfg: dingTalkClientConfig{ClientID: "k", ClientSecret: "s", TokenURL: server.URL + "/gettoken"},
httpClient: server.Client(),
}
t1, err := cli.GetAppToken(context.Background())
require.NoError(t, err)
t2, err := cli.GetAppToken(context.Background())
require.NoError(t, err)
require.Equal(t, t1, t2)
require.Equal(t, 1, callCount, "second call should hit cache")
}
func TestDingTalkClient_GetUserIdByUnionId_60011(t *testing.T) {
appTokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`{"accessToken":"APP_TKN","expireIn":7200}`))
}))
defer appTokenServer.Close()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"errcode":60011,"errmsg":"not in directory"}`))
}))
defer server.Close()
cli := &DingTalkClient{
cfg: dingTalkClientConfig{TokenURL: appTokenServer.URL + "/gettoken"},
httpClient: server.Client(),
}
cli.appToken = "APP_TKN"
cli.appTokenExp = time.Now().Add(time.Hour)
cli.cfg.UserInfoURL = server.URL + "/v1.0/contact/users/byUnionId"
_, err := cli.GetUserIdByUnionId(context.Background(), "UID_AAA")
require.Error(t, err)
apiErr, ok := err.(*DingTalkAPIError)
require.True(t, ok)
require.Equal(t, "60011", apiErr.Code)
}
// TestDingTalkClient_GetDeptInfo_Success 验证 GetDeptInfo 正常情况返回部门信息。
func TestDingTalkClient_GetDeptInfo_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"errcode":0,"errmsg":"ok","result":{"dept_id":42,"name":"AI数据","parent_id":1}}`))
}))
defer server.Close()
cli := &DingTalkClient{
cfg: dingTalkClientConfig{
UserInfoURL: server.URL + "/stub", // 不含 /contact/users/me,走 test stub 路径
},
httpClient: server.Client(),
}
cli.appToken = "APP_TKN"
cli.appTokenExp = time.Now().Add(time.Hour)
info, err := cli.GetDeptInfo(context.Background(), 42)
require.NoError(t, err)
require.Equal(t, int64(42), info.DeptID)
require.Equal(t, "AI数据", info.Name)
require.Equal(t, int64(1), info.ParentID)
}
// TestDingTalkClient_GetDeptInfo_ErrCode60003 验证 errcode=60003(部门不存在)时返回错误。
func TestDingTalkClient_GetDeptInfo_ErrCode60003(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"errcode":60003,"errmsg":"dept not found"}`))
}))
defer server.Close()
cli := &DingTalkClient{
cfg: dingTalkClientConfig{UserInfoURL: server.URL + "/stub"},
httpClient: server.Client(),
}
cli.appToken = "APP_TKN"
cli.appTokenExp = time.Now().Add(time.Hour)
_, err := cli.GetDeptInfo(context.Background(), 999)
require.Error(t, err)
apiErr, ok := err.(*DingTalkAPIError)
require.True(t, ok)
require.Equal(t, "60003", apiErr.Code)
}