github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/nbs/dynamo_fake_test.go (about) 1 // Copyright 2019 Dolthub, Inc. 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 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2017 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package nbs 23 24 import ( 25 "bytes" 26 "sync/atomic" 27 "testing" 28 29 "github.com/aws/aws-sdk-go/aws" 30 "github.com/aws/aws-sdk-go/aws/request" 31 "github.com/aws/aws-sdk-go/service/dynamodb" 32 "github.com/stretchr/testify/assert" 33 34 "github.com/dolthub/dolt/go/store/constants" 35 ) 36 37 type fakeDDB struct { 38 data map[string]interface{} 39 t *testing.T 40 numPuts, numGets int64 41 } 42 43 type record struct { 44 lock, root []byte 45 vers, specs, appendix string 46 } 47 48 func makeFakeDDB(t *testing.T) *fakeDDB { 49 return &fakeDDB{ 50 data: map[string]interface{}{}, 51 t: t, 52 } 53 } 54 55 func (m *fakeDDB) readerForTable(name addr) (chunkReader, error) { 56 if i, present := m.data[fmtTableName(name)]; present { 57 buff, ok := i.([]byte) 58 assert.True(m.t, ok) 59 ti, err := parseTableIndex(buff) 60 61 if err != nil { 62 return nil, err 63 } 64 65 return newTableReader(ti, tableReaderAtFromBytes(buff), fileBlockSize), nil 66 } 67 return nil, nil 68 } 69 70 func (m *fakeDDB) GetItemWithContext(ctx aws.Context, input *dynamodb.GetItemInput, opts ...request.Option) (*dynamodb.GetItemOutput, error) { 71 key := input.Key[dbAttr].S 72 assert.NotNil(m.t, key, "key should have been a String: %+v", input.Key[dbAttr]) 73 74 item := map[string]*dynamodb.AttributeValue{} 75 if e, present := m.data[*key]; present { 76 item[dbAttr] = &dynamodb.AttributeValue{S: key} 77 switch e := e.(type) { 78 case record: 79 item[nbsVersAttr] = &dynamodb.AttributeValue{S: aws.String(AWSStorageVersion)} 80 item[versAttr] = &dynamodb.AttributeValue{S: aws.String(e.vers)} 81 item[rootAttr] = &dynamodb.AttributeValue{B: e.root} 82 item[lockAttr] = &dynamodb.AttributeValue{B: e.lock} 83 if e.specs != "" { 84 item[tableSpecsAttr] = &dynamodb.AttributeValue{S: aws.String(e.specs)} 85 } 86 if e.appendix != "" { 87 item[appendixAttr] = &dynamodb.AttributeValue{S: aws.String(e.appendix)} 88 } 89 case []byte: 90 item[dataAttr] = &dynamodb.AttributeValue{B: e} 91 } 92 } 93 atomic.AddInt64(&m.numGets, 1) 94 return &dynamodb.GetItemOutput{Item: item}, nil 95 } 96 97 func (m *fakeDDB) putRecord(k string, l, r []byte, v string, s string, a string) { 98 m.data[k] = record{l, r, v, s, a} 99 } 100 101 func (m *fakeDDB) putData(k string, d []byte) { 102 m.data[k] = d 103 } 104 105 func (m *fakeDDB) PutItemWithContext(ctx aws.Context, input *dynamodb.PutItemInput, opts ...request.Option) (*dynamodb.PutItemOutput, error) { 106 assert.NotNil(m.t, input.Item[dbAttr], "%s should have been present", dbAttr) 107 assert.NotNil(m.t, input.Item[dbAttr].S, "key should have been a String: %+v", input.Item[dbAttr]) 108 key := *input.Item[dbAttr].S 109 110 if input.Item[dataAttr] != nil { 111 assert.NotNil(m.t, input.Item[dataAttr].B, "data should have been a blob: %+v", input.Item[dataAttr]) 112 m.putData(key, input.Item[dataAttr].B) 113 return &dynamodb.PutItemOutput{}, nil 114 } 115 116 assert.NotNil(m.t, input.Item[nbsVersAttr], "%s should have been present", nbsVersAttr) 117 assert.NotNil(m.t, input.Item[nbsVersAttr].S, "nbsVers should have been a String: %+v", input.Item[nbsVersAttr]) 118 assert.Equal(m.t, AWSStorageVersion, *input.Item[nbsVersAttr].S) 119 120 assert.NotNil(m.t, input.Item[versAttr], "%s should have been present", versAttr) 121 assert.NotNil(m.t, input.Item[versAttr].S, "nbsVers should have been a String: %+v", input.Item[versAttr]) 122 assert.Equal(m.t, constants.NomsVersion, *input.Item[versAttr].S) 123 124 assert.NotNil(m.t, input.Item[lockAttr], "%s should have been present", lockAttr) 125 assert.NotNil(m.t, input.Item[lockAttr].B, "lock should have been a blob: %+v", input.Item[lockAttr]) 126 lock := input.Item[lockAttr].B 127 128 assert.NotNil(m.t, input.Item[rootAttr], "%s should have been present", rootAttr) 129 assert.NotNil(m.t, input.Item[rootAttr].B, "root should have been a blob: %+v", input.Item[rootAttr]) 130 root := input.Item[rootAttr].B 131 132 specs := "" 133 if attr, present := input.Item[tableSpecsAttr]; present { 134 assert.NotNil(m.t, attr.S, "specs should have been a String: %+v", input.Item[tableSpecsAttr]) 135 specs = *attr.S 136 } 137 138 apps := "" 139 if attr, present := input.Item[appendixAttr]; present { 140 assert.NotNil(m.t, attr.S, "appendix specs should have been a String: %+v", input.Item[appendixAttr]) 141 apps = *attr.S 142 } 143 144 mustNotExist := *(input.ConditionExpression) == valueNotExistsOrEqualsExpression 145 current, present := m.data[key] 146 147 if mustNotExist && present { 148 return nil, mockAWSError("ConditionalCheckFailedException") 149 } else if !mustNotExist && !checkCondition(current.(record), input.ExpressionAttributeValues) { 150 return nil, mockAWSError("ConditionalCheckFailedException") 151 } 152 153 m.putRecord(key, lock, root, constants.NomsVersion, specs, apps) 154 155 atomic.AddInt64(&m.numPuts, 1) 156 return &dynamodb.PutItemOutput{}, nil 157 } 158 159 func checkCondition(current record, expressionAttrVals map[string]*dynamodb.AttributeValue) bool { 160 return current.vers == *expressionAttrVals[versExpressionValuesKey].S && bytes.Equal(current.lock, expressionAttrVals[prevLockExpressionValuesKey].B) 161 162 } 163 164 func (m *fakeDDB) NumGets() int64 { 165 return atomic.LoadInt64(&m.numGets) 166 }