@@ -18,6 +18,7 @@ import (
1818 "context"
1919 stderrors "errors"
2020 "fmt"
21+ "reflect"
2122 "sort"
2223 "strings"
2324 "testing"
@@ -182,8 +183,8 @@ func TestStrictGapErrorRendering(t *testing.T) {
182183 name string
183184 gaps []strictGap
184185 excluded []ExcludedOverlay
186+ warnings []ConstraintWarning
185187 wantContains []string
186- wantExcluded bool
187188 }{
188189 {
189190 name : "single value" ,
@@ -208,17 +209,26 @@ func TestStrictGapErrorRendering(t *testing.T) {
208209 // a failing constraint. The demand still stands, but the caller
209210 // needs the exclusion context or they will state the os and only
210211 // then meet the real failure.
211- name : "constraint exclusions are attached" ,
212- gaps : []strictGap {{dimension : string (FieldOS ), validValues : []string {"ubuntu" }}},
213- excluded : []ExcludedOverlay {{Name : "h100-eks-ubuntu-training" }},
212+ name : "constraint context is attached" ,
213+ gaps : []strictGap {{dimension : string (FieldOS ), validValues : []string {"ubuntu" }}},
214+ excluded : []ExcludedOverlay {{
215+ Name : "h100-eks-ubuntu-training" ,
216+ Reason : ExcludedOverlayReasonConstraintFailed ,
217+ }},
218+ warnings : []ConstraintWarning {{
219+ Overlay : "h100-eks-ubuntu-training" ,
220+ Constraint : "K8s.server.version" ,
221+ Expected : ">= 1.34" ,
222+ Actual : "1.30" ,
223+ Reason : "constraint not satisfied" ,
224+ }},
214225 wantContains : []string {"specify os (valid: ubuntu)" },
215- wantExcluded : true ,
216226 },
217227 }
218228
219229 for _ , tt := range tests {
220230 t .Run (tt .name , func (t * testing.T ) {
221- err := strictGapError (criteria , tt .gaps , tt .excluded , nil )
231+ err := strictGapError (criteria , tt .gaps , tt .excluded , tt . warnings )
222232 for _ , want := range tt .wantContains {
223233 if ! strings .Contains (err .Error (), want ) {
224234 t .Errorf ("message = %q, want it to contain %q" , err .Error (), want )
@@ -232,9 +242,51 @@ func TestStrictGapErrorRendering(t *testing.T) {
232242 if se .Context ["uncovered" ] != nil {
233243 t .Error ("strict-gap failure must not populate `uncovered`; relaxation would clear it" )
234244 }
235- if (se .Context ["excludedOverlays" ] != nil ) != tt .wantExcluded {
236- t .Errorf ("excludedOverlays present = %v, want %v" ,
237- se .Context ["excludedOverlays" ] != nil , tt .wantExcluded )
245+
246+ // Assert the whole strictDimensions payload, not just the keys: a
247+ // caller reconstructing a retry query reads validValues, so a
248+ // regression that dropped or reordered them would otherwise pass.
249+ entries , ok := se .Context ["strictDimensions" ].([]map [string ]any )
250+ if ! ok {
251+ t .Fatalf ("strictDimensions = %#v, want []map[string]any" , se .Context ["strictDimensions" ])
252+ }
253+ if len (entries ) != len (tt .gaps ) {
254+ t .Fatalf ("strictDimensions has %d entries, want %d" , len (entries ), len (tt .gaps ))
255+ }
256+ for i , gap := range tt .gaps {
257+ if entries [i ]["dimension" ] != gap .dimension {
258+ t .Errorf ("entry %d dimension = %v, want %v" , i , entries [i ]["dimension" ], gap .dimension )
259+ }
260+ values , ok := entries [i ]["validValues" ].([]string )
261+ if ! ok {
262+ t .Errorf ("entry %d validValues = %#v, want []string" , i , entries [i ]["validValues" ])
263+ continue
264+ }
265+ if ! equalStrings (values , gap .validValues ) {
266+ t .Errorf ("entry %d validValues = %v, want %v" , i , values , gap .validValues )
267+ }
268+ }
269+
270+ // excludedOverlays / constraintWarnings are attached verbatim, and
271+ // absent rather than empty when there is nothing to report.
272+ if tt .excluded == nil {
273+ if se .Context ["excludedOverlays" ] != nil {
274+ t .Errorf ("excludedOverlays = %#v, want absent" , se .Context ["excludedOverlays" ])
275+ }
276+ } else if got , ok := se .Context ["excludedOverlays" ].([]ExcludedOverlay ); ! ok {
277+ t .Errorf ("excludedOverlays = %#v, want []ExcludedOverlay" , se .Context ["excludedOverlays" ])
278+ } else if ! reflect .DeepEqual (got , tt .excluded ) {
279+ t .Errorf ("excludedOverlays = %+v, want %+v" , got , tt .excluded )
280+ }
281+
282+ if tt .warnings == nil {
283+ if se .Context ["constraintWarnings" ] != nil {
284+ t .Errorf ("constraintWarnings = %#v, want absent" , se .Context ["constraintWarnings" ])
285+ }
286+ } else if got , ok := se .Context ["constraintWarnings" ].([]ConstraintWarning ); ! ok {
287+ t .Errorf ("constraintWarnings = %#v, want []ConstraintWarning" , se .Context ["constraintWarnings" ])
288+ } else if ! reflect .DeepEqual (got , tt .warnings ) {
289+ t .Errorf ("constraintWarnings = %+v, want %+v" , got , tt .warnings )
238290 }
239291
240292 // The golden matrix classifies via this extraction, so exercise it
0 commit comments