mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 04:14:56 +00:00
- Revert leader workspace override: leader keeps personal workspace as default, team workspace accessible via ToolTeamWorkspaceFromCtx - Auto-copy: when leader creates team_tasks, scan subject+description for file paths → copy from personal to team workspace so members can access them - Safety: Lstat (reject symlinks), 10MB size cap, .env excluded from allowed extensions, path traversal blocked - Prompt hint: clarify members can only access team workspace files, referenced files are auto-copied
122 lines
3.7 KiB
Go
122 lines
3.7 KiB
Go
package tools
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestExtractFilePaths(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
text string
|
|
want []string
|
|
}{
|
|
{"quoted_double", `see "report.md" for details`, []string{"report.md"}},
|
|
{"quoted_single", `check 'docs/notes.txt' please`, []string{"docs/notes.txt"}},
|
|
{"path_param", `path="docs/a.md"`, []string{"docs/a.md"}},
|
|
{"markdown_link", `[report](analysis.md)`, []string{"analysis.md"}},
|
|
{"backtick", "check `config.yaml` file", []string{"config.yaml"}},
|
|
{"multiple", `see "a.md" and "b.txt"`, []string{"a.md", "b.txt"}},
|
|
{"nested", `"docs/sub/report.md"`, []string{"docs/sub/report.md"}},
|
|
{"skip_url", `see https://example.com/file.md`, nil},
|
|
{"skip_abs", `see "/etc/passwd"`, nil},
|
|
{"skip_traversal", `see "../secret.md"`, nil},
|
|
{"skip_unknown_ext", `see "binary.exe"`, nil},
|
|
{"no_matches", "do some research on AI", nil},
|
|
{"dedup", `"report.md" and path="report.md"`, []string{"report.md"}},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := extractFilePaths(tt.text)
|
|
if len(got) != len(tt.want) {
|
|
t.Fatalf("extractFilePaths(%q) = %v, want %v", tt.text, got, tt.want)
|
|
}
|
|
for i := range got {
|
|
if got[i] != tt.want[i] {
|
|
t.Errorf("extractFilePaths(%q)[%d] = %q, want %q", tt.text, i, got[i], tt.want[i])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAutoShareFiles_CopiesFromPersonalToTeam(t *testing.T) {
|
|
personal := t.TempDir()
|
|
team := t.TempDir()
|
|
|
|
// Create file in personal workspace.
|
|
os.MkdirAll(filepath.Join(personal, "docs"), 0755)
|
|
os.WriteFile(filepath.Join(personal, "report.md"), []byte("# Report"), 0644)
|
|
os.WriteFile(filepath.Join(personal, "docs", "notes.txt"), []byte("notes"), 0644)
|
|
|
|
n := autoShareFiles(`see "report.md" and "docs/notes.txt"`, personal, team)
|
|
if n != 2 {
|
|
t.Fatalf("autoShareFiles copied %d files, want 2", n)
|
|
}
|
|
|
|
// Verify files exist in team workspace.
|
|
if _, err := os.Stat(filepath.Join(team, "report.md")); err != nil {
|
|
t.Error("report.md not copied to team workspace")
|
|
}
|
|
if _, err := os.Stat(filepath.Join(team, "docs", "notes.txt")); err != nil {
|
|
t.Error("docs/notes.txt not copied to team workspace")
|
|
}
|
|
|
|
// Verify content.
|
|
data, _ := os.ReadFile(filepath.Join(team, "report.md"))
|
|
if string(data) != "# Report" {
|
|
t.Errorf("copied content = %q, want %q", data, "# Report")
|
|
}
|
|
}
|
|
|
|
func TestAutoShareFiles_SkipAlreadyInTeam(t *testing.T) {
|
|
personal := t.TempDir()
|
|
team := t.TempDir()
|
|
|
|
os.WriteFile(filepath.Join(personal, "report.md"), []byte("personal"), 0644)
|
|
os.WriteFile(filepath.Join(team, "report.md"), []byte("team version"), 0644)
|
|
|
|
n := autoShareFiles(`see "report.md"`, personal, team)
|
|
if n != 0 {
|
|
t.Fatalf("autoShareFiles copied %d files, want 0 (already in team)", n)
|
|
}
|
|
|
|
// Team file should be unchanged.
|
|
data, _ := os.ReadFile(filepath.Join(team, "report.md"))
|
|
if string(data) != "team version" {
|
|
t.Errorf("team file was overwritten: %q", data)
|
|
}
|
|
}
|
|
|
|
func TestAutoShareFiles_SkipNonExistent(t *testing.T) {
|
|
personal := t.TempDir()
|
|
team := t.TempDir()
|
|
|
|
n := autoShareFiles(`see "nonexistent.md"`, personal, team)
|
|
if n != 0 {
|
|
t.Fatalf("autoShareFiles copied %d files, want 0", n)
|
|
}
|
|
}
|
|
|
|
func TestAutoShareFiles_SameWorkspace(t *testing.T) {
|
|
dir := t.TempDir()
|
|
os.WriteFile(filepath.Join(dir, "a.md"), []byte("data"), 0644)
|
|
|
|
n := autoShareFiles(`see "a.md"`, dir, dir)
|
|
if n != 0 {
|
|
t.Fatalf("autoShareFiles should skip when personal==team, got %d", n)
|
|
}
|
|
}
|
|
|
|
func TestAutoShareFiles_EmptyWorkspace(t *testing.T) {
|
|
n := autoShareFiles(`see "a.md"`, "", "/tmp/team")
|
|
if n != 0 {
|
|
t.Fatalf("autoShareFiles should skip empty personal ws, got %d", n)
|
|
}
|
|
n = autoShareFiles(`see "a.md"`, "/tmp/personal", "")
|
|
if n != 0 {
|
|
t.Fatalf("autoShareFiles should skip empty team ws, got %d", n)
|
|
}
|
|
}
|