github.com/willyham/dosa@v2.3.1-0.20171024181418-1e446d37ee71+incompatible/connectors/cassandra/datastore_query_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 cassandra_test 22 23 import ( 24 "context" 25 "testing" 26 27 gouuid "github.com/satori/go.uuid" 28 "github.com/stretchr/testify/assert" 29 "github.com/uber-go/dosa" 30 ) 31 32 const pageSize = 5 33 34 var ( 35 strKeys = []string{"aa", "bb", "cc"} 36 ) 37 38 func TestRangeQuery(t *testing.T) { 39 sut := GetTestConnector(t) 40 partitionKey := dosa.UUID(gouuid.NewV4().String()) 41 populateEntityRange(t, partitionKey) 42 43 var ( 44 res []map[string]dosa.FieldValue 45 token string 46 err error 47 ) 48 // range query with paging for entire partition 49 for i := range strKeys { 50 res, token, err = sut.Range(context.TODO(), testEntityInfo, map[string][]*dosa.Condition{ 51 uuidKeyField: {{Op: dosa.Eq, Value: partitionKey}}, 52 }, []string{uuidKeyField, stringKeyField, int64KeyField, int32Field}, token, pageSize) 53 assert.NoError(t, err) 54 assert.Len(t, res, pageSize) 55 for j, row := range res { 56 assert.Equal(t, partitionKey, row[uuidKeyField]) 57 // res ordered by strKey ASC 58 assert.Equal(t, strKeys[i], row[stringKeyField]) 59 // res ordered by int64Key DESC 60 assert.Equal(t, int64(pageSize-j-1), row[int64KeyField]) 61 assert.Contains(t, row, int32Field) 62 assert.EqualValues(t, row[int64KeyField], row[int32Field]) 63 } 64 } 65 66 // range query with constraints on first clustering key 67 res, token, err = sut.Range(context.TODO(), testEntityInfo, map[string][]*dosa.Condition{ 68 uuidKeyField: {{Op: dosa.Eq, Value: partitionKey}}, 69 stringKeyField: {{Op: dosa.Gt, Value: strKeys[0]}, {Op: dosa.Lt, Value: strKeys[2]}}, // "aa" < strKey < "cc" 70 }, []string{uuidKeyField, stringKeyField, int64KeyField, int32Field}, "", pageSize*2) 71 assert.NoError(t, err) 72 assert.Len(t, res, pageSize) // limit == pageSize * 2 but we only have pageSize rows in range 73 assert.Empty(t, token) 74 for i, row := range res { 75 assert.Equal(t, partitionKey, row[uuidKeyField]) 76 assert.Equal(t, strKeys[1], row[stringKeyField]) 77 // res ordered by int64Key DESC 78 assert.Equal(t, int64(pageSize-i-1), row[int64KeyField]) 79 assert.Contains(t, row, int32Field) 80 assert.EqualValues(t, row[int64KeyField], row[int32Field]) 81 } 82 83 // range query with constraints for all clustering keys 84 res, token, err = sut.Range(context.TODO(), testEntityInfo, map[string][]*dosa.Condition{ 85 uuidKeyField: {{Op: dosa.Eq, Value: partitionKey}}, 86 stringKeyField: {{Op: dosa.Eq, Value: strKeys[0]}}, 87 int64KeyField: {{Op: dosa.GtOrEq, Value: int64(1)}, {Op: dosa.LtOrEq, Value: int64(3)}}, 88 }, []string{uuidKeyField, stringKeyField, int64KeyField, int32Field}, "", pageSize*2) 89 assert.NoError(t, err) 90 assert.Len(t, res, 3) // limit == pageSize * 2 but we only have 3 rows in range 91 assert.Empty(t, token) 92 for i, row := range res { 93 assert.Equal(t, partitionKey, row[uuidKeyField]) 94 assert.Equal(t, strKeys[0], row[stringKeyField]) 95 // res ordered by int64Key DESC 96 assert.Equal(t, int64(3-i), row[int64KeyField]) 97 assert.Contains(t, row, int32Field) 98 assert.EqualValues(t, row[int64KeyField], row[int32Field]) 99 } 100 } 101 102 func TestRangeQueryInvalidToken(t *testing.T) { 103 sut := GetTestConnector(t) 104 _, _, err := sut.Range(context.TODO(), testEntityInfo, map[string][]*dosa.Condition{ 105 uuidKeyField: {{Op: dosa.Eq, Value: dosa.UUID(gouuid.NewV4().String())}}, 106 }, []string{int32Field}, "西瓜", pageSize) 107 assert.Error(t, err) 108 assert.Contains(t, err.Error(), "bad token") 109 } 110 111 func TestRangeQueryFieldsToRead(t *testing.T) { 112 sut := GetTestConnector(t) 113 partitionKey := dosa.UUID(gouuid.NewV4().String()) 114 populateEntityRange(t, partitionKey) 115 116 res, token, err := sut.Range(context.TODO(), testEntityInfo, map[string][]*dosa.Condition{ 117 uuidKeyField: {{Op: dosa.Eq, Value: partitionKey}}, 118 stringKeyField: {{Op: dosa.Eq, Value: strKeys[0]}}, 119 int64KeyField: {{Op: dosa.GtOrEq, Value: int64(1)}, {Op: dosa.LtOrEq, Value: int64(3)}}, 120 }, nil, "", pageSize*2) 121 assert.NoError(t, err) 122 assert.Empty(t, token) 123 assert.Len(t, res, 3) 124 for _, row := range res { 125 // should include all columns in result 126 for _, c := range testEntityInfo.Def.Columns { 127 assert.Contains(t, row, c.Name) 128 } 129 } 130 } 131 132 // as Scan shares doCommonQuery code path with Range, we'll skip invalid token test and fieldsToRead test 133 func TestScan(t *testing.T) { 134 sut := GetTestConnector(t) 135 sp := "datastore_scan_test" 136 entityInfo := newTestEntityInfo(sp) 137 138 expectedUUIDSet := make(map[dosa.UUID]struct{}) 139 expectedIntValueSet := make(map[int]struct{}) 140 141 // remove any entities from any previous test 142 for { 143 res, _, err := sut.Scan(context.TODO(), entityInfo, []string{uuidKeyField, stringKeyField, int64KeyField}, "", 200) 144 if err != nil || len(res) == 0 { 145 break 146 } 147 for _, e := range res { 148 _ = sut.Remove(context.TODO(), entityInfo, e) 149 } 150 } 151 152 for i := 0; i < 100; i++ { 153 id := dosa.UUID(gouuid.NewV4().String()) 154 expectedUUIDSet[id] = struct{}{} 155 expectedIntValueSet[i] = struct{}{} 156 err := sut.Upsert(context.TODO(), entityInfo, map[string]dosa.FieldValue{ 157 uuidKeyField: id, 158 stringKeyField: "apple", 159 int64KeyField: int64(i), 160 int32Field: int32(i), 161 }) 162 if !assert.NoError(t, err) { 163 assert.FailNow(t, "failed to populate entities for scan test", err) 164 } 165 } 166 167 actualUUIDSet := make(map[dosa.UUID]struct{}) 168 actualIntValueSet := make(map[int]struct{}) 169 var ( 170 res []map[string]dosa.FieldValue 171 token string 172 ) 173 for { 174 var err error 175 res, token, err = sut.Scan(context.TODO(), entityInfo, []string{uuidKeyField, int32Field}, token, 39) 176 assert.NoError(t, err) 177 for _, row := range res { 178 actualUUIDSet[row[uuidKeyField].(dosa.UUID)] = struct{}{} 179 actualIntValueSet[int(row[int32Field].(int32))] = struct{}{} 180 } 181 if len(token) == 0 { 182 break 183 } 184 } 185 186 assert.Equal(t, expectedUUIDSet, actualUUIDSet) 187 assert.Equal(t, expectedIntValueSet, actualIntValueSet) 188 } 189 190 func populateEntityRange(t *testing.T, uuid dosa.UUID) { 191 sut := GetTestConnector(t) 192 for _, strKey := range strKeys { 193 for i := 0; i < pageSize; i++ { 194 err := sut.Upsert(context.TODO(), testEntityInfo, map[string]dosa.FieldValue{ 195 uuidKeyField: uuid, 196 stringKeyField: strKey, 197 int64KeyField: int64(i), 198 int32Field: int32(i), 199 }) 200 if !assert.NoError(t, err) { 201 assert.FailNow(t, "failed to populate entity range", err) 202 } 203 } 204 } 205 }