touch_test.go

 1package tools
 2
 3import (
 4	"context"
 5	"encoding/json"
 6	"os"
 7	"path/filepath"
 8	"testing"
 9	"time"
10
11	"charm.land/fantasy"
12	"github.com/stretchr/testify/require"
13)
14
15type mockFileTrackerService struct{}
16
17func (m mockFileTrackerService) RecordRead(ctx context.Context, sessionID, path string) {}
18
19func (m mockFileTrackerService) LastReadTime(ctx context.Context, sessionID, path string) time.Time {
20	return time.Now()
21}
22
23func (m mockFileTrackerService) ListReadFiles(ctx context.Context, sessionID string) ([]string, error) {
24	return nil, nil
25}
26
27func TestTouchToolCreatesEmptyFile(t *testing.T) {
28	t.Parallel()
29
30	workingDir := t.TempDir()
31	tool := NewTouchTool(nil, &mockPermissionService{}, &mockHistoryService{}, mockFileTrackerService{}, workingDir)
32	ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session")
33
34	resp := runTouchTool(t, tool, ctx, TouchParams{FilePath: "nested/empty.txt"})
35	require.False(t, resp.IsError)
36
37	filePath := filepath.Join(workingDir, "nested", "empty.txt")
38	info, err := os.Stat(filePath)
39	require.NoError(t, err)
40	require.False(t, info.IsDir())
41	require.Zero(t, info.Size())
42}
43
44func TestTouchToolRefusesExistingFile(t *testing.T) {
45	t.Parallel()
46
47	workingDir := t.TempDir()
48	filePath := filepath.Join(workingDir, "existing.txt")
49	require.NoError(t, os.WriteFile(filePath, []byte("content"), 0o644))
50
51	tool := NewTouchTool(nil, &mockPermissionService{}, &mockHistoryService{}, mockFileTrackerService{}, workingDir)
52	ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session")
53
54	resp := runTouchTool(t, tool, ctx, TouchParams{FilePath: "existing.txt"})
55	require.True(t, resp.IsError)
56	require.Contains(t, resp.Content, "File already exists")
57
58	content, err := os.ReadFile(filePath)
59	require.NoError(t, err)
60	require.Equal(t, "content", string(content))
61}
62
63func TestWriteToolEmptyContentPointsToTouch(t *testing.T) {
64	t.Parallel()
65
66	tool := NewWriteTool(nil, nil, nil, nil, t.TempDir())
67
68	input, err := json.Marshal(WriteParams{FilePath: "empty.txt"})
69	require.NoError(t, err)
70
71	resp, err := tool.Run(context.Background(), fantasy.ToolCall{
72		ID:    "test-call",
73		Name:  WriteToolName,
74		Input: string(input),
75	})
76	require.NoError(t, err)
77	require.True(t, resp.IsError)
78	require.Equal(t, `content is required. use the "touch" tool to create an empty file`, resp.Content)
79}
80
81func runTouchTool(t *testing.T, tool fantasy.AgentTool, ctx context.Context, params TouchParams) fantasy.ToolResponse {
82	t.Helper()
83
84	input, err := json.Marshal(params)
85	require.NoError(t, err)
86
87	resp, err := tool.Run(ctx, fantasy.ToolCall{
88		ID:    "test-call",
89		Name:  TouchToolName,
90		Input: string(input),
91	})
92	require.NoError(t, err)
93	return resp
94}