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  }