157 lines
6.0 KiB
Go
157 lines
6.0 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
package app
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/skillprovenance"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/skillstate"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/upgrade"
|
|
)
|
|
|
|
// TestCrossPlatformCoverageUpgradeSkillLocationsMonoSeedMigratesToMulti is the fake-HOME E2E for
|
|
// the 2026-08-05 owner decision: upgrade is not disk-sticky. Seeding a mono
|
|
// layout then calling UpgradeSkillLocations with a multi bundle must install
|
|
// product skills, remove dws/, and leave non-DWS dirs alone.
|
|
func TestCrossPlatformCoverageUpgradeSkillLocationsMonoSeedMigratesToMulti(t *testing.T) {
|
|
home := t.TempDir()
|
|
setTestHome(t, home)
|
|
upgrade.SwapUserHomeDirForTest(t, func() (string, error) { return home, nil })
|
|
|
|
agentsBase := filepath.Join(home, ".agents", "skills")
|
|
if err := os.MkdirAll(filepath.Join(agentsBase, "dws"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(agentsBase, "dws", "SKILL.md"), []byte("old mono"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(agentsBase, "other-skill"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(agentsBase, "other-skill", "SKILL.md"), []byte("not dws"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
extract := t.TempDir()
|
|
multiRoot := filepath.Join(extract, "multi")
|
|
for _, name := range []string{"dingtalk-chat", "dws-shared"} {
|
|
dir := filepath.Join(multiRoot, name)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte("# "+name), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
result, err := upgrade.UpgradeSkillLocations(multiRoot)
|
|
if err != nil {
|
|
t.Fatalf("UpgradeSkillLocations() error = %v", err)
|
|
}
|
|
if failed := result.Failed(); len(failed) != 0 {
|
|
t.Fatalf("expected 0 failures, got %v", failed)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(agentsBase, "dws")); !os.IsNotExist(err) {
|
|
t.Fatalf("mono leftover dws/ must be gone, stat err=%v", err)
|
|
}
|
|
for _, name := range []string{"dingtalk-chat", "dws-shared"} {
|
|
if _, err := os.Stat(filepath.Join(agentsBase, name, "SKILL.md")); err != nil {
|
|
t.Errorf("multi skill missing: %s: %v", name, err)
|
|
}
|
|
}
|
|
if _, err := os.Stat(filepath.Join(agentsBase, "other-skill", "SKILL.md")); err != nil {
|
|
t.Errorf("non-DWS dir should be preserved: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestUpgradeSkillLocationsRealBundleMigratesMono runs the upgrade path against
|
|
// the repository's actual multi bundle (skills/multi, post-#887 layout with
|
|
// dingtalk-shared). Disk is seeded with a mono install plus the leftovers the
|
|
// rename and stale releases leave behind: a pre-rename dws-shared directory
|
|
// and a dingtalk-* skill no longer in the bundle. All three must be gone after
|
|
// the upgrade, every bundle skill installed, non-DWS dirs untouched, and the
|
|
// ~/.dws/skills/multi cache refreshed.
|
|
func TestCrossPlatformCoverageUpgradeSkillLocationsRealBundleMigratesMono(t *testing.T) {
|
|
home := t.TempDir()
|
|
setTestHome(t, home)
|
|
upgrade.SwapUserHomeDirForTest(t, func() (string, error) { return home, nil })
|
|
|
|
bundle := filepath.Join("..", "..", "skills", "multi")
|
|
entries, err := os.ReadDir(bundle)
|
|
if err != nil {
|
|
t.Fatalf("real multi bundle missing: %v", err)
|
|
}
|
|
var bundleNames []string
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
bundleNames = append(bundleNames, e.Name())
|
|
}
|
|
}
|
|
if len(bundleNames) == 0 {
|
|
t.Fatal("real multi bundle is empty")
|
|
}
|
|
|
|
agentsBase := filepath.Join(home, ".agents", "skills")
|
|
// Mono install plus pre-rename shared and a stale product skill.
|
|
for _, name := range []string{"dws", "dws-shared", "dingtalk-stale", "other-skill"} {
|
|
dir := filepath.Join(agentsBase, name)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte("# "+name), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := skillstate.Write(home, skillstate.State{ManagedSkills: []skillprovenance.Record{{Name: "dingtalk-stale"}}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
custom := filepath.Join(agentsBase, "dingtalk-custom")
|
|
if err := os.MkdirAll(custom, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(custom, "SKILL.md"), []byte("market skill"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
result, err := upgrade.UpgradeSkillLocations(bundle)
|
|
if err != nil {
|
|
t.Fatalf("UpgradeSkillLocations() error = %v", err)
|
|
}
|
|
if failed := result.Failed(); len(failed) != 0 {
|
|
t.Fatalf("expected 0 failures, got %v", failed)
|
|
}
|
|
|
|
for _, stale := range []string{"dws", "dws-shared", "dingtalk-stale"} {
|
|
if _, err := os.Stat(filepath.Join(agentsBase, stale)); !os.IsNotExist(err) {
|
|
t.Errorf("leftover %q must be removed by the upgrade, stat err=%v", stale, err)
|
|
}
|
|
}
|
|
for _, name := range bundleNames {
|
|
if _, err := os.Stat(filepath.Join(agentsBase, name, "SKILL.md")); err != nil {
|
|
t.Errorf("bundle skill %q missing after upgrade: %v", name, err)
|
|
}
|
|
}
|
|
if _, err := os.Stat(filepath.Join(agentsBase, "other-skill", "SKILL.md")); err != nil {
|
|
t.Errorf("non-DWS dir should be preserved: %v", err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(custom, "SKILL.md")); err != nil {
|
|
t.Errorf("unregistered market/user dingtalk-* dir should be preserved: %v", err)
|
|
}
|
|
// Shared skill must come from the renamed bundle dir, never the legacy name.
|
|
if _, err := os.Stat(filepath.Join(agentsBase, "dingtalk-shared", "SKILL.md")); err != nil {
|
|
t.Errorf("dingtalk-shared missing: %v", err)
|
|
}
|
|
// Cache refresh so `dws skill setup` fallbacks stay on the upgraded version.
|
|
if _, err := os.Stat(filepath.Join(home, ".dws", "skills", "multi", "SKILL.md")); err != nil {
|
|
// multi cache mirrors the bundle root, which has no top-level SKILL.md;
|
|
// check a bundle skill inside the cache instead.
|
|
if _, err2 := os.Stat(filepath.Join(home, ".dws", "skills", "multi", bundleNames[0], "SKILL.md")); err2 != nil {
|
|
t.Errorf("multi cache not refreshed: %v / %v", err, err2)
|
|
}
|
|
}
|
|
}
|