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  }