| 1528 | } |
| 1529 | |
| 1530 | func TestEditFiles_FollowsSymlinks(t *testing.T) { |
| 1531 | t.Parallel() |
| 1532 | |
| 1533 | if runtime.GOOS == "windows" { |
| 1534 | t.Skip("symlinks are not reliably supported on Windows") |
| 1535 | } |
| 1536 | |
| 1537 | dir := t.TempDir() |
| 1538 | logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) |
| 1539 | osFs := afero.NewOsFs() |
| 1540 | api := agentfiles.NewAPI(logger, osFs, nil) |
| 1541 | |
| 1542 | // Create a real file and a symlink pointing to it. |
| 1543 | realPath := filepath.Join(dir, "real.txt") |
| 1544 | err := afero.WriteFile(osFs, realPath, []byte("hello world"), 0o644) |
| 1545 | require.NoError(t, err) |
| 1546 | |
| 1547 | linkPath := filepath.Join(dir, "link.txt") |
| 1548 | err = os.Symlink(realPath, linkPath) |
| 1549 | require.NoError(t, err) |
| 1550 | |
| 1551 | ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) |
| 1552 | defer cancel() |
| 1553 | |
| 1554 | body := workspacesdk.FileEditRequest{ |
| 1555 | Files: []workspacesdk.FileEdits{ |
| 1556 | { |
| 1557 | Path: linkPath, |
| 1558 | Edits: []workspacesdk.FileEdit{ |
| 1559 | { |
| 1560 | Search: "hello", |
| 1561 | Replace: "goodbye", |
| 1562 | }, |
| 1563 | }, |
| 1564 | }, |
| 1565 | }, |
| 1566 | } |
| 1567 | buf := bytes.NewBuffer(nil) |
| 1568 | enc := json.NewEncoder(buf) |
| 1569 | enc.SetEscapeHTML(false) |
| 1570 | err = enc.Encode(body) |
| 1571 | require.NoError(t, err) |
| 1572 | |
| 1573 | w := httptest.NewRecorder() |
| 1574 | r := httptest.NewRequestWithContext(ctx, http.MethodPost, "/edit-files", buf) |
| 1575 | api.Routes().ServeHTTP(w, r) |
| 1576 | require.Equal(t, http.StatusOK, w.Code) |
| 1577 | |
| 1578 | // The symlink must still be a symlink. |
| 1579 | fi, err := os.Lstat(linkPath) |
| 1580 | require.NoError(t, err) |
| 1581 | require.NotZero(t, fi.Mode()&os.ModeSymlink, "symlink was replaced") |
| 1582 | |
| 1583 | // The real file must have the edited content. |
| 1584 | data, err := os.ReadFile(realPath) |
| 1585 | require.NoError(t, err) |
| 1586 | require.Equal(t, "goodbye world", string(data)) |
| 1587 | } |