1+ """Validation tests for RVConfig, RqsGeneratorInput and SimulationSettings."""
2+
3+ from __future__ import annotations
4+
15import pytest
26from pydantic import ValidationError
37
48from app .config .constants import Distribution , TimeDefaults
59from app .schemas .random_variables_config import RVConfig
610from app .schemas .requests_generator_input import RqsGeneratorInput
11+ from app .schemas .simulation_settings_input import SimulationSettings
12+
13+ # ---------------------------------------------------------------------------
14+ # RVCONFIG
15+ # ---------------------------------------------------------------------------
716
8- # --------------------------------------------------------------------------
9- # TEST RANDOM VARIABLE CONFIGURATION
10- # --------------------------------------------------------------------------
1117
1218def test_normal_sets_variance_to_mean () -> None :
13- """When distribution='normal' and variance is omitted, variance == mean."""
19+ """If variance is omitted with 'normal', it defaults to mean."""
1420 cfg = RVConfig (mean = 10 , distribution = Distribution .NORMAL )
1521 assert cfg .variance == 10.0
1622
1723
1824def test_poisson_keeps_variance_none () -> None :
19- """When distribution='poisson' and variance is omitted, variance stays None."""
25+ """If variance is omitted with 'poisson', it remains None."""
2026 cfg = RVConfig (mean = 5 , distribution = Distribution .POISSON )
2127 assert cfg .variance is None
2228
2329
2430def test_explicit_variance_is_preserved () -> None :
25- """If the user supplies variance explicitly, it is preserved unchanged ."""
31+ """An explicit variance value is not modified ."""
2632 cfg = RVConfig (mean = 8 , distribution = Distribution .NORMAL , variance = 4 )
2733 assert cfg .variance == 4.0
2834
2935
3036def test_mean_must_be_numeric () -> None :
31- """A non- numeric mean raises a ValidationError with our custom message ."""
32- with pytest .raises (ValidationError ) as excinfo :
37+ """A non numeric mean triggers a ValidationError."""
38+ with pytest .raises (ValidationError ) as exc :
3339 RVConfig (mean = "not a number" , distribution = Distribution .POISSON )
3440
35- # Check that at least one error refers to the 'mean' field
36- assert any (err ["loc" ] == ("mean" ,) for err in excinfo .value .errors ())
37- assert "mean must be a number" in excinfo .value .errors ()[0 ]["msg" ]
41+ assert any (err ["loc" ] == ("mean" ,) for err in exc .value .errors ())
3842
3943
4044def test_missing_mean_field () -> None :
41- """Omitting the mean field raises a 'field required' ValidationError."""
42- with pytest .raises (ValidationError ) as excinfo :
43- # Using model_validate avoids the constructor signature check
45+ """Omitting mean raises a 'field required' ValidationError."""
46+ with pytest .raises (ValidationError ) as exc :
4447 RVConfig .model_validate ({"distribution" : Distribution .NORMAL })
4548
4649 assert any (
4750 err ["loc" ] == ("mean" ,) and err ["type" ] == "missing"
48- for err in excinfo .value .errors ()
51+ for err in exc .value .errors ()
4952 )
5053
51- def test_gaussian_sets_variance_to_mean () -> None :
52- """When distribution='gaussian' and variance is omitted, variance == mean."""
53- cfg = RVConfig (mean = 12.5 , distribution = Distribution .NORMAL )
54- assert cfg .variance == pytest .approx (12.5 )
55-
5654
5755def test_default_distribution_is_poisson () -> None :
58- """
59- When distribution is omitted, it defaults to 'poisson' and
60- variance stays None.
61- """
56+ """If distribution is missing, it defaults to 'poisson'."""
6257 cfg = RVConfig (mean = 3.3 )
6358 assert cfg .distribution == Distribution .POISSON
6459 assert cfg .variance is None
6560
6661
6762def test_explicit_variance_kept_for_poisson () -> None :
68- """If the user supplies variance even for poisson, it is preserved ."""
63+ """Variance is kept even when distribution is poisson ."""
6964 cfg = RVConfig (mean = 4.0 , distribution = Distribution .POISSON , variance = 2.2 )
7065 assert cfg .variance == pytest .approx (2.2 )
7166
7267
7368def test_invalid_distribution_raises () -> None :
74- """Supplying a non-supported distribution literal raises ValidationError."""
75- with pytest .raises (ValidationError ) as excinfo :
69+ """An unsupported distribution literal raises ValidationError."""
70+ with pytest .raises (ValidationError ):
7671 RVConfig (mean = 5.0 , distribution = "not_a_dist" )
7772
78- errors = excinfo . value . errors ()
79- # Only assert there is at least one error for the 'distribution' field:
80- assert any ( e [ "loc" ] == ( "distribution" ,) for e in errors )
73+ # ---------------------------------------------------------------------------
74+ # RQSGENERATORINPUT - USER_SAMPLING_WINDOW
75+ # ---------------------------------------------------------------------------
8176
82- # --------------------------------------------------------------------------
83- # TEST FIELD VALIDATOR USER SAMPLING WINDOW
84- # --------------------------------------------------------------------------
8577
8678def test_default_user_sampling_window () -> None :
87- """When user_sampling_window is omitted, it defaults to USER_SAMPLING_WINDOW ."""
79+ """If user_sampling_window is missing it defaults to the constant ."""
8880 inp = RqsGeneratorInput (
8981 avg_active_users = {"mean" : 1.0 , "distribution" : Distribution .POISSON },
9082 avg_request_per_minute_per_user = {
@@ -96,47 +88,35 @@ def test_default_user_sampling_window() -> None:
9688
9789
9890def test_explicit_user_sampling_window_kept () -> None :
99- """An explicit user_sampling_window value is preserved unchanged."""
100- custom_window = 30
91+ """An explicit user_sampling_window is preserved."""
10192 inp = RqsGeneratorInput (
10293 avg_active_users = {"mean" : 1.0 , "distribution" : Distribution .POISSON },
10394 avg_request_per_minute_per_user = {
10495 "mean" : 1.0 ,
10596 "distribution" : Distribution .POISSON ,
10697 },
107- user_sampling_window = custom_window ,
98+ user_sampling_window = 30 ,
10899 )
109- assert inp .user_sampling_window == custom_window
100+ assert inp .user_sampling_window == 30
110101
111102
112103def test_user_sampling_window_not_int_raises () -> None :
113- """A non-integer user_sampling_window raises a ValidationError."""
114- with pytest .raises (ValidationError ) as excinfo :
115-
104+ """A non integer user_sampling_window raises ValidationError."""
105+ with pytest .raises (ValidationError ):
116106 RqsGeneratorInput (
117107 avg_active_users = {"mean" : 1.0 , "distribution" : Distribution .POISSON },
118108 avg_request_per_minute_per_user = {
119109 "mean" : 1.0 ,
120110 "distribution" : Distribution .POISSON ,
121111 },
122- user_sampling_window = "not-an- int" ,
112+ user_sampling_window = "not-int" ,
123113 )
124114
125- errors = excinfo .value .errors ()
126- assert any (err ["loc" ] == ("user_sampling_window" ,) for err in errors )
127-
128- # Pydantic v2 wording
129- assert any ("valid integer" in err ["msg" ] for err in errors )
130-
131-
132115
133116def test_user_sampling_window_above_max_raises () -> None :
134- """
135- Passing user_sampling_window > MAX_USER_SAMPLING_WINDOW
136- must raise a ValidationError.
137- """
117+ """user_sampling_window above the max constant raises ValidationError."""
138118 too_large = TimeDefaults .MAX_USER_SAMPLING_WINDOW + 1
139- with pytest .raises (ValidationError ) as excinfo :
119+ with pytest .raises (ValidationError ):
140120 RqsGeneratorInput (
141121 avg_active_users = {"mean" : 1.0 , "distribution" : Distribution .POISSON },
142122 avg_request_per_minute_per_user = {
@@ -146,90 +126,33 @@ def test_user_sampling_window_above_max_raises() -> None:
146126 user_sampling_window = too_large ,
147127 )
148128
149- errors = excinfo .value .errors ()
150- assert any (err ["loc" ] == ("user_sampling_window" ,) for err in errors )
151-
152- expected_snippet = (
153- f"less than or equal to { TimeDefaults .MAX_USER_SAMPLING_WINDOW } "
154- )
155- assert any (expected_snippet in err ["msg" ] for err in errors )
156129
157130
131+ # ---------------------------------------------------------------------------
132+ # SIMULATIONSETTINGS - TOTAL_SIMULATION_TIME
133+ # ---------------------------------------------------------------------------
158134
159- # --------------------------------------------------------------------------
160- # TEST FIELD VALIDATOR TOTAL SIMULATION TIME
161- # --------------------------------------------------------------------------
162135
163136def test_default_total_simulation_time () -> None :
164- """When total_simulation_time is omitted, it defaults to SIMULATION_TIME."""
165- inp = RqsGeneratorInput (
166- avg_active_users = {"mean" : 1.0 , "distribution" : Distribution .POISSON },
167- avg_request_per_minute_per_user = {
168- "mean" : 1.0 ,
169- "distribution" : Distribution .POISSON ,
170- },
171- )
172- assert inp .total_simulation_time == TimeDefaults .SIMULATION_TIME
137+ """If total_simulation_time is missing it defaults to the constant."""
138+ settings = SimulationSettings ()
139+ assert settings .total_simulation_time == TimeDefaults .SIMULATION_TIME
173140
174141
175142def test_explicit_total_simulation_time_kept () -> None :
176- """An explicit total_simulation_time value is preserved unchanged."""
177- custom_time = 3_000
178- inp = RqsGeneratorInput (
179- avg_active_users = {"mean" : 1.0 , "distribution" : Distribution .POISSON },
180- avg_request_per_minute_per_user = {
181- "mean" : 1.0 ,
182- "distribution" : Distribution .POISSON ,
183- },
184- total_simulation_time = custom_time ,
185- )
186- assert inp .total_simulation_time == custom_time
143+ """An explicit total_simulation_time is preserved."""
144+ settings = SimulationSettings (total_simulation_time = 3_000 )
145+ assert settings .total_simulation_time == 3_000
187146
188147
189148def test_total_simulation_time_not_int_raises () -> None :
190- """A non-integer total_simulation_time raises a ValidationError."""
191- with pytest .raises (ValidationError ) as excinfo :
192-
193- RqsGeneratorInput (
194- avg_active_users = {"mean" : 1.0 , "distribution" : Distribution .POISSON },
195- avg_request_per_minute_per_user = {
196- "mean" : 1.0 ,
197- "distribution" : Distribution .POISSON ,
198- },
199- total_simulation_time = "three thousand" ,
200- )
201-
202- errors = excinfo .value .errors ()
203- assert any (err ["loc" ] == ("total_simulation_time" ,) for err in errors )
204-
205- # Pydantic v2 wording: “Input should be a valid integer”
206- assert any ("valid integer" in err ["msg" ] for err in errors )
207-
149+ """A non integer total_simulation_time raises ValidationError."""
150+ with pytest .raises (ValidationError ):
151+ SimulationSettings (total_simulation_time = "three thousand" )
208152
209153
210154def test_total_simulation_time_below_minimum_raises () -> None :
211- """
212- Passing total_simulation_time < MIN_SIMULATION_TIME
213- must raise a ValidationError.
214- """
155+ """A total_simulation_time below the minimum constant raises ValidationError."""
215156 too_small = TimeDefaults .MIN_SIMULATION_TIME - 1
216- with pytest .raises (ValidationError ) as excinfo :
217- RqsGeneratorInput (
218- avg_active_users = {"mean" : 1.0 , "distribution" : Distribution .POISSON },
219- avg_request_per_minute_per_user = {
220- "mean" : 1.0 ,
221- "distribution" : Distribution .POISSON ,
222- },
223- total_simulation_time = too_small ,
224- )
225-
226- errors = excinfo .value .errors ()
227- # c'è almeno un errore sul campo giusto
228- assert any (err ["loc" ] == ("total_simulation_time" ,) for err in errors )
229-
230- expected_snippet = (
231- f"greater than or equal to { TimeDefaults .MIN_SIMULATION_TIME } "
232- )
233- assert any (expected_snippet in err ["msg" ] for err in errors )
234-
235-
157+ with pytest .raises (ValidationError ):
158+ SimulationSettings (total_simulation_time = too_small )
0 commit comments