| 1076 | } |
| 1077 | |
| 1078 | func TestEditFiles_PreservesPermissions(t *testing.T) { |
| 1079 | t.Parallel() |
| 1080 | |
| 1081 | if runtime.GOOS == "windows" { |
| 1082 | t.Skip("file permissions are not reliably supported on Windows") |
| 1083 | } |
| 1084 | |
| 1085 | dir := t.TempDir() |
| 1086 | logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) |
| 1087 | osFs := afero.NewOsFs() |
| 1088 | api := agentfiles.NewAPI(logger, osFs, nil) |
| 1089 | |
| 1090 | path := filepath.Join(dir, "script.sh") |
| 1091 | err := afero.WriteFile(osFs, path, []byte("#!/bin/sh\necho hello\n"), 0o755) |
| 1092 | require.NoError(t, err) |
| 1093 | |
| 1094 | // Sanity-check the initial mode. |
| 1095 | info, err := osFs.Stat(path) |
| 1096 | require.NoError(t, err) |
| 1097 | require.Equal(t, os.FileMode(0o755), info.Mode().Perm()) |
| 1098 | |
| 1099 | ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) |
| 1100 | defer cancel() |
| 1101 | |
| 1102 | body := workspacesdk.FileEditRequest{ |
| 1103 | Files: []workspacesdk.FileEdits{ |
| 1104 | { |
| 1105 | Path: path, |
| 1106 | Edits: []workspacesdk.FileEdit{ |
| 1107 | { |
| 1108 | Search: "hello", |
| 1109 | Replace: "world", |
| 1110 | }, |
| 1111 | }, |
| 1112 | }, |
| 1113 | }, |
| 1114 | } |
| 1115 | buf := bytes.NewBuffer(nil) |
| 1116 | enc := json.NewEncoder(buf) |
| 1117 | enc.SetEscapeHTML(false) |
| 1118 | err = enc.Encode(body) |
| 1119 | require.NoError(t, err) |
| 1120 | |
| 1121 | w := httptest.NewRecorder() |
| 1122 | r := httptest.NewRequestWithContext(ctx, http.MethodPost, "/edit-files", buf) |
| 1123 | api.Routes().ServeHTTP(w, r) |
| 1124 | require.Equal(t, http.StatusOK, w.Code) |
| 1125 | |
| 1126 | // Verify content was updated. |
| 1127 | data, err := afero.ReadFile(osFs, path) |
| 1128 | require.NoError(t, err) |
| 1129 | require.Equal(t, "#!/bin/sh\necho world\n", string(data)) |
| 1130 | |
| 1131 | // Verify permissions are preserved after the |
| 1132 | // temp-file-and-rename cycle. |
| 1133 | info, err = osFs.Stat(path) |
| 1134 | require.NoError(t, err) |
| 1135 | require.Equal(t, os.FileMode(0o755), info.Mode().Perm(), |