github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/subject_transform_test.go (about) 1 // Copyright 2023 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package server 15 16 import ( 17 "errors" 18 "reflect" 19 "testing" 20 ) 21 22 func TestPlaceHolderIndex(t *testing.T) { 23 testString := "$1" 24 transformType, indexes, nbPartitions, _, err := indexPlaceHolders(testString) 25 var position int32 26 27 if err != nil || transformType != Wildcard || len(indexes) != 1 || indexes[0] != 1 || nbPartitions != -1 { 28 t.Fatalf("Error parsing %s", testString) 29 } 30 31 testString = "{{partition(10,1,2,3)}}" 32 33 transformType, indexes, nbPartitions, _, err = indexPlaceHolders(testString) 34 35 if err != nil || transformType != Partition || !reflect.DeepEqual(indexes, []int{1, 2, 3}) || nbPartitions != 10 { 36 t.Fatalf("Error parsing %s", testString) 37 } 38 39 testString = "{{ Partition (10,1,2,3) }}" 40 41 transformType, indexes, nbPartitions, _, err = indexPlaceHolders(testString) 42 43 if err != nil || transformType != Partition || !reflect.DeepEqual(indexes, []int{1, 2, 3}) || nbPartitions != 10 { 44 t.Fatalf("Error parsing %s", testString) 45 } 46 47 testString = "{{wildcard(2)}}" 48 transformType, indexes, nbPartitions, _, err = indexPlaceHolders(testString) 49 50 if err != nil || transformType != Wildcard || len(indexes) != 1 || indexes[0] != 2 || nbPartitions != -1 { 51 t.Fatalf("Error parsing %s", testString) 52 } 53 54 testString = "{{SplitFromLeft(2,1)}}" 55 transformType, indexes, position, _, err = indexPlaceHolders(testString) 56 57 if err != nil || transformType != SplitFromLeft || len(indexes) != 1 || indexes[0] != 2 || position != 1 { 58 t.Fatalf("Error parsing %s", testString) 59 } 60 61 testString = "{{SplitFromRight(3,2)}}" 62 transformType, indexes, position, _, err = indexPlaceHolders(testString) 63 64 if err != nil || transformType != SplitFromRight || len(indexes) != 1 || indexes[0] != 3 || position != 2 { 65 t.Fatalf("Error parsing %s", testString) 66 } 67 68 testString = "{{SliceFromLeft(2,2)}}" 69 transformType, indexes, sliceSize, _, err := indexPlaceHolders(testString) 70 71 if err != nil || transformType != SliceFromLeft || len(indexes) != 1 || indexes[0] != 2 || sliceSize != 2 { 72 t.Fatalf("Error parsing %s", testString) 73 } 74 } 75 76 func TestSubjectTransformHelpers(t *testing.T) { 77 equals := func(a, b []string) bool { 78 if len(a) != len(b) { 79 return false 80 } 81 for i, v := range a { 82 if v != b[i] { 83 return false 84 } 85 } 86 return true 87 } 88 89 filter, placeHolders := transformUntokenize("bar") 90 if filter != "bar" || len(placeHolders) != 0 { 91 t.Fatalf("transformUntokenize for not returning expected result") 92 } 93 94 filter, placeHolders = transformUntokenize("foo.$2.$1") 95 if filter != "foo.*.*" || !equals(placeHolders, []string{"$2", "$1"}) { 96 t.Fatalf("transformUntokenize for not returning expected result") 97 } 98 99 filter, placeHolders = transformUntokenize("foo.{{wildcard(2)}}.{{wildcard(1)}}") 100 if filter != "foo.*.*" || !equals(placeHolders, []string{"{{wildcard(2)}}", "{{wildcard(1)}}"}) { 101 t.Fatalf("transformUntokenize for not returning expected result") 102 } 103 104 newReversibleTransform := func(src, dest string) *subjectTransform { 105 tr, err := NewSubjectTransformStrict(src, dest) 106 if err != nil { 107 t.Fatalf("Error getting reversible transform: %s to %s", src, dest) 108 } 109 return tr 110 } 111 112 tr := newReversibleTransform("foo.*.*", "bar.$2.{{Wildcard(1)}}") 113 subject := "foo.b.a" 114 transformed := tr.TransformSubject(subject) 115 reverse := tr.reverse() 116 if reverse.TransformSubject(transformed) != subject { 117 t.Fatal("Reversed transform subject not matching") 118 } 119 } 120 121 func TestSubjectTransforms(t *testing.T) { 122 shouldErr := func(src, dest string, strict bool) { 123 t.Helper() 124 if _, err := NewSubjectTransformWithStrict(src, dest, strict); err != ErrBadSubject && !errors.Is(err, ErrInvalidMappingDestination) { 125 t.Fatalf("Did not get an error for src=%q and dest=%q", src, dest) 126 } 127 } 128 129 // Must be valid subjects. 130 shouldErr("foo..", "bar", false) 131 132 // Wildcards are allowed in src, but must be matched by token placements on the other side. 133 // e.g. foo.* -> bar.$1. 134 // Need to have as many pwcs as placements on other side 135 136 shouldErr("foo.*", "bar.*", false) 137 shouldErr("foo.*", "bar.$2", false) // Bad pwc token identifier 138 shouldErr("foo.*", "bar.$1.>", false) // fwcs have to match. 139 shouldErr("foo.>", "bar.baz", false) // fwcs have to match. 140 shouldErr("foo.*.*", "bar.$2", true) // Must place all pwcs. 141 shouldErr("foo.*", "foo.$foo", true) // invalid $ value 142 shouldErr("foo.*", "bar.{{Partition(2,1)}}", true) // can only use Wildcard function (and old-style $x) in import transform 143 shouldErr("foo.*", "foo.{{wildcard(2)}}", false) // Mapping function being passed an out of range wildcard index 144 shouldErr("foo.*", "foo.{{unimplemented(1)}}", false) // Mapping trying to use an unknown mapping function 145 shouldErr("foo.*", "foo.{{partition(10)}}", false) // Not enough arguments passed to the mapping function 146 shouldErr("foo.*", "foo.{{wildcard(foo)}}", false) // Invalid argument passed to the mapping function 147 shouldErr("foo.*", "foo.{{wildcard()}}", false) // Not enough arguments passed to the mapping function 148 shouldErr("foo.*", "foo.{{wildcard(1,2)}}", false) // Too many arguments passed to the mapping function 149 shouldErr("foo.*", "foo.{{ wildcard5) }}", false) // Bad mapping function 150 shouldErr("foo.*", "foo.{{splitLeft(2,2}}", false) // arg out of range 151 shouldErr("foo", "bla.{{wildcard(1)}}", false) // arg out of range with no wildcard in the source 152 153 shouldBeOK := func(src, dest string, strict bool) *subjectTransform { 154 t.Helper() 155 tr, err := NewSubjectTransformWithStrict(src, dest, strict) 156 if err != nil { 157 t.Fatalf("Got an error %v for src=%q and dest=%q", err, src, dest) 158 } 159 return tr 160 } 161 162 shouldBeOK("foo.*", "bar.{{Wildcard(1)}}", true) 163 164 shouldBeOK("foo.*.*", "bar.$2", false) // don't have to use all pwcs. 165 shouldBeOK("foo.*.*", "bar.{{wildcard(1)}}", false) // don't have to use all pwcs. 166 shouldBeOK("foo", "bar", false) 167 shouldBeOK("foo.*.bar.*.baz", "req.$2.$1", false) 168 shouldBeOK("baz.>", "mybaz.>", false) 169 shouldBeOK("*", "{{splitfromleft(1,1)}}", false) 170 shouldBeOK("", "prefix.>", false) 171 shouldBeOK("*.*", "{{partition(10,1,2)}}", false) 172 shouldBeOK("foo.*.*", "foo.{{wildcard(1)}}.{{wildcard(2)}}.{{partition(5,1,2)}}", false) 173 174 shouldMatch := func(src, dest, sample, expected string) { 175 t.Helper() 176 tr := shouldBeOK(src, dest, false) 177 if tr != nil { 178 s, err := tr.Match(sample) 179 if err != nil { 180 t.Fatalf("Got an error %v when expecting a match for %q to %q", err, sample, expected) 181 } 182 if s != expected { 183 t.Fatalf("Dest does not match what was expected. Got %q, expected %q", s, expected) 184 } 185 } 186 } 187 188 shouldMatch("", "prefix.>", "foo", "prefix.foo") 189 shouldMatch("foo", "", "foo", "foo") 190 shouldMatch("foo", "bar", "foo", "bar") 191 shouldMatch("foo.*.bar.*.baz", "req.$2.$1", "foo.A.bar.B.baz", "req.B.A") 192 shouldMatch("foo.*.bar.*.baz", "req.{{wildcard(2)}}.{{wildcard(1)}}", "foo.A.bar.B.baz", "req.B.A") 193 shouldMatch("baz.>", "my.pre.>", "baz.1.2.3", "my.pre.1.2.3") 194 shouldMatch("baz.>", "foo.bar.>", "baz.1.2.3", "foo.bar.1.2.3") 195 shouldMatch("*", "foo.bar.$1", "foo", "foo.bar.foo") 196 shouldMatch("*", "{{splitfromleft(1,3)}}", "12345", "123.45") 197 shouldMatch("*", "{{SplitFromRight(1,3)}}", "12345", "12.345") 198 shouldMatch("*", "{{SliceFromLeft(1,3)}}", "1234567890", "123.456.789.0") 199 shouldMatch("*", "{{SliceFromRight(1,3)}}", "1234567890", "1.234.567.890") 200 shouldMatch("*", "{{split(1,-)}}", "-abc-def--ghi-", "abc.def.ghi") 201 shouldMatch("*", "{{split(1,-)}}", "abc-def--ghi-", "abc.def.ghi") 202 shouldMatch("*.*", "{{split(2,-)}}.{{splitfromleft(1,2)}}", "foo.-abc-def--ghij-", "abc.def.ghij.fo.o") // combo + checks split for multiple instance of deliminator and deliminator being at the start or end 203 }