88
99from __future__ import annotations
1010
11+ from typing import Any
1112from uuid import uuid4
1213
1314import pytest
9293# ---------------------------------------------------------------------------
9394
9495
95- def _monitor (** kw : object ) -> dict :
96- base : dict = {
96+ def _monitor (** kw : object ) -> dict [ str , Any ] :
97+ base : dict [ str , Any ] = {
9798 "id" : UID ,
9899 "organizationId" : 1 ,
99100 "name" : "M" ,
@@ -110,8 +111,8 @@ def _monitor(**kw: object) -> dict:
110111 return base
111112
112113
113- def _incident (** kw : object ) -> dict :
114- base : dict = {
114+ def _incident (** kw : object ) -> dict [ str , Any ] :
115+ base : dict [ str , Any ] = {
115116 "id" : UID ,
116117 "organizationId" : 1 ,
117118 "source" : "MANUAL" ,
@@ -128,8 +129,8 @@ def _incident(**kw: object) -> dict:
128129 return base
129130
130131
131- def _alert_channel (** kw : object ) -> dict :
132- base : dict = {
132+ def _alert_channel (** kw : object ) -> dict [ str , Any ] :
133+ base : dict [ str , Any ] = {
133134 "id" : UID ,
134135 "name" : "ch" ,
135136 "channelType" : "slack" ,
@@ -140,8 +141,8 @@ def _alert_channel(**kw: object) -> dict:
140141 return base
141142
142143
143- def _api_key (** kw : object ) -> dict :
144- base : dict = {
144+ def _api_key (** kw : object ) -> dict [ str , Any ] :
145+ base : dict [ str , Any ] = {
145146 "id" : 1 ,
146147 "name" : "k" ,
147148 "key" : "dh_live_x" ,
@@ -152,8 +153,8 @@ def _api_key(**kw: object) -> dict:
152153 return base
153154
154155
155- def _environment (** kw : object ) -> dict :
156- base : dict = {
156+ def _environment (** kw : object ) -> dict [ str , Any ] :
157+ base : dict [ str , Any ] = {
157158 "id" : UID ,
158159 "orgId" : 1 ,
159160 "name" : "prod" ,
@@ -168,8 +169,8 @@ def _environment(**kw: object) -> dict:
168169 return base
169170
170171
171- def _secret (** kw : object ) -> dict :
172- base : dict = {
172+ def _secret (** kw : object ) -> dict [ str , Any ] :
173+ base : dict [ str , Any ] = {
173174 "id" : UID ,
174175 "key" : "MY_KEY" ,
175176 "dekVersion" : 1 ,
@@ -181,8 +182,8 @@ def _secret(**kw: object) -> dict:
181182 return base
182183
183184
184- def _tag (** kw : object ) -> dict :
185- base : dict = {
185+ def _tag (** kw : object ) -> dict [ str , Any ] :
186+ base : dict [ str , Any ] = {
186187 "id" : UID ,
187188 "organizationId" : 1 ,
188189 "name" : "prod" ,
@@ -194,8 +195,8 @@ def _tag(**kw: object) -> dict:
194195 return base
195196
196197
197- def _webhook (** kw : object ) -> dict :
198- base : dict = {
198+ def _webhook (** kw : object ) -> dict [ str , Any ] :
199+ base : dict [ str , Any ] = {
199200 "id" : UID ,
200201 "url" : "https://hook.example.com" ,
201202 "subscribedEvents" : ["monitor.created" ],
@@ -208,14 +209,19 @@ def _webhook(**kw: object) -> dict:
208209 return base
209210
210211
211- def _deploy_lock (** kw : object ) -> dict :
212- base : dict = {"id" : UID , "lockedBy" : "ci-job-42" , "lockedAt" : NOW , "expiresAt" : NOW }
212+ def _deploy_lock (** kw : object ) -> dict [str , Any ]:
213+ base : dict [str , Any ] = {
214+ "id" : UID ,
215+ "lockedBy" : "ci-job-42" ,
216+ "lockedAt" : NOW ,
217+ "expiresAt" : NOW ,
218+ }
213219 base .update (kw )
214220 return base
215221
216222
217- def _resource_group (** kw : object ) -> dict :
218- base : dict = {
223+ def _resource_group (** kw : object ) -> dict [ str , Any ] :
224+ base : dict [ str , Any ] = {
219225 "id" : UID ,
220226 "organizationId" : 1 ,
221227 "name" : "rg" ,
@@ -234,8 +240,8 @@ def _resource_group(**kw: object) -> dict:
234240 return base
235241
236242
237- def _notification_policy (** kw : object ) -> dict :
238- base : dict = {
243+ def _notification_policy (** kw : object ) -> dict [ str , Any ] :
244+ base : dict [ str , Any ] = {
239245 "id" : UID ,
240246 "organizationId" : 1 ,
241247 "name" : "np" ,
@@ -250,8 +256,8 @@ def _notification_policy(**kw: object) -> dict:
250256 return base
251257
252258
253- def _status_page (** kw : object ) -> dict :
254- base : dict = {
259+ def _status_page (** kw : object ) -> dict [ str , Any ] :
260+ base : dict [ str , Any ] = {
255261 "id" : UID ,
256262 "organizationId" : 1 ,
257263 "workspaceId" : 1 ,
@@ -268,8 +274,8 @@ def _status_page(**kw: object) -> dict:
268274 return base
269275
270276
271- def _sp_component (** kw : object ) -> dict :
272- base : dict = {
277+ def _sp_component (** kw : object ) -> dict [ str , Any ] :
278+ base : dict [ str , Any ] = {
273279 "id" : UID ,
274280 "statusPageId" : UID ,
275281 "name" : "API" ,
@@ -286,8 +292,8 @@ def _sp_component(**kw: object) -> dict:
286292 return base
287293
288294
289- def _sp_group (** kw : object ) -> dict :
290- base : dict = {
295+ def _sp_group (** kw : object ) -> dict [ str , Any ] :
296+ base : dict [ str , Any ] = {
291297 "id" : UID ,
292298 "statusPageId" : UID ,
293299 "name" : "Infra" ,
@@ -301,8 +307,8 @@ def _sp_group(**kw: object) -> dict:
301307 return base
302308
303309
304- def _sp_incident (** kw : object ) -> dict :
305- base : dict = {
310+ def _sp_incident (** kw : object ) -> dict [ str , Any ] :
311+ base : dict [ str , Any ] = {
306312 "id" : UID ,
307313 "statusPageId" : UID ,
308314 "title" : "Down" ,
@@ -318,8 +324,8 @@ def _sp_incident(**kw: object) -> dict:
318324 return base
319325
320326
321- def _sp_incident_update (** kw : object ) -> dict :
322- base : dict = {
327+ def _sp_incident_update (** kw : object ) -> dict [ str , Any ] :
328+ base : dict [ str , Any ] = {
323329 "id" : UID ,
324330 "status" : "INVESTIGATING" ,
325331 "body" : "Looking into it" ,
@@ -330,14 +336,19 @@ def _sp_incident_update(**kw: object) -> dict:
330336 return base
331337
332338
333- def _sp_subscriber (** kw : object ) -> dict :
334- base : dict = {"id" : UID , "email" : "a@b.com" , "confirmed" : True , "createdAt" : NOW }
339+ def _sp_subscriber (** kw : object ) -> dict [str , Any ]:
340+ base : dict [str , Any ] = {
341+ "id" : UID ,
342+ "email" : "a@b.com" ,
343+ "confirmed" : True ,
344+ "createdAt" : NOW ,
345+ }
335346 base .update (kw )
336347 return base
337348
338349
339- def _sp_custom_domain (** kw : object ) -> dict :
340- base : dict = {
350+ def _sp_custom_domain (** kw : object ) -> dict [ str , Any ] :
351+ base : dict [ str , Any ] = {
341352 "id" : UID ,
342353 "hostname" : "status.example.com" ,
343354 "status" : "ACTIVE" ,
@@ -352,8 +363,8 @@ def _sp_custom_domain(**kw: object) -> dict:
352363 return base
353364
354365
355- def _sp_incident_component (** kw : object ) -> dict :
356- base : dict = {
366+ def _sp_incident_component (** kw : object ) -> dict [ str , Any ] :
367+ base : dict [ str , Any ] = {
357368 "statusPageComponentId" : UID ,
358369 "componentStatus" : "OPERATIONAL" ,
359370 "componentName" : "API" ,
@@ -362,14 +373,19 @@ def _sp_incident_component(**kw: object) -> dict:
362373 return base
363374
364375
365- def _check_result (** kw : object ) -> dict :
366- base : dict = {"id" : UID , "timestamp" : NOW , "region" : "us-east" , "passed" : True }
376+ def _check_result (** kw : object ) -> dict [str , Any ]:
377+ base : dict [str , Any ] = {
378+ "id" : UID ,
379+ "timestamp" : NOW ,
380+ "region" : "us-east" ,
381+ "passed" : True ,
382+ }
367383 base .update (kw )
368384 return base
369385
370386
371- def _incident_policy (** kw : object ) -> dict :
372- base : dict = {
387+ def _incident_policy (** kw : object ) -> dict [ str , Any ] :
388+ base : dict [ str , Any ] = {
373389 "id" : UID ,
374390 "monitorId" : UID ,
375391 "triggerRules" : [
@@ -392,8 +408,8 @@ def _incident_policy(**kw: object) -> dict:
392408 return base
393409
394410
395- def _monitor_version (** kw : object ) -> dict :
396- base : dict = {
411+ def _monitor_version (** kw : object ) -> dict [ str , Any ] :
412+ base : dict [ str , Any ] = {
397413 "id" : UID ,
398414 "monitorId" : UID ,
399415 "version" : 1 ,
@@ -405,7 +421,7 @@ def _monitor_version(**kw: object) -> dict:
405421 return base
406422
407423
408- def _del (d : dict , key : str ) -> dict :
424+ def _del (d : dict [ str , Any ], key : str ) -> dict [ str , Any ] :
409425 c = dict (d )
410426 del c [key ]
411427 return c
@@ -904,44 +920,24 @@ def test_missing_name(self) -> None:
904920 }
905921 )
906922
907- def test_missing_match_rules (self ) -> None :
908- with pytest .raises (ValidationError , match = "matchRules" ):
909- CreateNotificationPolicyRequest .model_validate (
910- {
911- "name" : "np" ,
912- "escalation" : {"steps" : [{"delayMinutes" : 0 , "channelIds" : [UID ]}]},
913- "enabled" : True ,
914- "priority" : 0 ,
915- }
916- )
917-
918923 def test_missing_escalation (self ) -> None :
919924 with pytest .raises (ValidationError , match = "escalation" ):
920925 CreateNotificationPolicyRequest .model_validate (
921926 {"name" : "np" , "matchRules" : [], "enabled" : True , "priority" : 0 }
922927 )
923928
924- def test_missing_enabled (self ) -> None :
925- with pytest .raises (ValidationError , match = "enabled" ):
926- CreateNotificationPolicyRequest .model_validate (
927- {
928- "name" : "np" ,
929- "matchRules" : [],
930- "escalation" : {"steps" : [{"delayMinutes" : 0 , "channelIds" : [UID ]}]},
931- "priority" : 0 ,
932- }
933- )
934-
935- def test_missing_priority (self ) -> None :
936- with pytest .raises (ValidationError , match = "priority" ):
937- CreateNotificationPolicyRequest .model_validate (
938- {
939- "name" : "np" ,
940- "matchRules" : [],
941- "escalation" : {"steps" : [{"delayMinutes" : 0 , "channelIds" : [UID ]}]},
942- "enabled" : True ,
943- }
944- )
929+ def test_missing_optional_fields_accepted (self ) -> None :
930+ """matchRules / enabled / priority are optional in the spec; only name +
931+ escalation are required. The model must accept payloads without them."""
932+ model = CreateNotificationPolicyRequest .model_validate (
933+ {
934+ "name" : "np" ,
935+ "escalation" : {"steps" : [{"delayMinutes" : 0 , "channelIds" : [UID ]}]},
936+ }
937+ )
938+ assert model .match_rules is None
939+ assert model .enabled is None or model .enabled is True
940+ assert model .priority is None or model .priority == 0
945941
946942 def test_null_name (self ) -> None :
947943 with pytest .raises (ValidationError ):
@@ -2781,13 +2777,15 @@ def test_invalid_component_id(self) -> None:
27812777
27822778
27832779class TestResolveIncidentRequestNegative :
2784- def test_missing_body (self ) -> None :
2785- with pytest .raises (ValidationError , match = "body" ):
2786- ResolveIncidentRequest .model_validate ({})
2787-
2788- def test_null_body (self ) -> None :
2789- with pytest .raises (ValidationError ):
2790- ResolveIncidentRequest .model_validate ({"body" : None })
2780+ def test_missing_body_accepted (self ) -> None :
2781+ """body is optional (nullable) — empty payload should be accepted."""
2782+ model = ResolveIncidentRequest .model_validate ({})
2783+ assert model .body is None
2784+
2785+ def test_null_body_accepted (self ) -> None :
2786+ """Explicit null body matches the spec's nullable=true and should be ok."""
2787+ model = ResolveIncidentRequest .model_validate ({"body" : None })
2788+ assert model .body is None
27912789
27922790 def test_wrong_body_type (self ) -> None :
27932791 with pytest .raises (ValidationError ):
0 commit comments