Complete restructure of domain and infra layer
parent
a70e8ef74b
commit
8a0e6b80e3
@ -0,0 +1,2 @@
|
|||||||
|
sql.db
|
||||||
|
bog
|
@ -1,39 +0,0 @@
|
|||||||
package application
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http/httptest"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"caj-larsson/bog/domain"
|
|
||||||
"caj-larsson/bog/test/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestApplication(t *testing.T) {
|
|
||||||
|
|
||||||
file_service := domain.NewBogFileService(
|
|
||||||
mock.NewMockUserAgentRepository(),
|
|
||||||
mock.NewMockFileRepository(),
|
|
||||||
1000,
|
|
||||||
time.Hour,
|
|
||||||
)
|
|
||||||
|
|
||||||
bog := Bog {
|
|
||||||
router: new(http.ServeMux),
|
|
||||||
file_service: file_service,
|
|
||||||
address: "fake",
|
|
||||||
}
|
|
||||||
bog.routes()
|
|
||||||
req := httptest.NewRequest("POST", "/apath", strings.NewReader("testdata"))
|
|
||||||
req.Header.Add("User-Agent", "testingclient")
|
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
bog.router.ServeHTTP(w, req)
|
|
||||||
|
|
||||||
if (w.Code != 200){
|
|
||||||
fmt.Printf("%v", w)
|
|
||||||
t.Error("not ok")
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,15 @@
|
|||||||
|
package namespace
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoNamespace = errors.New("that namespace does not exist")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Repository interface{
|
||||||
|
Create(namespace Namespace) (*Namespace, error)
|
||||||
|
All() ([]Namespace, error)
|
||||||
|
GetByName(name string) (*Namespace, error)
|
||||||
|
Update(id int64, namespace Namespace) (*Namespace, error)
|
||||||
|
Delete(id int64) error
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
package dataswamp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"caj-larsson/bog/dataswamp/namespace"
|
||||||
|
"caj-larsson/bog/dataswamp/swampfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type SwampFileService struct {
|
||||||
|
namespace_repo namespace.Repository
|
||||||
|
swamp_file_repo swampfile.Repository
|
||||||
|
default_allowance_bytes int64
|
||||||
|
default_allowance_duration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSwampFileService(
|
||||||
|
namespace_repo namespace.Repository,
|
||||||
|
swamp_file_repo swampfile.Repository,
|
||||||
|
da_bytes int64,
|
||||||
|
da_duration time.Duration,
|
||||||
|
) SwampFileService {
|
||||||
|
return SwampFileService {namespace_repo, swamp_file_repo, da_bytes, da_duration}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SwampFileService) getOrCreateNs(namespace_in string) *namespace.Namespace{
|
||||||
|
ns, err := s.namespace_repo.GetByName(namespace_in)
|
||||||
|
|
||||||
|
if err == namespace.ErrNotExists {
|
||||||
|
new_ns := namespace.Namespace {
|
||||||
|
0,
|
||||||
|
namespace_in,
|
||||||
|
time.Now(),
|
||||||
|
s.default_allowance_duration,
|
||||||
|
namespace.FileSizeQuota { s.default_allowance_bytes, 0 },
|
||||||
|
}
|
||||||
|
created_ns, err := s.namespace_repo.Create(new_ns)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return created_ns
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SwampFileService) SaveFile(ref swampfile.FileReference, src io.Reader, size int64) error {
|
||||||
|
ns := s.getOrCreateNs(ref.UserAgent)
|
||||||
|
|
||||||
|
if !ns.FileQuota.Allows(size) {
|
||||||
|
return namespace.ErrExceedQuota
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := s.swamp_file_repo.Create(ref.Path, strconv.FormatInt(ns.ID, 10))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Copy(f, src)
|
||||||
|
f.Close()
|
||||||
|
ns.FileQuota.Add(size)
|
||||||
|
s.namespace_repo.Update(ns.ID, *ns)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SwampFileService) OpenOutFile(ref swampfile.FileReference) (swampfile.SwampOutFile, error) {
|
||||||
|
ns, err := s.namespace_repo.GetByName(ref.UserAgent)
|
||||||
|
|
||||||
|
if err == namespace.ErrNotExists {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := s.swamp_file_repo.Open(ref.Path, strconv.FormatInt(ns.ID, 10))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CleanPath(inpath string) string {
|
||||||
|
return filepath.FromSlash(path.Clean("/" + strings.Trim(inpath, "/")))
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
package dataswamp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
"github.com/matryer/is"
|
||||||
|
"caj-larsson/bog/dataswamp/swampfile"
|
||||||
|
"caj-larsson/bog/dataswamp/namespace"
|
||||||
|
m_namespace "caj-larsson/bog/infrastructure/memory/namespace"
|
||||||
|
m_swampfile "caj-larsson/bog/infrastructure/memory/swampfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
var file_ref1 = swampfile.FileReference { "ptah1", "ua1" }
|
||||||
|
var file_ref2 = swampfile.FileReference { "path1", "ua2" }
|
||||||
|
var file_ref3 = swampfile.FileReference { "path2", "ua1" }
|
||||||
|
|
||||||
|
|
||||||
|
func NewTestSwampFileService() SwampFileService {
|
||||||
|
file_repo := m_swampfile.NewRepository()
|
||||||
|
ns_repo := m_namespace.NewRepository()
|
||||||
|
return NewSwampFileService(ns_repo, file_repo, 1024, time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileDontExist(t *testing.T) {
|
||||||
|
s := NewTestSwampFileService()
|
||||||
|
outfile, err := s.OpenOutFile(file_ref1)
|
||||||
|
|
||||||
|
if outfile != nil && err != swampfile.ErrNotExists {
|
||||||
|
t.Errorf("File shall not exist by default")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileIsStored(t *testing.T) {
|
||||||
|
s := NewTestSwampFileService()
|
||||||
|
|
||||||
|
fakefile := bytes.NewBufferString("My bog data")
|
||||||
|
|
||||||
|
err := s.SaveFile(file_ref1, fakefile, int64(fakefile.Len()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("A small file should be writable %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
largefakefile := bytes.NewBufferString("")
|
||||||
|
|
||||||
|
for largefakefile.Len() < 64000 {
|
||||||
|
_, err = largefakefile.WriteString("A very repetitive file")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.SaveFile(file_ref3, largefakefile, int64(largefakefile.Len()))
|
||||||
|
|
||||||
|
if err != namespace.ErrExceedQuota {
|
||||||
|
t.Errorf("too large files should not be excepted")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestFileIsReadBack(t *testing.T) {
|
||||||
|
s := NewTestSwampFileService()
|
||||||
|
|
||||||
|
infile := bytes.NewBufferString("My bog data")
|
||||||
|
|
||||||
|
_ = s.SaveFile(file_ref1, infile, int64(infile.Len()))
|
||||||
|
|
||||||
|
outswampfile, _ := s.OpenOutFile(file_ref1)
|
||||||
|
|
||||||
|
outfile := bytes.NewBufferString("")
|
||||||
|
_, _ = outfile.ReadFrom(outswampfile)
|
||||||
|
|
||||||
|
if outfile.String() != "My bog data" {
|
||||||
|
t.Errorf("file corrupted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestUAIsolation(t *testing.T) {
|
||||||
|
s := NewTestSwampFileService()
|
||||||
|
|
||||||
|
ns1_file := bytes.NewBufferString("My bog data ua1")
|
||||||
|
ns2_file := bytes.NewBufferString("My bog data ua2")
|
||||||
|
|
||||||
|
_ = s.SaveFile(file_ref1, ns1_file, int64(ns1_file.Len()))
|
||||||
|
_ = s.SaveFile(file_ref2, ns2_file, int64(ns2_file.Len()))
|
||||||
|
|
||||||
|
outswampfile, _ := s.OpenOutFile(file_ref1)
|
||||||
|
|
||||||
|
outfile := bytes.NewBufferString("")
|
||||||
|
_, _ = outfile.ReadFrom(outswampfile)
|
||||||
|
|
||||||
|
if outfile.String() != "My bog data ua1" {
|
||||||
|
t.Errorf("file corrupted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestCleanPath(t *testing.T) {
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
is.Equal(CleanPath("/"), "/")
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package swampfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SwampFile struct {
|
||||||
|
UserAgentId int64
|
||||||
|
Path string
|
||||||
|
Size int64
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrDuplicate = errors.New("record already exists")
|
||||||
|
ErrExceedQuota = errors.New("file too large")
|
||||||
|
ErrQuotaInvalid = errors.New("quota invalid")
|
||||||
|
ErrNotExists = errors.New("row not exists")
|
||||||
|
ErrUpdateFailed = errors.New("update failed")
|
||||||
|
ErrDeleteFailed = errors.New("delete failed")
|
||||||
|
)
|
@ -0,0 +1,7 @@
|
|||||||
|
package swampfile
|
||||||
|
|
||||||
|
type Repository interface {
|
||||||
|
Create(filename string, user_agent_label string) (SwampInFile, error)
|
||||||
|
Open(filename string, user_agent_label string) (SwampOutFile, error)
|
||||||
|
Delete(filename string, user_agent_label string)
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package swampfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SwampOutFile interface {
|
||||||
|
Path() string
|
||||||
|
Size() int64
|
||||||
|
Modified() time.Time
|
||||||
|
io.Reader
|
||||||
|
io.Seeker
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwampInFile interface {
|
||||||
|
Path() string
|
||||||
|
Size() int64
|
||||||
|
Modified() time.Time
|
||||||
|
io.Writer
|
||||||
|
io.Seeker
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileReference struct {
|
||||||
|
Path string
|
||||||
|
UserAgent string
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
package domain
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrNoUserAgent = errors.New("that useragent does not exist")
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserAgentRepository interface{
|
|
||||||
Create(useragent UserAgent) (*UserAgent, error)
|
|
||||||
All() ([]UserAgent, error)
|
|
||||||
GetByName(name string) (*UserAgent, error)
|
|
||||||
Update(id int64, useragent UserAgent) (*UserAgent, error)
|
|
||||||
Delete(id int64) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileDataRepository interface {
|
|
||||||
Create(filename string, user_agent_label string) (BogInFile, error)
|
|
||||||
Open(filename string, user_agent_label string) (BogOutFile, error)
|
|
||||||
Delete(filename string, user_agent_label string)
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package domain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
type BogFileService struct {
|
|
||||||
user_agent_repo UserAgentRepository
|
|
||||||
file_data_repo FileDataRepository
|
|
||||||
default_allowance_bytes int64
|
|
||||||
default_allowance_duration time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBogFileService(
|
|
||||||
user_agent_repo UserAgentRepository,
|
|
||||||
file_data_repo FileDataRepository,
|
|
||||||
da_bytes int64,
|
|
||||||
da_duration time.Duration,
|
|
||||||
) BogFileService {
|
|
||||||
return BogFileService {user_agent_repo, file_data_repo, da_bytes, da_duration}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BogFileService) getOrCreateUA(useragent_in string) *UserAgent{
|
|
||||||
ua, err := b.user_agent_repo.GetByName(useragent_in)
|
|
||||||
|
|
||||||
if err == ErrNotExists {
|
|
||||||
new_ua := UserAgent {
|
|
||||||
0, useragent_in, time.Now(), b.default_allowance_duration, FileSizeQuota { b.default_allowance_bytes, 0 },
|
|
||||||
}
|
|
||||||
created_ua, err := b.user_agent_repo.Create(new_ua)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return created_ua
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ua
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BogFileService) SaveFile(ref FileReference, src io.Reader, size int64) error {
|
|
||||||
user_agent := b.getOrCreateUA(ref.UserAgent)
|
|
||||||
|
|
||||||
if !user_agent.FileQuota.Allows(size) {
|
|
||||||
return ErrExceedQuota
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := b.file_data_repo.Create(ref.Path, strconv.FormatInt(user_agent.ID, 10))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
io.Copy(f, src)
|
|
||||||
f.Close()
|
|
||||||
user_agent.FileQuota.Add(size)
|
|
||||||
b.user_agent_repo.Update(user_agent.ID, *user_agent)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BogFileService) OpenOutFile(ref FileReference) (BogOutFile, error) {
|
|
||||||
user_agent, err := b.user_agent_repo.GetByName(ref.UserAgent)
|
|
||||||
if err == ErrNotExists {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := b.file_data_repo.Open(ref.Path, strconv.FormatInt(user_agent.ID, 10))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return f, nil
|
|
||||||
}
|
|
@ -0,0 +1,93 @@
|
|||||||
|
package swampfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"caj-larsson/bog/dataswamp/swampfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileSystemSwampFileData struct {
|
||||||
|
path string
|
||||||
|
size int64
|
||||||
|
mod_time time.Time
|
||||||
|
file *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileSystemSwampFileData) Read(p []byte) (int, error) {
|
||||||
|
return f.file.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileSystemSwampFileData) Write(p []byte) (int, error) {
|
||||||
|
return f.file.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileSystemSwampFileData) Close() error {
|
||||||
|
return f.file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileSystemSwampFileData) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
return f.file.Seek(offset, whence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileSystemSwampFileData) Path() string {
|
||||||
|
return f.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileSystemSwampFileData) Size() int64 {
|
||||||
|
return f.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileSystemSwampFileData) Modified() time.Time{
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Repository struct {
|
||||||
|
Root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Repository) absPath(filename string, namespace_ns string) string {
|
||||||
|
return path.Join(f.Root, namespace_ns, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Repository) Create(filename string, namespace_ns string) (swampfile.SwampInFile, error) {
|
||||||
|
abs_path := f.absPath(filename, namespace_ns)
|
||||||
|
|
||||||
|
dir := path.Dir(abs_path)
|
||||||
|
os.MkdirAll(dir, 0750)
|
||||||
|
file, err := os.OpenFile(abs_path, os.O_RDWR|os.O_CREATE, 0644)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stat_info, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bfd := FileSystemSwampFileData {filename, stat_info.Size(), stat_info.ModTime(), file}
|
||||||
|
|
||||||
|
return bfd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Repository) Open(filename string, namespace_ns string) (swampfile.SwampOutFile, error) {
|
||||||
|
abs_path := f.absPath(filename, namespace_ns)
|
||||||
|
|
||||||
|
dir := path.Dir(abs_path)
|
||||||
|
os.MkdirAll(dir, 0750)
|
||||||
|
file, err := os.OpenFile(abs_path, os.O_RDONLY, 0644)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, swampfile.ErrNotExists
|
||||||
|
}
|
||||||
|
|
||||||
|
bfd := FileSystemSwampFileData {filename, 0, time.Now(), file}
|
||||||
|
|
||||||
|
return bfd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Repository) Delete(filename string, namespace_ns string) {
|
||||||
|
abs_path := f.absPath(filename, namespace_ns)
|
||||||
|
os.Remove(abs_path)
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package swampfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
"caj-larsson/bog/dataswamp/swampfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFsFileRepo(t *testing.T) {
|
||||||
|
|
||||||
|
var fac = func() swampfile.Repository {
|
||||||
|
r := t.TempDir()
|
||||||
|
d := path.Join(r, "fs")
|
||||||
|
repo := Repository { d }
|
||||||
|
return &repo
|
||||||
|
}
|
||||||
|
|
||||||
|
swampfile.RepositoryContract(fac, t)
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package memory
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "time"
|
||||||
|
"caj-larsson/bog/dataswamp/namespace"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type Repository struct {
|
||||||
|
IdIdx map[int64] *namespace.Namespace
|
||||||
|
NameIdx map[string] *namespace.Namespace
|
||||||
|
NextId int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRepository() *Repository {
|
||||||
|
r := new(Repository)
|
||||||
|
r.NextId = 0
|
||||||
|
r.IdIdx = make(map[int64]*namespace.Namespace)
|
||||||
|
r.NameIdx = make(map[string]*namespace.Namespace)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repository) Create(ns namespace.Namespace) (*namespace.Namespace, error) {
|
||||||
|
r.NextId += 1
|
||||||
|
ns.ID = r.NextId
|
||||||
|
|
||||||
|
r.IdIdx[ns.ID] = &ns
|
||||||
|
r.NameIdx[ns.Name] = &ns
|
||||||
|
return &ns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (r *Repository) All() ([]namespace.Namespace, error) {
|
||||||
|
ns := make([]namespace.Namespace, 0, len(r.IdIdx))
|
||||||
|
|
||||||
|
for _, value := range r.IdIdx {
|
||||||
|
ns = append(ns, *value)
|
||||||
|
}
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (r *Repository) GetByName(name string) (*namespace.Namespace, error) {
|
||||||
|
ns, exists := r.NameIdx[name]
|
||||||
|
if exists {
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
return nil, namespace.ErrNotExists
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (r *Repository) Update(id int64, ns namespace.Namespace) (*namespace.Namespace, error) {
|
||||||
|
original := *r.IdIdx[id]
|
||||||
|
ns.ID = id
|
||||||
|
r.IdIdx[id] = &ns
|
||||||
|
r.NameIdx[original.Name] = &ns
|
||||||
|
return &ns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (r *Repository) Delete(id int64) error {
|
||||||
|
original := *r.IdIdx[id]
|
||||||
|
delete(r.NameIdx, original.Name)
|
||||||
|
delete(r.IdIdx, original.ID)
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package memory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
"caj-larsson/bog/dataswamp/namespace"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUserAgentRepo(t *testing.T) {
|
||||||
|
r := NewRepository()
|
||||||
|
|
||||||
|
all, err := r.All()
|
||||||
|
|
||||||
|
if len(all) != 0 && err != nil {
|
||||||
|
t.Errorf("New repo should be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := namespace.Namespace {23, "n1", time.Now(), time.Duration(time.Hour * 3), namespace.FileSizeQuota {1000, 0} }
|
||||||
|
|
||||||
|
|
||||||
|
ns1, _ := r.Create(ns)
|
||||||
|
ns.Name = "n2"
|
||||||
|
ns2, _ := r.Create(ns)
|
||||||
|
if ns1 == ns2 {
|
||||||
|
t.Errorf("Must create unique items")
|
||||||
|
}
|
||||||
|
|
||||||
|
all, err = r.All()
|
||||||
|
|
||||||
|
if len(all) != 2 && err != nil {
|
||||||
|
t.Errorf("After adding there should be two Useragent")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ns.ID != 23 {
|
||||||
|
t.Errorf("It does not change the original UserAgent")
|
||||||
|
}
|
||||||
|
|
||||||
|
ns3, _ := r.GetByName("n2")
|
||||||
|
|
||||||
|
if ns3 != ns2 {
|
||||||
|
t.Errorf("It the correct ns is acquired")
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Delete(ns2.ID) != nil {
|
||||||
|
t.Errorf("Must delete without error")
|
||||||
|
}
|
||||||
|
|
||||||
|
all, err = r.All()
|
||||||
|
if len(all) != 1 && err != nil {
|
||||||
|
t.Errorf("After deleting one there should be one NS ")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package swampfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"path"
|
||||||
|
"os"
|
||||||
|
// "io"
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
"caj-larsson/bog/dataswamp/swampfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type SwampFile struct {
|
||||||
|
filename string
|
||||||
|
file afero.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f SwampFile) Path() string {
|
||||||
|
return f.filename
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f SwampFile) Size() int64 {
|
||||||
|
stat, _ := f.file.Stat()
|
||||||
|
return int64(stat.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f SwampFile) Read(p []byte) (int, error) {
|
||||||
|
return f.file.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f SwampFile) Write(p []byte) (int, error) {
|
||||||
|
return f.file.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f SwampFile) Close() error {
|
||||||
|
return f.file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f SwampFile) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
return f.file.Seek(offset, whence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f SwampFile) Modified() time.Time {
|
||||||
|
stat, _ := f.file.Stat()
|
||||||
|
return stat.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual repository
|
||||||
|
type Repository struct {
|
||||||
|
fs afero.Fs
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRepository() swampfile.Repository {
|
||||||
|
return Repository { afero.NewMemMapFs() }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Repository) Create(filename string, namespace_stub string) (swampfile.SwampInFile, error) {
|
||||||
|
abs_path := path.Join(filename, namespace_stub)
|
||||||
|
dir := path.Dir(abs_path)
|
||||||
|
r.fs.MkdirAll(dir, 0750)
|
||||||
|
file, err := r.fs.OpenFile(abs_path, os.O_RDWR|os.O_CREATE, 0644)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bf := SwampFile {filename, file}
|
||||||
|
|
||||||
|
return bf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Repository) Open(filename string, namespace_stub string) (swampfile.SwampOutFile, error) {
|
||||||
|
abs_path := path.Join(filename, namespace_stub)
|
||||||
|
|
||||||
|
dir := path.Dir(abs_path)
|
||||||
|
r.fs.MkdirAll(dir, 0750)
|
||||||
|
file, err := r.fs.OpenFile(abs_path, os.O_RDONLY, 0644)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, swampfile.ErrNotExists
|
||||||
|
}
|
||||||
|
|
||||||
|
bf := SwampFile {filename, file}
|
||||||
|
return bf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Repository) Delete(filename string, namespace_stub string) {
|
||||||
|
abs_path := path.Join(filename, namespace_stub)
|
||||||
|
r.fs.Remove(abs_path)
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package swampfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"caj-larsson/bog/dataswamp/swampfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func TestFileRepo(t *testing.T) {
|
||||||
|
swampfile.RepositoryContract(NewRepository, t)
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package namespace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
"caj-larsson/bog/dataswamp/namespace"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NamespaceRecord struct {
|
||||||
|
ID int64
|
||||||
|
Name string
|
||||||
|
LastSeen string
|
||||||
|
AllowanceSeconds int64
|
||||||
|
QuotaKB int64
|
||||||
|
QuotaUsedKB int64
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrUnparseableRecord = errors.New("record could not be mapped to entity")
|
||||||
|
|
||||||
|
func (r *NamespaceRecord) toEntity() (*namespace.Namespace, error) {
|
||||||
|
lastseen, err := time.Parse(time.RFC3339, r.LastSeen)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrUnparseableRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
var ns = new(namespace.Namespace)
|
||||||
|
ns.ID = r.ID
|
||||||
|
ns.Name = r.Name
|
||||||
|
ns.LastSeen = lastseen
|
||||||
|
ns.AllowanceDuration = time.Duration(r.AllowanceSeconds * int64(time.Second))
|
||||||
|
ns.FileQuota = namespace.FileSizeQuota { r.QuotaKB, r.QuotaUsedKB }
|
||||||
|
return ns, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromEntity(ns namespace.Namespace) (*NamespaceRecord, error) {
|
||||||
|
var record = new(NamespaceRecord)
|
||||||
|
record.ID = ns.ID
|
||||||
|
record.Name = ns.Name
|
||||||
|
record.LastSeen = ns.LastSeen.Format(time.RFC3339)
|
||||||
|
record.AllowanceSeconds = int64(ns.AllowanceDuration.Seconds())
|
||||||
|
record.QuotaKB = ns.FileQuota.AllowanceKB
|
||||||
|
record.QuotaUsedKB = ns.FileQuota.CurrentUsage
|
||||||
|
return record, nil
|
||||||
|
}
|
@ -1,94 +0,0 @@
|
|||||||
package integration
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"caj-larsson/bog/domain"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FileSystemBogFileData struct {
|
|
||||||
path string
|
|
||||||
size int64
|
|
||||||
mod_time time.Time
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (f FileSystemBogFileData) Read(p []byte) (int, error) {
|
|
||||||
return f.file.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogFileData) Write(p []byte) (int, error) {
|
|
||||||
return f.file.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogFileData) Close() error {
|
|
||||||
return f.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogFileData) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
return f.file.Seek(offset, whence)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogFileData) Path() string {
|
|
||||||
return f.path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogFileData) Size() int64 {
|
|
||||||
return f.size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogFileData) Modified() time.Time{
|
|
||||||
return time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileSystemBogRepository struct {
|
|
||||||
Root string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogRepository) absPath(filename string, user_agent_label string) string {
|
|
||||||
return path.Join(f.Root, user_agent_label, filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogRepository) Create(filename string, user_agent_label string) (domain.BogInFile, error) {
|
|
||||||
abs_path := f.absPath(filename, user_agent_label)
|
|
||||||
|
|
||||||
dir := path.Dir(abs_path)
|
|
||||||
os.MkdirAll(dir, 0750)
|
|
||||||
file, err := os.OpenFile(abs_path, os.O_RDWR|os.O_CREATE, 0644)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stat_info, err := file.Stat()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bfd := FileSystemBogFileData {filename, stat_info.Size(), stat_info.ModTime(), file}
|
|
||||||
|
|
||||||
return bfd, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogRepository) Open(filename string, user_agent_label string) (domain.BogOutFile, error) {
|
|
||||||
abs_path := f.absPath(filename, user_agent_label)
|
|
||||||
|
|
||||||
dir := path.Dir(abs_path)
|
|
||||||
os.MkdirAll(dir, 0750)
|
|
||||||
file, err := os.OpenFile(abs_path, os.O_RDONLY, 0644)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, domain.ErrNotExists
|
|
||||||
}
|
|
||||||
|
|
||||||
bfd := FileSystemBogFileData {filename, 0, time.Now(), file}
|
|
||||||
|
|
||||||
return bfd, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileSystemBogRepository) Delete(filename string, user_agent_label string) {
|
|
||||||
abs_path := f.absPath(filename, user_agent_label)
|
|
||||||
os.Remove(abs_path)
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
package integration
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
"caj-larsson/bog/domain"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserAgentDBRecord struct {
|
|
||||||
ID int64
|
|
||||||
Name string
|
|
||||||
LastSeen string
|
|
||||||
AllowanceSeconds int64
|
|
||||||
QuotaKB int64
|
|
||||||
QuotaUsedKB int64
|
|
||||||
}
|
|
||||||
|
|
||||||
var ErrUnparseableRecord = errors.New("record could not be mapped to entity")
|
|
||||||
|
|
||||||
func (r *UserAgentDBRecord) toEntity() (*domain.UserAgent, error) {
|
|
||||||
lastseen, err := time.Parse(time.RFC3339, r.LastSeen)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, ErrUnparseableRecord
|
|
||||||
}
|
|
||||||
|
|
||||||
var useragent = new(domain.UserAgent)
|
|
||||||
useragent.ID = r.ID
|
|
||||||
useragent.Name = r.Name
|
|
||||||
useragent.LastSeen = lastseen
|
|
||||||
useragent.AllowanceDuration = time.Duration(r.AllowanceSeconds * int64(time.Second))
|
|
||||||
useragent.FileQuota = domain.FileSizeQuota { r.QuotaKB, r.QuotaUsedKB }
|
|
||||||
return useragent, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromEntity(useragent domain.UserAgent) (*UserAgentDBRecord, error) {
|
|
||||||
var record = new(UserAgentDBRecord)
|
|
||||||
record.ID = useragent.ID
|
|
||||||
record.Name = useragent.Name
|
|
||||||
record.LastSeen = useragent.LastSeen.Format(time.RFC3339)
|
|
||||||
record.AllowanceSeconds = int64(useragent.AllowanceDuration.Seconds())
|
|
||||||
record.QuotaKB = useragent.FileQuota.AllowanceKB
|
|
||||||
record.QuotaUsedKB = useragent.FileQuota.CurrentUsage
|
|
||||||
return record, nil
|
|
||||||
}
|
|
@ -0,0 +1,40 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
// "fmt"
|
||||||
|
// "net/http/httptest"
|
||||||
|
// "net/http"
|
||||||
|
|
||||||
|
// "strings"
|
||||||
|
// "time"
|
||||||
|
// "caj-larsson/bog/domain_dataswamp"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApplication(t *testing.T) {
|
||||||
|
|
||||||
|
// file_service := domain_dataswamp.NewBogFileService(
|
||||||
|
// mock.NewMockUserAgentRepository(),
|
||||||
|
// mock.NewMockFileRepository(),
|
||||||
|
// 1000,
|
||||||
|
// time.Hour,
|
||||||
|
// )
|
||||||
|
|
||||||
|
// bog := Bog {
|
||||||
|
// router: new(http.ServeMux),
|
||||||
|
// file_service: file_service,
|
||||||
|
// address: "fake",
|
||||||
|
// }
|
||||||
|
// bog.routes()
|
||||||
|
// req := httptest.NewRequest("POST", "/apath", strings.NewReader("testdata"))
|
||||||
|
// req.Header.Add("User-Agent", "testingclient")
|
||||||
|
// req.Header.Add("Content-Length", "8")
|
||||||
|
// w := httptest.NewRecorder()
|
||||||
|
// bog.router.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
// if (w.Code != 200){
|
||||||
|
// fmt.Printf("%v", w)
|
||||||
|
// t.Error("not ok")
|
||||||
|
// }
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package application
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -1,4 +1,4 @@
|
|||||||
package application
|
package server
|
||||||
|
|
||||||
|
|
||||||
import (
|
import (
|
@ -1,94 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
"caj-larsson/bog/domain"
|
|
||||||
"caj-larsson/bog/test/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var file_ref1 = domain.FileReference { "path1", "ua1" }
|
|
||||||
var file_ref2 = domain.FileReference { "path1", "ua2" }
|
|
||||||
var file_ref3 = domain.FileReference { "path2", "ua1" }
|
|
||||||
|
|
||||||
|
|
||||||
func NewTestBogFileService() domain.BogFileService {
|
|
||||||
file_repo := mock.NewMockFileRepository()
|
|
||||||
ua_repo := mock.NewMockUserAgentRepository()
|
|
||||||
return domain.NewBogFileService(ua_repo, file_repo, 1024, time.Hour)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFileDontExist(t *testing.T) {
|
|
||||||
s := NewTestBogFileService()
|
|
||||||
outfile, err := s.OpenOutFile(file_ref1)
|
|
||||||
|
|
||||||
if outfile != nil && err != domain.ErrNotExists {
|
|
||||||
t.Errorf("File shall not exist by default")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFileIsStored(t *testing.T) {
|
|
||||||
s := NewTestBogFileService()
|
|
||||||
|
|
||||||
fakefile := bytes.NewBufferString("My bog data")
|
|
||||||
|
|
||||||
err := s.SaveFile(file_ref1, fakefile, int64(fakefile.Len()))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("A small file should be writable %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
largefakefile := bytes.NewBufferString("")
|
|
||||||
|
|
||||||
for largefakefile.Len() < 64000 {
|
|
||||||
_, err = largefakefile.WriteString("A very repetitive file")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.SaveFile(file_ref3, largefakefile, int64(largefakefile.Len()))
|
|
||||||
|
|
||||||
if err != domain.ErrExceedQuota {
|
|
||||||
t.Errorf("too large files should not be excepted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestFileIsReadBack(t *testing.T) {
|
|
||||||
s := NewTestBogFileService()
|
|
||||||
|
|
||||||
infile := bytes.NewBufferString("My bog data")
|
|
||||||
|
|
||||||
_ = s.SaveFile(file_ref1, infile, int64(infile.Len()))
|
|
||||||
|
|
||||||
outbogfile, _ := s.OpenOutFile(file_ref1)
|
|
||||||
|
|
||||||
outfile := bytes.NewBufferString("")
|
|
||||||
_, _ = outfile.ReadFrom(outbogfile)
|
|
||||||
|
|
||||||
if outfile.String() != "My bog data" {
|
|
||||||
t.Errorf("file corrupted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestUAIsolation(t *testing.T) {
|
|
||||||
file_repo := mock.NewMockFileRepository()
|
|
||||||
ua_repo := mock.NewMockUserAgentRepository()
|
|
||||||
s := domain.NewBogFileService(ua_repo, file_repo, 1024, time.Hour)
|
|
||||||
|
|
||||||
ua1_file := bytes.NewBufferString("My bog data ua1")
|
|
||||||
ua2_file := bytes.NewBufferString("My bog data ua2")
|
|
||||||
|
|
||||||
_ = s.SaveFile(file_ref1, ua1_file, int64(ua1_file.Len()))
|
|
||||||
_ = s.SaveFile(file_ref2, ua2_file, int64(ua2_file.Len()))
|
|
||||||
|
|
||||||
outbogfile, _ := s.OpenOutFile(file_ref1)
|
|
||||||
|
|
||||||
outfile := bytes.NewBufferString("")
|
|
||||||
_, _ = outfile.ReadFrom(outbogfile)
|
|
||||||
|
|
||||||
if outfile.String() != "My bog data ua1" {
|
|
||||||
t.Errorf("file corrupted")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
"caj-larsson/bog/domain"
|
|
||||||
"caj-larsson/bog/integration"
|
|
||||||
"caj-larsson/bog/test/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFsFileRepo(t *testing.T) {
|
|
||||||
|
|
||||||
var fac = func()domain.FileDataRepository {
|
|
||||||
r := t.TempDir()
|
|
||||||
d := path.Join(r, "fs")
|
|
||||||
repo := integration.FileSystemBogRepository { d }
|
|
||||||
return &repo
|
|
||||||
}
|
|
||||||
|
|
||||||
mock.BogFileRepositoryContract(fac, t)
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"path"
|
|
||||||
"os"
|
|
||||||
// "io"
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
"caj-larsson/bog/domain"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
type MockBogFile struct {
|
|
||||||
filename string
|
|
||||||
file afero.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f MockBogFile) Path() string {
|
|
||||||
return f.filename
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f MockBogFile) Size() int64 {
|
|
||||||
stat, _ := f.file.Stat()
|
|
||||||
return int64(stat.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f MockBogFile) Read(p []byte) (int, error) {
|
|
||||||
return f.file.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f MockBogFile) Write(p []byte) (int, error) {
|
|
||||||
return f.file.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f MockBogFile) Close() error {
|
|
||||||
return f.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f MockBogFile) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
return f.file.Seek(offset, whence)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f MockBogFile) Modified() time.Time {
|
|
||||||
stat, _ := f.file.Stat()
|
|
||||||
return stat.ModTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
// The actual repository
|
|
||||||
type MockFileRepository struct {
|
|
||||||
fs afero.Fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMockFileRepository() domain.FileDataRepository {
|
|
||||||
return MockFileRepository { afero.NewMemMapFs() }
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r MockFileRepository) Create(filename string, user_agent_label string) (domain.BogInFile, error) {
|
|
||||||
abs_path := path.Join( filename, user_agent_label)
|
|
||||||
dir := path.Dir(abs_path)
|
|
||||||
r.fs.MkdirAll(dir, 0750)
|
|
||||||
file, err := r.fs.OpenFile(abs_path, os.O_RDWR|os.O_CREATE, 0644)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bf := MockBogFile {filename, file}
|
|
||||||
|
|
||||||
return bf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r MockFileRepository) Open(filename string, user_agent_label string) (domain.BogOutFile, error) {
|
|
||||||
abs_path := path.Join(filename, user_agent_label)
|
|
||||||
|
|
||||||
dir := path.Dir(abs_path)
|
|
||||||
r.fs.MkdirAll(dir, 0750)
|
|
||||||
file, err := r.fs.OpenFile(abs_path, os.O_RDONLY, 0644)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, domain.ErrNotExists
|
|
||||||
}
|
|
||||||
|
|
||||||
bf := MockBogFile {filename, file}
|
|
||||||
return bf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r MockFileRepository) Delete(filename string, user_agent_label string) {
|
|
||||||
abs_path := path.Join(filename, user_agent_label)
|
|
||||||
r.fs.Remove(abs_path)
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
//"caj-larsson/bog/domain"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestMockFileRepo(t *testing.T) {
|
|
||||||
BogFileRepositoryContract(NewMockFileRepository, t)
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
// "time"
|
|
||||||
"caj-larsson/bog/domain"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
type MockUserAgentRepository struct {
|
|
||||||
IdIdx map[int64]*domain.UserAgent
|
|
||||||
NameIdx map[string]*domain.UserAgent
|
|
||||||
NextId int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMockUserAgentRepository() *MockUserAgentRepository {
|
|
||||||
r := new(MockUserAgentRepository)
|
|
||||||
r.NextId = 0
|
|
||||||
r.IdIdx = make(map[int64]*domain.UserAgent)
|
|
||||||
r.NameIdx = make(map[string]*domain.UserAgent)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *MockUserAgentRepository) Create(useragent domain.UserAgent) (*domain.UserAgent, error) {
|
|
||||||
r.NextId += 1
|
|
||||||
useragent.ID = r.NextId
|
|
||||||
|
|
||||||
r.IdIdx[useragent.ID] = &useragent
|
|
||||||
r.NameIdx[useragent.Name] = &useragent
|
|
||||||
return &useragent, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (r *MockUserAgentRepository) All() ([]domain.UserAgent, error) {
|
|
||||||
v := make([]domain.UserAgent, 0, len(r.IdIdx))
|
|
||||||
|
|
||||||
for _, value := range r.IdIdx {
|
|
||||||
v = append(v, *value)
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (r *MockUserAgentRepository) GetByName(name string) (*domain.UserAgent, error) {
|
|
||||||
useragent, exists := r.NameIdx[name]
|
|
||||||
if exists {
|
|
||||||
return useragent, nil
|
|
||||||
}
|
|
||||||
return nil, domain.ErrNotExists
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (r *MockUserAgentRepository) Update(id int64, useragent domain.UserAgent) (*domain.UserAgent, error) {
|
|
||||||
original := *r.IdIdx[id]
|
|
||||||
useragent.ID = id
|
|
||||||
r.IdIdx[id] = &useragent
|
|
||||||
r.NameIdx[original.Name] = &useragent
|
|
||||||
return &useragent, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (r *MockUserAgentRepository) Delete(id int64) error {
|
|
||||||
original := *r.IdIdx[id]
|
|
||||||
delete(r.NameIdx, original.Name)
|
|
||||||
delete(r.IdIdx, original.ID)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
"caj-larsson/bog/domain"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMockUserAgentRepo(t *testing.T) {
|
|
||||||
r := NewMockUserAgentRepository()
|
|
||||||
|
|
||||||
all, err := r.All()
|
|
||||||
|
|
||||||
if len(all) != 0 && err != nil {
|
|
||||||
t.Errorf("New repo should be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
ua := domain.UserAgent {23, "n1", time.Now(), time.Duration(time.Hour * 3), domain.FileSizeQuota {1000, 0} }
|
|
||||||
|
|
||||||
|
|
||||||
ua1, _ := r.Create(ua)
|
|
||||||
ua.Name = "n2"
|
|
||||||
ua2, _ := r.Create(ua)
|
|
||||||
if ua1 == ua2 {
|
|
||||||
t.Errorf("Must create unique items")
|
|
||||||
}
|
|
||||||
|
|
||||||
all, err = r.All()
|
|
||||||
|
|
||||||
if len(all) != 2 && err != nil {
|
|
||||||
t.Errorf("After adding there should be two Useragent")
|
|
||||||
}
|
|
||||||
|
|
||||||
if ua.ID != 23 {
|
|
||||||
t.Errorf("It does not change the original UserAgent")
|
|
||||||
}
|
|
||||||
|
|
||||||
ua3, _ := r.GetByName("n2")
|
|
||||||
|
|
||||||
if ua3 != ua2 {
|
|
||||||
t.Errorf("It the correct ua is acquired")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Delete(ua2.ID) != nil {
|
|
||||||
t.Errorf("Must delete without error")
|
|
||||||
}
|
|
||||||
|
|
||||||
all, err = r.All()
|
|
||||||
if len(all) != 1 && err != nil {
|
|
||||||
t.Errorf("After deleting one there should be one UA ")
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue