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
150 lines
5.5 KiB
Go
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"))
|
|
}
|