storj/pkg/piecestore/rpc/server/server_test.go
Bryan White 1ebd66d880 Cleanup & bolt-backed cache (#87)
* wip post-demos cleanup

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Reorganize:

storage
├── common
│   └── common.go `package storage`
├── boltdb
│   └── ...
└── redis
    └── ...

storage
├── common.go `package storage`
├── boltdb
│   └── ...
└── redis
    └── ...

storage
├── common
│   ├── common.go `package storage`
│   └── testing.go `package storage` <--
└── ...

internal
├── app
│   └── cli
├── pkg
│   └── readcloser
└── test
    └── util.go `package test` <--

* remove comment

* add and use goimports

* add test types & fix some lint errors

* better typing

* fixing linter issues/comments

* goimports

* goimports

* more linter fixes; replace panic with NoError assertions in tests

* fix typo/more linter errors

* moar better linter fixes

* even moar better linter fixes

* linter

* add localPort back

* fixing exports, imports, and add comments
2018-06-13 14:22:32 -04:00

430 lines
10 KiB
Go

// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package server
import (
"bytes"
"database/sql"
"fmt"
"io"
"log"
"net"
"os"
"path"
"testing"
_ "github.com/mattn/go-sqlite3"
"golang.org/x/net/context"
"google.golang.org/grpc"
"storj.io/storj/pkg/piecestore"
"storj.io/storj/pkg/piecestore/rpc/server/ttl"
pb "storj.io/storj/protos/piecestore"
)
var tempDir string = path.Join(os.TempDir(), "test-data", "3000")
var tempDBPath string = path.Join(os.TempDir(), "test.db")
var db *sql.DB
var s Server
var c pb.PieceStoreRoutesClient
var testId string = "11111111111111111111"
var testCreatedDate int64 = 1234567890
var testExpiration int64 = 9999999999
func TestPiece(t *testing.T) {
// simulate piece stored with farmer
file, err := pstore.StoreWriter(testId, s.PieceStoreDir)
if err != nil {
return
}
// Close when finished
defer file.Close()
_, err = io.Copy(file, bytes.NewReader([]byte("butts")))
if err != nil {
t.Errorf("Error: %v\nCould not create test piece", err)
return
}
defer pstore.Delete(testId, s.PieceStoreDir)
// set up test cases
tests := []struct {
id string
size int64
expiration int64
err string
}{
{ // should successfully retrieve piece meta-data
id: testId,
size: 5,
expiration: testExpiration,
err: "",
},
{ // server should err with invalid id
id: "123",
size: 5,
expiration: testExpiration,
err: "rpc error: code = Unknown desc = argError: Invalid id length",
},
{ // server should err with nonexistent file
id: "22222222222222222222",
size: 5,
expiration: testExpiration,
err: fmt.Sprintf("rpc error: code = Unknown desc = stat %s: no such file or directory", path.Join(os.TempDir(), "/test-data/3000/22/22/2222222222222222")),
},
}
for _, tt := range tests {
t.Run("should return expected PieceSummary values", func(t *testing.T) {
// simulate piece TTL entry
_, err = db.Exec(fmt.Sprintf(`INSERT INTO ttl (id, created, expires) VALUES ("%s", "%d", "%d")`, tt.id, testCreatedDate, testExpiration))
if err != nil {
t.Errorf("Error: %v\nCould not make TTL entry", err)
return
}
defer db.Exec(fmt.Sprintf(`DELETE FROM ttl WHERE id="%s"`, tt.id))
req := &pb.PieceId{Id: tt.id}
resp, err := c.Piece(context.Background(), req)
if len(tt.err) > 0 {
if err != nil {
if err.Error() == tt.err {
return
}
}
t.Errorf("\nExpected: %s\nGot: %v\n", tt.err, err)
return
}
if err != nil && tt.err == "" {
t.Errorf("\nExpected: %s\nGot: %v\n", tt.err, err)
return
}
if resp.Id != tt.id || resp.Size != tt.size || resp.Expiration != tt.expiration {
t.Errorf("Expected: %v, %v, %v\nGot: %v, %v, %v\n", tt.id, tt.size, tt.expiration, resp.Id, resp.Size, resp.Expiration)
return
}
// clean up DB entry
_, err = db.Exec(fmt.Sprintf(`DELETE FROM ttl WHERE id="%s"`, testId))
if err != nil {
t.Errorf("Error cleaning test DB entry")
return
}
})
}
}
func TestRetrieve(t *testing.T) {
// simulate piece stored with farmer
file, err := pstore.StoreWriter(testId, s.PieceStoreDir)
if err != nil {
return
}
// Close when finished
defer file.Close()
_, err = io.Copy(file, bytes.NewReader([]byte("butts")))
if err != nil {
t.Errorf("Error: %v\nCould not create test piece", err)
return
}
defer pstore.Delete(testId, s.PieceStoreDir)
// set up test cases
tests := []struct {
id string
reqSize int64
respSize int64
offset int64
content []byte
err string
}{
{ // should successfully retrieve data
id: testId,
reqSize: 5,
respSize: 5,
offset: 0,
content: []byte("butts"),
err: "",
},
{ // server should err with invalid id
id: "123",
reqSize: 5,
respSize: 5,
offset: 0,
content: []byte("butts"),
err: "rpc error: code = Unknown desc = argError: Invalid id length",
},
{ // server should err with nonexistent file
id: "22222222222222222222",
reqSize: 5,
respSize: 5,
offset: 0,
content: []byte("butts"),
err: fmt.Sprintf("rpc error: code = Unknown desc = stat %s: no such file or directory", path.Join(os.TempDir(), "/test-data/3000/22/22/2222222222222222")),
},
{ // server should return expected content and respSize with offset and excess reqSize
id: testId,
reqSize: 5,
respSize: 4,
offset: 1,
content: []byte("utts"),
err: "",
},
{ // server should return expected content with reduced reqSize
id: testId,
reqSize: 4,
respSize: 4,
offset: 0,
content: []byte("butt"),
err: "",
},
}
for _, tt := range tests {
t.Run("should return expected PieceRetrievalStream values", func(t *testing.T) {
req := &pb.PieceRetrieval{Id: tt.id, Size: tt.reqSize, Offset: tt.offset}
stream, err := c.Retrieve(context.Background(), req)
if err != nil {
t.Errorf("Unexpected error: %v\n", err)
return
}
resp, err := stream.Recv()
if len(tt.err) > 0 {
if err != nil {
if err.Error() == tt.err {
return
}
}
t.Errorf("\nExpected: %s\nGot: %v\n", tt.err, err)
return
}
if err != nil && tt.err == "" {
t.Errorf("\nExpected: %s\nGot: %v\n", tt.err, err)
return
}
if resp.Size != tt.respSize || bytes.Equal(resp.Content, tt.content) != true {
t.Errorf("Expected: %v, %v\nGot: %v, %v\n", tt.respSize, tt.content, resp.Size, resp.Content)
return
}
})
}
}
func TestStore(t *testing.T) {
tests := []struct {
id string
size int64
ttl int64
offset int64
content []byte
message string
totalReceived int64
err string
}{
{ // should successfully store data
id: testId,
ttl: testExpiration,
content: []byte("butts"),
message: "OK",
totalReceived: 5,
err: "",
},
{ // should err with invalid id length
id: "butts",
ttl: testExpiration,
content: []byte("butts"),
message: "",
totalReceived: 0,
err: "rpc error: code = Unknown desc = argError: Invalid id length",
},
}
for _, tt := range tests {
t.Run("should return expected PieceStoreSummary values", func(t *testing.T) {
stream, err := c.Store(context.Background())
if err != nil {
t.Errorf("Unexpected error: %v\n", err)
return
}
// Write the buffer to the stream we opened earlier
if err := stream.Send(&pb.PieceStore{Id: tt.id, Ttl: tt.ttl}); err != nil {
t.Errorf("Unexpected error: %v\n", err)
return
}
// Write the buffer to the stream we opened earlier
if err := stream.Send(&pb.PieceStore{Content: tt.content}); err != nil {
t.Errorf("Unexpected error: %v\n", err)
return
}
resp, err := stream.CloseAndRecv()
defer db.Exec(fmt.Sprintf(`DELETE FROM ttl WHERE id="%s"`, tt.id))
if len(tt.err) > 0 {
if err != nil {
if err.Error() == tt.err {
return
}
}
t.Errorf("\nExpected: %s\nGot: %v\n", tt.err, err)
return
}
if err != nil && tt.err == "" {
t.Errorf("\nExpected: %s\nGot: %v\n", tt.err, err)
return
}
if resp.Message != tt.message || resp.TotalReceived != tt.totalReceived {
t.Errorf("Expected: %v, %v\nGot: %v, %v\n", tt.message, tt.totalReceived, resp.Message, resp.TotalReceived)
}
})
}
}
func TestDelete(t *testing.T) {
// set up test cases
tests := []struct {
id string
message string
err string
}{
{ // should successfully delete data
id: testId,
message: "OK",
err: "",
},
{ // should err with invalid id length
id: "123",
message: "rpc error: code = Unknown desc = argError: Invalid id length",
err: "rpc error: code = Unknown desc = argError: Invalid id length",
},
{ // should return OK with nonexistent file
id: "22222222222222222223",
message: "OK",
err: "",
},
}
for _, tt := range tests {
t.Run("should return expected PieceDeleteSummary values", func(t *testing.T) {
// simulate piece stored with farmer
file, err := pstore.StoreWriter(testId, s.PieceStoreDir)
if err != nil {
return
}
// Close when finished
defer file.Close()
_, err = io.Copy(file, bytes.NewReader([]byte("butts")))
if err != nil {
t.Errorf("Error: %v\nCould not create test piece", err)
return
}
// simulate piece TTL entry
_, err = db.Exec(fmt.Sprintf(`INSERT INTO ttl (id, created, expires) VALUES ("%s", "%d", "%d")`, tt.id, testCreatedDate, testCreatedDate))
if err != nil {
t.Errorf("Error: %v\nCould not make TTL entry", err)
return
}
defer db.Exec(fmt.Sprintf(`DELETE FROM ttl WHERE id="%s"`, tt.id))
defer pstore.Delete(testId, s.PieceStoreDir)
req := &pb.PieceDelete{Id: tt.id}
resp, err := c.Delete(context.Background(), req)
if len(tt.err) > 0 {
if err != nil {
if err.Error() == tt.err {
return
}
}
t.Errorf("\nExpected: %s\nGot: %v\n", tt.err, err)
return
}
if err != nil && tt.err == "" {
t.Errorf("\nExpected: %s\nGot: %v\n", tt.err, err)
return
}
if resp.Message != tt.message {
t.Errorf("Expected: %v\nGot: %v\n", tt.message, resp.Message)
return
}
// if test passes, check if file was indeed deleted
filePath, err := pstore.PathByID(tt.id, s.PieceStoreDir)
if _, err = os.Stat(filePath); os.IsNotExist(err) != true {
t.Errorf("File not deleted")
return
}
})
}
}
func StartServer() {
lis, err := net.Listen("tcp", ":3000")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcs := grpc.NewServer()
pb.RegisterPieceStoreRoutesServer(grpcs, &s)
if err := grpcs.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func TestMain(m *testing.M) {
go StartServer()
// Set up a connection to the Server.
const address = "localhost:3000"
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
fmt.Printf("did not connect: %v", err)
return
}
defer conn.Close()
c = pb.NewPieceStoreRoutesClient(conn)
ttlDB, err := ttl.NewTTL(tempDBPath)
if err != nil {
log.Fatal(err)
}
s = Server{tempDir, ttlDB}
db = ttlDB.DB
// clean up temp files
defer os.RemoveAll(path.Join(tempDir, "/test-data"))
defer os.Remove(tempDBPath)
defer db.Close()
m.Run()
}