@@ -18,8 +18,16 @@ class Configuration
1818
1919 private array $ target ;
2020
21+ private string $ httpTimeout = '' ;
22+
2123 private bool $ httpDebug = false ;
2224
25+ /** @var Stage[] */
26+ private array $ stages = [];
27+
28+ /** @var array<string, int> */
29+ private array $ regionConfig = [];
30+
2331 public function __construct (string $ name , string $ description = '' )
2432 {
2533 $ this ->name = $ name ;
@@ -38,15 +46,34 @@ public function toArray(): array
3846 $ array = [
3947 'name ' => $ this ->name ,
4048 'description ' => $ this ->description ,
41- 'virtual_users ' => $ this ->virtualUsers ,
4249 'target ' => $ this ->target ,
4350 'http_debug ' => $ this ->httpDebug ,
4451 ];
45- if (trim ($ this ->rampUp ) !== '' ) {
46- $ array ['ramp_up ' ] = $ this ->rampUp ;
52+
53+ if (count ($ this ->stages ) > 0 ) {
54+ // Stages mode: omit virtual_users, duration, ramp_up
55+ $ array ['stages ' ] = array_map (fn (Stage $ s ) => $ s ->toArray (), $ this ->stages );
56+ } else {
57+ // Constant mode
58+ $ array ['virtual_users ' ] = $ this ->virtualUsers ;
59+ if (trim ($ this ->rampUp ) !== '' ) {
60+ $ array ['ramp_up ' ] = $ this ->rampUp ;
61+ }
62+ if (trim ($ this ->duration ) !== '' ) {
63+ $ array ['duration ' ] = $ this ->duration ;
64+ }
4765 }
48- if (trim ($ this ->duration ) !== '' ) {
49- $ array ['duration ' ] = $ this ->duration ;
66+
67+ if (trim ($ this ->httpTimeout ) !== '' ) {
68+ $ array ['http_timeout ' ] = $ this ->httpTimeout ;
69+ }
70+
71+ if (count ($ this ->regionConfig ) > 0 ) {
72+ $ array ['region_config ' ] = array_map (
73+ fn (string $ region , int $ weight ) => ['region ' => $ region , 'weight ' => $ weight ],
74+ array_keys ($ this ->regionConfig ),
75+ array_values ($ this ->regionConfig )
76+ );
5077 }
5178
5279 return $ array ;
@@ -82,7 +109,21 @@ public function setRampUp(string $rampUp): self
82109 return $ this ;
83110 }
84111
85- public function setTarget (string $ idleTimeout = '30s ' ): self
112+ public function setTargetUrl (string $ url ): self
113+ {
114+ if (! preg_match ('/^https?:\/\// ' , $ url )) {
115+ throw new VoltTestException ('Target URL must start with http:// or https:// ' );
116+ }
117+ $ parsed = parse_url ($ url );
118+ if ($ parsed === false || ! isset ($ parsed ['host ' ])) {
119+ throw new VoltTestException ('Invalid target URL ' );
120+ }
121+ $ this ->target ['url ' ] = $ url ;
122+
123+ return $ this ;
124+ }
125+
126+ public function setIdleTimeout (string $ idleTimeout = '30s ' ): self
86127 {
87128 if (! preg_match ('/^\d+[smh]$/ ' , $ idleTimeout )) {
88129 throw new VoltTestException ('Invalid idle timeout format. Use <number>[s|m|h] ' );
@@ -92,10 +133,111 @@ public function setTarget(string $idleTimeout = '30s'): self
92133 return $ this ;
93134 }
94135
136+ /**
137+ * @deprecated Use setIdleTimeout() instead
138+ */
139+ public function setTarget (string $ idleTimeout = '30s ' ): self
140+ {
141+ return $ this ->setIdleTimeout ($ idleTimeout );
142+ }
143+
144+ public function setHttpTimeout (string $ httpTimeout ): self
145+ {
146+ if (! preg_match ('/^\d+[smh]$/ ' , $ httpTimeout )) {
147+ throw new VoltTestException ('Invalid HTTP timeout format. Use <number>[s|m|h] ' );
148+ }
149+ $ this ->httpTimeout = $ httpTimeout ;
150+
151+ return $ this ;
152+ }
153+
95154 public function setHttpDebug (bool $ httpDebug ): self
96155 {
97156 $ this ->httpDebug = $ httpDebug ;
98157
99158 return $ this ;
100159 }
160+
161+ /**
162+ * @throws VoltTestException
163+ */
164+ public function addStage (string $ duration , int $ target ): self
165+ {
166+ $ this ->stages [] = new Stage ($ duration , $ target );
167+
168+ return $ this ;
169+ }
170+
171+ public function hasStages (): bool
172+ {
173+ return count ($ this ->stages ) > 0 ;
174+ }
175+
176+ public function hasConstantLoad (): bool
177+ {
178+ return $ this ->virtualUsers > 1 || trim ($ this ->duration ) !== '' || trim ($ this ->rampUp ) !== '' ;
179+ }
180+
181+ public function clearConstantLoad (): self
182+ {
183+ $ this ->virtualUsers = 1 ;
184+ $ this ->duration = '' ;
185+ $ this ->rampUp = '' ;
186+
187+ return $ this ;
188+ }
189+
190+ /**
191+ * @param array $regions Region code => weight (e.g., ['us-east-1' => 60, 'eu-west-1' => 40])
192+ *
193+ * @throws VoltTestException
194+ */
195+ public function setRegions (array $ regions ): self
196+ {
197+ if (empty ($ regions )) {
198+ throw new VoltTestException ('Region distribution cannot be empty ' );
199+ }
200+
201+ foreach ($ regions as $ region => $ weight ) {
202+ if (! is_string ($ region ) || trim ($ region ) === '' ) {
203+ throw new VoltTestException ('Region code must be a non-empty string ' );
204+ }
205+
206+ if (! is_int ($ weight )) {
207+ throw new VoltTestException ('Region weight must be an integer ' );
208+ }
209+
210+ if ($ weight <= 0 ) {
211+ throw new VoltTestException ('Region weight must be greater than 0 ' );
212+ }
213+ }
214+
215+ $ sum = array_sum ($ regions );
216+ if ($ sum !== 100 ) {
217+ throw new VoltTestException ("Region weights must sum to 100, got {$ sum }" );
218+ }
219+
220+ $ this ->regionConfig = $ regions ;
221+
222+ return $ this ;
223+ }
224+
225+ public function hasRegions (): bool
226+ {
227+ return count ($ this ->regionConfig ) > 0 ;
228+ }
229+
230+ public function setName (string $ name ): self
231+ {
232+ $ this ->name = $ name ;
233+
234+ return $ this ;
235+ }
236+
237+ public function setDescription (string $ description ): self
238+ {
239+ $ this ->description = $ description ;
240+
241+ return $ this ;
242+ }
101243}
0 commit comments