Files
multica/server/internal/daemon/agents_probe_qoder_test.go
Multica Eve e4b6f7a31b MUL-5581: add Qoder CN CLI runtime (#6232)
* feat(agent): add Qoder CN CLI runtime

Co-authored-by: multica-agent <github@multica.ai>

* fix(agent): address Qoder CN review nits

Co-authored-by: multica-agent <github@multica.ai>

* fix(agent): defer Qoder CN version gate

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Eve <eve@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
2026-07-31 16:01:53 +08:00

107 lines
4.1 KiB
Go

package daemon
import (
"testing"
)
// TestProbeAgentCLIs_QoderResolvesViaLoginShell locks down the fix for the
// Qoder discovery gap reported from the desktop app (MUL-5524).
//
// Every other provider is probed through the shared probe() helper, which falls
// back to the user's login shell when the daemon's own PATH can't see a bare
// command name. Qoder was probed with a bare resolveAgentExecutablePath call
// and therefore had no fallback at all: a GUI/Launchpad-started daemon (the
// apple.dmg desktop build) does not inherit the interactive shell PATH, so a
// `qodercli` living in an npm global prefix or any ~/.zshrc-added dir was
// undetectable no matter how many times the daemon restarted.
func TestProbeAgentCLIs_QoderResolvesViaLoginShell(t *testing.T) {
orig := resolveAgentsViaLoginShell
t.Cleanup(func() { resolveAgentsViaLoginShell = orig })
resolveAgentsViaLoginShell = func([]string) map[string]string {
return map[string]string{"qodercli": "/fake/npm-global/bin/qodercli"}
}
resetShellResolveCacheForTest(t)
// An empty PATH guarantees the exec.LookPath leg misses, so the only way
// qoder can resolve is the login-shell fallback.
t.Setenv("PATH", "")
agents := probeAgentCLIs()
entry, ok := agents["qoder"]
if !ok {
t.Fatal("qoder was not discovered via the login-shell fallback; " +
"a GUI-launched daemon cannot see a qodercli installed on the interactive shell PATH")
}
if entry.Path != "/fake/npm-global/bin/qodercli" {
t.Errorf("qoder path = %q, want /fake/npm-global/bin/qodercli", entry.Path)
}
if entry.Command != "qodercli" {
t.Errorf("qoder command = %q, want qodercli", entry.Command)
}
}
func TestProbeAgentCLIs_QoderCNResolvesIndependently(t *testing.T) {
orig := resolveAgentsViaLoginShell
t.Cleanup(func() { resolveAgentsViaLoginShell = orig })
resolveAgentsViaLoginShell = func([]string) map[string]string {
return map[string]string{
"qodercli": "/fake/bin/qodercli",
"qoderclicn": "/fake/bin/qoderclicn",
}
}
resetShellResolveCacheForTest(t)
t.Setenv("PATH", "")
agents := probeAgentCLIs()
entry, ok := agents["qoderclicn"]
if !ok {
t.Fatal("qoderclicn was not discovered via the login-shell fallback")
}
if entry.Path != "/fake/bin/qoderclicn" {
t.Errorf("qoderclicn path = %q, want /fake/bin/qoderclicn", entry.Path)
}
if entry.Command != "qoderclicn" {
t.Errorf("qoderclicn command = %q, want qoderclicn", entry.Command)
}
if _, ok := agents["qoder"]; !ok {
t.Fatal("qodercli and qoderclicn must be registered independently when both are installed")
}
}
// TestProbeAgentCLIs_QoderPinnedPathStaysHardMiss keeps the fallback from
// rescuing an operator-pinned MULTICA_QODER_PATH. A pinned absolute path that
// no longer exists must stay a miss rather than silently resolve a different
// binary — the same rule probe() applies to every other provider.
func TestProbeAgentCLIs_QoderPinnedPathStaysHardMiss(t *testing.T) {
orig := resolveAgentsViaLoginShell
t.Cleanup(func() { resolveAgentsViaLoginShell = orig })
resolveAgentsViaLoginShell = func([]string) map[string]string {
return map[string]string{"qodercli": "/fake/npm-global/bin/qodercli"}
}
resetShellResolveCacheForTest(t)
t.Setenv("PATH", "")
t.Setenv("MULTICA_QODER_PATH", "/nonexistent/pinned/qodercli")
if entry, ok := probeAgentCLIs()["qoder"]; ok {
t.Errorf("pinned-but-missing MULTICA_QODER_PATH resolved to %q, want a hard miss", entry.Path)
}
}
// TestDefaultAgentCommandNamesIncludesQoder guards the other half of the same
// bug: cachedShellResolvedAgents only asks the login shell about the names in
// defaultAgentCommandNames, so omitting "qodercli" would leave the fallback
// permanently blind to Qoder even once the probe consults it.
func TestDefaultAgentCommandNamesIncludesQoder(t *testing.T) {
found := map[string]bool{}
for _, name := range defaultAgentCommandNames {
found[name] = true
}
for _, name := range []string{"qodercli", "qoderclicn"} {
if !found[name] {
t.Fatalf("defaultAgentCommandNames is missing %q; the login-shell resolver "+
"only pre-fetches names in that list, so Qoder stays undetectable on a GUI-launched daemon", name)
}
}
}