diff --git a/pkg/rag/manager.go b/pkg/rag/manager.go index ddc275bca..17e77675f 100644 --- a/pkg/rag/manager.go +++ b/pkg/rag/manager.go @@ -574,12 +574,25 @@ func (m *Manager) readFile(path string) (string, error) { return string(data), nil } -// GetAbsolutePaths converts doc paths to absolute paths +// GetAbsolutePaths converts doc paths to absolute paths relative to basePath. +// If basePath is empty (e.g. for OCI/URL sources), relative paths are resolved +// against the current working directory. func GetAbsolutePaths(basePath string, docPaths []string) []string { var absPaths []string for _, p := range docPaths { if filepath.IsAbs(p) { absPaths = append(absPaths, p) + continue + } + if basePath == "" { + slog.Debug("Resolving relative path with empty basePath, using working directory", "path", p) + abs, err := filepath.Abs(p) + if err != nil { + slog.Warn("Failed to resolve absolute path, using as-is", "path", p, "error", err) + absPaths = append(absPaths, p) + } else { + absPaths = append(absPaths, abs) + } } else { absPaths = append(absPaths, filepath.Join(basePath, p)) } diff --git a/pkg/rag/manager_test.go b/pkg/rag/manager_test.go new file mode 100644 index 000000000..2ebadd1df --- /dev/null +++ b/pkg/rag/manager_test.go @@ -0,0 +1,33 @@ +package rag + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetAbsolutePaths_WithBasePath(t *testing.T) { + result := GetAbsolutePaths("/base", []string{"relative/file.go", "/absolute/file.go"}) + assert.Equal(t, []string{"/base/relative/file.go", "/absolute/file.go"}, result) +} + +func TestGetAbsolutePaths_EmptyBasePath(t *testing.T) { + // When basePath is empty (OCI/URL sources), relative paths should be + // resolved against the current working directory instead of producing + // broken paths like "relative/file.go". + cwd, err := os.Getwd() + require.NoError(t, err) + + result := GetAbsolutePaths("", []string{"relative/file.go", "/absolute/file.go"}) + + assert.Equal(t, filepath.Join(cwd, "relative", "file.go"), result[0]) + assert.Equal(t, "/absolute/file.go", result[1]) +} + +func TestGetAbsolutePaths_NilInput(t *testing.T) { + result := GetAbsolutePaths("/base", nil) + assert.Nil(t, result) +} diff --git a/pkg/rag/strategy/helpers.go b/pkg/rag/strategy/helpers.go index ac34744a4..8b14f1632 100644 --- a/pkg/rag/strategy/helpers.go +++ b/pkg/rag/strategy/helpers.go @@ -60,6 +60,14 @@ func ResolveDatabasePath(dbCfg latest.RAGDatabaseConfig, parentDir, defaultName // If it's a relative file path, make it absolute if !filepath.IsAbs(dbStr) { + if parentDir == "" { + slog.Debug("Resolving relative database path with empty parentDir, using working directory", "path", dbStr) + abs, err := filepath.Abs(dbStr) + if err != nil { + return "", fmt.Errorf("failed to resolve absolute path for %q: %w", dbStr, err) + } + return abs, nil + } return filepath.Join(parentDir, dbStr), nil } @@ -165,12 +173,23 @@ func GetParamPtr[T any](params map[string]any, key string) *T { } } -// makeAbsolute makes a path absolute relative to parentDir -func makeAbsolute(path, parentDir string) string { - if filepath.IsAbs(path) { - return path +// makeAbsolute makes a path absolute relative to parentDir. +// If parentDir is empty (e.g. for OCI/URL sources), the path is resolved +// against the current working directory. +func makeAbsolute(p, parentDir string) string { + if filepath.IsAbs(p) { + return p + } + if parentDir == "" { + slog.Debug("Resolving relative path with empty parentDir, using working directory", "path", p) + abs, err := filepath.Abs(p) + if err != nil { + slog.Warn("Failed to resolve absolute path, using as-is", "path", p, "error", err) + return p + } + return abs } - return filepath.Join(parentDir, path) + return filepath.Join(parentDir, p) } // EmitEvent sends an event to the events channel using non-blocking send diff --git a/pkg/rag/strategy/helpers_test.go b/pkg/rag/strategy/helpers_test.go new file mode 100644 index 000000000..019e1e858 --- /dev/null +++ b/pkg/rag/strategy/helpers_test.go @@ -0,0 +1,73 @@ +package strategy + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/docker/docker-agent/pkg/config/latest" +) + +// newDBConfig creates a RAGDatabaseConfig for testing via YAML unmarshaling. +func newDBConfig(t *testing.T, value string) latest.RAGDatabaseConfig { + t.Helper() + var cfg latest.RAGDatabaseConfig + err := cfg.UnmarshalYAML(func(v any) error { + p, ok := v.(*string) + if !ok { + return nil + } + *p = value + return nil + }) + require.NoError(t, err) + return cfg +} + +func TestMakeAbsolute_WithParentDir(t *testing.T) { + assert.Equal(t, "/parent/relative.go", makeAbsolute("relative.go", "/parent")) + assert.Equal(t, "/absolute/file.go", makeAbsolute("/absolute/file.go", "/parent")) +} + +func TestMakeAbsolute_EmptyParentDir(t *testing.T) { + cwd, err := os.Getwd() + require.NoError(t, err) + + result := makeAbsolute("relative.go", "") + assert.Equal(t, filepath.Join(cwd, "relative.go"), result) +} + +func TestResolveDatabasePath_EmptyParentDir(t *testing.T) { + cwd, err := os.Getwd() + require.NoError(t, err) + + result, err := ResolveDatabasePath(newDBConfig(t, "./my.db"), "", "default") + require.NoError(t, err) + assert.Equal(t, filepath.Join(cwd, "my.db"), result) +} + +func TestResolveDatabasePath_AbsolutePathIgnoresParentDir(t *testing.T) { + result, err := ResolveDatabasePath(newDBConfig(t, "/absolute/my.db"), "/parent", "default") + require.NoError(t, err) + assert.Equal(t, "/absolute/my.db", result) +} + +func TestResolveDatabasePath_RelativeWithParentDir(t *testing.T) { + result, err := ResolveDatabasePath(newDBConfig(t, "./my.db"), "/parent", "default") + require.NoError(t, err) + assert.Equal(t, "/parent/my.db", result) +} + +func TestMergeDocPaths_EmptyParentDir(t *testing.T) { + cwd, err := os.Getwd() + require.NoError(t, err) + + result := MergeDocPaths([]string{"shared.go"}, []string{"extra.go"}, "") + assert.Equal(t, []string{ + filepath.Join(cwd, "shared.go"), + filepath.Join(cwd, "extra.go"), + }, result) +}