github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/configuration/core/config_util_test.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package core
    21  
    22  import (
    23  	"reflect"
    24  	"strings"
    25  	"testing"
    26  
    27  	. "github.com/onsi/ginkgo/v2"
    28  	. "github.com/onsi/gomega"
    29  
    30  	"sigs.k8s.io/controller-runtime/pkg/client"
    31  
    32  	"github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    33  	cfgutil "github.com/1aal/kubeblocks/pkg/configuration/util"
    34  	testutil "github.com/1aal/kubeblocks/pkg/testutil/k8s"
    35  )
    36  
    37  var _ = Describe("config_util", func() {
    38  
    39  	var k8sMockClient *testutil.K8sClientMockHelper
    40  
    41  	BeforeEach(func() {
    42  		// Add any setup steps that needs to be executed before each test
    43  		k8sMockClient = testutil.NewK8sMockClient()
    44  	})
    45  
    46  	AfterEach(func() {
    47  		// Add any teardown steps that needs to be executed after each test
    48  		k8sMockClient.Finish()
    49  	})
    50  
    51  	Context("common funcs test", func() {
    52  		It("GetReloadOptions Should success without error", func() {
    53  			mockTpl := v1alpha1.ConfigConstraint{
    54  				Spec: v1alpha1.ConfigConstraintSpec{
    55  					ReloadOptions: &v1alpha1.ReloadOptions{
    56  						UnixSignalTrigger: &v1alpha1.UnixSignalTrigger{
    57  							Signal:      "HUB",
    58  							ProcessName: "for_test",
    59  						},
    60  					},
    61  				},
    62  			}
    63  			tests := []struct {
    64  				name    string
    65  				tpls    []v1alpha1.ComponentConfigSpec
    66  				want    *v1alpha1.ReloadOptions
    67  				wantErr bool
    68  			}{{
    69  				// empty config templates
    70  				name:    "test",
    71  				tpls:    nil,
    72  				want:    nil,
    73  				wantErr: false,
    74  			}, {
    75  				// empty config templates
    76  				name:    "test",
    77  				tpls:    []v1alpha1.ComponentConfigSpec{},
    78  				want:    nil,
    79  				wantErr: false,
    80  			}, {
    81  				// config templates without configConstraintObj
    82  				name: "test",
    83  				tpls: []v1alpha1.ComponentConfigSpec{{
    84  					ComponentTemplateSpec: v1alpha1.ComponentTemplateSpec{
    85  						Name: "for_test",
    86  					},
    87  				}, {
    88  					ComponentTemplateSpec: v1alpha1.ComponentTemplateSpec{
    89  						Name: "for_test2",
    90  					},
    91  				}},
    92  				want:    nil,
    93  				wantErr: false,
    94  			}, {
    95  				// normal
    96  				name: "test",
    97  				tpls: []v1alpha1.ComponentConfigSpec{{
    98  					ComponentTemplateSpec: v1alpha1.ComponentTemplateSpec{
    99  						Name: "for_test",
   100  					},
   101  					ConfigConstraintRef: "eg_v1",
   102  				}},
   103  				want:    mockTpl.Spec.ReloadOptions,
   104  				wantErr: false,
   105  			}, {
   106  				// not exist config constraint
   107  				name: "test",
   108  				tpls: []v1alpha1.ComponentConfigSpec{{
   109  					ComponentTemplateSpec: v1alpha1.ComponentTemplateSpec{
   110  						Name: "for_test",
   111  					},
   112  					ConfigConstraintRef: "not_exist",
   113  				}},
   114  				want:    nil,
   115  				wantErr: true,
   116  			}}
   117  
   118  			k8sMockClient.MockGetMethod(testutil.WithGetReturned(func(key client.ObjectKey, obj client.Object) error {
   119  				if strings.Contains(key.Name, "not_exist") {
   120  					return MakeError("not exist config!")
   121  				}
   122  				testutil.SetGetReturnedObject(obj, &mockTpl)
   123  				return nil
   124  			}, testutil.WithMaxTimes(len(tests))))
   125  
   126  			for _, tt := range tests {
   127  				got, _, err := GetReloadOptions(k8sMockClient.Client(), ctx, tt.tpls)
   128  				Expect(err != nil).Should(BeEquivalentTo(tt.wantErr))
   129  				Expect(reflect.DeepEqual(got, tt.want)).Should(BeTrue())
   130  			}
   131  		})
   132  	})
   133  
   134  })
   135  
   136  func TestMergeUpdatedConfig(t *testing.T) {
   137  	type args struct {
   138  		baseMap    map[string]string
   139  		updatedMap map[string]string
   140  	}
   141  	tests := []struct {
   142  		name string
   143  		args args
   144  		want map[string]string
   145  	}{{
   146  		name: "normal_test",
   147  		args: args{
   148  			baseMap: map[string]string{
   149  				"key1": "context1",
   150  				"key2": "context2",
   151  				"key3": "context3",
   152  			},
   153  			updatedMap: map[string]string{
   154  				"key2": "new context",
   155  			},
   156  		},
   157  		want: map[string]string{
   158  			"key1": "context1",
   159  			"key2": "new context",
   160  			"key3": "context3",
   161  		},
   162  	}, {
   163  		name: "not_expected_update_test",
   164  		args: args{
   165  			baseMap: map[string]string{
   166  				"key1": "context1",
   167  				"key2": "context2",
   168  				"key3": "context3",
   169  			},
   170  			updatedMap: map[string]string{
   171  				"key6": "context6",
   172  			},
   173  		},
   174  		want: map[string]string{
   175  			"key1": "context1",
   176  			"key2": "context2",
   177  			"key3": "context3",
   178  		},
   179  	}}
   180  	for _, tt := range tests {
   181  		t.Run(tt.name, func(t *testing.T) {
   182  			if got := MergeUpdatedConfig(tt.args.baseMap, tt.args.updatedMap); !reflect.DeepEqual(got, tt.want) {
   183  				t.Errorf("MergeUpdatedConfig() = %v, want %v", got, tt.want)
   184  			}
   185  		})
   186  	}
   187  }
   188  
   189  func TestApplyConfigPatch(t *testing.T) {
   190  	type args struct {
   191  		baseCfg           []byte
   192  		updatedParameters map[string]string
   193  		formatConfig      *v1alpha1.FormatterConfig
   194  	}
   195  	tests := []struct {
   196  		name    string
   197  		args    args
   198  		want    string
   199  		wantErr bool
   200  	}{{
   201  		name: "normal_test",
   202  		args: args{
   203  			baseCfg: []byte(`[test]
   204  test=test`),
   205  			updatedParameters: map[string]string{
   206  				"a":               "b",
   207  				"max_connections": "600",
   208  			},
   209  			formatConfig: &v1alpha1.FormatterConfig{
   210  				Format: v1alpha1.Ini,
   211  				FormatterOptions: v1alpha1.FormatterOptions{
   212  					IniConfig: &v1alpha1.IniConfig{
   213  						SectionName: "test",
   214  					}}},
   215  		},
   216  		want: `[test]
   217  a=b
   218  max_connections=600
   219  test=test
   220  `,
   221  		wantErr: false,
   222  	}, {
   223  		name: "normal_test",
   224  		args: args{
   225  			baseCfg: []byte(` `),
   226  			updatedParameters: map[string]string{
   227  				"a": "b",
   228  				"c": "d e f g",
   229  			},
   230  			formatConfig: &v1alpha1.FormatterConfig{
   231  				Format: v1alpha1.RedisCfg,
   232  			},
   233  		},
   234  		want:    "a b\nc d e f g",
   235  		wantErr: false,
   236  	}, {
   237  		name: "badcase_test",
   238  		args: args{
   239  			baseCfg: []byte(` `),
   240  			updatedParameters: map[string]string{
   241  				"ENABLE_MODULES":     "true",
   242  				"HUGGINGFACE_APIKEY": "kssdlsdjskwssl",
   243  			},
   244  			formatConfig: &v1alpha1.FormatterConfig{
   245  				Format: v1alpha1.Dotenv,
   246  			},
   247  		},
   248  		// fix begin
   249  		// ENABLE_MODULES=0x1400004f130
   250  		// HUGGINGFACE_APIKEY=0x1400004f140
   251  		want:    "ENABLE_MODULES=true\nHUGGINGFACE_APIKEY=kssdlsdjskwssl\n",
   252  		wantErr: false,
   253  	}}
   254  	for _, tt := range tests {
   255  		t.Run(tt.name, func(t *testing.T) {
   256  			got, err := ApplyConfigPatch(tt.args.baseCfg, FromStringPointerMap(tt.args.updatedParameters), tt.args.formatConfig)
   257  			if (err != nil) != tt.wantErr {
   258  				t.Errorf("ApplyConfigPatch() error = %v, wantErr %v", err, tt.wantErr)
   259  				return
   260  			}
   261  			if got != tt.want {
   262  				t.Errorf("ApplyConfigPatch() got = %v, want %v", got, tt.want)
   263  			}
   264  		})
   265  	}
   266  }
   267  
   268  func TestFromValueToString(t *testing.T) {
   269  	type args struct {
   270  		val interface{}
   271  	}
   272  	tests := []struct {
   273  		name string
   274  		args args
   275  		want string
   276  	}{{
   277  		name: "test",
   278  		args: args{
   279  			val: "testTest",
   280  		},
   281  		want: "testTest",
   282  	}, {
   283  		name: "test",
   284  		args: args{
   285  			val: "",
   286  		},
   287  		want: "",
   288  	}, {
   289  		name: "test",
   290  		args: args{
   291  			val: nil,
   292  		},
   293  		want: "",
   294  	}, {
   295  		name: "test",
   296  		args: args{
   297  			val: "/abdet/sds",
   298  		},
   299  		want: "",
   300  	}, {
   301  		name: "test",
   302  		args: args{
   303  			val: "abdet/sds-",
   304  		},
   305  		want: "",
   306  	}, {
   307  		name: "test",
   308  		args: args{
   309  			val: "abcdASls-sda_102.382",
   310  		},
   311  		want: "abcdASls-sda_102.382",
   312  	}}
   313  	for _, tt := range tests {
   314  		t.Run(tt.name, func(t *testing.T) {
   315  			if got := FromValueToString(tt.args.val); got != tt.want {
   316  				t.Errorf("FromValueToString() = %v, want %v", got, tt.want)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  func TestFromStringMap(t *testing.T) {
   323  	type args struct {
   324  		m map[string]*string
   325  	}
   326  	tests := []struct {
   327  		name string
   328  		args args
   329  		want map[string]interface{}
   330  	}{{
   331  		name: "test",
   332  		args: args{
   333  			m: map[string]*string{
   334  				"abcd":       cfgutil.ToPointer("test"),
   335  				"null_field": nil,
   336  			},
   337  		},
   338  		want: map[string]interface{}{
   339  			"abcd":       "test",
   340  			"null_field": nil,
   341  		},
   342  	}}
   343  	for _, tt := range tests {
   344  		t.Run(tt.name, func(t *testing.T) {
   345  			if got := FromStringMap(tt.args.m); !reflect.DeepEqual(got, tt.want) {
   346  				t.Errorf("FromStringMap() = %v, want %v", got, tt.want)
   347  			}
   348  		})
   349  	}
   350  }