Files
2024-02-15 17:01:50 +00:00

128 lines
4.5 KiB
Python

from __future__ import annotations
from collections.abc import Mapping
from typing import Any, Literal, TypedDict
import pytest
from drf_spectacular.openapi import AutoSchema
from drf_spectacular.utils import extend_schema_serializer
from sentry.api.serializers import Serializer
from sentry.apidocs.extensions import (
SentryInlineResponseSerializerExtension,
SentryResponseSerializerExtension,
)
from sentry.apidocs.utils import inline_sentry_response_serializer
class NestedDict(TypedDict):
zz: str
class BasicSerializerOptional(TypedDict, total=False):
a: int
@extend_schema_serializer(exclude_fields=["excluded"])
class BasicSerializerResponse(BasicSerializerOptional):
b: str
c: bool
d: list[int]
e: NestedDict
f: Literal[3]
g: str | bool
h: str | None
i: int | float | None
excluded: str
class BasicSerializer(Serializer):
def serialize(
self, obj: Any, attrs: Mapping[Any, Any], user: Any, **kwargs: Any
) -> BasicSerializerResponse:
raise NotImplementedError
class FailSerializer(Serializer):
def serialize(self, obj: Any, attrs: Mapping[Any, Any], user: Any, **kwargs: Any):
raise NotImplementedError
def test_sentry_response_serializer_extension():
seralizer_extension = SentryResponseSerializerExtension(BasicSerializer)
schema = seralizer_extension.map_serializer(AutoSchema(), "response")
assert schema == {
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "string"},
"c": {"type": "boolean"},
"d": {"type": "array", "items": {"type": "integer"}},
"e": {"type": "object", "properties": {"zz": {"type": "string"}}, "required": ["zz"]},
"f": {"enum": [3], "type": "integer"},
# Test that a Union generates an anyOf
"g": {"anyOf": [{"type": "string"}, {"type": "boolean"}]},
# Test that including None with a 2 type Union adds nullable: True
# but does not create an anyOf
"h": {"type": "string", "nullable": True},
# Test that including None with a >2 type Union does not add nullable: True
# but includes {type: "object", nullable: True} in the anyOf
"i": {
"anyOf": [
{"type": "integer"},
{"format": "double", "type": "number"},
{"type": "object", "nullable": True},
]
},
},
"required": ["b", "c", "d", "e", "f", "g", "h", "i"],
}
def test_sentry_inline_response_serializer_extension():
inline_serializer = inline_sentry_response_serializer(
"BasicStuff", list[BasicSerializerResponse]
)
seralizer_extension = SentryInlineResponseSerializerExtension(inline_serializer)
schema = seralizer_extension.map_serializer(AutoSchema(), "response")
assert schema == {
"type": "array",
"items": {
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "string"},
"c": {"type": "boolean"},
"d": {"type": "array", "items": {"type": "integer"}},
"e": {
"type": "object",
"properties": {"zz": {"type": "string"}},
"required": ["zz"],
},
"f": {"enum": [3], "type": "integer"},
# Test that a Union generates an anyOf
"g": {"anyOf": [{"type": "string"}, {"type": "boolean"}]},
# Test that including None with a 2 type Union adds nullable: True
# but does not create an anyOf
"h": {"type": "string", "nullable": True},
# Test that including None with a >2 type Union does not add nullable: True
# but includes {type: "object", nullable: True} in the anyOf
"i": {
"anyOf": [
{"type": "integer"},
{"format": "double", "type": "number"},
{"type": "object", "nullable": True},
]
},
},
"required": ["b", "c", "d", "e", "f", "g", "h", "i"],
},
}
def test_sentry_fails_when_serializer_not_typed():
seralizer_extension = SentryResponseSerializerExtension(FailSerializer)
with pytest.raises(TypeError):
seralizer_extension.map_serializer(AutoSchema(), "response")