Files
dingtalk-workspace-cli/internal/skillprovenance/provenance_test.go

116 lines
3.9 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0
package skillprovenance
import (
"errors"
"io/fs"
"os"
"path/filepath"
"reflect"
"testing"
)
type fakeDirEntry struct {
name string
mode fs.FileMode
}
func (entry fakeDirEntry) Name() string { return entry.name }
func (entry fakeDirEntry) IsDir() bool { return entry.mode.IsDir() }
func (entry fakeDirEntry) Type() fs.FileMode { return entry.mode.Type() }
func (entry fakeDirEntry) Info() (fs.FileInfo, error) { return nil, errors.New("unused") }
func swapProvenanceSeam[T any](t *testing.T, target *T, replacement T) {
t.Helper()
original := *target
*target = replacement
t.Cleanup(func() { *target = original })
}
func TestCrossPlatformCoverageSkillProvenanceBuildDigestAndMerge(t *testing.T) {
dir := t.TempDir()
if err := os.MkdirAll(filepath.Join(dir, "references"), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte("skill"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "references", "a.md"), []byte("ref"), 0o644); err != nil {
t.Fatal(err)
}
record, err := Build("dingtalk-a", dir, "", SourceUpgrade)
if err != nil {
t.Fatal(err)
}
if record.Version != "unknown" || record.Source != SourceUpgrade || record.DigestScope != DigestScope {
t.Fatalf("record = %#v", record)
}
first := record.Digest
if err := os.WriteFile(filepath.Join(dir, "references", "a.md"), []byte("changed"), 0o644); err != nil {
t.Fatal(err)
}
second, err := Build("dingtalk-a", dir, "v2", SourceSkillSetup)
if err != nil || second.Digest == first {
t.Fatalf("second = %#v, %v", second, err)
}
merged := Merge([]Record{record, {Name: "dingtalk-b"}}, []Record{second, {}})
if !reflect.DeepEqual(merged, []Record{second, {Name: "dingtalk-b"}}) {
t.Fatalf("merged = %#v", merged)
}
if got := Names(merged); !got["dingtalk-a"] || !got["dingtalk-b"] || len(got) != 2 {
t.Fatalf("names = %#v", got)
}
if _, err := Build("", dir, "v1", "test"); err == nil {
t.Fatal("empty name accepted")
}
if _, err := Build("dingtalk-a", dir, "v1", " "); err == nil {
t.Fatal("empty source accepted")
}
if _, err := Build("dingtalk-a", filepath.Join(dir, "missing"), "v1", "test"); err == nil {
t.Fatal("missing Skill directory accepted")
}
}
func TestCrossPlatformCoverageSkillProvenanceDigestErrors(t *testing.T) {
want := errors.New("injected")
t.Run("walk", func(t *testing.T) {
swapProvenanceSeam(t, &provenanceWalkDir, func(string, fs.WalkDirFunc) error { return want })
if _, err := DigestDir(t.TempDir()); !errors.Is(err, want) {
t.Fatal(err)
}
})
t.Run("callback", func(t *testing.T) {
swapProvenanceSeam(t, &provenanceWalkDir, func(root string, walk fs.WalkDirFunc) error { return walk(root, nil, want) })
if _, err := DigestDir(t.TempDir()); !errors.Is(err, want) {
t.Fatal(err)
}
})
t.Run("relative", func(t *testing.T) {
swapProvenanceSeam(t, &provenanceWalkDir, func(root string, walk fs.WalkDirFunc) error {
return walk(filepath.Join(root, "file"), fakeDirEntry{name: "file"}, nil)
})
swapProvenanceSeam(t, &provenanceRelative, func(string, string) (string, error) { return "", want })
if _, err := DigestDir(t.TempDir()); !errors.Is(err, want) {
t.Fatal(err)
}
})
t.Run("nonregular", func(t *testing.T) {
swapProvenanceSeam(t, &provenanceWalkDir, func(root string, walk fs.WalkDirFunc) error {
return walk(filepath.Join(root, "link"), fakeDirEntry{name: "link", mode: fs.ModeSymlink}, nil)
})
if _, err := DigestDir(t.TempDir()); err == nil {
t.Fatal("nonregular accepted")
}
})
t.Run("read", func(t *testing.T) {
dir := t.TempDir()
_ = os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte("x"), 0o644)
swapProvenanceSeam(t, &provenanceReadFile, func(string) ([]byte, error) { return nil, want })
if _, err := DigestDir(dir); !errors.Is(err, want) {
t.Fatal(err)
}
})
}