github.com/willyham/dosa@v2.3.1-0.20171024181418-1e446d37ee71+incompatible/range_conditions_test.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package dosa_test
    22  
    23  import (
    24  	"testing"
    25  
    26  	"time"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/uber-go/dosa"
    30  )
    31  
    32  // pk = ((a, b), c, d, e)
    33  var testEntityRange = &dosa.EntityDefinition{
    34  	Name: "testentityrange",
    35  	Key: &dosa.PrimaryKey{
    36  		PartitionKeys: []string{"a", "b"},
    37  		ClusteringKeys: []*dosa.ClusteringKey{
    38  			{
    39  				Name:       "c",
    40  				Descending: true,
    41  			},
    42  			{
    43  				Name:       "d",
    44  				Descending: true,
    45  			},
    46  			{
    47  				Name:       "e",
    48  				Descending: true,
    49  			},
    50  		},
    51  	},
    52  	Columns: []*dosa.ColumnDefinition{
    53  		{
    54  			Name: "a",
    55  			Type: dosa.TUUID,
    56  		},
    57  
    58  		{
    59  			Name: "c",
    60  			Type: dosa.Int32,
    61  		},
    62  		{
    63  			Name: "e",
    64  			Type: dosa.String,
    65  		},
    66  		{
    67  			Name: "d",
    68  			Type: dosa.Timestamp,
    69  		},
    70  		{
    71  			Name: "b",
    72  			Type: dosa.Int64,
    73  		},
    74  		{
    75  			Name: "f",
    76  			Type: dosa.Blob,
    77  		},
    78  	},
    79  }
    80  
    81  var columnToFieldMap = map[string]string{
    82  	"a": "FieldA",
    83  	"b": "FieldB",
    84  	"c": "FieldC",
    85  	"d": "FieldD",
    86  	"e": "FieldE",
    87  	"f": "FieldF",
    88  }
    89  
    90  var simpleTransformer = func(x string) string {
    91  	return columnToFieldMap[x]
    92  }
    93  
    94  func TestEnsureValidRangeConditions(t *testing.T) {
    95  	assert.NoError(t, testEntityRange.EnsureValid()) // sanity check
    96  
    97  	type validCase struct {
    98  		conds map[string][]*dosa.Condition
    99  		desc  string
   100  	}
   101  	validCases := []validCase{
   102  		{
   103  			conds: map[string][]*dosa.Condition{
   104  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   105  				"b": {{dosa.Eq, int64(100)}},
   106  			},
   107  			desc: "supply only partition keys is allowed, no conditions on clustering keys",
   108  		},
   109  		{
   110  			conds: map[string][]*dosa.Condition{
   111  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   112  				"b": {{dosa.Eq, int64(100)}},
   113  				"c": {{dosa.Eq, int32(99)}},
   114  			},
   115  			desc: "eq condition on first clustering key, no condition on second and third",
   116  		},
   117  		{
   118  			conds: map[string][]*dosa.Condition{
   119  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   120  				"b": {{dosa.Eq, int64(100)}},
   121  				"c": {{dosa.GtOrEq, int32(99)}, {dosa.Lt, int32(200)}},
   122  			},
   123  			desc: "close range condition on first clustering key, no condition on second and third",
   124  		},
   125  		{
   126  			conds: map[string][]*dosa.Condition{
   127  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   128  				"b": {{dosa.Eq, int64(100)}},
   129  				"c": {{dosa.Eq, int32(99)}},
   130  				"d": {{dosa.LtOrEq, time.Now()}},
   131  			},
   132  			desc: "open range condition on second clustering key, no restaint on third",
   133  		},
   134  		{
   135  			conds: map[string][]*dosa.Condition{
   136  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   137  				"b": {{dosa.Eq, int64(100)}},
   138  				"c": {{dosa.Eq, int32(99)}},
   139  				"d": {{dosa.Eq, time.Unix(100, 0)}},
   140  			},
   141  			desc: "eq condition on second clustering key, no restaint on third",
   142  		},
   143  		{
   144  			conds: map[string][]*dosa.Condition{
   145  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   146  				"b": {{dosa.Eq, int64(100)}},
   147  				"c": {{dosa.Eq, int32(99)}},
   148  				"d": {{dosa.Eq, time.Unix(100, 0)}},
   149  				"e": {{dosa.Gt, "aaa"}, {dosa.Lt, "zzz"}},
   150  			},
   151  			desc: "close range condition on third/last clustering key with < and >",
   152  		},
   153  		{
   154  			conds: map[string][]*dosa.Condition{
   155  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   156  				"b": {{dosa.Eq, int64(100)}},
   157  				"c": {{dosa.Eq, int32(99)}},
   158  				"d": {{dosa.Eq, time.Unix(100, 0)}},
   159  				"e": {{dosa.GtOrEq, "aaa"}, {dosa.LtOrEq, "zzz"}},
   160  			},
   161  			desc: "close range condition on third/last clustering key with <= and >=",
   162  		},
   163  		{
   164  			conds: map[string][]*dosa.Condition{
   165  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   166  				"b": {{dosa.Eq, int64(100)}},
   167  				"c": {{dosa.Eq, int32(99)}},
   168  				"d": {{dosa.Eq, time.Unix(100, 0)}},
   169  				"e": {{dosa.GtOrEq, "aaa"}, {dosa.Lt, "zzz"}},
   170  			},
   171  			desc: "close range condition on third/last clustering key with < and >=",
   172  		},
   173  		{
   174  			conds: map[string][]*dosa.Condition{
   175  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   176  				"b": {{dosa.Eq, int64(100)}},
   177  				"c": {{dosa.Eq, int32(99)}},
   178  				"d": {{dosa.Eq, time.Unix(100, 0)}},
   179  				"e": {{dosa.Gt, "aaa"}, {dosa.LtOrEq, "zzz"}},
   180  			},
   181  			desc: "close range condition on third/last clustering key with <= and >",
   182  		},
   183  	}
   184  
   185  	type invalidCase struct {
   186  		conds    map[string][]*dosa.Condition
   187  		desc     string
   188  		errMsg   string
   189  		errField string
   190  	}
   191  
   192  	invalidCases := []invalidCase{
   193  		{
   194  			conds: map[string][]*dosa.Condition{
   195  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   196  				"b": {{dosa.Eq, int64(100)}},
   197  				"f": {{dosa.Eq, []byte{1, 2, 3}}},
   198  			},
   199  			errMsg:   "cannot enforce condition on non-key column",
   200  			desc:     "conditions on non-key column",
   201  			errField: columnToFieldMap["f"],
   202  		},
   203  		{
   204  			conds: map[string][]*dosa.Condition{
   205  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   206  				"c": {{dosa.Gt, int32(100)}},
   207  			},
   208  			errMsg:   "missing Eq condition on partition keys",
   209  			desc:     "missing partition key condition",
   210  			errField: columnToFieldMap["b"],
   211  		},
   212  		{
   213  			conds: map[string][]*dosa.Condition{
   214  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   215  				"b": {{dosa.Gt, int64(100)}},
   216  			},
   217  			errMsg:   "invalid conditions for partition key",
   218  			desc:     "Gt condition on partition key",
   219  			errField: columnToFieldMap["b"],
   220  		},
   221  		{
   222  			conds: map[string][]*dosa.Condition{
   223  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   224  				"b": {{dosa.Eq, int64(100)}, {dosa.Eq, int64(200)}},
   225  			},
   226  			errMsg:   "invalid conditions for partition key",
   227  			desc:     "more than one conditions on partition key",
   228  			errField: columnToFieldMap["b"],
   229  		},
   230  		{
   231  			conds: map[string][]*dosa.Condition{
   232  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   233  				"b": {{dosa.Eq, "100"}},
   234  			},
   235  			errMsg:   "does not have expected type",
   236  			desc:     "wrong value type for partition key",
   237  			errField: columnToFieldMap["b"],
   238  		},
   239  		{
   240  			conds: map[string][]*dosa.Condition{
   241  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   242  				"b": {{dosa.Eq, int64(100)}},
   243  				"c": {{dosa.Eq, "100"}},
   244  			},
   245  			errMsg:   "invalid value for",
   246  			desc:     "wrong value type for clustering key",
   247  			errField: columnToFieldMap["c"],
   248  		},
   249  		{
   250  			conds: map[string][]*dosa.Condition{
   251  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   252  				"b": {{dosa.Eq, int64(100)}},
   253  				"c": {{dosa.Lt, int32(100)}, {dosa.Gt, int32(200)}},
   254  			},
   255  			errMsg:   "invalid or unsupported conditions for clustering key",
   256  			desc:     "invalid range condition on clustering key",
   257  			errField: columnToFieldMap["c"],
   258  		},
   259  		{
   260  			conds: map[string][]*dosa.Condition{
   261  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   262  				"b": {{dosa.Eq, int64(100)}},
   263  				"c": {{dosa.Lt, int32(100)}, {dosa.Gt, int32(200)}},
   264  				"d": {{dosa.GtOrEq, time.Now()}},
   265  			},
   266  			errMsg:   "exact one Eq condition can be applied except for the last",
   267  			desc:     "applying conditions other than eq to clustering keys that's not the last retrained",
   268  			errField: columnToFieldMap["c"],
   269  		},
   270  		{
   271  			conds: map[string][]*dosa.Condition{
   272  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   273  				"b": {{dosa.Eq, int64(100)}},
   274  				"c": {{dosa.Lt, int32(100)}, {dosa.Gt, int32(200)}},
   275  				"d": {{dosa.Eq, time.Unix(100, 0)}},
   276  				"e": {{dosa.Eq, "aaa"}},
   277  			},
   278  			errMsg: "exact one Eq condition can be applied except for the last",
   279  			desc: "applying conditions other than eq to clustering keys that's not the last retrained, " +
   280  				"different last retained clusterin key",
   281  			errField: columnToFieldMap["c"],
   282  		},
   283  		{
   284  			conds: map[string][]*dosa.Condition{
   285  				"a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}},
   286  				"b": {{dosa.Eq, int64(100)}},
   287  				"c": {{dosa.Eq, int32(100)}},
   288  				"e": {{dosa.Eq, "aaa"}},
   289  			},
   290  			errMsg:   "conditions must be applied consecutively on clustering keys",
   291  			desc:     "applying conditions non-consecutively to clustering keys",
   292  			errField: columnToFieldMap["e"],
   293  		},
   294  	}
   295  
   296  	for _, c := range validCases {
   297  		assert.NoError(t, dosa.EnsureValidRangeConditions(testEntityRange, testEntityRange.Key, c.conds, simpleTransformer), c.desc)
   298  	}
   299  
   300  	for _, c := range invalidCases {
   301  		err := dosa.EnsureValidRangeConditions(testEntityRange, testEntityRange.Key, c.conds, simpleTransformer)
   302  		if assert.Error(t, err, c.desc) {
   303  			assert.Contains(t, err.Error(), c.errMsg, c.desc)
   304  			assert.Contains(t, err.Error(), c.errField, c.desc)
   305  		}
   306  	}
   307  }