Files
dingtalk-workspace-cli/internal/auth/vendor_authcode_test.go

179 lines
5.7 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestFetchVendorAuthCodeSuccessEnvelope(t *testing.T) {
var gotBody map[string]string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != MCPVendorAuthCodePath {
t.Fatalf("path = %q, want %s", r.URL.Path, MCPVendorAuthCodePath)
}
if r.Method != http.MethodPost {
t.Fatalf("method = %s, want POST", r.Method)
}
if got := r.Header.Get("x-user-access-token"); got != "user-token" {
t.Fatalf("x-user-access-token = %q", got)
}
if got := r.Header.Get("x-dws-client-id"); got != "dws-client" {
t.Fatalf("x-dws-client-id = %q", got)
}
if got := r.Header.Get("x-dws-cli-version"); got != "1.2.3" {
t.Fatalf("x-dws-cli-version = %q", got)
}
if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil {
t.Fatalf("decode body: %v", err)
}
w.Header().Set("Cache-Control", "no-store")
_, _ = io.WriteString(w, `{"authCode":"tmp-code","expiresIn":120}`)
}))
defer srv.Close()
got, err := FetchVendorAuthCode(context.Background(), VendorAuthCodeInput{
AccessToken: "user-token",
ClientID: "dws-client",
CLIVersion: "1.2.3",
Vendor: "SafeChat",
CorpID: "dingxxxxxxxxxxxx",
HTTPClient: srv.Client(),
BaseURL: srv.URL,
})
if err != nil {
t.Fatalf("FetchVendorAuthCode() = %v", err)
}
if got.AuthCode != "tmp-code" || got.ExpiresIn != 120 {
t.Fatalf("result = %+v", got)
}
if gotBody["vendor"] != "safechat" || gotBody["corpId"] != "dingxxxxxxxxxxxx" {
t.Fatalf("posted body = %v", gotBody)
}
if _, ok := gotBody["redirectURI"]; ok {
t.Fatalf("posted redirectURI, body = %v", gotBody)
}
if _, ok := gotBody["domain"]; ok {
t.Fatalf("posted domain, body = %v", gotBody)
}
}
func TestFetchVendorAuthCodeParsesAlways200ServiceResult(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = io.WriteString(w, `{"success":false,"errorCode":"VENDOR_NOT_ENABLED","errorMsg":"not installed"}`)
}))
defer srv.Close()
_, err := FetchVendorAuthCode(context.Background(), VendorAuthCodeInput{
AccessToken: "user-token",
ClientID: "dws-client",
Vendor: "safechat",
CorpID: "dingxxxxxxxxxxxx",
HTTPClient: srv.Client(),
BaseURL: srv.URL,
})
var verr *VendorAuthCodeError
if !errors.As(err, &verr) || verr.Code != VendorAuthCodeVendorNotEnabled {
t.Fatalf("error = %v, want VENDOR_NOT_ENABLED", err)
}
if verr.Retryable() {
t.Fatal("VENDOR_NOT_ENABLED must not be retryable")
}
}
func TestFetchVendorAuthCodeRequiresLocalFields(t *testing.T) {
_, err := FetchVendorAuthCode(context.Background(), VendorAuthCodeInput{Vendor: "safechat", CorpID: "ding"})
var verr *VendorAuthCodeError
if !errors.As(err, &verr) || verr.Code != VendorAuthCodeParamError {
t.Fatalf("error = %v, want PARAM_ERROR", err)
}
}
func TestFetchVendorAuthCodeKeepsHTTPStatusOnNon200(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
http.Error(w, "oops", http.StatusBadGateway)
}))
defer srv.Close()
_, err := FetchVendorAuthCode(context.Background(), VendorAuthCodeInput{
AccessToken: "user-token",
ClientID: "dws-client",
Vendor: "safechat",
CorpID: "dingxxxxxxxxxxxx",
HTTPClient: srv.Client(),
BaseURL: srv.URL,
})
var statusErr *HTTPStatusError
if !errors.As(err, &statusErr) || statusErr.StatusCode != http.StatusBadGateway {
t.Fatalf("error = %v, want HTTP 502", err)
}
}
func TestParseVendorAuthCodeResponseDefaultsExpiresIn(t *testing.T) {
got, err := parseVendorAuthCodeResponse([]byte(`{"authCode":"x"}`))
if err != nil {
t.Fatalf("parse: %v", err)
}
if got.ExpiresIn != DefaultVendorAuthCodeExpiresIn {
t.Fatalf("expiresIn = %d, want %d", got.ExpiresIn, DefaultVendorAuthCodeExpiresIn)
}
}
func TestVendorAuthCodeErrorRetryable(t *testing.T) {
for _, code := range []string{VendorAuthCodeTokenInvalid, VendorAuthCodeRateLimited, VendorAuthCodeInternalError} {
if !(&VendorAuthCodeError{Code: code}).Retryable() {
t.Fatalf("%s should be retryable", code)
}
}
if (&VendorAuthCodeError{Code: VendorAuthCodeOrgMismatch}).Retryable() {
t.Fatal("ORG_MISMATCH must not be retryable")
}
}
func TestFetchVendorAuthCodeDoesNotSendBlankCLIVersionHeader(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got := r.Header.Get("x-dws-cli-version"); got != "" {
t.Fatalf("x-dws-cli-version = %q, want empty", got)
}
_, _ = io.WriteString(w, `{"authCode":"tmp-code","expiresIn":90}`)
}))
defer srv.Close()
got, err := FetchVendorAuthCode(context.Background(), VendorAuthCodeInput{
AccessToken: "user-token",
ClientID: "dws-client",
Vendor: "safechat",
CorpID: "dingxxxxxxxxxxxx",
HTTPClient: srv.Client(),
BaseURL: srv.URL,
})
if err != nil {
t.Fatalf("FetchVendorAuthCode() = %v", err)
}
if got.ExpiresIn != 90 {
t.Fatalf("expiresIn = %d, want 90 from response", got.ExpiresIn)
}
if strings.Contains(got.AuthCode, "redirect") {
t.Fatalf("unexpected code %q", got.AuthCode)
}
}