Coverage for tests/test_compat.py: 100%

60 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-01-13 13:38 +0000

1from typing import Any, Dict, List, Union 1abcde

2 

3from fastapi import FastAPI, UploadFile 1abcde

4from fastapi._compat import ( 1abcde

5 ModelField, 

6 Undefined, 

7 _get_model_config, 

8 get_cached_model_fields, 

9 get_model_fields, 

10 is_bytes_sequence_annotation, 

11 is_scalar_field, 

12 is_uploadfile_sequence_annotation, 

13) 

14from fastapi.testclient import TestClient 1abcde

15from pydantic import BaseConfig, BaseModel, ConfigDict 1abcde

16from pydantic.fields import FieldInfo 1abcde

17 

18from .utils import needs_pydanticv1, needs_pydanticv2 1abcde

19 

20 

21@needs_pydanticv2 1abcde

22def test_model_field_default_required(): 1abcde

23 # For coverage 

24 field_info = FieldInfo(annotation=str) 1EFGHI

25 field = ModelField(name="foo", field_info=field_info) 1EFGHI

26 assert field.default is Undefined 1EFGHI

27 

28 

29@needs_pydanticv1 1abcde

30def test_upload_file_dummy_with_info_plain_validator_function(): 1abcde

31 # For coverage 

32 assert UploadFile.__get_pydantic_core_schema__(str, lambda x: None) == {} 1JKLMN

33 

34 

35@needs_pydanticv1 1abcde

36def test_union_scalar_list(): 1abcde

37 # For coverage 

38 # TODO: there might not be a current valid code path that uses this, it would 

39 # potentially enable query parameters defined as both a scalar and a list 

40 # but that would require more refactors, also not sure it's really useful 

41 from fastapi._compat import is_pv1_scalar_field 1uvwxy

42 

43 field_info = FieldInfo() 1uvwxy

44 field = ModelField( 1uvwxy

45 name="foo", 

46 field_info=field_info, 

47 type_=Union[str, List[int]], 

48 class_validators={}, 

49 model_config=BaseConfig, 

50 ) 

51 assert not is_pv1_scalar_field(field) 1uvwxy

52 

53 

54@needs_pydanticv2 1abcde

55def test_get_model_config(): 1abcde

56 # For coverage in Pydantic v2 

57 class Foo(BaseModel): 1pqrst

58 model_config = ConfigDict(from_attributes=True) 1pqrst

59 

60 foo = Foo() 1pqrst

61 config = _get_model_config(foo) 1pqrst

62 assert config == {"from_attributes": True} 1pqrst

63 

64 

65def test_complex(): 1abcde

66 app = FastAPI() 1fghij

67 

68 @app.post("/") 1fghij

69 def foo(foo: Union[str, List[int]]): 1fghij

70 return foo 1fghij

71 

72 client = TestClient(app) 1fghij

73 

74 response = client.post("/", json="bar") 1fghij

75 assert response.status_code == 200, response.text 1fghij

76 assert response.json() == "bar" 1fghij

77 

78 response2 = client.post("/", json=[1, 2]) 1fghij

79 assert response2.status_code == 200, response2.text 1fghij

80 assert response2.json() == [1, 2] 1fghij

81 

82 

83def test_is_bytes_sequence_annotation_union(): 1abcde

84 # For coverage 

85 # TODO: in theory this would allow declaring types that could be lists of bytes 

86 # to be read from files and other types, but I'm not even sure it's a good idea 

87 # to support it as a first class "feature" 

88 assert is_bytes_sequence_annotation(Union[List[str], List[bytes]]) 1OPQRS

89 

90 

91def test_is_uploadfile_sequence_annotation(): 1abcde

92 # For coverage 

93 # TODO: in theory this would allow declaring types that could be lists of UploadFile 

94 # and other types, but I'm not even sure it's a good idea to support it as a first 

95 # class "feature" 

96 assert is_uploadfile_sequence_annotation(Union[List[str], List[UploadFile]]) 1TUVWX

97 

98 

99def test_is_pv1_scalar_field(): 1abcde

100 # For coverage 

101 class Model(BaseModel): 1zABCD

102 foo: Union[str, Dict[str, Any]] 1zABCD

103 

104 fields = get_model_fields(Model) 1zABCD

105 assert not is_scalar_field(fields[0]) 1zABCD

106 

107 

108def test_get_model_fields_cached(): 1abcde

109 class Model(BaseModel): 1klmno

110 foo: str 1klmno

111 

112 non_cached_fields = get_model_fields(Model) 1klmno

113 non_cached_fields2 = get_model_fields(Model) 1klmno

114 cached_fields = get_cached_model_fields(Model) 1klmno

115 cached_fields2 = get_cached_model_fields(Model) 1klmno

116 for f1, f2 in zip(cached_fields, cached_fields2): 1klmno

117 assert f1 is f2 1klmno

118 

119 assert non_cached_fields is not non_cached_fields2 1klmno

120 assert cached_fields is cached_fields2 1klmno