Added piecestore (#36)

* Added piecestore

* gofmt

* Added requested changes

* Added cli

* Removed ranger because I wanted something that can stand alone

* Changed piecestore code to make it more optial for error handelling

* Added missing package

* Forgot io import

* gofmt

* Make path by hash exported

* updated to simplify again whoops

* Forgot ampersand

* Updated to match FilePiece

* Change store to use io.CopyN

* Clear golinter errors

* Updated go.mod

* Updated go.mod
This commit is contained in:
Alexander Leitner 2018-05-16 21:40:44 +09:00 committed by JT Olio
parent 6b49aed88a
commit 4b03c08cf6
4 changed files with 860 additions and 26 deletions

125
cmd/piecestore/main.go Normal file
View File

@ -0,0 +1,125 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"fmt"
"log"
"os"
"sort"
"github.com/urfave/cli"
"github.com/zeebo/errs"
"storj.io/storj/pkg/piecestore"
)
var argError = errs.Class("argError")
func main() {
app := cli.NewApp()
app.Name = "Piece Store CLI"
app.Usage = "Store data in hash folder structure"
app.Version = "1.0.0"
app.Flags = []cli.Flag{}
app.Commands = []cli.Command{
{
Name: "store",
Aliases: []string{"s"},
Usage: "Store data by hash",
ArgsUsage: "[hash] [dataPath] [storeDir]",
Action: func(c *cli.Context) error {
if c.Args().Get(0) == "" {
return argError.New("Missing data Hash")
}
if c.Args().Get(1) == "" {
return argError.New("No input file specified")
}
if c.Args().Get(2) == "" {
return argError.New("No output directory specified")
}
file, err := os.Open(c.Args().Get(1))
if err != nil {
return err
}
// Close the file when we are done
defer file.Close()
fileInfo, err := os.Stat(c.Args().Get(1))
if err != nil {
return err
}
if fileInfo.IsDir() {
return argError.New(fmt.Sprintf("Path (%s) is a directory, not a file", c.Args().Get(1)))
}
_, err = pstore.Store(c.Args().Get(0), file, int64(fileInfo.Size()), 0, c.Args().Get(2))
return err
},
},
{
Name: "retrieve",
Aliases: []string{"r"},
Usage: "Retrieve data by hash and print to Stdout",
ArgsUsage: "[hash] [storeDir]",
Action: func(c *cli.Context) error {
if c.Args().Get(0) == "" {
return argError.New("Missing data Hash")
}
if c.Args().Get(1) == "" {
return argError.New("Missing file path")
}
fileInfo, err := os.Stat(c.Args().Get(1))
if err != nil {
return err
}
if fileInfo.IsDir() != true {
return argError.New(fmt.Sprintf("Path (%s) is a file, not a directory", c.Args().Get(1)))
}
_, err = pstore.Retrieve(c.Args().Get(0), os.Stdout, -1, 0, c.Args().Get(1))
if err != nil {
return err
}
return nil
},
},
{
Name: "delete",
Aliases: []string{"d"},
Usage: "Delete data by hash",
ArgsUsage: "[hash] [storeDir]",
Action: func(c *cli.Context) error {
if c.Args().Get(0) == "" {
return argError.New("Missing data Hash")
}
if c.Args().Get(1) == "" {
return argError.New("No directory specified")
}
err := pstore.Delete(c.Args().Get(0), c.Args().Get(1))
return err
},
},
}
sort.Sort(cli.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands))
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}

53
go.mod
View File

@ -1,29 +1,30 @@
module storj.io/storj
module "storj.io/storj"
require (
github.com/fsnotify/fsnotify v1.4.7
github.com/go-redis/redis v0.0.0-20180417061816-9ccc23344a52
github.com/gogo/protobuf v1.0.0
github.com/golang/protobuf v1.0.0
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce
github.com/magiconair/properties v1.7.6
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238
github.com/pelletier/go-toml v1.1.0
github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1
github.com/spf13/afero v1.1.0
github.com/spf13/cast v1.2.0
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec
github.com/spf13/pflag v1.0.1
github.com/spf13/viper v1.0.2
github.com/tyler-smith/go-bip39 v0.0.0-20160629163856-8e7a99b3e716
github.com/urfave/cli v1.20.0
github.com/vivint/infectious v0.0.0-20180418194855-57d6abddc3d4
github.com/zeebo/errs v0.1.0
golang.org/x/crypto v0.0.0-20180410182641-f70185d77e82
golang.org/x/net v0.0.0-20180420171651-5f9ae10d9af5
golang.org/x/sys v0.0.0-20180430173509-4adea008a5e5
golang.org/x/text v0.3.0
google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4
google.golang.org/grpc v1.11.3
gopkg.in/yaml.v2 v2.2.1
"github.com/aleitner/FilePiece" v0.0.0-20180516062859-b020bc25bf96
"github.com/fsnotify/fsnotify" v1.4.7
"github.com/go-redis/redis" v0.0.0-20180417061816-9ccc23344a52
"github.com/gogo/protobuf" v1.0.0
"github.com/golang/protobuf" v1.0.0
"github.com/hashicorp/hcl" v0.0.0-20180404174102-ef8a98b0bbce
"github.com/magiconair/properties" v1.7.6
"github.com/mitchellh/mapstructure" v0.0.0-20180220230111-00c29f56e238
"github.com/pelletier/go-toml" v1.1.0
"github.com/spacemonkeygo/errors" v0.0.0-20171212215202-9064522e9fd1
"github.com/spf13/afero" v1.1.0
"github.com/spf13/cast" v1.2.0
"github.com/spf13/jWalterWeatherman" v0.0.0-20180109140146-7c0cea34c8ec
"github.com/spf13/pflag" v1.0.1
"github.com/spf13/viper" v1.0.2
"github.com/tyler-smith/go-bip39" v0.0.0-20160629163856-8e7a99b3e716
"github.com/urfave/cli" v1.20.0
"github.com/vivint/infectious" v0.0.0-20180418194855-57d6abddc3d4
"github.com/zeebo/errs" v0.1.0
"golang.org/x/crypto" v0.0.0-20180410182641-f70185d77e82
"golang.org/x/net" v0.0.0-20180420171651-5f9ae10d9af5
"golang.org/x/sys" v0.0.0-20180430173509-4adea008a5e5
"golang.org/x/text" v0.3.0
"google.golang.org/genproto" v0.0.0-20180427144745-86e600f69ee4
"google.golang.org/grpc" v1.11.3
"gopkg.in/yaml.v2" v2.2.1
)

152
pkg/piecestore/pstore.go Normal file
View File

@ -0,0 +1,152 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package pstore
import (
"io"
"os"
"path"
"path/filepath"
"github.com/aleitner/FilePiece"
"github.com/zeebo/errs"
)
// Errors
var (
ArgError = errs.Class("argError")
FSError = errs.Class("fsError")
)
// PathByHash -- creates datapath from hash and dir
func PathByHash(hash, dir string) (string, error) {
if len(hash) < 20 {
return "", ArgError.New("Invalid hash length")
}
folder1 := string(hash[0:2])
folder2 := string(hash[2:4])
fileName := string(hash[4:])
return path.Join(dir, folder1, folder2, fileName), nil
}
// Store -- Store data into piece store
// hash (string) Hash of the data to be stored
// r (io.Reader) File/Stream that contains the contents of the data to be stored
// length (length) Size of the data to be stored
// psFileOffset (offset) Offset of the data that you are writing. Useful for multiple connections to split the data transfer
// dir (string) pstore directory containing all other data stored
// returns (error) error if failed and nil if successful
func Store(hash string, r io.Reader, length int64, psFileOffset int64, dir string) (int64, error) {
if psFileOffset < 0 {
return 0, ArgError.New("Offset is less than 0. Must be greater than or equal to 0")
}
if length < 0 {
return 0, ArgError.New("Length is less than 0. Must be greater than or equal to 0")
}
if dir == "" {
return 0, ArgError.New("No path provided")
}
dataPath, err := PathByHash(hash, dir)
if err != nil {
return 0, err
}
// Create directory path on file system
if err = os.MkdirAll(filepath.Dir(dataPath), 0700); err != nil {
return 0, err
}
// Create File on file system
dataFile, err := os.OpenFile(dataPath, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return 0, err
}
dataFileChunk := fpiece.NewChunk(dataFile, psFileOffset, length)
// Close when finished
defer dataFile.Close()
return io.CopyN(dataFileChunk, r, length)
}
// Retrieve -- Retrieve data from pstore directory
// hash (string) Hash of the stored data
// w (io.Writer) Stream that recieves the stored data
// length (length) Amount of data to read. Read all data if -1
// readPosOffset (offset) Offset of the data that you are reading. Useful for multiple connections to split the data transfer
// dir (string) pstore directory containing all other data stored
// returns (int64, error) returns err if failed and the number of bytes retrieved if successful
func Retrieve(hash string, w io.Writer, length int64, readPosOffset int64, dir string) (int64, error) {
if dir == "" {
return 0, ArgError.New("No path provided")
}
dataPath, err := PathByHash(hash, dir)
if err != nil {
return 0, err
}
fileInfo, err := os.Stat(dataPath)
if err != nil {
return 0, err
}
// If offset is greater than file size return
if readPosOffset >= fileInfo.Size() || readPosOffset < 0 {
return 0, ArgError.New("Invalid offset: %v", readPosOffset)
}
// If length less than 0 read the entire file
if length <= -1 {
length = fileInfo.Size()
}
// If trying to read past the end of the file, just read to the end
if fileInfo.Size() < readPosOffset+length {
length = fileInfo.Size() - readPosOffset
}
dataFile, err := os.OpenFile(dataPath, os.O_RDONLY, 0755)
if err != nil {
return 0, err
}
// Close when finished
defer dataFile.Close()
// Created a section reader so that we can concurrently retrieve the same file.
dataFileChunk := fpiece.NewChunk(dataFile, readPosOffset, length)
total, err := io.CopyN(w, dataFileChunk, length)
return total, err
}
// Delete -- Delete data from farmer
// hash (string) Hash of the data to be stored
// dir (string) pstore directory containing all other data stored
// returns (error) if failed and nil if successful
func Delete(hash string, dir string) error {
if dir == "" {
return ArgError.New("No path provided")
}
dataPath, err := PathByHash(hash, dir)
if err != nil {
return err
}
if _, err = os.Stat(dataPath); os.IsNotExist(err) {
return nil
}
if err = os.Remove(dataPath); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,556 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package pstore
import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"testing"
"github.com/stretchr/testify/assert"
)
var tmpfile string
func TestStore(t *testing.T) {
t.Run("it stores data successfully", func(t *testing.T) {
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
folder1 := string(hash[0:2])
folder2 := string(hash[2:4])
fileName := string(hash[4:])
createdFilePath := path.Join(os.TempDir(), folder1, folder2, fileName)
defer os.RemoveAll(path.Join(os.TempDir(), folder1))
_, lStatErr := os.Lstat(createdFilePath)
if lStatErr != nil {
t.Errorf("No file was created from Store(): %s", lStatErr.Error())
return
}
createdFile, openCreatedError := os.Open(createdFilePath)
if openCreatedError != nil {
t.Errorf("Error: %s opening created file %s", openCreatedError.Error(), createdFilePath)
}
defer createdFile.Close()
buffer := make([]byte, 5)
createdFile.Seek(0, 0)
_, _ = createdFile.Read(buffer)
if string(buffer) != "butts" {
t.Errorf("Expected data butts does not equal Actual data %s", string(buffer))
}
})
t.Run("it stores data by offset successfully", func(t *testing.T) {
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 2, os.TempDir())
folder1 := string(hash[0:2])
folder2 := string(hash[2:4])
fileName := string(hash[4:])
createdFilePath := path.Join(os.TempDir(), folder1, folder2, fileName)
defer os.RemoveAll(path.Join(os.TempDir(), folder1))
_, lStatErr := os.Lstat(createdFilePath)
if lStatErr != nil {
t.Errorf("No file was created from Store(): %s", lStatErr.Error())
return
}
createdFile, openCreatedError := os.Open(createdFilePath)
if openCreatedError != nil {
t.Errorf("Error: %s opening created file %s", openCreatedError.Error(), createdFilePath)
}
defer createdFile.Close()
buffer := make([]byte, 7)
createdFile.Seek(0, 0)
_, _ = createdFile.Read(buffer)
// \0\0butts
expected := []byte{0, 0, 98, 117, 116, 116, 115}
if string(buffer) != string(expected) {
t.Errorf("Expected data (%v) does not equal Actual data (%v)", expected, buffer)
}
})
t.Run("it stores data by chunk successfully", func(t *testing.T) {
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, 4, 0, os.TempDir())
folder1 := string(hash[0:2])
folder2 := string(hash[2:4])
fileName := string(hash[4:])
createdFilePath := path.Join(os.TempDir(), folder1, folder2, fileName)
defer os.RemoveAll(path.Join(os.TempDir(), folder1))
_, lStatErr := os.Lstat(createdFilePath)
if lStatErr != nil {
t.Errorf("No file was created from Store(): %s", lStatErr.Error())
return
}
createdFile, openCreatedError := os.Open(createdFilePath)
if openCreatedError != nil {
t.Errorf("Error: %s opening created file %s", openCreatedError.Error(), createdFilePath)
}
defer createdFile.Close()
buffer := make([]byte, 4)
createdFile.Seek(0, 0)
_, _ = createdFile.Read(buffer)
// butt
expected := []byte{98, 117, 116, 116}
if string(buffer) != string(expected) {
t.Errorf("Expected data %s does not equal Actual data %s", string(expected), string(buffer))
}
})
t.Run("it should return hash err if the hash is too short", func(t *testing.T) {
assert := assert.New(t)
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
defer file.Close()
hash := "11111111"
_, err = Store(hash, file, 5, 0, os.TempDir())
assert.NotNil(err)
if err != nil {
assert.Equal(err.Error(), "argError: Invalid hash length", "They should have the same error message")
}
})
// Test passing in negative offset
t.Run("it should return an error when given negative offset", func(t *testing.T) {
assert := assert.New(t)
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
_, err = Store(hash, file, int64(fi.Size()), -12, os.TempDir())
assert.NotNil(err)
if err != nil {
assert.Equal(err.Error(), "argError: Offset is less than 0. Must be greater than or equal to 0", err.Error())
}
})
// Test passing in a negative length
t.Run("it should return an error when given length less than 0", func(t *testing.T) {
assert := assert.New(t)
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
_, err = file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
_, err = Store(hash, file, -1, 0, os.TempDir())
assert.NotNil(err)
if err != nil {
assert.Equal(err.Error(), "argError: Length is less than 0. Must be greater than or equal to 0", err.Error())
}
})
}
func TestRetrieve(t *testing.T) {
t.Run("it retrieves data successfully", func(t *testing.T) {
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
// Create file for retrieving data into
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
if err != nil {
t.Errorf("Error creating file: %s", err.Error())
return
}
defer os.RemoveAll(retrievalFilePath)
defer retrievalFile.Close()
_, err = Retrieve(hash, retrievalFile, int64(fi.Size()), 0, os.TempDir())
if err != nil {
if err != io.EOF {
t.Errorf("Retrieve Error: %s", err.Error())
}
}
buffer := make([]byte, 5)
retrievalFile.Seek(0, 0)
_, _ = retrievalFile.Read(buffer)
fmt.Printf("Retrieved data: %s", string(buffer))
if string(buffer) != "butts" {
t.Errorf("Expected data butts does not equal Actual data %s", string(buffer))
}
})
t.Run("it retrieves data by offset successfully", func(t *testing.T) {
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
// Create file for retrieving data into
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
if err != nil {
t.Errorf("Error creating file: %s", err.Error())
return
}
defer os.RemoveAll(retrievalFilePath)
defer retrievalFile.Close()
_, err = Retrieve(hash, retrievalFile, int64(fi.Size()), 2, os.TempDir())
if err != nil {
if err != io.EOF {
t.Errorf("Retrieve Error: %s", err.Error())
}
}
buffer := make([]byte, 3)
retrievalFile.Seek(0, 0)
_, _ = retrievalFile.Read(buffer)
fmt.Printf("Retrieved data: %s", string(buffer))
if string(buffer) != "tts" {
t.Errorf("Expected data (tts) does not equal Actual data (%s)", string(buffer))
}
})
t.Run("it retrieves data by chunk successfully", func(t *testing.T) {
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
// Create file for retrieving data into
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
if err != nil {
t.Errorf("Error creating file: %s", err.Error())
return
}
defer os.RemoveAll(retrievalFilePath)
defer retrievalFile.Close()
_, err = Retrieve(hash, retrievalFile, 3, 0, os.TempDir())
if err != nil {
if err != io.EOF {
t.Errorf("Retrieve Error: %s", err.Error())
}
}
buffer := make([]byte, 3)
retrievalFile.Seek(0, 0)
_, _ = retrievalFile.Read(buffer)
fmt.Printf("Retrieved data: %s", string(buffer))
if string(buffer) != "but" {
t.Errorf("Expected data (but) does not equal Actual data (%s)", string(buffer))
}
})
// Test passing in negative offset
t.Run("it should return an error when retrieving with offset less 0", func(t *testing.T) {
assert := assert.New(t)
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
// Create file for retrieving data into
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
if err != nil {
t.Errorf("Error creating file: %s", err.Error())
return
}
defer os.RemoveAll(retrievalFilePath)
defer retrievalFile.Close()
_, err = Retrieve(hash, retrievalFile, int64(fi.Size()), -1, os.TempDir())
assert.NotNil(err)
if err != nil {
assert.Equal("argError: Invalid offset: -1", err.Error(), err.Error())
}
})
// Test passing in negative length
t.Run("it should return the entire file successfully when retrieving with negative length", func(t *testing.T) {
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
// Create file for retrieving data into
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
if err != nil {
t.Errorf("Error creating file: %s", err.Error())
return
}
defer os.RemoveAll(retrievalFilePath)
defer retrievalFile.Close()
_, err = Retrieve(hash, retrievalFile, -1, 0, os.TempDir())
if err != nil {
if err != io.EOF {
t.Errorf("Retrieve Error: %s", err.Error())
}
}
buffer := make([]byte, 5)
retrievalFile.Seek(0, 0)
_, _ = retrievalFile.Read(buffer)
fmt.Printf("Retrieved data: %s", string(buffer))
if string(buffer) != "butts" {
t.Errorf("Expected data butts does not equal Actual data %s", string(buffer))
}
})
}
func TestDelete(t *testing.T) {
t.Run("it deletes data successfully", func(t *testing.T) {
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
folder1 := string(hash[0:2])
folder2 := string(hash[2:4])
fileName := string(hash[4:])
if _, err := os.Stat(path.Join(os.TempDir(), folder1, folder2, fileName)); err != nil {
t.Errorf("Failed to Store test file")
return
}
Delete(hash, os.TempDir())
_, err = os.Stat(path.Join(os.TempDir(), folder1, folder2, fileName))
if err == nil {
t.Errorf("Failed to Delete test file")
return
}
})
// Test passing in a hash that doesn't exist
t.Run("it returns an error if hash doesn't exist", func(t *testing.T) {
assert := assert.New(t)
file, err := os.Open(tmpfile)
if err != nil {
t.Errorf("Error opening tmp file: %s", err.Error())
return
}
fi, err := file.Stat()
if err != nil {
t.Errorf("Could not stat test file: %s", err.Error())
return
}
defer file.Close()
hash := "0123456789ABCDEFGHIJ"
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
folder1 := string(hash[0:2])
folder2 := string(hash[2:4])
fileName := string(hash[4:])
if _, err := os.Stat(path.Join(os.TempDir(), folder1, folder2, fileName)); err != nil {
t.Errorf("Failed to Store test file")
return
}
falseHash := ""
err = Delete(falseHash, os.TempDir())
assert.NotNil(err)
if err != nil {
assert.NotEqual(err.Error(), "argError: Hash folder does not exist", "They should be equal")
}
})
}
func TestMain(m *testing.M) {
content := []byte("butts")
tmpfilePtr, err := ioutil.TempFile("", "api_test")
if err != nil {
log.Fatal(err)
}
tmpfile = tmpfilePtr.Name()
defer os.Remove(tmpfile) // clean up
if _, err := tmpfilePtr.Write(content); err != nil {
log.Fatal(err)
}
if err := tmpfilePtr.Close(); err != nil {
log.Fatal(err)
}
m.Run()
}