Skip to content

Commit 32a5413

Browse files
jvansch1meta-codesync[bot]
authored andcommitted
Add file-level safe delete test coverage
Summary: Add 3 new LSP interaction tests covering file-level safe delete: - from-import blocking (from target import func) - unused file in from-import directory (should offer delete) - relative import blocking (from . import target) Also add fixture directories with pyrefly.toml configs and Python source files for each test scenario. Reviewed By: kinto0 Differential Revision: D94951767 fbshipit-source-id: 40cea1dab2a81eebef69cba6942a218c6ef91b99
1 parent 7059fb9 commit 32a5413

File tree

9 files changed

+180
-0
lines changed

9 files changed

+180
-0
lines changed

pyrefly/lib/test/lsp/lsp_interaction/safe_delete_file.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,134 @@ fn test_safe_delete_file_rejects_usages() {
129129

130130
interaction.shutdown().unwrap();
131131
}
132+
133+
#[test]
134+
fn test_safe_delete_file_rejects_from_import() {
135+
let root = get_test_files_root();
136+
let root_path = root.path().join("safe_delete_file_from_import");
137+
let (interaction, _scope_uri) = init_with_delete_support(&root_path);
138+
139+
let file = "target.py";
140+
let file_path = root_path.join(file);
141+
let uri = Url::from_file_path(&file_path).unwrap();
142+
143+
interaction.client.did_open(file);
144+
interaction.client.did_open("consumer.py");
145+
146+
interaction
147+
.client
148+
.send_request::<CodeActionRequest>(json!({
149+
"textDocument": { "uri": uri },
150+
"range": {
151+
"start": { "line": 0, "character": 0 },
152+
"end": { "line": 0, "character": 0 }
153+
},
154+
"context": { "diagnostics": [] }
155+
}))
156+
.expect_response_with(|response| {
157+
let Some(actions) = response else {
158+
return true;
159+
};
160+
actions.iter().all(|action| {
161+
let CodeActionOrCommand::CodeAction(code_action) = action else {
162+
return true;
163+
};
164+
code_action.title != "Safe delete file `target.py`"
165+
})
166+
})
167+
.unwrap();
168+
169+
interaction.shutdown().unwrap();
170+
}
171+
172+
#[test]
173+
fn test_safe_delete_file_from_import_unused() {
174+
let root = get_test_files_root();
175+
let root_path = root.path().join("safe_delete_file_from_import");
176+
let (interaction, _scope_uri) = init_with_delete_support(&root_path);
177+
178+
let file = "unused.py";
179+
let file_path = root_path.join(file);
180+
let uri = Url::from_file_path(&file_path).unwrap();
181+
182+
interaction.client.did_open(file);
183+
184+
interaction
185+
.client
186+
.send_request::<CodeActionRequest>(json!({
187+
"textDocument": { "uri": uri },
188+
"range": {
189+
"start": { "line": 0, "character": 0 },
190+
"end": { "line": 0, "character": 0 }
191+
},
192+
"context": { "diagnostics": [] }
193+
}))
194+
.expect_response_with(|response| {
195+
let Some(actions) = response else {
196+
return false;
197+
};
198+
actions.iter().any(|action| {
199+
let CodeActionOrCommand::CodeAction(code_action) = action else {
200+
return false;
201+
};
202+
if code_action.title != "Safe delete file `unused.py`" {
203+
return false;
204+
}
205+
let Some(edit) = &code_action.edit else {
206+
return false;
207+
};
208+
let Some(DocumentChanges::Operations(ops)) = &edit.document_changes else {
209+
return false;
210+
};
211+
if ops.len() != 1 {
212+
return false;
213+
}
214+
match &ops[0] {
215+
DocumentChangeOperation::Op(ResourceOp::Delete(delete)) => delete.uri == uri,
216+
_ => false,
217+
}
218+
})
219+
})
220+
.unwrap();
221+
222+
interaction.shutdown().unwrap();
223+
}
224+
225+
#[test]
226+
fn test_safe_delete_file_rejects_relative_import() {
227+
let root = get_test_files_root();
228+
let root_path = root.path().join("safe_delete_file_relative");
229+
let (interaction, _scope_uri) = init_with_delete_support(&root_path);
230+
231+
let file = "pkg/target.py";
232+
let file_path = root_path.join(file);
233+
let uri = Url::from_file_path(&file_path).unwrap();
234+
235+
interaction.client.did_open(file);
236+
interaction.client.did_open("pkg/consumer.py");
237+
238+
interaction
239+
.client
240+
.send_request::<CodeActionRequest>(json!({
241+
"textDocument": { "uri": uri },
242+
"range": {
243+
"start": { "line": 0, "character": 0 },
244+
"end": { "line": 0, "character": 0 }
245+
},
246+
"context": { "diagnostics": [] }
247+
}))
248+
.expect_response_with(|response| {
249+
let Some(actions) = response else {
250+
return true;
251+
};
252+
actions.iter().all(|action| {
253+
let CodeActionOrCommand::CodeAction(code_action) = action else {
254+
return true;
255+
};
256+
code_action.title != "Safe delete file `target.py`"
257+
})
258+
})
259+
.unwrap();
260+
261+
interaction.shutdown().unwrap();
262+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
7+
from target import func
8+
9+
func()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
search_path = ["."]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
7+
def func() -> int:
8+
return 1
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
7+
def bar() -> int:
8+
return 1
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
7+
from . import target
8+
9+
target.func()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
7+
def func() -> int:
8+
return 1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
search_path = ["."]

0 commit comments

Comments
 (0)