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  }