99 "encoding/json"
1010 "net/http"
1111 "net/http/httptest"
12+ "slices"
1213 "strings"
1314 "testing"
1415
@@ -135,7 +136,10 @@ func TestCapacityCalculator(t *testing.T) {
135136 Build ()
136137
137138 calculator := NewCapacityCalculator (fakeClient )
138- _ , err := calculator .CalculateCapacity (context .Background ())
139+ req := liquid.ServiceCapacityRequest {
140+ AllAZs : []liquid.AvailabilityZone {"az-one" , "az-two" },
141+ }
142+ _ , err := calculator .CalculateCapacity (context .Background (), req )
139143 if err == nil {
140144 t .Fatal ("Expected error when flavor groups knowledge doesn't exist, got nil" )
141145 }
@@ -154,7 +158,10 @@ func TestCapacityCalculator(t *testing.T) {
154158 Build ()
155159
156160 calculator := NewCapacityCalculator (fakeClient )
157- report , err := calculator .CalculateCapacity (context .Background ())
161+ req := liquid.ServiceCapacityRequest {
162+ AllAZs : []liquid.AvailabilityZone {"az-one" , "az-two" },
163+ }
164+ report , err := calculator .CalculateCapacity (context .Background (), req )
158165 if err != nil {
159166 t .Fatalf ("Expected no error, got: %v" , err )
160167 }
@@ -168,55 +175,116 @@ func TestCapacityCalculator(t *testing.T) {
168175 }
169176 })
170177
171- t .Run ("CalculateCapacity returns empty perAZ when no HostDetails exist" , func (t * testing.T ) {
172- // Create a flavor group knowledge without host details
178+ t .Run ("CalculateCapacity returns perAZ entries for all AZs from request" , func (t * testing.T ) {
173179 flavorGroupKnowledge := createTestFlavorGroupKnowledge (t , "test-group" )
174-
175180 fakeClient := fake .NewClientBuilder ().
176181 WithScheme (scheme ).
177182 WithObjects (flavorGroupKnowledge ).
178183 Build ()
179184
180185 calculator := NewCapacityCalculator (fakeClient )
181- report , err := calculator .CalculateCapacity (context .Background ())
186+ req := liquid.ServiceCapacityRequest {
187+ AllAZs : []liquid.AvailabilityZone {"qa-de-1a" , "qa-de-1b" , "qa-de-1d" },
188+ }
189+ report , err := calculator .CalculateCapacity (context .Background (), req )
182190 if err != nil {
183191 t .Fatalf ("Expected no error, got: %v" , err )
184192 }
185193
186- // Now we have 3 resources per flavor group: _ram, _cores, _instances
187194 if len (report .Resources ) != 3 {
188195 t .Fatalf ("Expected 3 resources (_ram, _cores, _instances), got %d" , len (report .Resources ))
189196 }
190197
191- // Check RAM resource
192- ramResource := report .Resources [liquid .ResourceName ("hw_version_test-group_ram" )]
193- if ramResource == nil {
194- t .Fatal ("Expected hw_version_test-group_ram resource to exist" )
198+ // Verify all resources have exactly the requested AZs
199+ verifyPerAZMatchesRequest (t , report .Resources ["hw_version_test-group_ram" ], req .AllAZs )
200+ verifyPerAZMatchesRequest (t , report .Resources ["hw_version_test-group_cores" ], req .AllAZs )
201+ verifyPerAZMatchesRequest (t , report .Resources ["hw_version_test-group_instances" ], req .AllAZs )
202+ })
203+
204+ t .Run ("CalculateCapacity with empty AllAZs returns empty perAZ maps" , func (t * testing.T ) {
205+ flavorGroupKnowledge := createTestFlavorGroupKnowledge (t , "test-group" )
206+ fakeClient := fake .NewClientBuilder ().
207+ WithScheme (scheme ).
208+ WithObjects (flavorGroupKnowledge ).
209+ Build ()
210+
211+ calculator := NewCapacityCalculator (fakeClient )
212+ req := liquid.ServiceCapacityRequest {AllAZs : []liquid.AvailabilityZone {}}
213+ report , err := calculator .CalculateCapacity (context .Background (), req )
214+ if err != nil {
215+ t .Fatalf ("Expected no error, got: %v" , err )
195216 }
196- if len (ramResource .PerAZ ) != 0 {
197- t .Errorf ("Expected 0 AZs for RAM resource, got %d" , len (ramResource .PerAZ ))
217+
218+ if len (report .Resources ) != 3 {
219+ t .Fatalf ("Expected 3 resources, got %d" , len (report .Resources ))
198220 }
199221
200- // Check Cores resource
201- coresResource := report . Resources [ liquid . ResourceName ( "hw_version_test-group_cores" )]
202- if coresResource == nil {
203- t . Fatal ( "Expected hw_version_test-group_cores resource to exist" )
222+ for resName , res := range report . Resources {
223+ if len ( res . PerAZ ) != 0 {
224+ t . Errorf ( "%s: expected empty PerAZ, got %d entries" , resName , len ( res . PerAZ ))
225+ }
204226 }
205- if len (coresResource .PerAZ ) != 0 {
206- t .Errorf ("Expected 0 AZs for Cores resource, got %d" , len (coresResource .PerAZ ))
227+ })
228+
229+ t .Run ("CalculateCapacity responds to different AZ sets correctly" , func (t * testing.T ) {
230+ flavorGroupKnowledge := createTestFlavorGroupKnowledge (t , "test-group" )
231+ fakeClient := fake .NewClientBuilder ().
232+ WithScheme (scheme ).
233+ WithObjects (flavorGroupKnowledge ).
234+ Build ()
235+
236+ calculator := NewCapacityCalculator (fakeClient )
237+
238+ req1 := liquid.ServiceCapacityRequest {
239+ AllAZs : []liquid.AvailabilityZone {"eu-de-1a" , "eu-de-1b" },
240+ }
241+ report1 , err := calculator .CalculateCapacity (context .Background (), req1 )
242+ if err != nil {
243+ t .Fatalf ("Expected no error, got: %v" , err )
244+ }
245+
246+ req2 := liquid.ServiceCapacityRequest {
247+ AllAZs : []liquid.AvailabilityZone {"us-west-1a" , "us-west-1b" , "us-west-1c" , "us-west-1d" },
248+ }
249+ report2 , err := calculator .CalculateCapacity (context .Background (), req2 )
250+ if err != nil {
251+ t .Fatalf ("Expected no error, got: %v" , err )
207252 }
208253
209- // Check Instances resource
210- instancesResource := report .Resources [liquid .ResourceName ("hw_version_test-group_instances" )]
211- if instancesResource == nil {
212- t .Fatal ("Expected hw_version_test-group_instances resource to exist" )
254+ // Verify reports have exactly the requested AZs
255+ for _ , res := range report1 .Resources {
256+ verifyPerAZMatchesRequest (t , res , req1 .AllAZs )
213257 }
214- if len ( instancesResource . PerAZ ) != 0 {
215- t . Errorf ( "Expected 0 AZs for Instances resource, got %d" , len ( instancesResource . PerAZ ) )
258+ for _ , res := range report2 . Resources {
259+ verifyPerAZMatchesRequest ( t , res , req2 . AllAZs )
216260 }
217261 })
218262}
219263
264+ // verifyPerAZMatchesRequest checks that perAZ entries match exactly the requested AZs.
265+ // This follows the same semantics as nova liquid: the response must contain
266+ // entries for all AZs in AllAZs, no more and no less.
267+ func verifyPerAZMatchesRequest (t * testing.T , res * liquid.ResourceCapacityReport , requestedAZs []liquid.AvailabilityZone ) {
268+ t .Helper ()
269+ if res == nil {
270+ t .Error ("resource is nil" )
271+ return
272+ }
273+ if len (res .PerAZ ) != len (requestedAZs ) {
274+ t .Errorf ("expected %d AZs, got %d" , len (requestedAZs ), len (res .PerAZ ))
275+ }
276+ for _ , az := range requestedAZs {
277+ if _ , ok := res .PerAZ [az ]; ! ok {
278+ t .Errorf ("missing entry for requested AZ %s" , az )
279+ }
280+ }
281+ for az := range res .PerAZ {
282+ if ! slices .Contains (requestedAZs , az ) {
283+ t .Errorf ("unexpected AZ %s in response (not in request)" , az )
284+ }
285+ }
286+ }
287+
220288// createEmptyFlavorGroupKnowledge creates an empty flavor groups Knowledge CRD
221289func createEmptyFlavorGroupKnowledge () * v1alpha1.Knowledge {
222290 // Box empty array properly
0 commit comments