...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/resource_test.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dcl_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl"
    21  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
    22  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
    23  
    24  	"github.com/nasa9084/go-openapi"
    25  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  )
    27  
    28  func TestValidateResourceIDIfSupported(t *testing.T) {
    29  	meta := v1.ObjectMeta{
    30  		Namespace: "test-ns",
    31  		Name:      "test-resource",
    32  	}
    33  	tests := []struct {
    34  		name         string
    35  		resource     dcl.Resource
    36  		expectedSpec map[string]interface{}
    37  		hasError     bool
    38  	}{
    39  		{
    40  			name: "spec.resourceID field not supported",
    41  			resource: dcl.Resource{
    42  				Resource: k8s.Resource{
    43  					ObjectMeta: meta,
    44  				},
    45  				Schema: &openapi.Schema{},
    46  			},
    47  		},
    48  		{
    49  			name: "non server-generated ID, spec.resourceID field set",
    50  			resource: dcl.Resource{
    51  				Resource: k8s.Resource{
    52  					ObjectMeta: meta,
    53  					Spec: map[string]interface{}{
    54  						"resourceID": "test-id",
    55  					},
    56  				},
    57  				Schema: &openapi.Schema{
    58  					Properties: map[string]*openapi.Schema{
    59  						"name": &openapi.Schema{
    60  							Type: "string",
    61  						},
    62  					},
    63  				},
    64  			},
    65  			expectedSpec: map[string]interface{}{
    66  				"resourceID": "test-id",
    67  			},
    68  			hasError: false,
    69  		},
    70  		{
    71  			name: "non server-generated ID, spec.resourceID field unset",
    72  			resource: dcl.Resource{
    73  				Resource: k8s.Resource{
    74  					ObjectMeta: meta,
    75  				},
    76  				Schema: &openapi.Schema{
    77  					Properties: map[string]*openapi.Schema{
    78  						"name": &openapi.Schema{
    79  							Type: "string",
    80  						},
    81  					},
    82  				},
    83  			},
    84  			hasError: false,
    85  		},
    86  		{
    87  			name: "non server-generated ID, spec.resourceID empty",
    88  			resource: dcl.Resource{
    89  				Resource: k8s.Resource{
    90  					ObjectMeta: meta,
    91  					Spec: map[string]interface{}{
    92  						"resourceID": "",
    93  					},
    94  				},
    95  				Schema: &openapi.Schema{
    96  					Properties: map[string]*openapi.Schema{
    97  						"name": &openapi.Schema{
    98  							Type: "string",
    99  						},
   100  					},
   101  				},
   102  			},
   103  			hasError: true,
   104  		},
   105  		{
   106  			name: "server-generated ID, spec.resourceID field set",
   107  			resource: dcl.Resource{
   108  				Resource: k8s.Resource{
   109  					ObjectMeta: meta,
   110  					Spec: map[string]interface{}{
   111  						"resourceID": "test-id",
   112  					},
   113  				},
   114  				Schema: &openapi.Schema{
   115  					Properties: map[string]*openapi.Schema{
   116  						"name": &openapi.Schema{
   117  							Type: "string",
   118  							Extension: map[string]interface{}{
   119  								"x-dcl-server-generated-parameter": true,
   120  							},
   121  						},
   122  					},
   123  				},
   124  			},
   125  			expectedSpec: map[string]interface{}{
   126  				"resourceID": "test-id",
   127  			},
   128  			hasError: false,
   129  		},
   130  		{
   131  			name: "server-generated ID, spec.resourceID field unset",
   132  			resource: dcl.Resource{
   133  				Resource: k8s.Resource{
   134  					ObjectMeta: meta,
   135  				},
   136  				Schema: &openapi.Schema{
   137  					Properties: map[string]*openapi.Schema{
   138  						"name": &openapi.Schema{
   139  							Type: "string",
   140  							Extension: map[string]interface{}{
   141  								"x-dcl-server-generated-parameter": true,
   142  							},
   143  						},
   144  					},
   145  				},
   146  			},
   147  			hasError: false,
   148  		},
   149  	}
   150  
   151  	for _, tc := range tests {
   152  		tc := tc
   153  		t.Run(tc.name, func(t *testing.T) {
   154  			t.Parallel()
   155  			err := tc.resource.ValidateResourceIDIfSupported()
   156  			if tc.hasError {
   157  				if err == nil {
   158  					t.Fatalf("got nil, want an error")
   159  				}
   160  				return
   161  			}
   162  			if err != nil {
   163  				t.Fatalf("error setting resource ID: %v", err)
   164  			}
   165  			if got, want := tc.resource.Spec, tc.expectedSpec; !test.Equals(t, got, want) {
   166  				t.Fatalf("got: %v, want: %v", got, want)
   167  			}
   168  		})
   169  	}
   170  }
   171  
   172  func TestHasServerGeneratedIDButNotConfigured(t *testing.T) {
   173  	tests := []struct {
   174  		name     string
   175  		resource *dcl.Resource
   176  		result   bool
   177  	}{
   178  		{
   179  			name: "user specified name",
   180  			resource: &dcl.Resource{
   181  				Resource: k8s.Resource{
   182  					Spec: map[string]interface{}{
   183  						"resourceID": "my-name",
   184  					},
   185  				},
   186  				Schema: &openapi.Schema{
   187  					Properties: map[string]*openapi.Schema{
   188  						"name": &openapi.Schema{
   189  							Type: "string",
   190  						},
   191  					},
   192  					Type: "object",
   193  				},
   194  			},
   195  			result: false,
   196  		},
   197  		{
   198  			name: "configured server-generated id",
   199  			resource: &dcl.Resource{
   200  				Resource: k8s.Resource{
   201  					Spec: map[string]interface{}{
   202  						"resourceID": "server-generated-value",
   203  					},
   204  				},
   205  				Schema: &openapi.Schema{
   206  					Properties: map[string]*openapi.Schema{
   207  						"name": &openapi.Schema{
   208  							Type: "string",
   209  							Extension: map[string]interface{}{
   210  								"x-dcl-server-generated-parameter": true,
   211  							},
   212  						},
   213  					},
   214  					Type: "object",
   215  				},
   216  			},
   217  			result: false,
   218  		},
   219  		{
   220  			name: "non-configured server-generated id",
   221  			resource: &dcl.Resource{
   222  				Resource: k8s.Resource{},
   223  				Schema: &openapi.Schema{
   224  					Properties: map[string]*openapi.Schema{
   225  						"name": &openapi.Schema{
   226  							Type: "string",
   227  							Extension: map[string]interface{}{
   228  								"x-dcl-server-generated-parameter": true,
   229  							},
   230  						},
   231  					},
   232  					Type: "object",
   233  				},
   234  			},
   235  			result: true,
   236  		},
   237  	}
   238  
   239  	for _, tc := range tests {
   240  		tc := tc
   241  		t.Run(tc.name, func(t *testing.T) {
   242  			t.Parallel()
   243  			actual, err := tc.resource.HasServerGeneratedIDButNotConfigured()
   244  			if err != nil {
   245  				t.Fatalf("unexpected error: %v", err)
   246  			}
   247  			if actual != tc.result {
   248  				t.Fatalf("got: %v, want: %v", actual, tc.result)
   249  			}
   250  		})
   251  	}
   252  }
   253  
   254  func TestHasServerGeneratedIDAndConfigured(t *testing.T) {
   255  	tests := []struct {
   256  		name     string
   257  		resource *dcl.Resource
   258  		result   bool
   259  	}{
   260  		{
   261  			name: "user specified name",
   262  			resource: &dcl.Resource{
   263  				Resource: k8s.Resource{
   264  					Spec: map[string]interface{}{
   265  						"resourceID": "my-name",
   266  					},
   267  				},
   268  				Schema: &openapi.Schema{
   269  					Properties: map[string]*openapi.Schema{
   270  						"name": &openapi.Schema{
   271  							Type: "string",
   272  						},
   273  					},
   274  					Type: "object",
   275  				},
   276  			},
   277  			result: false,
   278  		},
   279  		{
   280  			name: "configured server-generated id",
   281  			resource: &dcl.Resource{
   282  				Resource: k8s.Resource{
   283  					Spec: map[string]interface{}{
   284  						"resourceID": "server-generated-value",
   285  					},
   286  				},
   287  				Schema: &openapi.Schema{
   288  					Properties: map[string]*openapi.Schema{
   289  						"name": &openapi.Schema{
   290  							Type: "string",
   291  							Extension: map[string]interface{}{
   292  								"x-dcl-server-generated-parameter": true,
   293  							},
   294  						},
   295  					},
   296  					Type: "object",
   297  				},
   298  			},
   299  			result: true,
   300  		},
   301  		{
   302  			name: "non-configured server-generated id",
   303  			resource: &dcl.Resource{
   304  				Resource: k8s.Resource{},
   305  				Schema: &openapi.Schema{
   306  					Properties: map[string]*openapi.Schema{
   307  						"name": &openapi.Schema{
   308  							Type: "string",
   309  							Extension: map[string]interface{}{
   310  								"x-dcl-server-generated-parameter": true,
   311  							},
   312  						},
   313  					},
   314  					Type: "object",
   315  				},
   316  			},
   317  			result: false,
   318  		},
   319  	}
   320  
   321  	for _, tc := range tests {
   322  		tc := tc
   323  		t.Run(tc.name, func(t *testing.T) {
   324  			t.Parallel()
   325  			actual, err := tc.resource.HasServerGeneratedIDAndConfigured()
   326  			if err != nil {
   327  				t.Fatalf("unexpected error: %v", err)
   328  			}
   329  			if actual != tc.result {
   330  				t.Fatalf("got: %v, want: %v", actual, tc.result)
   331  			}
   332  		})
   333  	}
   334  }
   335  
   336  func TestHasMutableButUnreadableFields(t *testing.T) {
   337  	tests := []struct {
   338  		name           string
   339  		resource       *dcl.Resource
   340  		expectedResult bool
   341  		hasError       bool
   342  	}{
   343  		{
   344  			name: "has mutable-but-unreadable fields",
   345  			resource: &dcl.Resource{
   346  				Resource: k8s.Resource{
   347  					Spec: map[string]interface{}{
   348  						"resourceID": "my-name",
   349  					},
   350  				},
   351  				Schema: &openapi.Schema{
   352  					Type: "object",
   353  					Extension: map[string]interface{}{
   354  						"x-dcl-uses-state-hint": true,
   355  					},
   356  				},
   357  			},
   358  			expectedResult: true,
   359  		},
   360  		{
   361  			name: "has no mutable-but-unreadable fields",
   362  			resource: &dcl.Resource{
   363  				Resource: k8s.Resource{
   364  					Spec: map[string]interface{}{
   365  						"resourceID": "my-name",
   366  					},
   367  				},
   368  				Schema: &openapi.Schema{
   369  					Type: "object",
   370  				},
   371  			},
   372  			expectedResult: false,
   373  		},
   374  		{
   375  			name: "error our when checking for mutable-but-unreadable fields",
   376  			resource: &dcl.Resource{
   377  				Resource: k8s.Resource{
   378  					Spec: map[string]interface{}{
   379  						"resourceID": "my-name",
   380  					},
   381  				},
   382  				Schema: &openapi.Schema{
   383  					Type: "object",
   384  					Extension: map[string]interface{}{
   385  						"x-dcl-uses-state-hint": "true",
   386  					},
   387  				},
   388  			},
   389  			hasError: true,
   390  		},
   391  	}
   392  
   393  	for _, tc := range tests {
   394  		tc := tc
   395  		t.Run(tc.name, func(t *testing.T) {
   396  			t.Parallel()
   397  			result, err := tc.resource.HasMutableButUnreadableFields()
   398  			if tc.hasError {
   399  				if err == nil {
   400  					t.Fatal("got no error, but want an error")
   401  				}
   402  				return
   403  			}
   404  			if err != nil {
   405  				t.Fatalf("unexpected error: %v", err)
   406  			}
   407  			if got, want := result, tc.expectedResult; got != want {
   408  				t.Fatalf("got %v, want %v", got, want)
   409  			}
   410  		})
   411  	}
   412  }
   413  

View as plain text