package plugins import ( "os" "path/filepath" "strings" "testing" ) func TestQwenInstallValidatesCredentialsAndManagedPathsBeforeWrites(t *testing.T) { tests := []struct { name string mutate func(*qwenFixture) pattern string }{ { "agent name", func(f *qwenFixture) { f.config.AgentName = "other" }, "requires agent name qwen", }, { "organization", func(f *qwenFixture) { f.config.OrgID = " " }, "organization ID is required", }, { "agent id", func(f *qwenFixture) { f.config.AgentID = "../agent" }, "single non-empty path segment", }, { "key id", func(f *qwenFixture) { f.config.KID = " " }, "key ID is required", }, { "private key", func(f *qwenFixture) { f.config.PrivateKey = "invalid" }, "private key", }, { "base URL", func(f *qwenFixture) { f.config.BaseURL = "https://api.test/path?secret=x" }, "base URL", }, { "token", func(f *qwenFixture) { f.config.Token = " " }, "token must contain", }, { "relative guard", func(f *qwenFixture) { f.config.GuardScriptPath = "guard.js" }, "managed agent directory", }, { "guard outside runtime", func(f *qwenFixture) { f.config.GuardScriptPath = filepath.Join(f.homeDir, "guard.js") }, "managed agent directory", }, { "audit outside runtime", func(f *qwenFixture) { f.config.HookScript = filepath.Join(f.homeDir, "hook.js") }, "audit runtime must use", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { fixture := prepareQwenFixture(t, qwenFixtureOptions{}) test.mutate(fixture) err := fixture.plugin.Install(fixture.config) if err == nil || !strings.Contains(err.Error(), test.pattern) { t.Fatalf("install error = %v, want substring %q", err, test.pattern) } for _, path := range []string{ fixture.configPath, fixture.guardPath, fixture.hookPath, fixture.runtimeConfig, fixture.privateKey, } { requireMissingQwenFile(t, path) } }) } } func TestQwenInstallRejectsNonPhysicalSources(t *testing.T) { t.Run("home environment directory", func(t *testing.T) { fixture := prepareQwenFixture(t, qwenFixtureOptions{}) envPath := filepath.Join(fixture.qwenDir, ".env") if err := os.MkdirAll(envPath, 0755); err != nil { t.Fatalf("create environment directory: %v", err) } err := fixture.plugin.Install(fixture.config) if err == nil || !strings.Contains(err.Error(), "physical file") { t.Fatalf("install error = %v", err) } requireMissingQwenFile(t, fixture.guardPath) }) t.Run("user settings symlink", func(t *testing.T) { fixture := prepareQwenFixture(t, qwenFixtureOptions{}) target := filepath.Join(filepath.Dir(fixture.homeDir), "settings-target.json") writeOptionalQwenFile(t, target, qwenString("{}\n")) if err := os.MkdirAll(filepath.Dir(fixture.configPath), 0755); err != nil { t.Fatalf("create settings directory: %v", err) } if err := os.Symlink(target, fixture.configPath); err != nil { t.Skipf("create settings symlink: %v", err) } err := fixture.plugin.Install(fixture.config) if err == nil || !strings.Contains(err.Error(), "physical file") { t.Fatalf("install error = %v", err) } requireMissingQwenFile(t, fixture.guardPath) }) t.Run("agent runtime symlink", func(t *testing.T) { fixture := prepareQwenFixture(t, qwenFixtureOptions{}) runtimeRoot := filepath.Dir(fixture.agentDir) target := filepath.Join(filepath.Dir(fixture.homeDir), "agent-target") for _, directory := range []string{runtimeRoot, target} { if err := os.MkdirAll(directory, 0700); err != nil { t.Fatalf("create runtime directory: %v", err) } } if err := os.Symlink(target, fixture.agentDir); err != nil { t.Skipf("create runtime symlink: %v", err) } err := fixture.plugin.PreflightInstall(fixture.config) if err == nil || !strings.Contains(err.Error(), "physical directory") { t.Fatalf("preflight error = %v", err) } }) } func TestQwenPreflightProtectsExistingRuntimeIdentity(t *testing.T) { tests := []struct { name string configure func(*testing.T, *qwenFixture) pattern string }{ { "orphan artifact", func(t *testing.T, f *qwenFixture) { writeOptionalQwenFile(t, f.guardPath, qwenString("external\n")) }, "cannot be verified without config.json", }, { "different identity", func(t *testing.T, f *qwenFixture) { writeOptionalQwenFile(t, f.runtimeConfig, qwenJSON(map[string]any{ "org_id": "org-1", "agent_id": "other-agent", "kid": "kid-1", "base_url": "https://api.elydora.test", "agent_name": qwenAgentKey, })) }, "identity does not match", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { fixture := prepareQwenFixture(t, qwenFixtureOptions{}) test.configure(t, fixture) err := fixture.plugin.PreflightInstall(fixture.config) if err == nil || !strings.Contains(err.Error(), test.pattern) { t.Fatalf("preflight error = %v", err) } }) } } func TestQwenStatusRejectsRuntimeSymlink(t *testing.T) { fixture := prepareQwenFixture(t, qwenFixtureOptions{}) installQwenFixture(t, fixture) target := filepath.Join(filepath.Dir(fixture.homeDir), "guard-target.js") writeOptionalQwenFile(t, target, qwenString(readQwenTestFile(t, fixture.guardPath))) if err := os.Remove(fixture.guardPath); err != nil { t.Fatalf("remove guard runtime: %v", err) } if err := os.Symlink(target, fixture.guardPath); err != nil { t.Skipf("create guard symlink: %v", err) } if _, err := fixture.plugin.Status(); err == nil || !strings.Contains(err.Error(), "physical file") { t.Fatalf("status error = %v", err) } }