Skip to content

Commit a658f41

Browse files
committed
add assert_diagnostics_diff tests for changed rules
1 parent 8c1bd06 commit a658f41

10 files changed

+631
-8
lines changed

crates/ruff_linter/src/rules/fastapi/mod.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ mod tests {
88
use anyhow::Result;
99
use test_case::test_case;
1010

11+
use ruff_python_ast::PythonVersion;
12+
1113
use crate::registry::Rule;
14+
use crate::settings::LinterSettings;
1215
use crate::test::test_path;
13-
use crate::{assert_diagnostics, settings};
16+
use crate::{assert_diagnostics, assert_diagnostics_diff};
1417

1518
#[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))]
1619
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))]
@@ -20,12 +23,35 @@ mod tests {
2023
let snapshot = format!("{}_{}", rule_code.name(), path.to_string_lossy());
2124
let diagnostics = test_path(
2225
Path::new("fastapi").join(path).as_path(),
23-
&settings::LinterSettings::for_rule(rule_code),
26+
&LinterSettings::for_rule(rule_code),
2427
)?;
2528
assert_diagnostics!(snapshot, diagnostics);
2629
Ok(())
2730
}
2831

32+
#[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))]
33+
#[test_case(Rule::FastApiUnusedPathParameter, Path::new("FAST003.py"))]
34+
fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> {
35+
let snapshot = format!(
36+
"deferred_annotations_diff_{}_{}",
37+
rule_code.name(),
38+
path.to_string_lossy()
39+
);
40+
assert_diagnostics_diff!(
41+
snapshot,
42+
Path::new("fastapi").join(path).as_path(),
43+
&LinterSettings {
44+
unresolved_target_version: PythonVersion::PY313.into(),
45+
..LinterSettings::for_rule(rule_code)
46+
},
47+
&LinterSettings {
48+
unresolved_target_version: PythonVersion::PY314.into(),
49+
..LinterSettings::for_rule(rule_code)
50+
},
51+
);
52+
Ok(())
53+
}
54+
2955
// FAST002 autofixes use `typing_extensions` on Python 3.8,
3056
// since `typing.Annotated` was added in Python 3.9
3157
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))]
@@ -34,9 +60,9 @@ mod tests {
3460
let snapshot = format!("{}_{}_py38", rule_code.name(), path.to_string_lossy());
3561
let diagnostics = test_path(
3662
Path::new("fastapi").join(path).as_path(),
37-
&settings::LinterSettings {
38-
unresolved_target_version: ruff_python_ast::PythonVersion::PY38.into(),
39-
..settings::LinterSettings::for_rule(rule_code)
63+
&LinterSettings {
64+
unresolved_target_version: PythonVersion::PY38.into(),
65+
..LinterSettings::for_rule(rule_code)
4066
},
4167
)?;
4268
assert_diagnostics!(snapshot, diagnostics);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
---
2+
source: crates/ruff_linter/src/rules/fastapi/mod.rs
3+
---
4+
--- Linter settings ---
5+
-linter.unresolved_target_version = 3.13
6+
+linter.unresolved_target_version = 3.14
7+
8+
--- Summary ---
9+
Removed: 10
10+
Added: 0
11+
12+
--- Removed ---
13+
FAST001 [*] FastAPI route with redundant `response_model` argument
14+
--> FAST001.py:17:22
15+
|
16+
17 | @app.post("/items/", response_model=Item)
17+
| ^^^^^^^^^^^^^^^^^^^
18+
18 | async def create_item(item: Item) -> Item:
19+
19 | return item
20+
|
21+
help: Remove argument
22+
14 | # Errors
23+
15 |
24+
16 |
25+
- @app.post("/items/", response_model=Item)
26+
17 + @app.post("/items/")
27+
18 | async def create_item(item: Item) -> Item:
28+
19 | return item
29+
20 |
30+
note: This is an unsafe fix and may change runtime behavior
31+
32+
33+
FAST001 [*] FastAPI route with redundant `response_model` argument
34+
--> FAST001.py:22:22
35+
|
36+
22 | @app.post("/items/", response_model=list[Item])
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
38+
23 | async def create_item(item: Item) -> list[Item]:
39+
24 | return item
40+
|
41+
help: Remove argument
42+
19 | return item
43+
20 |
44+
21 |
45+
- @app.post("/items/", response_model=list[Item])
46+
22 + @app.post("/items/")
47+
23 | async def create_item(item: Item) -> list[Item]:
48+
24 | return item
49+
25 |
50+
note: This is an unsafe fix and may change runtime behavior
51+
52+
53+
FAST001 [*] FastAPI route with redundant `response_model` argument
54+
--> FAST001.py:27:22
55+
|
56+
27 | @app.post("/items/", response_model=List[Item])
57+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
58+
28 | async def create_item(item: Item) -> List[Item]:
59+
29 | return item
60+
|
61+
help: Remove argument
62+
24 | return item
63+
25 |
64+
26 |
65+
- @app.post("/items/", response_model=List[Item])
66+
27 + @app.post("/items/")
67+
28 | async def create_item(item: Item) -> List[Item]:
68+
29 | return item
69+
30 |
70+
note: This is an unsafe fix and may change runtime behavior
71+
72+
73+
FAST001 [*] FastAPI route with redundant `response_model` argument
74+
--> FAST001.py:32:22
75+
|
76+
32 | @app.post("/items/", response_model=Dict[str, Item])
77+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
78+
33 | async def create_item(item: Item) -> Dict[str, Item]:
79+
34 | return item
80+
|
81+
help: Remove argument
82+
29 | return item
83+
30 |
84+
31 |
85+
- @app.post("/items/", response_model=Dict[str, Item])
86+
32 + @app.post("/items/")
87+
33 | async def create_item(item: Item) -> Dict[str, Item]:
88+
34 | return item
89+
35 |
90+
note: This is an unsafe fix and may change runtime behavior
91+
92+
93+
FAST001 [*] FastAPI route with redundant `response_model` argument
94+
--> FAST001.py:37:22
95+
|
96+
37 | @app.post("/items/", response_model=str)
97+
| ^^^^^^^^^^^^^^^^^^
98+
38 | async def create_item(item: Item) -> str:
99+
39 | return item
100+
|
101+
help: Remove argument
102+
34 | return item
103+
35 |
104+
36 |
105+
- @app.post("/items/", response_model=str)
106+
37 + @app.post("/items/")
107+
38 | async def create_item(item: Item) -> str:
108+
39 | return item
109+
40 |
110+
note: This is an unsafe fix and may change runtime behavior
111+
112+
113+
FAST001 [*] FastAPI route with redundant `response_model` argument
114+
--> FAST001.py:42:21
115+
|
116+
42 | @app.get("/items/", response_model=Item)
117+
| ^^^^^^^^^^^^^^^^^^^
118+
43 | async def create_item(item: Item) -> Item:
119+
44 | return item
120+
|
121+
help: Remove argument
122+
39 | return item
123+
40 |
124+
41 |
125+
- @app.get("/items/", response_model=Item)
126+
42 + @app.get("/items/")
127+
43 | async def create_item(item: Item) -> Item:
128+
44 | return item
129+
45 |
130+
note: This is an unsafe fix and may change runtime behavior
131+
132+
133+
FAST001 [*] FastAPI route with redundant `response_model` argument
134+
--> FAST001.py:47:21
135+
|
136+
47 | @app.get("/items/", response_model=Item)
137+
| ^^^^^^^^^^^^^^^^^^^
138+
48 | @app.post("/items/", response_model=Item)
139+
49 | async def create_item(item: Item) -> Item:
140+
|
141+
help: Remove argument
142+
44 | return item
143+
45 |
144+
46 |
145+
- @app.get("/items/", response_model=Item)
146+
47 + @app.get("/items/")
147+
48 | @app.post("/items/", response_model=Item)
148+
49 | async def create_item(item: Item) -> Item:
149+
50 | return item
150+
note: This is an unsafe fix and may change runtime behavior
151+
152+
153+
FAST001 [*] FastAPI route with redundant `response_model` argument
154+
--> FAST001.py:48:22
155+
|
156+
47 | @app.get("/items/", response_model=Item)
157+
48 | @app.post("/items/", response_model=Item)
158+
| ^^^^^^^^^^^^^^^^^^^
159+
49 | async def create_item(item: Item) -> Item:
160+
50 | return item
161+
|
162+
help: Remove argument
163+
45 |
164+
46 |
165+
47 | @app.get("/items/", response_model=Item)
166+
- @app.post("/items/", response_model=Item)
167+
48 + @app.post("/items/")
168+
49 | async def create_item(item: Item) -> Item:
169+
50 | return item
170+
51 |
171+
note: This is an unsafe fix and may change runtime behavior
172+
173+
174+
FAST001 [*] FastAPI route with redundant `response_model` argument
175+
--> FAST001.py:53:24
176+
|
177+
53 | @router.get("/items/", response_model=Item)
178+
| ^^^^^^^^^^^^^^^^^^^
179+
54 | async def create_item(item: Item) -> Item:
180+
55 | return item
181+
|
182+
help: Remove argument
183+
50 | return item
184+
51 |
185+
52 |
186+
- @router.get("/items/", response_model=Item)
187+
53 + @router.get("/items/")
188+
54 | async def create_item(item: Item) -> Item:
189+
55 | return item
190+
56 |
191+
note: This is an unsafe fix and may change runtime behavior
192+
193+
194+
FAST001 [*] FastAPI route with redundant `response_model` argument
195+
--> FAST001.py:118:23
196+
|
197+
116 | def setup_app(app_arg: FastAPI, non_app: str) -> None:
198+
117 | # Error
199+
118 | @app_arg.get("/", response_model=str)
200+
| ^^^^^^^^^^^^^^^^^^
201+
119 | async def get_root() -> str:
202+
120 | return "Hello World!"
203+
|
204+
help: Remove argument
205+
115 |
206+
116 | def setup_app(app_arg: FastAPI, non_app: str) -> None:
207+
117 | # Error
208+
- @app_arg.get("/", response_model=str)
209+
118 + @app_arg.get("/")
210+
119 | async def get_root() -> str:
211+
120 | return "Hello World!"
212+
121 |
213+
note: This is an unsafe fix and may change runtime behavior
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
source: crates/ruff_linter/src/rules/fastapi/mod.rs
3+
---
4+
--- Linter settings ---
5+
-linter.unresolved_target_version = 3.13
6+
+linter.unresolved_target_version = 3.14
7+
8+
--- Summary ---
9+
Removed: 3
10+
Added: 0
11+
12+
--- Removed ---
13+
FAST003 [*] Parameter `thing_id` appears in route path, but not in `single` signature
14+
--> FAST003.py:158:19
15+
|
16+
157 | ### Errors
17+
158 | @app.get("/things/{thing_id}")
18+
| ^^^^^^^^^^
19+
159 | async def single(other: Annotated[str, Depends(something_else)]): ...
20+
160 | @app.get("/things/{thing_id}")
21+
|
22+
help: Add `thing_id` to function signature
23+
156 |
24+
157 | ### Errors
25+
158 | @app.get("/things/{thing_id}")
26+
- async def single(other: Annotated[str, Depends(something_else)]): ...
27+
159 + async def single(other: Annotated[str, Depends(something_else)], thing_id): ...
28+
160 | @app.get("/things/{thing_id}")
29+
161 | async def default(other: str = Depends(something_else)): ...
30+
162 |
31+
note: This is an unsafe fix and may change runtime behavior
32+
33+
34+
FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_full` signature
35+
--> FAST003.py:197:12
36+
|
37+
196 | # Errors
38+
197 | @app.get("/{id}")
39+
| ^^^^
40+
198 | async def get_id_pydantic_full(
41+
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
42+
|
43+
help: Add `id` to function signature
44+
196 | # Errors
45+
197 | @app.get("/{id}")
46+
198 | async def get_id_pydantic_full(
47+
- params: Annotated[PydanticParams, Depends(PydanticParams)],
48+
199 + params: Annotated[PydanticParams, Depends(PydanticParams)], id,
49+
200 | ): ...
50+
201 | @app.get("/{id}")
51+
202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
52+
note: This is an unsafe fix and may change runtime behavior
53+
54+
55+
FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_short` signature
56+
--> FAST003.py:201:12
57+
|
58+
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
59+
200 | ): ...
60+
201 | @app.get("/{id}")
61+
| ^^^^
62+
202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
63+
203 | @app.get("/{id}")
64+
|
65+
help: Add `id` to function signature
66+
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
67+
200 | ): ...
68+
201 | @app.get("/{id}")
69+
- async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
70+
202 + async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()], id): ...
71+
203 | @app.get("/{id}")
72+
204 | async def get_id_init_not_annotated(params = Depends(InitParams)): ...
73+
205 |
74+
note: This is an unsafe fix and may change runtime behavior

crates/ruff_linter/src/rules/flake8_builtins/mod.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ mod tests {
1010
use anyhow::Result;
1111
use test_case::test_case;
1212

13-
use crate::assert_diagnostics;
1413
use crate::registry::Rule;
1514
use crate::rules::flake8_builtins;
1615
use crate::settings::LinterSettings;
1716
use crate::settings::types::PreviewMode;
1817
use crate::test::{test_path, test_resource_path};
18+
use crate::{assert_diagnostics, assert_diagnostics_diff};
1919
use ruff_python_ast::PythonVersion;
2020

2121
#[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))]
@@ -64,6 +64,28 @@ mod tests {
6464
Ok(())
6565
}
6666

67+
#[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))]
68+
fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> {
69+
let snapshot = format!(
70+
"deferred_annotations_diff_{}_{}",
71+
rule_code.name(),
72+
path.to_string_lossy()
73+
);
74+
assert_diagnostics_diff!(
75+
snapshot,
76+
Path::new("flake8_builtins").join(path).as_path(),
77+
&LinterSettings {
78+
unresolved_target_version: PythonVersion::PY313.into(),
79+
..LinterSettings::for_rule(rule_code)
80+
},
81+
&LinterSettings {
82+
unresolved_target_version: PythonVersion::PY314.into(),
83+
..LinterSettings::for_rule(rule_code)
84+
},
85+
);
86+
Ok(())
87+
}
88+
6789
#[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))]
6890
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
6991
let snapshot = format!(

0 commit comments

Comments
 (0)