github.com/gagliardetto/solana-go@v1.11.0/diff/diff_test.go (about)

     1  // Copyright 2020 dfuse Platform Inc.
     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 diff
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestDiff(t *testing.T) {
    26  	type pair struct {
    27  		left  interface{}
    28  		right interface{}
    29  	}
    30  
    31  	tests := []struct {
    32  		name      string
    33  		in        pair
    34  		expecteds []string
    35  	}{
    36  		// Plain
    37  		{"plain - left nil, right nil",
    38  			pair{nil, nil},
    39  			nil,
    40  		},
    41  		{"plain - left nil, right set",
    42  			pair{nil, &plainStruct{}},
    43  			[]string{"<nil> => &{0} (*diff.plainStruct) (added)"},
    44  		},
    45  		{"plain - left set, right nil",
    46  			pair{&plainStruct{}, nil},
    47  			[]string{"&{0} (*diff.plainStruct) => <nil> (removed)"},
    48  		},
    49  		{"plain - equal",
    50  			pair{&plainStruct{}, &plainStruct{}},
    51  			nil,
    52  		},
    53  		{"plain - diff",
    54  			pair{&plainStruct{Field: 1}, &plainStruct{Field: 2}},
    55  			[]string{"1 (int) => 2 (int) (changed @ Field)"},
    56  		},
    57  
    58  		// Slice
    59  		{"slice - equal both nil",
    60  			pair{[]string(nil), []string(nil)},
    61  			nil,
    62  		},
    63  		{"slice - equal both length 0",
    64  			pair{[]string{}, []string{}},
    65  			nil,
    66  		},
    67  		{"slice - diff both length 1",
    68  			pair{[]string{"a"}, []string{"b"}},
    69  			[]string{
    70  				"a (string) => <nil> (removed @ [0])",
    71  				"<nil> => b (string) (added @ [0])",
    72  			},
    73  		},
    74  		{"slice - diff both length 2 re-ordered",
    75  			pair{[]string{"a", "b"}, []string{"b", "a"}},
    76  			[]string{
    77  				"a (string) => <nil> (removed @ [0])",
    78  				"<nil> => b (string) (added @ [0])",
    79  				"b (string) => <nil> (removed @ [1])",
    80  				"<nil> => a (string) (added @ [1])",
    81  			},
    82  		},
    83  		{"slice - diff left is longer than right, all different",
    84  			pair{[]string{"a", "b"}, []string{"c"}},
    85  			[]string{
    86  				"a (string) => <nil> (removed @ [0])",
    87  				"<nil> => c (string) (added @ [0])",
    88  				"b (string) => <nil> (removed @ [1->?])",
    89  			},
    90  		},
    91  		{"slice - diff left is longer than right, some equals",
    92  			pair{[]string{"a", "b"}, []string{"a"}},
    93  			[]string{
    94  				"b (string) => <nil> (removed @ [1->?])",
    95  			},
    96  		},
    97  		{"slice - diff left is smaller than right, all different",
    98  			pair{[]string{"a"}, []string{"b", "c"}},
    99  			[]string{
   100  				"a (string) => <nil> (removed @ [0])",
   101  				"<nil> => b (string) (added @ [0])",
   102  				"<nil> => c (string) (added @ [?->1])",
   103  			},
   104  		},
   105  		{"slice - diff left is smaller than right, some equals",
   106  			pair{[]string{"a"}, []string{"a", "b"}},
   107  			[]string{
   108  				"<nil> => b (string) (added @ [?->1])",
   109  			},
   110  		},
   111  
   112  		// Full
   113  		{"full - everything diff",
   114  			pair{
   115  				&topStruct{
   116  					Literal: "x",
   117  					Pointer: &plainStruct{Field: 1},
   118  					Array:   []string{"a", "b"},
   119  					Child:   &childStruct{Literal: "1", Pointer: &plainStruct{Field: 10}, Array: []string{"1", "2"}},
   120  				},
   121  				&topStruct{
   122  					Literal: "y",
   123  					Pointer: &plainStruct{Field: 2},
   124  					Array:   []string{"b", "c"},
   125  					Child:   &childStruct{Literal: "2", Pointer: &plainStruct{Field: 20}, Array: []string{"2"}},
   126  				},
   127  			},
   128  			[]string{
   129  				"x (string) => y (string) (changed @ Literal)",
   130  				"1 (int) => 2 (int) (changed @ Pointer.Field)",
   131  				"a (string) => <nil> (removed @ Array[0])",
   132  				"<nil> => b (string) (added @ Array[0])",
   133  				"b (string) => <nil> (removed @ Array[1])",
   134  				"<nil> => c (string) (added @ Array[1])",
   135  				"1 (string) => 2 (string) (changed @ Child.Literal)",
   136  				"10 (int) => 20 (int) (changed @ Child.Pointer.Field)",
   137  				"1 (string) => <nil> (removed @ Child.Array[0->?])",
   138  			},
   139  		},
   140  	}
   141  
   142  	for _, test := range tests {
   143  		t.Run(test.name, func(t *testing.T) {
   144  			actuals := accumulateDiffStrings(test.in.left, test.in.right)
   145  			assert.Equal(t, test.expecteds, actuals)
   146  		})
   147  	}
   148  }
   149  
   150  func eventToString(event *Event) string {
   151  	path := ""
   152  	if len(event.Path) > 1 {
   153  		path = " @ " + event.Path.String()
   154  	}
   155  
   156  	return fmt.Sprintf("%s => %s (%s%s)", reflectValueToString(event.Old), reflectValueToString(event.New), event.Kind, path)
   157  }
   158  
   159  func TestDiff_EventMatch(t *testing.T) {
   160  	tests := []struct {
   161  		name           string
   162  		left           interface{}
   163  		right          interface{}
   164  		pattern        string
   165  		expectedMatch  bool
   166  		expectedGroups []string
   167  	}{
   168  		{
   169  			"deep array added one",
   170  			&topStruct{Child: &childStruct{Array: []string{"1"}}},
   171  			&topStruct{Child: &childStruct{Array: []string{"1", "2"}}},
   172  			"Child.Array[#]",
   173  			true,
   174  			[]string{"?->1"},
   175  		},
   176  	}
   177  
   178  	for _, test := range tests {
   179  		t.Run(test.name, func(t *testing.T) {
   180  			events := accumulateDiff(test.left, test.right)
   181  			require.Len(t, events, 1)
   182  
   183  			match, groups := events[0].Match(test.pattern)
   184  			if test.expectedMatch {
   185  				assert.True(t, match, "Expected pattern %q to match diff path %q", test.pattern, events[0].Path)
   186  				assert.Equal(t, test.expectedGroups, groups)
   187  			} else {
   188  				assert.False(t, match, "Expected pattern %q to NOT match diff path %q", test.pattern, events[0].Path)
   189  			}
   190  		})
   191  	}
   192  }
   193  
   194  // There is something inherently broken with this, the accumulation seems to broke leading to incorrect
   195  // results. I assume it's a Golang thing related to slice and struct as value versus pointers. It works
   196  // only single event but starts to act weirdly when there > 1, like the Event's Path is all wrong. It's
   197  // better to try to avoid it when possible.
   198  func accumulateDiff(left, right interface{}) (out []Event) {
   199  	Diff(left, right, OnEvent(func(event Event) {
   200  		out = append(out, event)
   201  	}))
   202  	return
   203  }
   204  
   205  func accumulateDiffStrings(left, right interface{}) (out []string) {
   206  	Diff(left, right, OnEvent(func(event Event) {
   207  		out = append(out, eventToString(&event))
   208  	}))
   209  	return
   210  }
   211  
   212  type topStruct struct {
   213  	Literal string
   214  	Pointer *plainStruct
   215  	Array   []string
   216  	Child   *childStruct
   217  }
   218  
   219  type childStruct struct {
   220  	Literal string
   221  	Pointer *plainStruct
   222  	Array   []string
   223  }
   224  
   225  type plainStruct struct {
   226  	Field int
   227  }