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) }