github.com/adowair/kvdb@v0.0.0-20231101174258-ceca93b03596/kv/util_test.go (about)

     1  package kv
     2  
     3  import (
     4  	"io/fs"
     5  	"os"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  )
    11  
    12  type testcase struct {
    13  	name        string
    14  	initialData map[string]string
    15  	key         string
    16  	expError    error
    17  	targetEntry *Entry
    18  	finalData   map[string]string
    19  }
    20  
    21  func setupTestDB(t *testing.T, entries map[string]string) string {
    22  	dir := t.TempDir()
    23  	assert.NoError(t, os.Chdir(dir))
    24  	for key, value := range entries {
    25  		os.WriteFile(key, []byte(value), fs.ModePerm)
    26  	}
    27  	return dir
    28  }
    29  
    30  func checkTestDB(t *testing.T, path string, entries map[string]string) {
    31  	dir, err := os.ReadDir(path)
    32  	assert.NoError(t, err)
    33  	assert.Equal(t, len(entries), len(dir))
    34  	for key, data := range entries {
    35  		assert.FileExists(t, key)
    36  		actual, err := os.ReadFile(key)
    37  		assert.NoError(t, err)
    38  		assert.Equal(t, data, string(actual))
    39  	}
    40  }
    41  
    42  func TestRead(t *testing.T) {
    43  	for _, tc := range []testcase{
    44  		{
    45  			name:        "TestReadOK",
    46  			key:         "foo",
    47  			initialData: map[string]string{"foo": "0,0,bar"},
    48  			expError:    nil,
    49  			targetEntry: &Entry{
    50  				Value:       "bar",
    51  				FirstEdited: time.Unix(0, 0),
    52  				LastEdited:  time.Unix(0, 0),
    53  			},
    54  			finalData: map[string]string{"foo": "0,0,bar"},
    55  		},
    56  		{
    57  			name:        "TestReadEmptyValue",
    58  			key:         "foo",
    59  			initialData: map[string]string{"foo": "0,0,"},
    60  			expError:    ErrBadFormat,
    61  			targetEntry: nil,
    62  			finalData:   map[string]string{"foo": "0,0,"},
    63  		},
    64  		{
    65  			name:        "TestReadNotExist",
    66  			key:         "foo",
    67  			initialData: nil,
    68  			expError:    ErrNotExist,
    69  			targetEntry: nil,
    70  			finalData:   nil,
    71  		},
    72  		{
    73  			name:        "TestReadDelimiterInValue",
    74  			key:         "foo",
    75  			initialData: map[string]string{"foo": "0,0,ba,r"},
    76  			expError:    nil,
    77  			targetEntry: &Entry{
    78  				Value:       "ba,r",
    79  				FirstEdited: time.Unix(0, 0),
    80  				LastEdited:  time.Unix(0, 0),
    81  			},
    82  			finalData: map[string]string{"foo": "0,0,ba,r"},
    83  		},
    84  		{
    85  			name:        "TestReadNoValue",
    86  			key:         "foo",
    87  			initialData: map[string]string{"foo": "0,0"},
    88  			expError:    ErrBadFormat,
    89  			targetEntry: nil,
    90  			finalData:   map[string]string{"foo": "0,0"},
    91  		},
    92  		{
    93  			name:        "TestReadNothing",
    94  			key:         "foo",
    95  			initialData: map[string]string{"foo": ""},
    96  			expError:    ErrBadFormat,
    97  			targetEntry: nil,
    98  			finalData:   map[string]string{"foo": ""},
    99  		},
   100  		{
   101  			name:        "TestReadNotTimestamp",
   102  			key:         "foo",
   103  			initialData: map[string]string{"foo": "0,alpha,bar"},
   104  			expError:    ErrBadFormat,
   105  			targetEntry: nil,
   106  			finalData:   map[string]string{"foo": "0,alpha,bar"},
   107  		},
   108  	} {
   109  		t.Run(tc.name, func(t *testing.T) {
   110  			dir := setupTestDB(t, tc.initialData)
   111  
   112  			entry, err := read(tc.key)
   113  			assert.Equal(t, tc.targetEntry, entry)
   114  			assert.ErrorIs(t, err, tc.expError)
   115  
   116  			checkTestDB(t, dir, tc.finalData)
   117  		})
   118  	}
   119  }
   120  
   121  func TestWrite(t *testing.T) {
   122  	for _, tc := range []testcase{
   123  		{
   124  			name:        "TestWriteOK",
   125  			initialData: nil,
   126  			key:         "foo",
   127  			targetEntry: &Entry{
   128  				Value:       "bar",
   129  				FirstEdited: time.Unix(0, 0),
   130  				LastEdited:  time.Unix(1, 0),
   131  			},
   132  			expError:  nil,
   133  			finalData: map[string]string{"foo": "0,1,bar"},
   134  		},
   135  		{
   136  			name:        "TestOverWrite",
   137  			initialData: map[string]string{"foo": "0,0,bar"},
   138  			key:         "foo",
   139  			targetEntry: &Entry{
   140  				Value:       "baz",
   141  				FirstEdited: time.Unix(0, 0),
   142  				LastEdited:  time.Unix(1, 0),
   143  			},
   144  			expError:  nil,
   145  			finalData: map[string]string{"foo": "0,1,baz"},
   146  		},
   147  		{
   148  			name:        "TestWriteEmptyValue",
   149  			initialData: nil,
   150  			key:         "foo",
   151  			targetEntry: &Entry{
   152  				Value:       "",
   153  				FirstEdited: time.Unix(0, 0),
   154  				LastEdited:  time.Unix(1, 0),
   155  			},
   156  			expError:  ErrBadFormat,
   157  			finalData: nil,
   158  		},
   159  		{
   160  			name:        "TestOverWriteEmptyValue",
   161  			initialData: map[string]string{"foo": "0,0,bar"},
   162  			key:         "foo",
   163  			targetEntry: &Entry{
   164  				Value:       "",
   165  				FirstEdited: time.Unix(0, 0),
   166  				LastEdited:  time.Unix(1, 0),
   167  			},
   168  			expError:  ErrBadFormat,
   169  			finalData: map[string]string{"foo": "0,0,bar"},
   170  		},
   171  		{
   172  			name:        "TestWriteEmptyEntry",
   173  			initialData: nil,
   174  			key:         "foo",
   175  			targetEntry: &Entry{},
   176  			expError:    ErrBadFormat,
   177  			finalData:   nil,
   178  		},
   179  	} {
   180  		t.Run(tc.name, func(t *testing.T) {
   181  			dir := setupTestDB(t, tc.initialData)
   182  
   183  			err := write(tc.key, tc.targetEntry)
   184  			assert.ErrorIs(t, err, tc.expError)
   185  
   186  			checkTestDB(t, dir, tc.finalData)
   187  		})
   188  	}
   189  }
   190  
   191  func TestDelete(t *testing.T) {
   192  	for _, tc := range []testcase{
   193  		{
   194  			name:        "TestDeleteOK",
   195  			initialData: map[string]string{"foo": "0,1,bar"},
   196  			key:         "foo",
   197  			targetEntry: &Entry{
   198  				Value:       "bar",
   199  				FirstEdited: time.Unix(0, 0),
   200  				LastEdited:  time.Unix(1, 0),
   201  			},
   202  			expError:  nil,
   203  			finalData: nil,
   204  		},
   205  		{
   206  			name:        "TestIdempotentDelete",
   207  			initialData: nil,
   208  			key:         "foo",
   209  			targetEntry: &Entry{
   210  				Value:       "bar",
   211  				FirstEdited: time.Unix(0, 0),
   212  				LastEdited:  time.Unix(1, 0),
   213  			},
   214  			expError:  nil,
   215  			finalData: nil,
   216  		},
   217  	} {
   218  		t.Run(tc.name, func(t *testing.T) {
   219  			testDir := setupTestDB(t, tc.initialData)
   220  
   221  			err := delete(tc.key)
   222  			assert.ErrorIs(t, err, tc.expError)
   223  
   224  			checkTestDB(t, testDir, tc.finalData)
   225  		})
   226  	}
   227  }