go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/swarming/server/model/utils_test.go (about) 1 // Copyright 2023 The LUCI 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 // 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 model 16 17 import ( 18 "bytes" 19 "compress/zlib" 20 "testing" 21 22 "go.chromium.org/luci/gae/service/datastore" 23 24 apipb "go.chromium.org/luci/swarming/proto/api_v2" 25 26 . "github.com/smartystreets/goconvey/convey" 27 . "go.chromium.org/luci/common/testing/assertions" 28 ) 29 30 func TestToJSONProperty(t *testing.T) { 31 t.Parallel() 32 33 Convey("With a value", t, func() { 34 p, err := ToJSONProperty(map[string]string{"a": "b"}) 35 So(err, ShouldBeNil) 36 So(p.Type(), ShouldEqual, datastore.PTString) 37 So(p.Value().(string), ShouldEqual, `{"a":"b"}`) 38 }) 39 40 Convey("With empty map", t, func() { 41 p, err := ToJSONProperty(map[string]string{}) 42 So(err, ShouldBeNil) 43 So(p.Type(), ShouldEqual, datastore.PTNull) 44 }) 45 46 Convey("With empty list", t, func() { 47 p, err := ToJSONProperty([]string{}) 48 So(err, ShouldBeNil) 49 So(p.Type(), ShouldEqual, datastore.PTNull) 50 }) 51 52 Convey("With nil", t, func() { 53 p, err := ToJSONProperty(nil) 54 So(err, ShouldBeNil) 55 So(p.Type(), ShouldEqual, datastore.PTNull) 56 }) 57 } 58 59 func TestFromJSONProperty(t *testing.T) { 60 t.Parallel() 61 62 Convey("Null", t, func() { 63 var v map[string]string 64 So(FromJSONProperty(datastore.MkProperty(nil), &v), ShouldBeNil) 65 So(v, ShouldResemble, map[string]string(nil)) 66 }) 67 68 Convey("Empty", t, func() { 69 var v map[string]string 70 So(FromJSONProperty(datastore.MkProperty(""), &v), ShouldBeNil) 71 So(v, ShouldResemble, map[string]string(nil)) 72 }) 73 74 Convey("Bytes", t, func() { 75 var v map[string]string 76 So(FromJSONProperty(datastore.MkProperty([]byte(`{"a":"b"}`)), &v), ShouldBeNil) 77 So(v, ShouldResemble, map[string]string{"a": "b"}) 78 }) 79 80 Convey("String", t, func() { 81 var v map[string]string 82 So(FromJSONProperty(datastore.MkProperty(`{"a":"b"}`), &v), ShouldBeNil) 83 So(v, ShouldResemble, map[string]string{"a": "b"}) 84 }) 85 86 Convey("Compressed", t, func() { 87 var v map[string]string 88 So(FromJSONProperty(datastore.MkProperty(deflate([]byte(`{"a":"b"}`))), &v), ShouldBeNil) 89 So(v, ShouldResemble, map[string]string{"a": "b"}) 90 }) 91 } 92 93 func TestDimensionsFlatToPb(t *testing.T) { 94 t.Parallel() 95 96 type testCase struct { 97 flat []string 98 list []*apipb.StringListPair 99 } 100 cases := []testCase{ 101 { 102 flat: nil, 103 list: nil, 104 }, 105 { 106 flat: []string{"a:1"}, 107 list: []*apipb.StringListPair{ 108 {Key: "a", Value: []string{"1"}}, 109 }, 110 }, 111 { 112 flat: []string{"a:1", "a:1"}, 113 list: []*apipb.StringListPair{ 114 {Key: "a", Value: []string{"1"}}, 115 }, 116 }, 117 { 118 flat: []string{"a:1", "a:2"}, 119 list: []*apipb.StringListPair{ 120 {Key: "a", Value: []string{"1", "2"}}, 121 }, 122 }, 123 { 124 flat: []string{"a:2", "a:1"}, 125 list: []*apipb.StringListPair{ 126 {Key: "a", Value: []string{"1", "2"}}, 127 }, 128 }, 129 { 130 flat: []string{"a:1", "b:2"}, 131 list: []*apipb.StringListPair{ 132 {Key: "a", Value: []string{"1"}}, 133 {Key: "b", Value: []string{"2"}}, 134 }, 135 }, 136 { 137 flat: []string{"a:1", "a:2", "b:1", "b:2"}, 138 list: []*apipb.StringListPair{ 139 {Key: "a", Value: []string{"1", "2"}}, 140 {Key: "b", Value: []string{"1", "2"}}, 141 }, 142 }, 143 { 144 flat: []string{"b:1", "b:2", "a:1", "a:2"}, 145 list: []*apipb.StringListPair{ 146 {Key: "a", Value: []string{"1", "2"}}, 147 {Key: "b", Value: []string{"1", "2"}}, 148 }, 149 }, 150 } 151 152 Convey("Works", t, func() { 153 for _, cs := range cases { 154 So(dimensionsFlatToPb(cs.flat), ShouldResembleProto, cs.list) 155 } 156 }) 157 } 158 159 func TestMapToStringListPair(t *testing.T) { 160 t.Parallel() 161 162 Convey("ok", t, func() { 163 m := map[string][]string{ 164 "key2": []string{"val3", "val4"}, 165 "key1": []string{"val1", "val2"}, 166 "key3": []string{"val3", "val2"}, 167 } 168 // Since iteration over a map randomizes the keys in Go, we need to 169 // assert that all the items are in the []*apipb.StringListPair and that 170 // the lengths match. If we compared tp an apipb.StringListPair type directly, 171 // the test would be flakey. 172 Convey("unsorted", func() { 173 spl := MapToStringListPair(m, false) 174 So(len(spl), ShouldEqual, len(m)) 175 So(&apipb.StringListPair{ 176 Key: "key2", 177 Value: []string{"val3", "val4"}, 178 }, ShouldBeIn, spl) 179 So(&apipb.StringListPair{ 180 Key: "key1", 181 Value: []string{"val1", "val2"}, 182 }, ShouldBeIn, spl) 183 So(&apipb.StringListPair{ 184 Key: "key3", 185 Value: []string{"val3", "val2"}, 186 }, ShouldBeIn, spl) 187 }) 188 189 Convey("sorted", func() { 190 So(MapToStringListPair(m, true), ShouldEqual, []*apipb.StringListPair{ 191 {Key: "key1", Value: []string{"val1", "val2"}}, 192 {Key: "key2", Value: []string{"val3", "val4"}}, 193 {Key: "key3", Value: []string{"val3", "val2"}}, 194 }) 195 }) 196 }) 197 198 Convey("empty", t, func() { 199 m := map[string][]string{} 200 So(MapToStringListPair(m, false), ShouldBeNil) 201 }) 202 Convey("nil", t, func() { 203 So(MapToStringListPair(nil, false), ShouldBeNil) 204 }) 205 206 } 207 208 func deflate(blob []byte) []byte { 209 out := bytes.NewBuffer(nil) 210 w := zlib.NewWriter(out) 211 if _, err := w.Write(blob); err != nil { 212 panic(err) 213 } 214 if err := w.Close(); err != nil { 215 panic(err) 216 } 217 return out.Bytes() 218 }