github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/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) GetItemWithContext(ctx aws.Context, input *dynamodb.GetItemInput, opts ...request.Option) (*dynamodb.GetItemOutput, error) { 56 key := input.Key[dbAttr].S 57 assert.NotNil(m.t, key, "key should have been a String: %+v", input.Key[dbAttr]) 58 59 item := map[string]*dynamodb.AttributeValue{} 60 if e, present := m.data[*key]; present { 61 item[dbAttr] = &dynamodb.AttributeValue{S: key} 62 switch e := e.(type) { 63 case record: 64 item[nbsVersAttr] = &dynamodb.AttributeValue{S: aws.String(AWSStorageVersion)} 65 item[versAttr] = &dynamodb.AttributeValue{S: aws.String(e.vers)} 66 item[rootAttr] = &dynamodb.AttributeValue{B: e.root} 67 item[lockAttr] = &dynamodb.AttributeValue{B: e.lock} 68 if e.specs != "" { 69 item[tableSpecsAttr] = &dynamodb.AttributeValue{S: aws.String(e.specs)} 70 } 71 if e.appendix != "" { 72 item[appendixAttr] = &dynamodb.AttributeValue{S: aws.String(e.appendix)} 73 } 74 } 75 } 76 atomic.AddInt64(&m.numGets, 1) 77 return &dynamodb.GetItemOutput{Item: item}, nil 78 } 79 80 func (m *fakeDDB) putRecord(k string, l, r []byte, v string, s string, a string) { 81 m.data[k] = record{l, r, v, s, a} 82 } 83 84 func (m *fakeDDB) putData(k string, d []byte) { 85 m.data[k] = d 86 } 87 88 func (m *fakeDDB) PutItemWithContext(ctx aws.Context, input *dynamodb.PutItemInput, opts ...request.Option) (*dynamodb.PutItemOutput, error) { 89 assert.NotNil(m.t, input.Item[dbAttr], "%s should have been present", dbAttr) 90 assert.NotNil(m.t, input.Item[dbAttr].S, "key should have been a String: %+v", input.Item[dbAttr]) 91 key := *input.Item[dbAttr].S 92 93 assert.NotNil(m.t, input.Item[nbsVersAttr], "%s should have been present", nbsVersAttr) 94 assert.NotNil(m.t, input.Item[nbsVersAttr].S, "nbsVers should have been a String: %+v", input.Item[nbsVersAttr]) 95 assert.Equal(m.t, AWSStorageVersion, *input.Item[nbsVersAttr].S) 96 97 assert.NotNil(m.t, input.Item[versAttr], "%s should have been present", versAttr) 98 assert.NotNil(m.t, input.Item[versAttr].S, "nbsVers should have been a String: %+v", input.Item[versAttr]) 99 assert.Equal(m.t, constants.FormatLD1String, *input.Item[versAttr].S) 100 101 assert.NotNil(m.t, input.Item[lockAttr], "%s should have been present", lockAttr) 102 assert.NotNil(m.t, input.Item[lockAttr].B, "lock should have been a blob: %+v", input.Item[lockAttr]) 103 lock := input.Item[lockAttr].B 104 105 assert.NotNil(m.t, input.Item[rootAttr], "%s should have been present", rootAttr) 106 assert.NotNil(m.t, input.Item[rootAttr].B, "root should have been a blob: %+v", input.Item[rootAttr]) 107 root := input.Item[rootAttr].B 108 109 specs := "" 110 if attr, present := input.Item[tableSpecsAttr]; present { 111 assert.NotNil(m.t, attr.S, "specs should have been a String: %+v", input.Item[tableSpecsAttr]) 112 specs = *attr.S 113 } 114 115 apps := "" 116 if attr, present := input.Item[appendixAttr]; present { 117 assert.NotNil(m.t, attr.S, "appendix specs should have been a String: %+v", input.Item[appendixAttr]) 118 apps = *attr.S 119 } 120 121 mustNotExist := *(input.ConditionExpression) == valueNotExistsOrEqualsExpression 122 current, present := m.data[key] 123 124 if mustNotExist && present { 125 return nil, mockAWSError("ConditionalCheckFailedException") 126 } else if !mustNotExist && !checkCondition(current.(record), input.ExpressionAttributeValues) { 127 return nil, mockAWSError("ConditionalCheckFailedException") 128 } 129 130 m.putRecord(key, lock, root, constants.FormatLD1String, specs, apps) 131 132 atomic.AddInt64(&m.numPuts, 1) 133 return &dynamodb.PutItemOutput{}, nil 134 } 135 136 func checkCondition(current record, expressionAttrVals map[string]*dynamodb.AttributeValue) bool { 137 return current.vers == *expressionAttrVals[versExpressionValuesKey].S && bytes.Equal(current.lock, expressionAttrVals[prevLockExpressionValuesKey].B) 138 139 } 140 141 func (m *fakeDDB) NumGets() int64 { 142 return atomic.LoadInt64(&m.numGets) 143 }