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  }