Skip to content

Commit

Permalink
Lab 7 - Errors
Browse files Browse the repository at this point in the history
  • Loading branch information
Kasun authored and Kasun committed Aug 9, 2019
1 parent 2e532bd commit 7d37597
Show file tree
Hide file tree
Showing 9 changed files with 493 additions and 0 deletions.
25 changes: 25 additions & 0 deletions 07_errors/kasunfdo/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import "fmt"

const (
ErrInvalidVal = 400
ErrNotFound = 404
ErrInternal = 500
ErrOpenDB = 501
)

var ErrStr = map[int]string{
ErrInvalidVal: "value cannot be negative",
ErrNotFound: "puppy with id=%v not found",
ErrInternal: "internal system error",
ErrOpenDB: "error occurred while opening db %v",
}

func NewError(code int, args ...interface{}) *Error {
return &Error{Code: code, Message: fmt.Sprintf(ErrStr[code], args...)}
}

func (e *Error) Error() string {
return fmt.Sprintf("ERROR %v:%v", e.Code, e.Message)
}
154 changes: 154 additions & 0 deletions 07_errors/kasunfdo/leveldbstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package main

import (
"encoding/json"
"log"
"strconv"

"github.com/sirupsen/logrus"
"github.com/syndtr/goleveldb/leveldb"
)

func NewLeveldbStore(path, dataFileName string) (*LeveldbStore, error) {
store := &LeveldbStore{
count: 0,
path: path,
dataFileName: dataFileName,
indexFileName: "index.db", indexName: "last_index"}

db, err := openDB(store.path+"/"+store.indexFileName, true)
if err != nil {
return store, err
}
defer closeDB(db)

exists, err := db.Has([]byte(store.indexName), nil)
if !exists {
log.Printf("Initialized store with index: %v\n", store.count)
return store, nil
}
logrus.Debug(err)

value, err := db.Get([]byte(store.indexName), nil)
logrus.Debug(err)

store.count, err = strconv.ParseUint(string(value), 10, 64)
log.Printf("Initialized store with index: %v", store.count)

return store, err
}

func (l *LeveldbStore) CreatePuppy(puppy Puppy) (uint64, error) {
if puppy.Value < 0 {
return 0, NewError(ErrInvalidVal)
}

dataDB, err := openDB(l.path+"/"+l.dataFileName, false)
if err != nil {
return 0, err
}
defer closeDB(dataDB)

indexDB, err := openDB(l.path+"/"+l.indexFileName, false)
if err != nil {
return 0, err
}
defer closeDB(indexDB)

l.count++
puppy.ID = l.count

encodedPuppy, err := json.Marshal(puppy)
logrus.Debug(err)

err = dataDB.Put([]byte(strconv.FormatUint(puppy.ID, 10)), encodedPuppy, nil)
logrus.Debug(err)

err = indexDB.Put([]byte(l.indexName), []byte(strconv.FormatUint(l.count, 10)), nil)
logrus.Debug(err)

return puppy.ID, nil
}

func (l *LeveldbStore) ReadPuppy(id uint64) (Puppy, error) {
db, err := openDB(l.path+"/"+l.dataFileName, true)
if err != nil {
return Puppy{}, err
}
defer closeDB(db)

encoded, err := db.Get([]byte(strconv.FormatUint(id, 10)), nil)
if err != nil {
return Puppy{}, NewError(ErrNotFound, id)
}

puppy := &Puppy{}
err = json.Unmarshal(encoded, puppy)
logrus.Debug(err)

return *puppy, nil
}

func (l *LeveldbStore) UpdatePuppy(id uint64, puppy Puppy) error {
if puppy.Value < 0 {
return NewError(ErrInvalidVal)
}

db, err := openDB(l.path+"/"+l.dataFileName, false)
if err != nil {
return err
}
defer closeDB(db)

exists, err := db.Has([]byte(strconv.FormatUint(id, 10)), nil)
if !exists {
return NewError(ErrNotFound, id)
}
logrus.Debug(err)

puppy.ID = id
encodedPuppy, err := json.Marshal(puppy)
logrus.Debug(err)

err = db.Put([]byte(strconv.FormatUint(puppy.ID, 10)), encodedPuppy, nil)
logrus.Debug(err)

return nil
}

func (l *LeveldbStore) DeletePuppy(id uint64) error {
db, err := openDB(l.path+"/"+l.dataFileName, false)
if err != nil {
return err
}
defer closeDB(db)

exists, err := db.Has([]byte(strconv.FormatUint(id, 10)), nil)
if !exists {
return NewError(ErrNotFound, id)
}
logrus.Debug(err)

err = db.Delete([]byte(strconv.FormatUint(id, 10)), nil)
logrus.Debug(err)

return nil
}

func openDB(dbPath string, isReadOnly bool) (*leveldb.DB, error) {
db, err := leveldb.OpenFile(dbPath, nil)
if err != nil {
return nil, NewError(ErrOpenDB, dbPath)
}

if isReadOnly {
err = db.SetReadOnly()
logrus.Debug(err)
}
return db, nil
}

func closeDB(db *leveldb.DB) {
err := db.Close()
logrus.Debug(err)
}
60 changes: 60 additions & 0 deletions 07_errors/kasunfdo/leveldbstore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewLeveldbStoreErrOpen(t *testing.T) {
os.RemoveAll("./test")
_, err := NewLeveldbStore("", "")
assert.Equal(t, "ERROR 501:error occurred while opening db /index.db", err.Error())
}

func TestNewLeveldbStoreInitialize(t *testing.T) {
store, _ := NewLeveldbStore("test", "puppy.db")
_, _ = store.CreatePuppy(Puppy{Breed: "Labrador", Colour: "Cream", Value: 2999.99})
store, _ = NewLeveldbStore("test", "puppy.db")
id, _ := store.CreatePuppy(Puppy{Breed: "Labrador", Colour: "Cream", Value: 2999.99})

assert.True(t, id == 2)
os.RemoveAll("./test")
}

func TestLeveldbStoreCreatePuppyErrOpen(t *testing.T) {
store := LeveldbStore{path: ""}
_, err := store.CreatePuppy(Puppy{})
assert.Equal(t, "ERROR 501:error occurred while opening db /", err.Error())

store.path = "test"
_, err = store.CreatePuppy(Puppy{})
assert.Equal(t, "ERROR 501:error occurred while opening db test/", err.Error())

os.RemoveAll("./test")
}

func TestLeveldbStoreReadPuppyErrOpen(t *testing.T) {
store := LeveldbStore{path: ""}
_, err := store.ReadPuppy(1)
assert.Equal(t, "ERROR 501:error occurred while opening db /", err.Error())

os.RemoveAll("./test")
}

func TestLeveldbStoreUpdatePuppyErrOpen(t *testing.T) {
store := LeveldbStore{path: ""}
err := store.UpdatePuppy(1, Puppy{})
assert.Equal(t, "ERROR 501:error occurred while opening db /", err.Error())

os.RemoveAll("./test")
}

func TestLeveldbStoreDeletePuppyErrOpen(t *testing.T) {
store := LeveldbStore{path: ""}
err := store.DeletePuppy(1)
assert.Equal(t, "ERROR 501:error occurred while opening db /", err.Error())

os.RemoveAll("./test")
}
44 changes: 44 additions & 0 deletions 07_errors/kasunfdo/mapstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

func NewMapStore() *MapStore {
return &MapStore{count: 0, store: map[uint64]Puppy{}}
}

func (m *MapStore) CreatePuppy(puppy Puppy) (uint64, error) {
if puppy.Value < 0 {
return 0, NewError(ErrInvalidVal)
}
m.count++
puppy.ID = m.count
m.store[puppy.ID] = puppy
return puppy.ID, nil
}

func (m *MapStore) ReadPuppy(id uint64) (Puppy, error) {
puppy, exists := m.store[id]
if exists {
return puppy, nil
}
return puppy, NewError(ErrNotFound, id)
}

func (m *MapStore) UpdatePuppy(id uint64, puppy Puppy) error {
if puppy.Value < 0 {
return NewError(ErrInvalidVal)
}

if _, exists := m.store[id]; exists {
puppy.ID = id
m.store[id] = puppy
return nil
}
return NewError(ErrNotFound, id)
}

func (m *MapStore) DeletePuppy(id uint64) error {
if _, exists := m.store[id]; exists {
delete(m.store, id)
return nil
}
return NewError(ErrNotFound, id)
}
99 changes: 99 additions & 0 deletions 07_errors/kasunfdo/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"fmt"
"os"
"testing"

"github.com/stretchr/testify/suite"
)

type StorerTest struct {
suite.Suite
store Storer
id uint64
}

func (suite *StorerTest) SetupTest() {
os.RemoveAll("./test")
suite.id, _ = suite.store.CreatePuppy(Puppy{Breed: "Labrador", Colour: "Cream", Value: 2999.99})
}

func (suite *StorerTest) TearDownSuite() {
os.RemoveAll("./test")
}

func (suite *StorerTest) TestCreatePuppy() {
id, err := suite.store.CreatePuppy(Puppy{Breed: "German Shepard", Colour: "Brown", Value: 3499.99})
suite.True(id > 1)
suite.Nil(err)

id, err = suite.store.CreatePuppy(Puppy{Breed: "Terrier", Colour: "White", Value: -3499.99})
suite.True(id == 0)
suite.Equal("ERROR 400:value cannot be negative", err.Error())
}

func (suite *StorerTest) TestReadPuppy() {
puppy, err := suite.store.ReadPuppy(suite.id)

suite.Nil(err)
suite.Equal(puppy.ID, suite.id)
suite.Equal(puppy.Breed, "Labrador")
suite.Equal(puppy.Colour, "Cream")
suite.Equal(puppy.Value, 2999.99)

_, err = suite.store.ReadPuppy(100)
suite.Equal("ERROR 404:puppy with id=100 not found", err.Error())
}

func (suite *StorerTest) TestUpdatePuppy() {
err := suite.store.UpdatePuppy(suite.id, Puppy{Breed: "Labrador Retriever", Colour: "Brown", Value: 3999.99})

suite.Nil(err)
puppy, err := suite.store.ReadPuppy(suite.id)

suite.Nil(err)
suite.Equal(puppy.ID, suite.id)
suite.Equal(puppy.Breed, "Labrador Retriever")
suite.Equal(puppy.Colour, "Brown")
suite.Equal(puppy.Value, 3999.99)

err = suite.store.UpdatePuppy(suite.id, Puppy{Breed: "Poodle", Colour: "White", Value: -1999.99})
suite.Equal("ERROR 400:value cannot be negative", err.Error())

err = suite.store.UpdatePuppy(100, Puppy{Breed: "Poodle", Colour: "White", Value: 1999.99})
suite.Equal("ERROR 404:puppy with id=100 not found", err.Error())
}

func (suite *StorerTest) TestDeletePuppy() {
err := suite.store.DeletePuppy(suite.id)
suite.Nil(err)

_, err = suite.store.ReadPuppy(suite.id)
suite.Equal(fmt.Sprintf("ERROR 404:puppy with id=%v not found", suite.id), err.Error())

err = suite.store.DeletePuppy(suite.id)
suite.Equal(fmt.Sprintf("ERROR 404:puppy with id=%v not found", suite.id), err.Error())
}

func TestMapStore(t *testing.T) {
s := StorerTest{
store: NewMapStore(),
}
suite.Run(t, &s)
}

func TestSyncStore(t *testing.T) {
s := StorerTest{
store: NewSyncStore(),
}
suite.Run(t, &s)
}

func TestLevelDBStore(t *testing.T) {
store, _ := NewLeveldbStore("./test", "puppy.db")
s := StorerTest{
store: store,
}
suite.Run(t, &s)
}
Loading

0 comments on commit 7d37597

Please sign in to comment.