knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/apis/deprecated_test.go (about)

     1  /*
     2  Copyright 2019 The Knative Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package apis_test
    18  
    19  import (
    20  	"context"
    21  	"strings"
    22  	"testing"
    23  
    24  	"knative.dev/pkg/apis"
    25  	"knative.dev/pkg/ptr"
    26  	. "knative.dev/pkg/testing"
    27  )
    28  
    29  func TestCheckDeprecated(t *testing.T) {
    30  	testCases := map[string]struct {
    31  		strict   bool
    32  		obj      interface{}
    33  		wantErrs []string
    34  	}{
    35  		"create strict, string": {
    36  			strict: true,
    37  			obj: &InnerDefaultSubSpec{
    38  				DeprecatedString: "an error",
    39  			},
    40  			wantErrs: []string{
    41  				"must not set",
    42  				"string",
    43  			},
    44  		},
    45  		"create strict, stringptr": {
    46  			strict: true,
    47  			obj: &InnerDefaultSubSpec{
    48  				DeprecatedStringPtr: ptr.String("test string"),
    49  			},
    50  			wantErrs: []string{
    51  				"must not set",
    52  				"stringPtr",
    53  			},
    54  		},
    55  		"create strict, int": {
    56  			strict: true,
    57  			obj: &InnerDefaultSubSpec{
    58  				DeprecatedInt: 42,
    59  			},
    60  			wantErrs: []string{
    61  				"must not set",
    62  				"int",
    63  			},
    64  		},
    65  		"create strict, intptr": {
    66  			strict: true,
    67  			obj: &InnerDefaultSubSpec{
    68  				DeprecatedIntPtr: ptr.Int64(42),
    69  			},
    70  			wantErrs: []string{
    71  				"must not set",
    72  				"intPtr",
    73  			},
    74  		},
    75  		"create strict, map": {
    76  			strict: true,
    77  			obj: &InnerDefaultSubSpec{
    78  				DeprecatedMap: map[string]string{"hello": "failure"},
    79  			},
    80  			wantErrs: []string{
    81  				"must not set",
    82  				"map",
    83  			},
    84  		},
    85  		"create strict, slice": {
    86  			strict: true,
    87  			obj: &InnerDefaultSubSpec{
    88  				DeprecatedSlice: []string{"hello", "failure"},
    89  			},
    90  			wantErrs: []string{
    91  				"must not set",
    92  				"slice",
    93  			},
    94  		},
    95  		"create strict, struct": {
    96  			strict: true,
    97  			obj: &InnerDefaultSubSpec{
    98  				DeprecatedStruct: InnerDefaultStruct{FieldAsString: "not ok"},
    99  			},
   100  			wantErrs: []string{
   101  				"must not set",
   102  				"struct",
   103  			},
   104  		},
   105  		"create strict, structptr": {
   106  			strict: true,
   107  			obj: &InnerDefaultSubSpec{
   108  				DeprecatedStructPtr: &InnerDefaultStruct{
   109  					FieldAsString: "fail",
   110  				},
   111  			},
   112  			wantErrs: []string{
   113  				"must not set",
   114  				"structPtr",
   115  			},
   116  		},
   117  		"create strict, not json": {
   118  			strict: true,
   119  			obj: &InnerDefaultSubSpec{
   120  				DeprecatedNotJSON: "fail",
   121  			},
   122  			wantErrs: []string{
   123  				"must not set",
   124  				"DeprecatedNotJSON",
   125  			},
   126  		},
   127  		"create strict, inlined": {
   128  			strict: true,
   129  			obj: &InnerDefaultSubSpec{
   130  				InlinedStruct: InlinedStruct{
   131  					DeprecatedField: "fail",
   132  				},
   133  			},
   134  			wantErrs: []string{
   135  				"must not set",
   136  				"fieldA",
   137  			},
   138  		},
   139  		"create strict, inlined ptr": {
   140  			strict: true,
   141  			obj: &InnerDefaultSubSpec{
   142  				InlinedPtrStruct: &InlinedPtrStruct{
   143  					DeprecatedField: "fail",
   144  				},
   145  			},
   146  			wantErrs: []string{
   147  				"must not set",
   148  				"fieldB",
   149  			},
   150  		},
   151  		"create strict, inlined nested": {
   152  			strict: true,
   153  			obj: &InnerDefaultSubSpec{
   154  				InlinedStruct: InlinedStruct{
   155  					InlinedPtrStruct: &InlinedPtrStruct{
   156  						DeprecatedField: "fail",
   157  					},
   158  				},
   159  			},
   160  			wantErrs: []string{
   161  				"must not set",
   162  				"fieldB",
   163  			},
   164  		},
   165  		"create strict, all errors": {
   166  			strict: true,
   167  			obj: &InnerDefaultSubSpec{
   168  				DeprecatedString:    "an error",
   169  				DeprecatedStringPtr: ptr.String("test string"),
   170  				DeprecatedInt:       42,
   171  				DeprecatedIntPtr:    ptr.Int64(42),
   172  				DeprecatedMap:       map[string]string{"hello": "failure"},
   173  				DeprecatedSlice:     []string{"hello", "failure"},
   174  				DeprecatedStruct:    InnerDefaultStruct{FieldAsString: "not ok"},
   175  				DeprecatedStructPtr: &InnerDefaultStruct{
   176  					FieldAsString: "fail",
   177  				},
   178  				InlinedStruct: InlinedStruct{
   179  					DeprecatedField: "fail",
   180  					InlinedPtrStruct: &InlinedPtrStruct{
   181  						DeprecatedField: "fail",
   182  					},
   183  				},
   184  			},
   185  			wantErrs: []string{
   186  				"string",
   187  				"stringPtr",
   188  				"int",
   189  				"intPtr",
   190  				"map",
   191  				"slice",
   192  				"struct",
   193  				"structPtr",
   194  				"fieldA",
   195  				"fieldB",
   196  			},
   197  		},
   198  	}
   199  	for n, tc := range testCases {
   200  		t.Run(n, func(t *testing.T) {
   201  			ctx := context.Background()
   202  			if tc.strict {
   203  				ctx = apis.DisallowDeprecated(ctx)
   204  			}
   205  			resp := apis.CheckDeprecated(ctx, tc.obj)
   206  
   207  			if len(tc.wantErrs) > 0 {
   208  				for _, err := range tc.wantErrs {
   209  					var gotErr string
   210  					if resp != nil {
   211  						gotErr = resp.Error()
   212  					}
   213  					if !strings.Contains(gotErr, err) {
   214  						t.Errorf("Expected failure containing %q got %q", err, gotErr)
   215  					}
   216  				}
   217  			} else if resp != nil {
   218  				t.Errorf("Expected no error, got %q", resp.Error())
   219  			}
   220  		})
   221  	}
   222  }
   223  
   224  // This test makes sure that errors will flatten the duped error for fieldB.
   225  // It comes in on obj.InlinedStruct.InlinedPtrStruct.DeprecatedField and
   226  // obj.InlinedPtrStruct.DeprecatedField.
   227  func TestCheckDeprecated_Dedupe(t *testing.T) {
   228  	obj := &InnerDefaultSubSpec{
   229  		InlinedStruct: InlinedStruct{
   230  			DeprecatedField: "fail",
   231  			InlinedPtrStruct: &InlinedPtrStruct{
   232  				DeprecatedField: "fail",
   233  			},
   234  		},
   235  		InlinedPtrStruct: &InlinedPtrStruct{
   236  			DeprecatedField: "fail",
   237  		},
   238  	}
   239  	wantErr := "must not set the field(s): fieldA, fieldB"
   240  
   241  	ctx := apis.DisallowDeprecated(context.Background())
   242  	resp := apis.CheckDeprecated(ctx, obj)
   243  
   244  	gotErr := resp.Error()
   245  	if gotErr != wantErr {
   246  		t.Errorf("Expected failure %q got %q", wantErr, gotErr)
   247  	}
   248  }
   249  
   250  func TestCheckDeprecatedUpdate(t *testing.T) {
   251  	testCases := map[string]struct {
   252  		strict   bool
   253  		obj      interface{}
   254  		org      interface{}
   255  		wantErrs []string
   256  	}{
   257  		"update strict, intptr": {
   258  			strict: true,
   259  			org:    &InnerDefaultSubSpec{},
   260  			obj: &InnerDefaultSubSpec{
   261  				DeprecatedIntPtr: ptr.Int64(42),
   262  			},
   263  			wantErrs: []string{
   264  				"must not set",
   265  				"intPtr",
   266  			},
   267  		},
   268  		"update strict, map": {
   269  			strict: true,
   270  			org:    &InnerDefaultSubSpec{},
   271  			obj: &InnerDefaultSubSpec{
   272  				DeprecatedMap: map[string]string{"hello": "failure"},
   273  			},
   274  
   275  			wantErrs: []string{
   276  				"must not set",
   277  				"map",
   278  			},
   279  		},
   280  		"update strict, slice": {
   281  			strict: true,
   282  			org:    &InnerDefaultSubSpec{},
   283  			obj: &InnerDefaultSubSpec{
   284  				DeprecatedSlice: []string{"hello", "failure"},
   285  			},
   286  			wantErrs: []string{
   287  				"must not set",
   288  				"slice",
   289  			},
   290  		},
   291  		"update strict, struct": {
   292  			strict: true,
   293  			org:    &InnerDefaultSubSpec{},
   294  			obj: &InnerDefaultSubSpec{
   295  				DeprecatedStruct: InnerDefaultStruct{
   296  					FieldAsString: "fail",
   297  				},
   298  			},
   299  			wantErrs: []string{
   300  				"must not set",
   301  				"struct",
   302  			},
   303  		},
   304  		"update strict, structptr": {
   305  			strict: true,
   306  			org:    &InnerDefaultSubSpec{},
   307  			obj: &InnerDefaultSubSpec{
   308  				DeprecatedStructPtr: &InnerDefaultStruct{
   309  					FieldAsString: "fail",
   310  				},
   311  			},
   312  			wantErrs: []string{
   313  				"must not set",
   314  				"structPtr",
   315  			},
   316  		},
   317  		"update strict, all errors": {
   318  			strict: true,
   319  			org:    &InnerDefaultSubSpec{},
   320  			obj: &InnerDefaultSubSpec{
   321  				DeprecatedString:    "an error",
   322  				DeprecatedStringPtr: ptr.String("test string"),
   323  				DeprecatedInt:       42,
   324  				DeprecatedIntPtr:    ptr.Int64(42),
   325  				DeprecatedMap:       map[string]string{"hello": "failure"},
   326  				DeprecatedSlice:     []string{"hello", "failure"},
   327  				DeprecatedStruct:    InnerDefaultStruct{FieldAsString: "not ok"},
   328  				DeprecatedStructPtr: &InnerDefaultStruct{
   329  					FieldAsString: "fail",
   330  				},
   331  				InlinedStruct: InlinedStruct{
   332  					DeprecatedField: "fail",
   333  					InlinedPtrStruct: &InlinedPtrStruct{
   334  						DeprecatedField: "fail",
   335  					},
   336  				},
   337  			},
   338  			wantErrs: []string{
   339  				"string",
   340  				"stringPtr",
   341  				"int",
   342  				"intPtr",
   343  				"map",
   344  				"slice",
   345  				"struct",
   346  				"structPtr",
   347  				"fieldA",
   348  				"fieldB",
   349  			},
   350  		},
   351  
   352  		"overwrite strict, string": {
   353  			strict: true,
   354  			org: &InnerDefaultSubSpec{
   355  				DeprecatedString: "original setting.",
   356  			},
   357  			obj: &InnerDefaultSubSpec{
   358  				DeprecatedString: "fail setting.",
   359  			},
   360  			wantErrs: []string{
   361  				"must not update",
   362  				"string",
   363  			},
   364  		},
   365  		"overwrite strict, stringptr": {
   366  			strict: true,
   367  			org: &InnerDefaultSubSpec{
   368  				DeprecatedStringPtr: ptr.String("original string"),
   369  			},
   370  			obj: &InnerDefaultSubSpec{
   371  				DeprecatedStringPtr: ptr.String("fail string"),
   372  			},
   373  			wantErrs: []string{
   374  				"must not update",
   375  				"stringPtr",
   376  			},
   377  		},
   378  		"overwrite strict, int": {
   379  			strict: true,
   380  			org: &InnerDefaultSubSpec{
   381  				DeprecatedInt: 10,
   382  			},
   383  			obj: &InnerDefaultSubSpec{
   384  				DeprecatedInt: 42,
   385  			},
   386  			wantErrs: []string{
   387  				"must not update",
   388  				"int",
   389  			},
   390  		},
   391  		"overwrite strict, intptr": {
   392  			strict: true,
   393  			org: &InnerDefaultSubSpec{
   394  				DeprecatedIntPtr: ptr.Int64(10),
   395  			},
   396  			obj: &InnerDefaultSubSpec{
   397  				DeprecatedIntPtr: ptr.Int64(42),
   398  			},
   399  			wantErrs: []string{
   400  				"must not update",
   401  				"intPtr",
   402  			},
   403  		},
   404  		"overwrite strict, map": {
   405  			strict: true,
   406  			org: &InnerDefaultSubSpec{
   407  				DeprecatedMap: map[string]string{"goodbye": "existing"},
   408  			},
   409  			obj: &InnerDefaultSubSpec{
   410  				DeprecatedMap: map[string]string{"hello": "failure"},
   411  			},
   412  			wantErrs: []string{
   413  				"must not update",
   414  				"map",
   415  			},
   416  		},
   417  		"overwrite strict, slice": {
   418  			strict: true,
   419  			org: &InnerDefaultSubSpec{
   420  				DeprecatedSlice: []string{"hello", "existing"},
   421  			},
   422  			obj: &InnerDefaultSubSpec{
   423  				DeprecatedSlice: []string{"hello", "failure"},
   424  			},
   425  			wantErrs: []string{
   426  				"must not update",
   427  				"slice",
   428  			},
   429  		},
   430  		"overwrite strict, struct": {
   431  			strict: true,
   432  			org: &InnerDefaultSubSpec{
   433  				DeprecatedStruct: InnerDefaultStruct{
   434  					FieldAsString: "original",
   435  				},
   436  			},
   437  			obj: &InnerDefaultSubSpec{
   438  				DeprecatedStruct: InnerDefaultStruct{
   439  					FieldAsString: "fail",
   440  				},
   441  			},
   442  			wantErrs: []string{
   443  				"must not update",
   444  				"struct",
   445  			},
   446  		},
   447  		"overwrite strict, structptr": {
   448  			strict: true,
   449  			org: &InnerDefaultSubSpec{
   450  				DeprecatedStructPtr: &InnerDefaultStruct{
   451  					FieldAsString: "original",
   452  				},
   453  			},
   454  			obj: &InnerDefaultSubSpec{
   455  				DeprecatedStructPtr: &InnerDefaultStruct{
   456  					FieldAsString: "fail",
   457  				},
   458  			},
   459  			wantErrs: []string{
   460  				"must not update",
   461  				"structPtr",
   462  			},
   463  		},
   464  
   465  		"create, not strict": {
   466  			strict: false,
   467  			obj: &InnerDefaultSubSpec{
   468  				DeprecatedString: "fail setting.",
   469  			},
   470  		},
   471  		"update, not strict": {
   472  			strict: false,
   473  			org:    &InnerDefaultSubSpec{},
   474  			obj: &InnerDefaultSubSpec{
   475  				DeprecatedString: "it's k",
   476  			},
   477  		},
   478  		"overwrite, not strict": {
   479  			strict: false,
   480  			org: &InnerDefaultSubSpec{
   481  				DeprecatedString: "org",
   482  			},
   483  			obj: &InnerDefaultSubSpec{
   484  				DeprecatedString: "it's k",
   485  			},
   486  		},
   487  	}
   488  	for n, tc := range testCases {
   489  		t.Run(n, func(t *testing.T) {
   490  			ctx := context.Background()
   491  			if tc.strict {
   492  				ctx = apis.DisallowDeprecated(ctx)
   493  			}
   494  			resp := apis.CheckDeprecatedUpdate(ctx, tc.obj, tc.org)
   495  
   496  			if len(tc.wantErrs) > 0 {
   497  				for _, err := range tc.wantErrs {
   498  					var gotErr string
   499  					if resp != nil {
   500  						gotErr = resp.Error()
   501  					}
   502  					if !strings.Contains(gotErr, err) {
   503  						t.Errorf("Expected failure containing %q got %q", err, gotErr)
   504  					}
   505  				}
   506  			} else if resp != nil {
   507  				t.Errorf("Expected no error, got %q", resp.Error())
   508  			}
   509  		})
   510  	}
   511  }