kubelatte-ce
Форк от sbertech/kubelatte-ce
882 строки · 19.9 Кб
1package templates2
3import (4"context"5"github.com/gohobby/deepcopy"6"github.com/stretchr/testify/assert"7"github.com/stretchr/testify/require"8"gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"9"gitverse.ru/synapse/kubelatte/pkg/observability/logger/lib"10"go.uber.org/zap"11"reflect"12"testing"13"text/template"14)
15
16func TestFromYAML(t *testing.T) {17tests := []struct {18name string19yaml string20want interface{}21wantErr bool22}{23{24"invalid yaml",25"'",26nil,27true,28},29{30"empty object",31"{}",32map[string]interface{}{},33false,34},35{36"raw string",37"hello world",38"hello world",39false,40},41{42"array of strings",43`["hello", "goodbye"]`,44[]interface{}{"hello", "goodbye"},45false,46},47{48"array of objects",49`["hello", {}]`,50[]interface{}{"hello", map[string]interface{}{}},51false,52},53{54"top-level object",55`age: "23"56hello: bar`,57map[string]interface{}{"age": "23", "hello": "bar"},58false,59},60}61for _, tt := range tests {62t.Run(tt.name, func(t *testing.T) {63got, err := FromYAML(tt.yaml)64if tt.wantErr {65require.Error(t, err, "Expected error unmarshalling yaml string")66return67}68
69require.NoError(t, err, "Unexpected error unmarshalling yaml string")70require.Equal(t, tt.want, got)71})72}73}
74
75func TestToYAML(t *testing.T) {76type args struct {77resource interface{}78}79tests := []struct {80name string81args args
82want string83wantErr bool84}{85{86name: "ok",87args: args{resource: map[string]interface{}{"bobob": 123, "bubu": map[string]interface{}{"lolo": "pufpuf"}}},88want: `bobob: 12389bubu:
90lolo: pufpuf
91`,92wantErr: false,93},94{95name: "empty",96args: args{},97want: "",98wantErr: false,99},100}101
102lib.ZapLogger = zap.NewNop()103
104for _, tt := range tests {105t.Run(tt.name, func(t *testing.T) {106got, err := ToYAML(tt.args.resource)107if (err != nil) != tt.wantErr {108t.Errorf("ToYAML() error = %v, wantErr %v", err, tt.wantErr)109return110}111if got != tt.want {112t.Errorf("ToYAML() got = %v, \nwant %v", []byte(got), []byte(tt.want))113}114})115}116}
117
118func TestNindent(t *testing.T) {119type args struct {120in string121spacesNum int122}123tests := []struct {124name string125args args
126want string127}{128{129name: "simple",130args: args{131in: `some:132yaml:
133simple:
134string`,135spacesNum: 2,136},137want: `138some:
139yaml:
140simple:
141string
142`,143},144{145name: "not-so-simple",146args: args{147in: `some:148yaml:
149not-so-simple:
150- strings
151- strings`,152spacesNum: 0,153},154want: `155some:
156yaml:
157not-so-simple:
158- strings
159- strings
160`,161},162{163name: "empty",164args: args{165in: "",166spacesNum: 0,167},168want: "\n\n",169},170}171for _, tt := range tests {172t.Run(tt.name, func(t *testing.T) {173if got := Nindent(tt.args.in, tt.args.spacesNum); got != tt.want {174t.Errorf("Nindent() = %v, want %v", got, tt.want)175}176})177}178}
179
180func TestIndent(t *testing.T) {181type args struct {182in string183spacesNum int184}185tests := []struct {186name string187args args
188want string189}{190{191name: "simple",192args: args{193in: `some:194yaml:
195simple:
196string`,197spacesNum: 2,198},199want: `some:200yaml:
201simple:
202string
203`,204},205{206name: "not-so-simple",207args: args{208in: `some:209yaml:
210not-so-simple:
211- strings
212- strings`,213spacesNum: 0,214},215want: `some:216yaml:
217not-so-simple:
218- strings
219- strings
220`,221},222{223name: "empty",224args: args{225in: "",226spacesNum: 0,227},228want: "\n",229},230}231for _, tt := range tests {232t.Run(tt.name, func(t *testing.T) {233if got := Indent(tt.args.in, tt.args.spacesNum); got != tt.want {234t.Errorf("Indent() = \n%v, want \n%v", got, tt.want)235}236})237}238}
239
240func TestLoadTemplate(t *testing.T) {241t.Run("test", func(t *testing.T) {242expected := template.New("common").Delims("{{%", "%}}")243got := LoadTemplate(context.Background(), v1alpha1.Template{})244if got.Name() != expected.Name() {245t.Errorf("LoadTemplate() = %+v, want %+v", got, expected)246}247})248}
249
250func TestConfigPatchesRBACToV3(t *testing.T) { //nolint:funlen251type args struct {252in interface{}253}254tests := []struct {255name string256args args
257want interface{}258wantErr bool259}{260{261name: "ok",262args: args{263in: map[string]interface{}{264"match": map[string]interface{}{265"listener": map[string]interface{}{266"filterChain": map[string]interface{}{267"filter": map[string]interface{}{268"name": "envoy.filters.network.rbac"},269},270},271},272"patch": map[string]interface{}{273"value": map[string]interface{}{274"config": map[string]interface{}{275"rules": map[string]interface{}{276"policies": map[string]interface{}{277"validate": map[string]interface{}{278"permissions": map[string]interface{}{279"or_rules": map[string]interface{}{280"rules": []interface{}{map[string]interface{}{281"header": map[string]interface{}{282"name": ":method",283},284}},285},286},287"principals": map[string]interface{}{288"or_ids": map[string]interface{}{289"ids": []interface{}{map[string]interface{}{290"header": map[string]interface{}{291"name": ":x-forwarded-client-cert",292},293},294},295},296},297},298},299},300},301},302},303},304},305want: map[string]interface{}{306"match": map[string]interface{}{307"listener": map[string]interface{}{308"filterChain": map[string]interface{}{309"filter": map[string]interface{}{310"name": "envoy.filters.network.http_connection_manager",311},312},313},314},315"patch": map[string]interface{}{316"value": map[string]interface{}{317"typed_config": map[string]interface{}{318"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",319"rules": map[string]interface{}{320"policies": map[string]interface{}{321"validate": map[string]interface{}{322"permissions": []map[string]interface{}{323{"or_rules": map[string]interface{}{324"rules": []interface{}{map[string]interface{}{325"header": map[string]interface{}{326"name": ":method",327},328},329},330},331},332},333"principals": []map[string]interface{}{334{"or_ids": map[string]interface{}{335"ids": []interface{}{map[string]interface{}{336"header": map[string]interface{}{337"name": ":x-forwarded-client-cert",338},339},340},341},342},343},344},345},346},347},348},349},350},351
352wantErr: false,353},354{355name: "!ok",356args: args{357in: map[string]interface{}{358"match": map[string]interface{}{},359},360},361want: map[string]interface{}{362"match": map[string]interface{}{},363},364wantErr: false,365},366}367for _, tt := range tests {368t.Run(tt.name, func(t *testing.T) {369got, err := ConfigPatchesRBACToV3(tt.args.in)370if (err != nil) != tt.wantErr {371t.Errorf("ConfigPatchesRBACToV3() error = %v, wantErr %v", err, tt.wantErr)372return373}374res, _ := ToYAML(tt.want)375if !reflect.DeepEqual(got, res) {376t.Errorf("ConfigPatchesRBACToV3() got = %v, want %v", got, tt.want)377}378})379}380}
381
382func TestOTTconfigPatchesToV3(t *testing.T) { //nolint:funlen383type args struct {384in interface{}385}386tests := []struct {387name string388args args
389want interface{}390wantErr bool391}{392{393name: "sbt_authz",394args: args{395in: map[string]interface{}{396"match": map[string]interface{}{397"listener": map[string]interface{}{398"filterChain": map[string]interface{}{399"filter": map[string]interface{}{400"name": "envoy.http_connection_manager"},401},402},403},404"patch": map[string]interface{}{405"value": map[string]interface{}{406"config": map[string]interface{}{407"grpc_service": map[string]interface{}{408"google_grpc": map[string]interface{}{409"stat_prefix": "sbt_authz",410},411},412},413},414},415},416},417want: map[string]interface{}{418"match": map[string]interface{}{419"listener": map[string]interface{}{420"filterChain": map[string]interface{}{421"filter": map[string]interface{}{422"name": "envoy.filters.network.http_connection_manager"},423},424},425},426"patch": map[string]interface{}{427"value": map[string]interface{}{428"typed_config": map[string]interface{}{429"@type": "type.googleapis.com/udpa.type.v1.TypedStruct",430"typeUrl": "type.googleapis.com/envoy.extensions.filters.http.sbt_authz.v3.SbtAuthz",431"value": map[string]interface{}{432"api": "STANDART",433"grpc_service": map[string]interface{}{434"google_grpc": map[string]interface{}{"stat_prefix": "sbt_authz"},435},436"transport_api_version": "V3",437},438},439},440},441},442wantErr: false,443},444{445name: "ext_authz",446args: args{447in: map[string]interface{}{448"match": map[string]interface{}{449"listener": map[string]interface{}{450"filterChain": map[string]interface{}{451"filter": map[string]interface{}{452"name": "envoy.http_connection_manager"},453},454},455},456"patch": map[string]interface{}{457"value": map[string]interface{}{458"config": map[string]interface{}{459"grpc_service": map[string]interface{}{460"google_grpc": map[string]interface{}{461"stat_prefix": "ext_authz",462},463},464},465},466},467},468},469want: map[string]interface{}{470"match": map[string]interface{}{471"listener": map[string]interface{}{472"filterChain": map[string]interface{}{473"filter": map[string]interface{}{474"name": "envoy.filters.network.http_connection_manager"},475},476},477},478"patch": map[string]interface{}{479"value": map[string]interface{}{480"typed_config": map[string]interface{}{481"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz",482"grpc_service": map[string]interface{}{483"google_grpc": map[string]interface{}{"stat_prefix": "ext_authz"},484},485"transport_api_version": "V3",486},487},488},489},490wantErr: false,491},492}493for _, tt := range tests {494t.Run(tt.name, func(t *testing.T) {495got, err := OTTconfigPatchesToV3(tt.args.in)496if (err != nil) != tt.wantErr {497t.Errorf("OTTconfigPatchesToV3() error = %v, wantErr %v", err, tt.wantErr)498return499}500res, _ := ToYAML(tt.want)501if !reflect.DeepEqual(got, res) {502t.Errorf("OTTconfigPatchesToV3() got = %v, want %v", got, res)503}504})505}506}
507
508func TestReplaceLimiterTypedConfig(t *testing.T) {509tests := []struct {510name string511input interface{}512wantErr bool513}{514{515name: "valid input",516input: map[string]interface{}{517"patch": map[string]interface{}{518"value": map[string]interface{}{519"config": map[string]interface{}{520"rate_limit_service": map[string]interface{}{521"transport_api_version": "V2",522},523},524},525},526},527wantErr: false,528},529{530name: "invalid input",531input: "invalid",532wantErr: true,533},534}535
536for _, tt := range tests {537t.Run(tt.name, func(t *testing.T) {538_, err := replaceLimiterTypedConfig(tt.input)539if (err != nil) != tt.wantErr {540t.Errorf("replaceLimiterTypedConfig() error = %v, wantErr %v", err, tt.wantErr)541return542}543})544}545}
546
547func TestRateLimiterPatchesToV3(t *testing.T) {548tests := []struct {549name string550input interface{}551wantErr bool552}{553{554name: "successful replacements",555input: map[string]interface{}{556"patch": "value",557},558wantErr: false,559},560{561name: "error in replaceFilterChainName",562input: "invalid",563wantErr: false,564},565{566name: "error in replaceLimiterTypedConfig",567input: map[string]interface{}{568"patch": "value",569},570wantErr: false,571},572}573
574for _, tt := range tests {575t.Run(tt.name, func(t *testing.T) {576_, err := RateLimiterPatchesToV3(tt.input)577if (err != nil) != tt.wantErr {578t.Errorf("RateLimiterPatchesToV3() error = %v, wantErr %v", err, tt.wantErr)579return580}581})582}583}
584
585func TestReplaceKey(t *testing.T) { //nolint:funlen586type args struct {587oldK string588newK string589in any
590}591tests := []struct {592name string593args args
594want any
595}{596{597name: "replace flat",598args: args{599oldK: "port",600newK: "hostport",601in: map[string]any{602"port": map[string]any{"number": 8979},603"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},604"outlierDetection": map[string]any{605"baseEjectionTime": "30s",606"maxEjectionPercent": "10",607"interval": "40s"},608},609},610want: map[string]any{611"hostport": map[string]any{"number": 8979},612"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},613"outlierDetection": map[string]any{614"baseEjectionTime": "30s",615"maxEjectionPercent": "10",616"interval": "40s"},617},618},619{620name: "replace with path",621args: args{622oldK: "outlierDetection.maxEjectionPercent",623newK: "consecutive5xxErrors",624in: map[string]any{625"port": map[string]any{"number": 8979},626"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},627"outlierDetection": map[string]any{628"baseEjectionTime": "30s",629"maxEjectionPercent": "10",630"interval": "40s"},631},632},633want: map[string]any{634"port": map[string]any{"number": 8979},635"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},636"outlierDetection": map[string]any{637"baseEjectionTime": "30s",638"consecutive5xxErrors": "10",639"interval": "40s"},640},641},642{ //nolint:dupl643name: "replace with loong path",644args: args{645oldK: "long.long.looong.path.outlierDetection.baseEjectionTime",646newK: "BamBamBum",647in: map[string]any{648"long": map[string]any{649"long": map[string]any{650"looong": map[string]any{651"path": map[string]any{652"port": map[string]any{"number": 8979},653"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},654"outlierDetection": map[string]any{655"baseEjectionTime": "30s",656"maxEjectionPercent": "10",657"interval": "40s"}},658},659"nil": nil,660},661"some other": "other",662"some another": "another",663},664"some other": "other",665},666},667want: map[string]any{668"long": map[string]any{669"long": map[string]any{670"looong": map[string]any{671"path": map[string]any{672"port": map[string]any{"number": 8979},673"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},674"outlierDetection": map[string]any{675"BamBamBum": "30s",676"maxEjectionPercent": "10",677"interval": "40s"}},678},679"nil": nil,680},681"some other": "other",682"some another": "another",683},684"some other": "other",685},686},687{ //nolint:dupl688name: "replace with loong path in the middle",689args: args{690oldK: "long.long.looong.path.outlierDetection",691newK: "BamBamBum",692in: map[string]any{693"long": map[string]any{694"long": map[string]any{695"looong": map[string]any{696"path": map[string]any{697"port": map[string]any{"number": 8979},698"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},699"outlierDetection": map[string]any{700"baseEjectionTime": "30s",701"maxEjectionPercent": "10",702"interval": "40s"}},703},704"nil": nil,705},706"some other": "other",707"some another": "another",708},709"some other": "other",710},711},712want: map[string]any{713"long": map[string]any{714"long": map[string]any{715"looong": map[string]any{716"path": map[string]any{717"port": map[string]any{"number": 8979},718"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},719"BamBamBum": map[string]any{720"baseEjectionTime": "30s",721"maxEjectionPercent": "10",722"interval": "40s"}},723},724"nil": nil,725},726"some other": "other",727"some another": "another",728},729"some other": "other",730},731},732{ //nolint:dupl733name: "replace with loong path in the middle 2",734args: args{735oldK: "long.long",736newK: "longlonglong",737in: map[string]any{738"long": map[string]any{739"long": map[string]any{740"looong": map[string]any{741"path": map[string]any{742"port": map[string]any{"number": 8979},743"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},744"outlierDetection": map[string]any{745"baseEjectionTime": "30s",746"maxEjectionPercent": "10",747"interval": "40s"}},748},749"nil": nil,750},751"some other": "other",752"some another": "another",753},754"some other": "other",755},756},757want: map[string]any{758"long": map[string]any{759"longlonglong": map[string]any{760"looong": map[string]any{761"path": map[string]any{762"port": map[string]any{"number": 8979},763"tls": map[string]any{"mode": "ISTIO_MUTUAL", "sni": "lallala"},764"outlierDetection": map[string]any{765"baseEjectionTime": "30s",766"maxEjectionPercent": "10",767"interval": "40s"}},768},769"nil": nil,770},771"some other": "other",772"some another": "another",773},774"some other": "other",775},776},777}778lib.ZapLogger = zap.NewNop()779for _, tt := range tests {780t.Run(tt.name, func(t *testing.T) {781copyin := deepcopy.DeepCopy(tt.args.in)782if got := ReplaceKey(tt.args.oldK, tt.args.newK, copyin); !reflect.DeepEqual(got, tt.want) {783assert.Equal(t, tt.args.in, copyin)784t.Errorf("ReplaceKey() = %v, want %v", got, tt.want)785}786})787}788}
789
790func TestDel(t *testing.T) {791type args struct {792path string793obj any
794}795tests := []struct {796name string797args args
798want any
799}{800{801name: "simple del",802args: args{803path: "key2",804obj: map[string]interface{}{805"key1": "val1",806"key2": "val2",807},808},809want: map[string]interface{}{810"key1": "val1",811},812},813{814name: "compound del",815args: args{816path: "key3.key3_1",817obj: map[string]interface{}{818"key1": "val1",819"key2": "val2",820"key3": map[string]interface{}{821"key3_1": "val3_1",822"key3_2": "val3_2",823},824},825},826want: map[string]interface{}{827"key1": "val1",828"key2": "val2",829"key3": map[string]interface{}{830"key3_2": "val3_2",831},832},833},834{835name: "compound del 2 lvl",836args: args{837path: "key3.key3_2.key3_2_2.key3_2_2_1",838obj: map[string]interface{}{839"key1": "val1",840"key2": "val2",841"key3": map[string]interface{}{842"key3_1": "val3_1",843"key3_2": map[string]interface{}{844"key3_2_1": "val3_2_1",845"key3_2_2": map[string]interface{}{846"key3_2_2_1": "val3_2_2_1",847"key3_2_2_2": "val3_2_2_2",848},849},850},851},852},853want: map[string]interface{}{854"key1": "val1",855"key2": "val2",856"key3": map[string]interface{}{857"key3_1": "val3_1",858"key3_2": map[string]interface{}{859"key3_2_1": "val3_2_1",860"key3_2_2": map[string]interface{}{861"key3_2_2_2": "val3_2_2_2",862},863},864},865},866},867{868name: "not a map",869args: args{870path: "",871obj: "shine bright like a diamond",872},873want: "shine bright like a diamond",874},875}876lib.ZapLogger = zap.NewNop()877for _, tt := range tests {878t.Run(tt.name, func(t *testing.T) {879assert.Equalf(t, tt.want, Del(tt.args.path, tt.args.obj), "Del(%v, %v)", tt.args.path, tt.args.obj)880})881}882}
883