Forked from github.com/sdomino/scribble
This commit is contained in:
parent
8f0b323da1
commit
8ce8dfb12e
3
LICENSE
3
LICENSE
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 Steve Domino
|
Copyright (c) 2019 Steve Domino, Atlas Cove
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
10
README.md
10
README.md
@ -1,18 +1,18 @@
|
|||||||
Scribble [![GoDoc](https://godoc.org/github.com/boltdb/bolt?status.svg)](http://godoc.org/github.com/sdomino/scribble) [![Go Report Card](https://goreportcard.com/badge/github.com/sdomino/scribble)](https://goreportcard.com/report/github.com/sdomino/scribble)
|
Scratch [![GoDoc](https://godoc.org/github.com/boltdb/bolt?status.svg)](http://godoc.org/github.com/sdomino/scribble) [![Go Report Card](https://goreportcard.com/badge/github.com/sdomino/scribble)](https://goreportcard.com/report/github.com/sdomino/scribble)
|
||||||
--------
|
--------
|
||||||
|
|
||||||
A tiny JSON database in Golang
|
A tiny BSON database in Golang, a drop-in replacement for [scribble](https://github.com/sdomino/scribble).
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
Install using `go get github.com/sdomino/scribble`.
|
Install using `go get git.sdf.org/Atlas48/scratch`.
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// a new scribble driver, providing the directory where it will be writing to,
|
// a new scratch driver, providing the directory where it will be writing to,
|
||||||
// and a qualified logger if desired
|
// and a qualified logger if desired
|
||||||
db, err := scribble.New(dir, nil)
|
db, err := scratch.New(dir, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error", err)
|
fmt.Println("Error", err)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// Package scribble is a tiny JSON database
|
// Package scratch is a tiny, flat-file BSON database
|
||||||
package scribble
|
package scratch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"gopkg.in/mgo.v2/bson"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -10,18 +9,14 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jcelliott/lumber"
|
"github.com/jcelliott/lumber"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the current version of the project
|
// Version is the current version of the project
|
||||||
const Version = "1.0.4"
|
const Version = "1.0.4"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrMissingResource = errors.New("missing resource - unable to save record")
|
ErrMissingResource = errors.New("missing resource - unable to save record")
|
||||||
ErrMissingCollection = errors.New("missing collection - no place to save record")
|
ErrMissingCollection = errors.New("missing collection - no place to save record")
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Logger is a generic logger interface
|
// Logger is a generic logger interface
|
||||||
Logger interface {
|
Logger interface {
|
||||||
@ -32,230 +27,169 @@ type (
|
|||||||
Debug(string, ...interface{})
|
Debug(string, ...interface{})
|
||||||
Trace(string, ...interface{})
|
Trace(string, ...interface{})
|
||||||
}
|
}
|
||||||
|
// Driver is what is used to interact with the scratch database. It runs
|
||||||
// Driver is what is used to interact with the scribble database. It runs
|
|
||||||
// transactions, and provides log output
|
// transactions, and provides log output
|
||||||
Driver struct {
|
Driver struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
mutexes map[string]*sync.Mutex
|
mutexes map[string]*sync.Mutex
|
||||||
dir string // the directory where scribble will create the database
|
dir string // the directory where scratch will create the database
|
||||||
log Logger // the logger scribble will log to
|
log Logger // the logger scratch will log to
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
// Options uses for specification of working golang-scratch
|
||||||
// Options uses for specification of working golang-scribble
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Logger // the logger scribble will use (configurable)
|
Logger // the logger scratch will use (configurable)
|
||||||
}
|
}
|
||||||
|
// New creates a new scratch database at the desired directory location, and
|
||||||
// New creates a new scribble database at the desired directory location, and
|
|
||||||
// returns a *Driver to then use for interacting with the database
|
// returns a *Driver to then use for interacting with the database
|
||||||
func New(dir string, options *Options) (*Driver, error) {
|
func New(dir string, options *Options) (*Driver, error) {
|
||||||
|
|
||||||
//
|
//
|
||||||
dir = filepath.Clean(dir)
|
dir = filepath.Clean(dir)
|
||||||
|
|
||||||
// create default options
|
// create default options
|
||||||
opts := Options{}
|
opts := Options{}
|
||||||
|
|
||||||
// if options are passed in, use those
|
// if options are passed in, use those
|
||||||
if options != nil {
|
if options != nil { opts = *options }
|
||||||
opts = *options
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no logger is provided, create a default
|
// if no logger is provided, create a default
|
||||||
if opts.Logger == nil {
|
if opts.Logger == nil {
|
||||||
opts.Logger = lumber.NewConsoleLogger(lumber.INFO)
|
opts.Logger = lumber.NewConsoleLogger(lumber.INFO)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
driver := Driver{
|
driver := Driver{
|
||||||
dir: dir,
|
dir: dir,
|
||||||
mutexes: make(map[string]*sync.Mutex),
|
mutexes: make(map[string]*sync.Mutex),
|
||||||
log: opts.Logger,
|
log: opts.Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the database already exists, just use it
|
// if the database already exists, just use it
|
||||||
if _, err := os.Stat(dir); err == nil {
|
if _, err := os.Stat(dir); err == nil {
|
||||||
opts.Logger.Debug("Using '%s' (database already exists)\n", dir)
|
opts.Logger.Debug("Using '%s' (database already exists)\n", dir)
|
||||||
return &driver, nil
|
return &driver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the database doesn't exist create it
|
// if the database doesn't exist create it
|
||||||
opts.Logger.Debug("Creating scribble database at '%s'...\n", dir)
|
opts.Logger.Debug("Creating scratch database at '%s'...\n", dir)
|
||||||
return &driver, os.MkdirAll(dir, 0755)
|
return &driver, os.MkdirAll(dir, 0755)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write locks the database and attempts to write the record to the database under
|
// Write locks the database and attempts to write the record to the database under
|
||||||
// the [collection] specified with the [resource] name given
|
// the [collection] specified with the [resource] name given
|
||||||
func (d *Driver) Write(collection, resource string, v interface{}) error {
|
func (d *Driver) Write(collection, resource string, v interface{}) error {
|
||||||
|
|
||||||
// ensure there is a place to save record
|
// ensure there is a place to save record
|
||||||
if collection == "" {
|
if collection == "" {
|
||||||
return ErrMissingCollection
|
return ErrMissingCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure there is a resource (name) to save record as
|
// ensure there is a resource (name) to save record as
|
||||||
if resource == "" {
|
if resource == "" {
|
||||||
return ErrMissingResource
|
return ErrMissingResource
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex := d.getOrCreateMutex(collection)
|
mutex := d.getOrCreateMutex(collection)
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
||||||
//
|
//
|
||||||
dir := filepath.Join(d.dir, collection)
|
dir := filepath.Join(d.dir, collection)
|
||||||
fnlPath := filepath.Join(dir, resource+".json")
|
fnlPath := filepath.Join(dir, resource+".bson")
|
||||||
tmpPath := fnlPath + ".tmp"
|
tmpPath := fnlPath + ".tmp"
|
||||||
|
|
||||||
return write(dir, tmpPath, fnlPath, v)
|
return write(dir, tmpPath, fnlPath, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func write(dir, tmpPath, dstPath string, v interface{}) error {
|
func write(dir, tmpPath, dstPath string, v interface{}) error {
|
||||||
|
|
||||||
// create collection directory
|
// create collection directory
|
||||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshal the pointer to a non-struct and indent with tab
|
// marshal the pointer to a non-struct and indent with tab
|
||||||
b, err := json.MarshalIndent(v, "", "\t")
|
b, err := bson.Marshal(v, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// write marshaled data to the temp file
|
// write marshaled data to the temp file
|
||||||
if err := ioutil.WriteFile(tmpPath, b, 0644); err != nil {
|
if err := ioutil.WriteFile(tmpPath, b, 0644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// move final file into place
|
// move final file into place
|
||||||
return os.Rename(tmpPath, dstPath)
|
return os.Rename(tmpPath, dstPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a record from the database
|
// Read a record from the database
|
||||||
func (d *Driver) Read(collection, resource string, v interface{}) error {
|
func (d *Driver) Read(collection, resource string, v interface{}) error {
|
||||||
|
|
||||||
// ensure there is a place to save record
|
// ensure there is a place to save record
|
||||||
if collection == "" {
|
if collection == "" {
|
||||||
return ErrMissingCollection
|
return ErrMissingCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure there is a resource (name) to save record as
|
// ensure there is a resource (name) to save record as
|
||||||
if resource == "" {
|
if resource == "" {
|
||||||
return ErrMissingResource
|
return ErrMissingResource
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
record := filepath.Join(d.dir, collection, resource)
|
record := filepath.Join(d.dir, collection, resource)
|
||||||
|
|
||||||
// check to see if file exists
|
// check to see if file exists
|
||||||
if _, err := stat(record); err != nil {
|
if _, err := stat(record); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// read record from database
|
// read record from database
|
||||||
return read(record, v)
|
return read(record, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func read(record string, v interface{}) error {
|
func read(record string, v interface{}) error {
|
||||||
|
b, err := ioutil.ReadFile(record + ".bson")
|
||||||
b, err := ioutil.ReadFile(record + ".json")
|
if err != nil { return err }
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// unmarshal data
|
// unmarshal data
|
||||||
return json.Unmarshal(b, &v)
|
return bson.Unmarshal(b, &v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAll records from a collection; this is returned as a slice of strings because
|
// ReadAll records from a collection; this is returned as a slice of strings because
|
||||||
// there is no way of knowing what type the record is.
|
// there is no way of knowing what type the record is.
|
||||||
func (d *Driver) ReadAll(collection string) ([][]byte, error) {
|
func (d *Driver) ReadAll(collection string) ([][]byte, error) {
|
||||||
|
|
||||||
// ensure there is a collection to read
|
// ensure there is a collection to read
|
||||||
if collection == "" {
|
if collection == "" {
|
||||||
return nil, ErrMissingCollection
|
return nil, ErrMissingCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
dir := filepath.Join(d.dir, collection)
|
dir := filepath.Join(d.dir, collection)
|
||||||
os.MkdirAll(dir, 0777)
|
os.MkdirAll(dir, 0777)
|
||||||
|
|
||||||
// read all the files in the transaction.Collection; an error here just means
|
// read all the files in the transaction.Collection; an error here just means
|
||||||
// the collection is either empty or doesn't exist
|
// the collection is either empty or doesn't exist
|
||||||
files, err := ioutil.ReadDir(dir)
|
files, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil { return nil, err }
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// read all the files in the transaction.Collection; an error here just means
|
// read all the files in the transaction.Collection; an error here just means
|
||||||
// the collection is either empty or doesn't exist
|
// the collection is either empty or doesn't exist
|
||||||
files, err := ioutil.ReadDir(dir)
|
files, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil { return nil, err }
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return readAll(files, dir)
|
return readAll(files, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readAll(files []os.FileInfo, dir string) ([][]byte, error) {
|
func readAll(files []os.FileInfo, dir string) ([][]byte, error) {
|
||||||
// the files read from the database
|
// the files read from the database
|
||||||
var records [][]byte
|
var records [][]byte
|
||||||
|
|
||||||
// iterate over each of the files, attempting to read the file. If successful
|
// iterate over each of the files, attempting to read the file. If successful
|
||||||
// append the files to the collection of read
|
// append the files to the collection of read
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
|
|
||||||
b, err := ioutil.ReadFile(filepath.Join(dir, file.Name()))
|
b, err := ioutil.ReadFile(filepath.Join(dir, file.Name()))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// append read file
|
// append read file
|
||||||
records = append(records, b)
|
records = append(records, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarhsal the read files as a comma delimeted byte array
|
// unmarhsal the read files as a comma delimeted byte array
|
||||||
return records, nil
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// List ID of records from a collection; this is returned as a slice of strings.
|
// List ID of records from a collection; this is returned as a slice of strings.
|
||||||
func (d *Driver) List(collection string) ([]string, error) {
|
func (d *Driver) List(collection string) ([]string, error) {
|
||||||
|
|
||||||
// ensure there is a collection to read
|
// ensure there is a collection to read
|
||||||
if collection == "" {
|
if collection == "" {
|
||||||
return nil, ErrMissingCollection
|
return nil, ErrMissingCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
dir := filepath.Join(d.dir, collection)
|
dir := filepath.Join(d.dir, collection)
|
||||||
|
|
||||||
// check to see if collection (directory) exists
|
// check to see if collection (directory) exists
|
||||||
//if _, err := stat(dir); err != nil {
|
//if _, err := stat(dir); err != nil {
|
||||||
// return nil, err
|
// return nil, err
|
||||||
//}
|
//}
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(dir)
|
files, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil { return nil, err }
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// the IDs of collection
|
// the IDs of collection
|
||||||
var recordsIDs []string
|
var recordsIDs []string
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
name := file.Name()
|
name := file.Name()
|
||||||
if strings.HasSuffix(name, ".json") && !strings.HasPrefix(name, ".#") {
|
if strings.HasSuffix(name, ".bson") && !strings.HasPrefix(name, ".#") {
|
||||||
recordsIDs = append(recordsIDs, name[:len(name)-5])
|
recordsIDs = append(recordsIDs, name[:len(name)-5])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return recordsIDs, nil
|
return recordsIDs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete locks that database and then attempts to remove the collection/resource
|
// Delete locks that database and then attempts to remove the collection/resource
|
||||||
// specified by [path]
|
// specified by [path]
|
||||||
func (d *Driver) Delete(collection, resource string) error {
|
func (d *Driver) Delete(collection, resource string) error {
|
||||||
@ -264,53 +198,39 @@ func (d *Driver) Delete(collection, resource string) error {
|
|||||||
mutex := d.getOrCreateMutex(collection)
|
mutex := d.getOrCreateMutex(collection)
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
||||||
//
|
//
|
||||||
dir := filepath.Join(d.dir, path)
|
dir := filepath.Join(d.dir, path)
|
||||||
|
|
||||||
switch fi, err := stat(dir); {
|
switch fi, err := stat(dir); {
|
||||||
|
|
||||||
// if fi is nil or error is not nil return
|
// if fi is nil or error is not nil return
|
||||||
case fi == nil, err != nil:
|
case fi == nil, err != nil:
|
||||||
return fmt.Errorf("Unable to find file or directory named %v\n", path)
|
return fmt.Errorf("Unable to find file or directory named %v\n", path)
|
||||||
|
|
||||||
// remove directory and all contents
|
// remove directory and all contents
|
||||||
case fi.Mode().IsDir():
|
case fi.Mode().IsDir():
|
||||||
return os.RemoveAll(dir)
|
return os.RemoveAll(dir)
|
||||||
|
|
||||||
// remove file
|
// remove file
|
||||||
case fi.Mode().IsRegular():
|
case fi.Mode().IsRegular():
|
||||||
return os.RemoveAll(dir + ".json")
|
return os.RemoveAll(dir + ".bson")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
func stat(path string) (fi os.FileInfo, err error) {
|
func stat(path string) (fi os.FileInfo, err error) {
|
||||||
|
|
||||||
// check for dir, if path isn't a directory check to see if it's a file
|
// check for dir, if path isn't a directory check to see if it's a file
|
||||||
if fi, err = os.Stat(path); os.IsNotExist(err) {
|
if fi, err = os.Stat(path); os.IsNotExist(err) {
|
||||||
fi, err = os.Stat(path + ".json")
|
fi, err = os.Stat(path + ".bson")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOrCreateMutex creates a new collection specific mutex any time a collection
|
// getOrCreateMutex creates a new collection specific mutex any time a collection
|
||||||
// is being modified to avoid unsafe operations
|
// is being modified to avoid unsafe operations
|
||||||
func (d *Driver) getOrCreateMutex(collection string) *sync.Mutex {
|
func (d *Driver) getOrCreateMutex(collection string) *sync.Mutex {
|
||||||
|
|
||||||
d.mutex.Lock()
|
d.mutex.Lock()
|
||||||
defer d.mutex.Unlock()
|
defer d.mutex.Unlock()
|
||||||
|
|
||||||
m, ok := d.mutexes[collection]
|
m, ok := d.mutexes[collection]
|
||||||
|
|
||||||
// if the mutex doesn't exist make it
|
// if the mutex doesn't exist make it
|
||||||
if !ok {
|
if !ok {
|
||||||
m = &sync.Mutex{}
|
m = &sync.Mutex{}
|
||||||
d.mutexes[collection] = m
|
d.mutexes[collection] = m
|
||||||
}
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
@ -1,16 +1,11 @@
|
|||||||
package scribble
|
package scratch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
|
||||||
type Fish struct {
|
type Fish struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
var (
|
var (
|
||||||
db *Driver
|
db *Driver
|
||||||
database = "./deep/school"
|
database = "./deep/school"
|
||||||
@ -20,169 +15,118 @@ var (
|
|||||||
redfish = Fish{Type: "red"}
|
redfish = Fish{Type: "red"}
|
||||||
bluefish = Fish{Type: "blue"}
|
bluefish = Fish{Type: "blue"}
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
|
||||||
// remove any thing for a potentially failed previous test
|
// remove any thing for a potentially failed previous test
|
||||||
os.RemoveAll("./deep")
|
os.RemoveAll("./deep")
|
||||||
|
|
||||||
// run
|
// run
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
os.RemoveAll("./deep")
|
os.RemoveAll("./deep")
|
||||||
|
|
||||||
// exit
|
// exit
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests creating a new database, and using an existing database
|
// Tests creating a new database, and using an existing database
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
|
|
||||||
// database should not exist
|
// database should not exist
|
||||||
if _, err := os.Stat(database); err == nil {
|
if _, err := os.Stat(database); err == nil {
|
||||||
t.Error("Expected nothing, got database")
|
t.Error("Expected nothing, got database")
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new database
|
// create a new database
|
||||||
createDB()
|
createDB()
|
||||||
|
|
||||||
// database should exist
|
// database should exist
|
||||||
if _, err := os.Stat(database); err != nil {
|
if _, err := os.Stat(database); err != nil {
|
||||||
t.Error("Expected database, got nothing")
|
t.Error("Expected database, got nothing")
|
||||||
}
|
}
|
||||||
|
|
||||||
// should use existing database
|
// should use existing database
|
||||||
createDB()
|
createDB()
|
||||||
|
|
||||||
// database should exist
|
// database should exist
|
||||||
if _, err := os.Stat(database); err != nil {
|
if _, err := os.Stat(database); err != nil {
|
||||||
t.Error("Expected database, got nothing")
|
t.Error("Expected database, got nothing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
func TestWriteAndRead(t *testing.T) {
|
func TestWriteAndRead(t *testing.T) {
|
||||||
|
|
||||||
createDB()
|
createDB()
|
||||||
|
|
||||||
// add fish to database
|
// add fish to database
|
||||||
if err := db.Write(collection, "redfish", redfish); err != nil {
|
if err := db.Write(collection, "redfish", redfish); err != nil {
|
||||||
t.Error("Create fish failed: ", err.Error())
|
t.Error("Create fish failed: ", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// read fish from database
|
// read fish from database
|
||||||
if err := db.Read(collection, "redfish", &onefish); err != nil {
|
if err := db.Read(collection, "redfish", &onefish); err != nil {
|
||||||
t.Error("Failed to read: ", err.Error())
|
t.Error("Failed to read: ", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
if onefish.Type != "red" {
|
if onefish.Type != "red" {
|
||||||
t.Error("Expected red fish, got: ", onefish.Type)
|
t.Error("Expected red fish, got: ", onefish.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
destroySchool()
|
destroySchool()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
func TestReadall(t *testing.T) {
|
func TestReadall(t *testing.T) {
|
||||||
|
|
||||||
createDB()
|
createDB()
|
||||||
createSchool()
|
createSchool()
|
||||||
|
|
||||||
fish, err := db.ReadAll(collection)
|
fish, err := db.ReadAll(collection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Failed to read: ", err.Error())
|
t.Error("Failed to read: ", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(fish) <= 0 {
|
if len(fish) <= 0 {
|
||||||
t.Error("Expected some fish, have none")
|
t.Error("Expected some fish, have none")
|
||||||
}
|
}
|
||||||
|
|
||||||
destroySchool()
|
destroySchool()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
func TestWriteAndReadEmpty(t *testing.T) {
|
func TestWriteAndReadEmpty(t *testing.T) {
|
||||||
|
|
||||||
createDB()
|
createDB()
|
||||||
|
|
||||||
// create a fish with no home
|
// create a fish with no home
|
||||||
if err := db.Write("", "redfish", redfish); err == nil {
|
if err := db.Write("", "redfish", redfish); err == nil {
|
||||||
t.Error("Allowed write of empty resource", err.Error())
|
t.Error("Allowed write of empty resource", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a home with no fish
|
// create a home with no fish
|
||||||
if err := db.Write(collection, "", redfish); err == nil {
|
if err := db.Write(collection, "", redfish); err == nil {
|
||||||
t.Error("Allowed write of empty resource", err.Error())
|
t.Error("Allowed write of empty resource", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// no place to read
|
// no place to read
|
||||||
if err := db.Read("", "redfish", onefish); err == nil {
|
if err := db.Read("", "redfish", onefish); err == nil {
|
||||||
t.Error("Allowed read of empty resource", err.Error())
|
t.Error("Allowed read of empty resource", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
destroySchool()
|
destroySchool()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
|
|
||||||
createDB()
|
createDB()
|
||||||
|
|
||||||
// add fish to database
|
// add fish to database
|
||||||
if err := db.Write(collection, "redfish", redfish); err != nil {
|
if err := db.Write(collection, "redfish", redfish); err != nil {
|
||||||
t.Error("Create fish failed: ", err.Error())
|
t.Error("Create fish failed: ", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the fish
|
// delete the fish
|
||||||
if err := db.Delete(collection, "redfish"); err != nil {
|
if err := db.Delete(collection, "redfish"); err != nil {
|
||||||
t.Error("Failed to delete: ", err.Error())
|
t.Error("Failed to delete: ", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// read fish from database
|
// read fish from database
|
||||||
if err := db.Read(collection, "redfish", &onefish); err == nil {
|
if err := db.Read(collection, "redfish", &onefish); err == nil {
|
||||||
t.Error("Expected nothing, got fish")
|
t.Error("Expected nothing, got fish")
|
||||||
}
|
}
|
||||||
|
|
||||||
destroySchool()
|
destroySchool()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
func TestDeleteall(t *testing.T) {
|
func TestDeleteall(t *testing.T) {
|
||||||
|
|
||||||
createDB()
|
createDB()
|
||||||
createSchool()
|
createSchool()
|
||||||
|
|
||||||
if err := db.Delete(collection, ""); err != nil {
|
if err := db.Delete(collection, ""); err != nil {
|
||||||
t.Error("Failed to delete: ", err.Error())
|
t.Error("Failed to delete: ", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(collection); err == nil {
|
if _, err := os.Stat(collection); err == nil {
|
||||||
t.Error("Expected nothing, have fish")
|
t.Error("Expected nothing, have fish")
|
||||||
}
|
}
|
||||||
|
|
||||||
destroySchool()
|
destroySchool()
|
||||||
}
|
}
|
||||||
|
// create a new scratch database
|
||||||
//
|
|
||||||
|
|
||||||
// create a new scribble database
|
|
||||||
func createDB() error {
|
func createDB() error {
|
||||||
var err error
|
var err error
|
||||||
if db, err = New(database, nil); err != nil {
|
if db, err = New(database, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a fish
|
// create a fish
|
||||||
func createFish(fish Fish) error {
|
func createFish(fish Fish) error {
|
||||||
return db.Write(collection, fish.Type, fish)
|
return db.Write(collection, fish.Type, fish)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create many fish
|
// create many fish
|
||||||
func createSchool() error {
|
func createSchool() error {
|
||||||
for _, f := range []Fish{{Type: "red"}, {Type: "blue"}} {
|
for _, f := range []Fish{{Type: "red"}, {Type: "blue"}} {
|
||||||
@ -190,15 +134,12 @@ func createSchool() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy a fish
|
// destroy a fish
|
||||||
func destroyFish(name string) error {
|
func destroyFish(name string) error {
|
||||||
return db.Delete(collection, name)
|
return db.Delete(collection, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy all fish
|
// destroy all fish
|
||||||
func destroySchool() error {
|
func destroySchool() error {
|
||||||
return db.Delete(collection, "")
|
return db.Delete(collection, "")
|
Loading…
Reference in New Issue
Block a user