github.com/thiagoyeds/go-cloud@v0.26.0/docstore/gcpfirestore/query_test.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 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 // https://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 gcpfirestore 16 17 import ( 18 "math" 19 "testing" 20 "time" 21 22 "github.com/google/go-cmp/cmp" 23 "gocloud.dev/docstore/driver" 24 "gocloud.dev/docstore/drivertest" 25 pb "google.golang.org/genproto/googleapis/firestore/v1" 26 "google.golang.org/protobuf/proto" 27 ) 28 29 func TestFilterToProto(t *testing.T) { 30 c := &collection{nameField: "name", collPath: "collPath"} 31 for _, test := range []struct { 32 in driver.Filter 33 want *pb.StructuredQuery_Filter 34 }{ 35 { 36 driver.Filter{[]string{"a"}, ">", 1}, 37 &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_FieldFilter{ 38 FieldFilter: &pb.StructuredQuery_FieldFilter{ 39 Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"}, 40 Op: pb.StructuredQuery_FieldFilter_GREATER_THAN, 41 Value: &pb.Value{ValueType: &pb.Value_IntegerValue{1}}, 42 }, 43 }}, 44 }, 45 { 46 driver.Filter{[]string{"a"}, driver.EqualOp, nil}, 47 &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_UnaryFilter{ 48 UnaryFilter: &pb.StructuredQuery_UnaryFilter{ 49 OperandType: &pb.StructuredQuery_UnaryFilter_Field{ 50 Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"}, 51 }, 52 Op: pb.StructuredQuery_UnaryFilter_IS_NULL, 53 }, 54 }}, 55 }, 56 { 57 driver.Filter{[]string{"a"}, driver.EqualOp, math.NaN()}, 58 &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_UnaryFilter{ 59 UnaryFilter: &pb.StructuredQuery_UnaryFilter{ 60 OperandType: &pb.StructuredQuery_UnaryFilter_Field{ 61 Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"}, 62 }, 63 Op: pb.StructuredQuery_UnaryFilter_IS_NAN, 64 }, 65 }}, 66 }, 67 { 68 driver.Filter{[]string{"name"}, "<", "foo"}, 69 &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_FieldFilter{ 70 FieldFilter: &pb.StructuredQuery_FieldFilter{ 71 Field: &pb.StructuredQuery_FieldReference{FieldPath: "__name__"}, 72 Op: pb.StructuredQuery_FieldFilter_LESS_THAN, 73 Value: &pb.Value{ValueType: &pb.Value_ReferenceValue{"collPath/foo"}}, 74 }, 75 }}, 76 }, 77 } { 78 got, err := c.filterToProto(test.in) 79 if err != nil { 80 t.Fatal(err) 81 } 82 if diff := cmp.Diff(got, test.want, cmp.Comparer(proto.Equal)); diff != "" { 83 t.Errorf("%+v: %s", test.in, diff) 84 } 85 } 86 } 87 88 func TestSplitFilters(t *testing.T) { 89 aEqual := driver.Filter{[]string{"a"}, "=", 1} 90 aLess := driver.Filter{[]string{"a"}, "<", 1} 91 aGreater := driver.Filter{[]string{"a"}, ">", 1} 92 bEqual := driver.Filter{[]string{"b"}, "=", 1} 93 bLess := driver.Filter{[]string{"b"}, "<", 1} 94 95 for _, test := range []struct { 96 in []driver.Filter 97 wantSend, wantLocal []driver.Filter 98 }{ 99 { 100 in: nil, 101 wantSend: nil, 102 wantLocal: nil, 103 }, 104 { 105 in: []driver.Filter{aEqual}, 106 wantSend: []driver.Filter{aEqual}, 107 wantLocal: nil, 108 }, 109 { 110 in: []driver.Filter{aLess}, 111 wantSend: []driver.Filter{aLess}, 112 wantLocal: nil, 113 }, 114 { 115 in: []driver.Filter{aLess, aGreater}, 116 wantSend: []driver.Filter{aLess, aGreater}, 117 wantLocal: nil, 118 }, 119 { 120 in: []driver.Filter{aLess, bEqual, aGreater}, 121 wantSend: []driver.Filter{aLess, bEqual, aGreater}, 122 wantLocal: nil, 123 }, 124 { 125 in: []driver.Filter{aLess, bLess, aGreater}, 126 wantSend: []driver.Filter{aLess, aGreater}, 127 wantLocal: []driver.Filter{bLess}, 128 }, 129 { 130 in: []driver.Filter{aEqual, aLess, bLess, aGreater, bEqual}, 131 wantSend: []driver.Filter{aEqual, aLess, aGreater, bEqual}, 132 wantLocal: []driver.Filter{bLess}, 133 }, 134 } { 135 gotSend, gotLocal := splitFilters(test.in) 136 if diff := cmp.Diff(gotSend, test.wantSend); diff != "" { 137 t.Errorf("%v, send:\n%s", test.in, diff) 138 } 139 if diff := cmp.Diff(gotLocal, test.wantLocal); diff != "" { 140 t.Errorf("%v, local:\n%s", test.in, diff) 141 } 142 } 143 } 144 145 func TestEvaluateFilter(t *testing.T) { 146 m := map[string]interface{}{ 147 "i": 32, 148 "f": 5.5, 149 "f2": 5.0, 150 "s": "32", 151 "t": time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), 152 "b": true, 153 "mi": int64(math.MaxInt64), 154 } 155 doc := drivertest.MustDocument(m) 156 for _, test := range []struct { 157 field, op string 158 value interface{} 159 want bool 160 }{ 161 // Firestore compares numbers to each other ignoring type (int vs. float). 162 // Just a few simple tests here; see driver.TestCompareNumbers for more. 163 {"i", "=", 32, true}, 164 {"i", ">", 32, false}, 165 {"i", "<", 32, false}, 166 {"i", "=", 32.0, true}, 167 {"i", ">", 32.0, false}, 168 {"i", "<", 32.0, false}, 169 {"f", "=", 5.5, true}, 170 {"f", "<", 5.5, false}, 171 {"f2", "=", 5, true}, 172 {"f2", ">", 5, false}, 173 // Firestore compares strings to each other, but not to numbers. 174 {"s", "=", "32", true}, 175 {"s", ">", "32", false}, 176 {"s", "<", "32", false}, 177 {"s", ">", "3", true}, 178 {"i", "=", "32", false}, 179 {"i", ">", "32", false}, 180 {"i", "<", "32", false}, 181 {"f", "=", "5.5", false}, 182 {"f", ">", "5.5", false}, 183 {"f", "<", "5.5", false}, 184 // Firestore compares times to each other. 185 {"t", "<", time.Date(2014, 1, 1, 0, 0, 0, 0, time.UTC), true}, 186 // Comparisons with other types fail. 187 {"b", "=", "true", false}, 188 {"b", ">", "true", false}, 189 {"b", "<", "true", false}, 190 {"t", "=", 0, false}, 191 {"t", ">", 0, false}, 192 {"t", "<", 0, false}, 193 } { 194 f := driver.Filter{FieldPath: []string{test.field}, Op: test.op, Value: test.value} 195 got := evaluateFilter(f, doc) 196 if got != test.want { 197 t.Errorf("%s %s %v: got %t, want %t", test.field, test.op, test.value, got, test.want) 198 } 199 } 200 }