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  }