Skip to content
This repository has been archived by the owner on Jul 8, 2024. It is now read-only.

Commit

Permalink
Improve error responses and tests
Browse files Browse the repository at this point in the history
- Add more context to errors
- Test invalid file contents Setup error
- Test writing and reading more complex map data to files
  • Loading branch information
calvinmclean committed May 25, 2023
1 parent 5c1e2bd commit 8d6f0a5
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 42 deletions.
9 changes: 7 additions & 2 deletions drivers/hashmap/hashmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,13 @@ func (db *Database) saveToLocalFile() error {
content, err = yaml.Marshal(db.data)
}
if err != nil {
return err
return fmt.Errorf("error marshalling data: %w", err)
}

return os.WriteFile(db.config.Filename, content, 0755)
err = os.WriteFile(db.config.Filename, content, 0755)
if err != nil {
return fmt.Errorf("error writing data to file %q: %w", db.config.Filename, err)
}

return nil
}
226 changes: 186 additions & 40 deletions drivers/hashmap/hashmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,27 @@ import (
"encoding/json"
"os"
"testing"

"gopkg.in/yaml.v3"
)

var fileTypeCases = []struct {
extension string
unmarshal func([]byte, interface{}) error
marshal func(interface{}) ([]byte, error)
}{
{
"yaml",
yaml.Unmarshal,
yaml.Marshal,
},
{
"json",
json.Unmarshal,
json.Marshal,
},
}

func TestSetupCreatesFileIfNotExist(t *testing.T) {
tests := []struct {
ext string
Expand Down Expand Up @@ -49,55 +68,44 @@ func TestDial(t *testing.T) {
}

func TestSaveToLocalFileAfterSet(t *testing.T) {
t.Run("SuccessfullySaveJSON", func(t *testing.T) {
filename := "testdata/save_test.json"
defer os.RemoveAll(filename)

db, err := Dial(Config{Filename: filename})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, tt := range fileTypeCases {
t.Run(tt.extension, func(t *testing.T) {
filename := "testdata/save_test." + tt.extension
defer os.RemoveAll(filename)

err = db.Setup()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
db, err := Dial(Config{Filename: filename})
if err != nil {
t.Errorf("unexpected error: %v", err)
}

err = db.Set("key", []byte("value"))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
err = db.Setup()
if err != nil {
t.Errorf("unexpected error: %v", err)
}

db.Close()
err = db.Set("key", []byte("value"))
if err != nil {
t.Errorf("unexpected error: %v", err)
}

data, err := os.ReadFile(filename)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
db.Close()

var parsedData map[string]ByteSlice
err = json.Unmarshal(data, &parsedData)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
data, err := readFile(filename, tt.unmarshal)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

result := string(parsedData["key"])
if result != "value" {
t.Errorf("unexpected value: %v", result)
}
})
result := string(data["key"])
if result != "value" {
t.Errorf("unexpected value: %v", result)
}
})
}
}

func TestLoadingFromExistingFile(t *testing.T) {
tests := []struct {
extension string
}{
{"json"},
{"yaml"},
}

for _, tt := range tests {
t.Run(tt.extension, func(t *testing.T) {
for _, tt := range fileTypeCases {
t.Run("ValidFileContents_"+tt.extension, func(t *testing.T) {
filename := "testdata/load_data_test." + tt.extension

db, err := Dial(Config{Filename: filename})
Expand All @@ -119,5 +127,143 @@ func TestLoadingFromExistingFile(t *testing.T) {
t.Errorf("unexpected value: %s", string(value))
}
})

t.Run("InvalidFileContents_"+tt.extension, func(t *testing.T) {
filename := "testdata/load_data_invalid_test." + tt.extension

db, err := Dial(Config{Filename: filename})
if err != nil {
t.Errorf("unexpected error: %v", err)
}

err = db.Setup()
if err == nil {
t.Error("expected error but was nil")
}

// different error expectations depending on format
var expectedErr string
switch tt.extension {
case "yaml":
expectedErr = "unable to unmarshal data from file: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `this is...` into map[string]hashmap.ByteSlice"
case "json":
expectedErr = "unable to unmarshal data from file: invalid character 'h' in literal true (expecting 'r')"
}

if err.Error() != expectedErr {
t.Errorf("error did not match: %v", err)
}
})
}
}

func TestComplexObjectSaveToFile(t *testing.T) {
data := map[string]interface{}{
"string": "string",
"integer": 1,
"float": 1.5,
"string_array": []string{"a", "b", "c"},
"integer_array": []int{1, 2, 3},
"object": map[string]interface{}{
"key": "value",
},
}

for _, tt := range fileTypeCases {
t.Run(tt.extension, func(t *testing.T) {
filename := "testdata/complex_data_test." + tt.extension
defer os.RemoveAll(filename)

db, err := Dial(Config{Filename: filename})
if err != nil {
t.Errorf("unexpected error: %v", err)
}

err = db.Setup()
if err != nil {
t.Errorf("unexpected error: %v", err)
}

// Save objects to DB as JSON and YAML
dataJSON, err := json.Marshal(data)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

err = db.Set("data_json", dataJSON)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

dataYAML, err := yaml.Marshal(data)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

err = db.Set("data_yaml", dataYAML)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
db.Close()

t.Run("CompareSavedFilesToExpectations", func(t *testing.T) {
data, err := readFile(filename, tt.unmarshal)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

expectedData, err := readFile("testdata/complex_data_test_expected."+tt.extension, tt.unmarshal)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

if string(data["data"]) != string(expectedData["data"]) {
t.Errorf("data did not match: %v", string(data["data"]))
}
})

t.Run("RecreateDBAndGetData", func(t *testing.T) {
db, err := Dial(Config{Filename: filename})
if err != nil {
t.Errorf("unexpected error: %v", err)
}

err = db.Setup()
if err != nil {
t.Errorf("unexpected error: %v", err)
}

newDataJSON, err := db.Get("data_json")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if string(newDataJSON) != string(dataJSON) {
t.Errorf("data did not match after reading from file: %s", string(newDataJSON))
}

newDataYAML, err := db.Get("data_yaml")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if string(newDataYAML) != string(dataYAML) {
t.Errorf("data did not match after reading from file: %s", string(newDataYAML))
}
})
})
}
}

func readFile(filename string, unmarshal func([]byte, interface{}) error) (map[string]ByteSlice, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}

var parsedData map[string]ByteSlice
err = unmarshal(data, &parsedData)
if err != nil {
return nil, err
}

return parsedData, nil
}
1 change: 1 addition & 0 deletions drivers/hashmap/testdata/complex_data_test_expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"data_json":"eyJmbG9hdCI6MS41LCJpbnRlZ2VyIjoxLCJpbnRlZ2VyX2FycmF5IjpbMSwyLDNdLCJvYmplY3QiOnsia2V5IjoidmFsdWUifSwic3RyaW5nIjoic3RyaW5nIiwic3RyaW5nX2FycmF5IjpbImEiLCJiIiwiYyJdfQ==","data_yaml":"ZmxvYXQ6IDEuNQppbnRlZ2VyOiAxCmludGVnZXJfYXJyYXk6CiAgICAtIDEKICAgIC0gMgogICAgLSAzCm9iamVjdDoKICAgIGtleTogdmFsdWUKc3RyaW5nOiBzdHJpbmcKc3RyaW5nX2FycmF5OgogICAgLSBhCiAgICAtIGIKICAgIC0gYwo="}
15 changes: 15 additions & 0 deletions drivers/hashmap/testdata/complex_data_test_expected.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
data_json: '{"float":1.5,"integer":1,"integer_array":[1,2,3],"object":{"key":"value"},"string":"string","string_array":["a","b","c"]}'
data_yaml: |
float: 1.5
integer: 1
integer_array:
- 1
- 2
- 3
object:
key: value
string: string
string_array:
- a
- b
- c
1 change: 1 addition & 0 deletions drivers/hashmap/testdata/load_data_invalid_test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is not JSON
1 change: 1 addition & 0 deletions drivers/hashmap/testdata/load_data_invalid_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is not YAML

0 comments on commit 8d6f0a5

Please sign in to comment.