Coverage for tests/test_file_and_form_order_issue_9116.py: 100%

40 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-12-04 08:29 +0000

1""" 

2Regression test, Error 422 if Form is declared before File 

3See https://github.com/tiangolo/fastapi/discussions/9116 

4""" 

5 

6from pathlib import Path 1abcdefg

7from typing import List 1abcdefg

8 

9import pytest 1abcdefg

10from fastapi import FastAPI, File, Form 1abcdefg

11from fastapi.testclient import TestClient 1abcdefg

12from typing_extensions import Annotated 1abcdefg

13 

14app = FastAPI() 1abcdefg

15 

16 

17@app.post("/file_before_form") 1abcdefg

18def file_before_form( 1abcdefg

19 file: bytes = File(), 

20 city: str = Form(), 

21): 

22 return {"file_content": file, "city": city} 1hijklmn

23 

24 

25@app.post("/file_after_form") 1abcdefg

26def file_after_form( 1abcdefg

27 city: str = Form(), 

28 file: bytes = File(), 

29): 

30 return {"file_content": file, "city": city} 1hijklmn

31 

32 

33@app.post("/file_list_before_form") 1abcdefg

34def file_list_before_form( 1abcdefg

35 files: Annotated[List[bytes], File()], 

36 city: Annotated[str, Form()], 

37): 

38 return {"file_contents": files, "city": city} 1opqrstu

39 

40 

41@app.post("/file_list_after_form") 1abcdefg

42def file_list_after_form( 1abcdefg

43 city: Annotated[str, Form()], 

44 files: Annotated[List[bytes], File()], 

45): 

46 return {"file_contents": files, "city": city} 1opqrstu

47 

48 

49client = TestClient(app) 1abcdefg

50 

51 

52@pytest.fixture 1abcdefg

53def tmp_file_1(tmp_path: Path) -> Path: 1abcdefg

54 f = tmp_path / "example1.txt" 1abcdefg

55 f.write_text("foo") 1abcdefg

56 return f 1abcdefg

57 

58 

59@pytest.fixture 1abcdefg

60def tmp_file_2(tmp_path: Path) -> Path: 1abcdefg

61 f = tmp_path / "example2.txt" 1abcdefg

62 f.write_text("bar") 1abcdefg

63 return f 1abcdefg

64 

65 

66@pytest.mark.parametrize("endpoint_path", ("/file_before_form", "/file_after_form")) 1abcdefg

67def test_file_form_order(endpoint_path: str, tmp_file_1: Path): 1abcdefg

68 response = client.post( 1hijklmn

69 url=endpoint_path, 

70 data={"city": "Thimphou"}, 

71 files={"file": (tmp_file_1.name, tmp_file_1.read_bytes())}, 

72 ) 

73 assert response.status_code == 200, response.text 1hijklmn

74 assert response.json() == {"file_content": "foo", "city": "Thimphou"} 1hijklmn

75 

76 

77@pytest.mark.parametrize( 1abcdefg

78 "endpoint_path", ("/file_list_before_form", "/file_list_after_form") 

79) 

80def test_file_list_form_order(endpoint_path: str, tmp_file_1: Path, tmp_file_2: Path): 1abcdefg

81 response = client.post( 1opqrstu

82 url=endpoint_path, 

83 data={"city": "Thimphou"}, 

84 files=( 

85 ("files", (tmp_file_1.name, tmp_file_1.read_bytes())), 

86 ("files", (tmp_file_2.name, tmp_file_2.read_bytes())), 

87 ), 

88 ) 

89 assert response.status_code == 200, response.text 1opqrstu

90 assert response.json() == {"file_contents": ["foo", "bar"], "city": "Thimphou"} 1opqrstu