Files
zzet--gortex/internal/mcp/tools_multi_worktree_test.go
wehub-resource-sync a06f331eb8
install-script / powershell-syntax (push) Waiting to run
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
install-script / install (macos-14) (push) Waiting to run
install-script / install (ubuntu-latest) (push) Waiting to run
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

150 lines
5.5 KiB
Go

package mcp
import (
"context"
"encoding/json"
"os"
"os/exec"
"path/filepath"
"testing"
mcplib "github.com/mark3labs/mcp-go/mcp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/zzet/gortex/internal/config"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/indexer"
"github.com/zzet/gortex/internal/query"
"github.com/zzet/gortex/internal/search"
)
// newWorktreeMCPServer builds a Server whose MultiIndexer has already
// indexed the supplied config repos. Used to exercise the runtime
// track_repository path for git-worktree instancing.
func newWorktreeMCPServer(t *testing.T, repos ...config.RepoEntry) (*Server, *indexer.MultiIndexer) {
t.Helper()
tmpCfg := filepath.Join(t.TempDir(), "config.yaml")
gc := &config.GlobalConfig{Repos: repos}
gc.SetConfigPath(tmpCfg)
require.NoError(t, gc.Save())
cm, err := config.NewConfigManager(tmpCfg)
require.NoError(t, err)
preg := testRegistry()
g := graph.New()
mi := indexer.NewMultiIndexer(g, preg, search.NewBM25(), cm, zap.NewNop())
_, err = mi.IndexAll()
require.NoError(t, err)
eng := query.NewEngine(g)
singleton := indexer.New(g, preg, config.IndexConfig{}, zap.NewNop())
srv := NewServer(eng, g, singleton, nil, zap.NewNop(), nil, MultiRepoOptions{
ConfigManager: cm,
MultiIndexer: mi,
})
return srv, mi
}
func gitInitWorktreeRepo(t *testing.T, dir string) {
t.Helper()
require.NoError(t, os.MkdirAll(dir, 0o755))
gitInit(t, dir, "init", "-q", "-b", "main")
gitInit(t, dir, "config", "user.email", "test@example.com")
gitInit(t, dir, "config", "user.name", "Test")
gitInit(t, dir, "config", "commit.gpgsign", "false")
require.NoError(t, os.WriteFile(filepath.Join(dir, "lib.go"),
[]byte("package lib\n\nfunc Canonical() {}\n"), 0o644))
gitInit(t, dir, "add", ".")
gitInit(t, dir, "commit", "-q", "-m", "init")
}
func trackRepoPrefix(t *testing.T, srv *Server, args map[string]any) (string, string) {
t.Helper()
req := mcplib.CallToolRequest{}
req.Params.Arguments = args
res, err := srv.handleTrackRepository(context.Background(), req)
require.NoError(t, err)
require.NotNil(t, res)
require.False(t, res.IsError, "track_repository failed: %+v", res.Content)
var payload struct {
Prefix string `json:"prefix"`
Status string `json:"status"`
}
require.NoError(t, json.Unmarshal([]byte(extractTextFromContent(t, res.Content)), &payload))
return payload.Prefix, payload.Status
}
// TestTrackRepository_WorktreeAutoInstance exercises the issue #47 flow
// end-to-end through the MCP track_repository tool: a worktree whose
// `.gortex.yaml` declares a different workspace is auto-tracked as an
// independent instance, and the tool reports the derived prefix.
func TestTrackRepository_WorktreeAutoInstance(t *testing.T) {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("git binary not available in PATH")
}
canon := filepath.Join(t.TempDir(), "oas-orm")
gitInitWorktreeRepo(t, canon)
// A placeholder repo keeps the indexer in multi-repo (prefixed) mode.
placeholder := filepath.Join(t.TempDir(), "placeholder")
require.NoError(t, os.MkdirAll(placeholder, 0o755))
require.NoError(t, os.WriteFile(filepath.Join(placeholder, "p.go"),
[]byte("package p\n\nfunc P() {}\n"), 0o644))
srv, mi := newWorktreeMCPServer(t,
config.RepoEntry{Path: canon},
config.RepoEntry{Path: placeholder},
)
require.NotNil(t, mi.GetMetadata("oas-orm"))
// Linked worktree, colliding basename, declaring a distinct workspace.
wt := filepath.Join(t.TempDir(), "oas-orm")
gitInit(t, canon, "worktree", "add", "-q", "-b", "task", wt)
require.NoError(t, os.WriteFile(filepath.Join(wt, ".gortex.yaml"),
[]byte("workspace: task-ws\n"), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(wt, "feature.go"),
[]byte("package lib\n\nfunc Feature() {}\n"), 0o644))
prefix, status := trackRepoPrefix(t, srv, map[string]any{"path": wt})
assert.Equal(t, "tracked", status)
assert.Equal(t, "oas-orm@task-ws", prefix, "worktree must get a derived instance prefix")
assert.NotNil(t, mi.GetMetadata("oas-orm@task-ws"), "worktree instance tracked")
assert.NotNil(t, mi.GetMetadata("oas-orm"), "canonical must remain tracked")
}
// TestTrackRepository_WorktreeForcedFlag covers the explicit
// `as_worktree` directive on a worktree that declares no workspace: the
// branch name (sanitised) becomes the instance tag.
func TestTrackRepository_WorktreeForcedFlag(t *testing.T) {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("git binary not available in PATH")
}
canon := filepath.Join(t.TempDir(), "oas-orm")
gitInitWorktreeRepo(t, canon)
placeholder := filepath.Join(t.TempDir(), "placeholder")
require.NoError(t, os.MkdirAll(placeholder, 0o755))
require.NoError(t, os.WriteFile(filepath.Join(placeholder, "p.go"),
[]byte("package p\n\nfunc P() {}\n"), 0o644))
srv, mi := newWorktreeMCPServer(t,
config.RepoEntry{Path: canon},
config.RepoEntry{Path: placeholder},
)
wt := filepath.Join(t.TempDir(), "oas-orm")
gitInit(t, canon, "worktree", "add", "-q", "-b", "feature/login", wt)
require.NoError(t, os.WriteFile(filepath.Join(wt, "feature.go"),
[]byte("package lib\n\nfunc Feature() {}\n"), 0o644))
prefix, status := trackRepoPrefix(t, srv, map[string]any{"path": wt, "as_worktree": true})
assert.Equal(t, "tracked", status)
assert.Equal(t, "oas-orm@feature-login", prefix,
"forced worktree without a declared workspace is tagged by its sanitised branch")
assert.NotNil(t, mi.GetMetadata("oas-orm@feature-login"))
assert.NotNil(t, mi.GetMetadata("oas-orm"))
}