Files
dingtalk-workspace-cli/internal/app/skill_command_full_coverage_test.go
玉澜 93d7e5a4c6 refactor(app): consolidate command-framework seams and unify recovery errors
Centralize dynamic-server command-key protection, profile runtime
selection, and endpoint-resolution error construction; drop the dead
dry-run catalog-miss branch; document host_compat stubs as edition-sync
anchors; keep test fixtures out of the package dir via t.TempDir.
2026-08-04 08:36:12 +08:00

337 lines
14 KiB
Go

package app
import (
"archive/zip"
"bytes"
"context"
"errors"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
authpkg "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/auth"
"github.com/spf13/cobra"
)
type skillErrorReadCloser struct{ err error }
func (r skillErrorReadCloser) Read([]byte) (int, error) { return 0, r.err }
func (skillErrorReadCloser) Close() error { return nil }
func skillCoverageResponse(status int, body string) *http.Response {
return &http.Response{StatusCode: status, Body: io.NopCloser(strings.NewReader(body)), Header: make(http.Header)}
}
func skillCoverageCommand() *cobra.Command {
cmd := &cobra.Command{}
cmd.SetContext(context.Background())
cmd.SetOut(io.Discard)
cmd.Flags().String("skill-id", "id", "")
cmd.Flags().String("query", "query", "")
cmd.Flags().String("source", "", "")
cmd.Flags().String("scopes", "scope", "")
return cmd
}
func TestCrossPlatformCoverageSkillCommandHighLevelRemainingCoverage(t *testing.T) {
oldToken := skillLoadAccessToken
oldTmp := skillDownloadToTmp
oldHTTP := skillHTTPDo
oldNewRequest := skillNewRequest
oldTarget := skillResolveTargetPath
oldFetch := skillFetchDownloadInfo
oldDownload := skillDownloadFile
oldExtract := skillExtractZip
t.Cleanup(func() {
skillLoadAccessToken = oldToken
skillDownloadToTmp = oldTmp
skillHTTPDo = oldHTTP
skillNewRequest = oldNewRequest
skillResolveTargetPath = oldTarget
skillFetchDownloadInfo = oldFetch
skillDownloadFile = oldDownload
skillExtractZip = oldExtract
})
fail := errors.New("failure")
cmd := skillCoverageCommand()
skillLoadAccessToken = func(context.Context) (string, error) { return "", fail }
if err := runSkillGet(cmd, nil); !errors.Is(err, fail) {
t.Fatalf("skill get auth error = %v", err)
}
skillLoadAccessToken = func(context.Context) (string, error) { return "token", nil }
skillNewRequest = func(context.Context, string, string, io.Reader) (*http.Request, error) { return nil, fail }
if err := runSkillFind(cmd, nil); err == nil {
t.Fatal("skill find request failure should propagate")
}
skillNewRequest = oldNewRequest
skillDownloadToTmp = func(context.Context, string, string) (string, error) { return "", fail }
if err := runSkillGet(cmd, nil); !errors.Is(err, fail) {
t.Fatalf("skill get download error = %v", err)
}
skillLoadAccessToken = func(context.Context) (string, error) { return "", fail }
if err := runSkillFind(cmd, nil); !errors.Is(err, fail) {
t.Fatalf("skill find auth error = %v", err)
}
skillLoadAccessToken = func(context.Context) (string, error) { return "token", nil }
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) { return nil, fail }
if err := runSkillFind(cmd, nil); err == nil {
t.Fatal("skill find network failure should propagate")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(http.StatusBadRequest, "bad"), nil
}
if err := runSkillFind(cmd, nil); err == nil {
t.Fatal("skill find HTTP failure should propagate")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(http.StatusOK, "{"), nil
}
if err := runSkillFind(cmd, nil); err == nil {
t.Fatal("skill find malformed response should fail")
}
for _, body := range []string{
`{"success":false,"errorMsg":"message"}`,
`{"success":false,"errorCode":"code"}`,
`{"success":false}`,
} {
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(http.StatusOK, body), nil
}
if err := runSkillFind(cmd, nil); err == nil {
t.Fatalf("skill find API failure %s should propagate", body)
}
}
skillResolveTargetPath = func(string) (string, error) { return "", fail }
if err := runSkillAdd(cmd, []string{"id", "target"}); err == nil {
t.Fatal("invalid skill target should fail")
}
skillResolveTargetPath = func(string) (string, error) { return filepath.Join(t.TempDir(), "dest"), nil }
skillLoadAccessToken = func(context.Context) (string, error) { return "", fail }
if err := runSkillAdd(cmd, []string{"id", "target"}); !errors.Is(err, fail) {
t.Fatalf("skill add auth error = %v", err)
}
skillLoadAccessToken = func(context.Context) (string, error) { return "token", nil }
skillFetchDownloadInfo = func(context.Context, string, string) (*downloadSkillResponse, error) { return nil, fail }
if err := runSkillAdd(cmd, []string{"id", "target"}); !errors.Is(err, fail) {
t.Fatalf("skill info error = %v", err)
}
for _, response := range []*downloadSkillResponse{
{Success: false, ErrorMsg: "message"},
{Success: false, ErrorCode: "code"},
{Success: false},
{Success: true},
{Success: true, Result: &downloadSkillResult{}},
} {
skillFetchDownloadInfo = func(context.Context, string, string) (*downloadSkillResponse, error) { return response, nil }
if err := runSkillAdd(cmd, []string{"id", "target"}); err == nil {
t.Fatalf("invalid download response %#v should fail", response)
}
}
skillFetchDownloadInfo = func(context.Context, string, string) (*downloadSkillResponse, error) {
return &downloadSkillResponse{Success: true, Result: &downloadSkillResult{DownloadURL: "url", FileName: "skill.zip"}}, nil
}
skillDownloadFile = func(context.Context, string, string) (string, error) { return "", fail }
if err := runSkillAdd(cmd, []string{"id", "target"}); !errors.Is(err, fail) {
t.Fatalf("skill file download error = %v", err)
}
skillDownloadFile = func(context.Context, string, string) (string, error) { return "temp.zip", nil }
skillExtractZip = func(string, string) error { return fail }
if err := runSkillAdd(cmd, []string{"id", "target"}); !errors.Is(err, fail) {
t.Fatalf("skill extraction error = %v", err)
}
skillExtractZip = func(string, string) error { return nil }
if err := runSkillAdd(cmd, []string{"id", "target"}); err != nil {
t.Fatal(err)
}
}
func TestCrossPlatformCoverageSkillCommandLowLevelRemainingCoverage(t *testing.T) {
oldHTTP := skillHTTPDo
oldNewRequest, oldResolveToken := skillNewRequest, skillResolveAccessToken
oldHome := skillUserHomeDir
oldMkdirTemp, oldCreate, oldCreateTemp := skillMkdirTemp, skillCreate, skillCreateTemp
oldRemoveAll, oldRemove, oldMkdir := skillRemoveAll, skillRemove, skillMkdirAll
oldOpen, oldCopy, oldZipOpen := skillOpenFile, skillCopy, skillOpenZipFile
t.Cleanup(func() {
skillHTTPDo = oldHTTP
skillNewRequest, skillResolveAccessToken = oldNewRequest, oldResolveToken
skillUserHomeDir = oldHome
skillMkdirTemp, skillCreate, skillCreateTemp = oldMkdirTemp, oldCreate, oldCreateTemp
skillRemoveAll, skillRemove, skillMkdirAll = oldRemoveAll, oldRemove, oldMkdir
skillOpenFile, skillCopy, skillOpenZipFile = oldOpen, oldCopy, oldZipOpen
})
fail := errors.New("failure")
skillResolveAccessToken = func(context.Context, string, string) (string, error) {
return "", authpkg.ErrTokenDataNotFound
}
if _, err := loadSkillAccessToken(context.Background()); err == nil {
t.Fatal("invalid skill access token succeeded")
}
canceled, cancel := context.WithCancel(context.Background())
cancel()
skillResolveAccessToken = func(ctx context.Context, _, _ string) (string, error) {
return "", ctx.Err()
}
if _, err := loadSkillAccessToken(canceled); !errors.Is(err, context.Canceled) {
t.Fatalf("skill token cancellation = %v", err)
}
skillResolveAccessToken = oldResolveToken
skillNewRequest = func(context.Context, string, string, io.Reader) (*http.Request, error) { return nil, fail }
if _, err := fetchSkillDownloadInfo(context.Background(), "token", "id"); err == nil {
t.Fatal("download-info request failure should propagate")
}
if _, err := downloadSkillFile(context.Background(), "https://skill.test", "token"); err == nil {
t.Fatal("skill-file request failure should propagate")
}
skillNewRequest = oldNewRequest
skillUserHomeDir = func() (string, error) { return "", fail }
if _, err := resolveSkillTargetPath("codex"); err == nil {
t.Fatal("skill target HOME error should fail")
}
t.Setenv("DWS_SKILL_API_HOST", "https://skill.test/")
if skillAPIHost() != "https://skill.test" {
t.Fatal("skill API override was not normalized")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) { return nil, fail }
if _, err := fetchSkillDownloadInfo(context.Background(), "token", "id"); err == nil {
t.Fatal("download-info network failure should propagate")
}
for _, status := range []int{http.StatusUnauthorized, http.StatusBadGateway} {
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(status, "body"), nil
}
if _, err := fetchSkillDownloadInfo(context.Background(), "token", "id"); err == nil {
t.Fatalf("download-info HTTP %d should fail", status)
}
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: http.StatusOK, Body: skillErrorReadCloser{err: fail}}, nil
}
if _, err := fetchSkillDownloadInfo(context.Background(), "token", "id"); err == nil {
t.Fatal("download-info body failure should propagate")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(http.StatusOK, "{"), nil
}
if _, err := fetchSkillDownloadInfo(context.Background(), "token", "id"); err == nil {
t.Fatal("malformed download-info should fail")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(http.StatusOK, "body"), nil
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) { return nil, fail }
if _, err := downloadSkillToTmpDir(context.Background(), "https://skill.test", "token"); err == nil {
t.Fatal("temporary skill download network failure should propagate")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(http.StatusOK, "body"), nil
}
skillMkdirTemp = func(string, string) (string, error) { return "", fail }
if _, err := downloadSkillToTmpDir(context.Background(), "https://skill.test", "token"); err == nil {
t.Fatal("download temp-dir failure should propagate")
}
tmpDir := t.TempDir()
skillMkdirTemp = func(string, string) (string, error) { return tmpDir, nil }
skillCreate = func(string) (*os.File, error) { return nil, fail }
if _, err := downloadSkillToTmpDir(context.Background(), "https://skill.test", "token"); err == nil {
t.Fatal("download temp-file failure should propagate")
}
skillCreate = func(string) (*os.File, error) { return os.CreateTemp(t.TempDir(), "skill") }
skillCopy = func(io.Writer, io.Reader) (int64, error) { return 0, fail }
if _, err := downloadSkillToTmpDir(context.Background(), "https://skill.test", "token"); err == nil {
t.Fatal("download save failure should propagate")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) { return nil, fail }
if _, err := downloadSkillFile(context.Background(), "https://skill.test", "x"); err == nil {
t.Fatal("skill-file network failure should propagate")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(http.StatusBadGateway, "body"), nil
}
if _, err := downloadSkillFile(context.Background(), "https://skill.test", "x"); err == nil {
t.Fatal("skill-file HTTP failure should propagate")
}
skillHTTPDo = func(*http.Client, *http.Request) (*http.Response, error) {
return skillCoverageResponse(http.StatusOK, "body"), nil
}
skillCreateTemp = func(string, string) (*os.File, error) { return nil, fail }
if _, err := downloadSkillFile(context.Background(), "https://skill.test", "x"); err == nil {
t.Fatal("skill-file temp failure should propagate")
}
skillCreateTemp = func(string, string) (*os.File, error) { return os.CreateTemp(t.TempDir(), "skill") }
skillCopy = func(io.Writer, io.Reader) (int64, error) { return 0, fail }
if _, err := downloadSkillFile(context.Background(), "https://skill.test", "x"); err == nil {
t.Fatal("skill-file copy failure should propagate")
}
skillCopy = func(w io.Writer, _ io.Reader) (int64, error) {
_ = w.(*os.File).Close()
return 0, nil
}
if _, err := downloadSkillFile(context.Background(), "https://skill.test", "x"); err == nil {
t.Fatal("skill-file close failure should propagate")
}
skillMkdirAll = func(string, os.FileMode) error { return fail }
if err := extractSkillZip("missing", filepath.Join(t.TempDir(), "dest")); err == nil {
t.Fatal("zip destination mkdir failure should propagate")
}
zipPath := filepath.Join(t.TempDir(), "files.zip")
buf := &bytes.Buffer{}
zw := zip.NewWriter(buf)
_, _ = zw.Create("dir/")
w, _ := zw.Create("file")
_, _ = w.Write([]byte("content"))
header := &zip.FileHeader{Name: "mode-file", Method: zip.Store}
header.SetMode(0o111)
w, _ = zw.CreateHeader(header)
_, _ = w.Write([]byte("mode"))
_ = zw.Close()
if err := os.WriteFile(zipPath, buf.Bytes(), 0o600); err != nil {
t.Fatal(err)
}
zr, err := zip.OpenReader(zipPath)
if err != nil {
t.Fatal(err)
}
defer zr.Close()
skillMkdirAll = func(string, os.FileMode) error { return fail }
if err := extractZipFile(zr.File[0], t.TempDir()); err == nil {
t.Fatal("zip directory mkdir failure should propagate")
}
if err := extractZipFile(zr.File[1], t.TempDir()); err == nil {
t.Fatal("zip parent mkdir failure should propagate")
}
skillMkdirAll = func(string, os.FileMode) error { return nil }
skillOpenZipFile = func(*zip.File) (io.ReadCloser, error) { return nil, fail }
if err := extractZipFile(zr.File[1], t.TempDir()); err == nil {
t.Fatal("zip source-open failure should propagate")
}
skillOpenZipFile = oldZipOpen
skillOpenFile = func(string, int, os.FileMode) (*os.File, error) { return nil, fail }
if err := extractZipFile(zr.File[1], t.TempDir()); err == nil {
t.Fatal("zip destination-open failure should propagate")
}
skillOpenFile = func(string, int, os.FileMode) (*os.File, error) { return os.CreateTemp(t.TempDir(), "out") }
skillCopy = func(io.Writer, io.Reader) (int64, error) { return 0, fail }
if err := extractZipFile(zr.File[1], t.TempDir()); err == nil {
t.Fatal("zip copy failure should propagate")
}
skillCopy = func(io.Writer, io.Reader) (int64, error) { return 0, nil }
var usedMode os.FileMode
skillOpenFile = func(_ string, _ int, mode os.FileMode) (*os.File, error) {
usedMode = mode
return os.CreateTemp(t.TempDir(), "mode")
}
if err := extractZipFile(zr.File[2], t.TempDir()); err != nil || usedMode != 0o644 {
t.Fatalf("zip fallback mode = %v, %v", usedMode, err)
}
}