99 "strings"
1010 "testing"
1111
12+ "coder/internal/config"
13+ "coder/internal/permission"
1214 "coder/internal/security"
1315)
1416
@@ -28,7 +30,9 @@ func TestReadToolSmallFileDefaultLimit(t *testing.T) {
2830 if err != nil {
2931 t .Fatal (err )
3032 }
31- tool := NewReadTool (ws )
33+ cfg , _ := permission .PresetConfig ("build" )
34+ policy := permission .New (cfg )
35+ tool := NewReadTool (ws , policy )
3236
3337 // 仅传 path,不带 offset/limit,期望读出全部 10 行
3438 args , _ := json .Marshal (map [string ]any {
@@ -77,7 +81,9 @@ func TestReadToolLargeFilePagination(t *testing.T) {
7781 if err != nil {
7882 t .Fatal (err )
7983 }
80- tool := NewReadTool (ws )
84+ cfg , _ := permission .PresetConfig ("build" )
85+ policy := permission .New (cfg )
86+ tool := NewReadTool (ws , policy )
8187
8288 // 默认只读前 50 行
8389 args , _ := json .Marshal (map [string ]any {
@@ -126,7 +132,9 @@ func TestReadToolOffsetAndLimit(t *testing.T) {
126132 if err != nil {
127133 t .Fatal (err )
128134 }
129- tool := NewReadTool (ws )
135+ cfg , _ := permission .PresetConfig ("build" )
136+ policy := permission .New (cfg )
137+ tool := NewReadTool (ws , policy )
130138
131139 // 从第 51 行开始读 50 行
132140 args , _ := json .Marshal (map [string ]any {
@@ -172,7 +180,9 @@ func TestReadToolOffsetBeyondEOFAndInvalidLimit(t *testing.T) {
172180 if err != nil {
173181 t .Fatal (err )
174182 }
175- tool := NewReadTool (ws )
183+ cfg , _ := permission .PresetConfig ("build" )
184+ policy := permission .New (cfg )
185+ tool := NewReadTool (ws , policy )
176186
177187 // offset 大于文件总行数,期望 content 为空、has_more=false
178188 argsBeyond , _ := json .Marshal (map [string ]any {
@@ -204,3 +214,125 @@ func TestReadToolOffsetBeyondEOFAndInvalidLimit(t *testing.T) {
204214 t .Fatalf ("execute read (invalid limit): %v" , err )
205215 }
206216}
217+
218+ // TestReadToolExternalPathApproval 测试外部路径审批流程
219+ func TestReadToolExternalPathApproval (t * testing.T ) {
220+ // 创建一个临时目录作为 workspace
221+ wsRoot := t .TempDir ()
222+ ws , err := security .NewWorkspace (wsRoot )
223+ if err != nil {
224+ t .Fatal (err )
225+ }
226+
227+ // 在工作区外创建一个文件
228+ externalDir := t .TempDir ()
229+ externalFile := filepath .Join (externalDir , "external.txt" )
230+ externalContent := "external file content"
231+ if err := os .WriteFile (externalFile , []byte (externalContent ), 0o644 ); err != nil {
232+ t .Fatal (err )
233+ }
234+
235+ // 测试策略为 ask 时,ApprovalRequest 应该返回需要审批
236+ t .Run ("ask_policy_returns_approval_request" , func (t * testing.T ) {
237+ cfg := config.PermissionConfig {ExternalDir : "ask" }
238+ policy := permission .New (cfg )
239+ tool := NewReadTool (ws , policy )
240+
241+ args , _ := json .Marshal (map [string ]any {
242+ "path" : externalFile ,
243+ })
244+
245+ req , err := tool .ApprovalRequest (args )
246+ if err != nil {
247+ t .Fatalf ("ApprovalRequest error: %v" , err )
248+ }
249+ if req == nil {
250+ t .Fatal ("expected ApprovalRequest for external path, got nil" )
251+ }
252+ if req .Tool != "read" {
253+ t .Fatalf ("expected tool name 'read', got %q" , req .Tool )
254+ }
255+ })
256+
257+ // 测试策略为 allow 时,ApprovalRequest 应该返回 nil
258+ t .Run ("allow_policy_no_approval_request" , func (t * testing.T ) {
259+ cfg := config.PermissionConfig {ExternalDir : "allow" }
260+ policy := permission .New (cfg )
261+ tool := NewReadTool (ws , policy )
262+
263+ args , _ := json .Marshal (map [string ]any {
264+ "path" : externalFile ,
265+ })
266+
267+ req , err := tool .ApprovalRequest (args )
268+ if err != nil {
269+ t .Fatalf ("ApprovalRequest error: %v" , err )
270+ }
271+ if req != nil {
272+ t .Fatalf ("expected no ApprovalRequest for allow policy, got %v" , req )
273+ }
274+ })
275+
276+ // 测试策略为 deny 时,ApprovalRequest 应该返回 nil(直接拒绝)
277+ t .Run ("deny_policy_no_approval_request" , func (t * testing.T ) {
278+ cfg := config.PermissionConfig {ExternalDir : "deny" }
279+ policy := permission .New (cfg )
280+ tool := NewReadTool (ws , policy )
281+
282+ args , _ := json .Marshal (map [string ]any {
283+ "path" : externalFile ,
284+ })
285+
286+ req , err := tool .ApprovalRequest (args )
287+ if err != nil {
288+ t .Fatalf ("ApprovalRequest error: %v" , err )
289+ }
290+ if req != nil {
291+ t .Fatalf ("expected no ApprovalRequest for deny policy, got %v" , req )
292+ }
293+ })
294+
295+ // 测试 ~ 路径展开和审批
296+ t .Run ("home_path_expansion_and_approval" , func (t * testing.T ) {
297+ cfg := config.PermissionConfig {ExternalDir : "ask" }
298+ policy := permission .New (cfg )
299+ tool := NewReadTool (ws , policy )
300+
301+ args , _ := json .Marshal (map [string ]any {
302+ "path" : "~/test_file.txt" ,
303+ })
304+
305+ req , err := tool .ApprovalRequest (args )
306+ if err != nil {
307+ t .Fatalf ("ApprovalRequest error: %v" , err )
308+ }
309+ if req == nil {
310+ t .Fatal ("expected ApprovalRequest for ~ path, got nil" )
311+ }
312+ })
313+
314+ // 测试工作区内路径不需要审批
315+ t .Run ("workspace_path_no_approval" , func (t * testing.T ) {
316+ cfg := config.PermissionConfig {ExternalDir : "ask" }
317+ policy := permission .New (cfg )
318+ tool := NewReadTool (ws , policy )
319+
320+ // 在工作区内创建文件
321+ internalFile := filepath .Join (wsRoot , "internal.txt" )
322+ if err := os .WriteFile (internalFile , []byte ("internal content" ), 0o644 ); err != nil {
323+ t .Fatal (err )
324+ }
325+
326+ args , _ := json .Marshal (map [string ]any {
327+ "path" : "internal.txt" ,
328+ })
329+
330+ req , err := tool .ApprovalRequest (args )
331+ if err != nil {
332+ t .Fatalf ("ApprovalRequest error: %v" , err )
333+ }
334+ if req != nil {
335+ t .Fatalf ("expected no ApprovalRequest for workspace path, got %v" , req )
336+ }
337+ })
338+ }
0 commit comments