-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make a lot of refactoring and get everything work
- Loading branch information
1 parent
861d99b
commit 8fa155e
Showing
47 changed files
with
1,605 additions
and
553 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
worklog.txt | ||
vendor/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package cache | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"path" | ||
"strings" | ||
) | ||
|
||
var cachePath = path.Join(os.TempDir(), "pullk_cache", "cache.json") | ||
|
||
// Cache is an interface for a Get/Set caching struct | ||
type Cache interface { | ||
Set(string, interface{}) error | ||
Get(string, interface{}) (bool, error) | ||
} | ||
|
||
// FS is an interface for interacting with the file system | ||
type FS interface { | ||
Mkdir(path string, perms os.FileMode) error | ||
WriteFile(path string, data []byte, perms os.FileMode) error | ||
ReadFile(path string) ([]byte, error) | ||
} | ||
|
||
// FSCache is a set of methods to write to and read from the cache, transforming JSON in the process | ||
type FSCache struct { | ||
CachePath string | ||
FS FS | ||
} | ||
|
||
// Set converts `x` to JSON and writes it to a file using `key` | ||
func (c FSCache) Set(key string, x interface{}) error { | ||
err := c.FS.Mkdir(c.CachePath, 0744) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
jsoned, err := json.Marshal(x) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.FS.WriteFile(c.filePath(key), jsoned, 0644) | ||
} | ||
|
||
// Get gets the contents of a file using `key` (if exists) and Unmarshals it to the struct `x` | ||
func (c FSCache) Get(key string, x interface{}) (bool, error) { | ||
data, err := c.FS.ReadFile(c.filePath(key)) | ||
if err != nil { | ||
// "no such file or directory" just should mean there's no cache entry | ||
if strings.Contains(err.Error(), "no such file or directory") { | ||
return false, nil | ||
} | ||
return false, err | ||
} | ||
|
||
return true, json.Unmarshal(data, x) | ||
} | ||
|
||
func (c FSCache) filePath(key string) string { | ||
return path.Join(c.CachePath, fmt.Sprintf("%s.json", key)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package cache | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestWrite(t *testing.T) { | ||
t.Run("Works when successfully JSON-ed and wrote to the file", func(t *testing.T) { | ||
m := mockFS{cache: map[string]cacheEntity{}} | ||
c := FSCache{ | ||
FS: &m, | ||
CachePath: "/tmp/", | ||
} | ||
err := c.Set("key1", testStruct{"val1"}) | ||
|
||
require.Nil(t, err) | ||
require.Equal(t, `{"x":"val1"}`, string(m.cache["/tmp/key1.json"].data)) | ||
require.Contains(t, m.mkdirs, "/tmp/") | ||
}) | ||
|
||
t.Run("Fails when couldn't do Mkdir", func(t *testing.T) { | ||
m := mockFS{ | ||
cache: map[string]cacheEntity{}, | ||
mkdirErr: fmt.Errorf("Mkdir: fail"), | ||
} | ||
c := FSCache{ | ||
FS: &m, | ||
CachePath: "/tmp/", | ||
} | ||
err := c.Set("key1", testStruct{"val1"}) | ||
|
||
require.EqualError(t, err, "Mkdir: fail") | ||
require.Nil(t, m.cache["/tmp/key1.json"].data) | ||
require.Contains(t, m.mkdirs, "/tmp/") | ||
}) | ||
|
||
t.Run("Fails when couldn't json.Marshal() the input", func(t *testing.T) { | ||
m := mockFS{cache: map[string]cacheEntity{}} | ||
c := FSCache{ | ||
FS: &m, | ||
CachePath: "/tmp/", | ||
} | ||
err := c.Set("key1", func() {}) // functions are unmarshable in Go | ||
|
||
log.Println(err) | ||
require.EqualError(t, err, "json: unsupported type: func()") | ||
require.Nil(t, m.cache["/tmp/key1.json"].data) | ||
require.Contains(t, m.mkdirs, "/tmp/") | ||
}) | ||
|
||
t.Run("Fails when couldn't write to the file", func(t *testing.T) { | ||
m := mockFS{ | ||
cache: map[string]cacheEntity{}, | ||
writeFileErr: fmt.Errorf("Failed to write to the file"), | ||
} | ||
c := FSCache{ | ||
FS: &m, | ||
CachePath: "/tmp/", | ||
} | ||
err := c.Set("key1", testStruct{"val1"}) | ||
|
||
require.EqualError(t, err, "Failed to write to the file") | ||
require.Nil(t, m.cache["/tmp/key1.json"].data) | ||
require.Contains(t, m.mkdirs, "/tmp/") | ||
}) | ||
} | ||
|
||
func TestRead(t *testing.T) { | ||
t.Run("Works when the file is readable and is correct JSON", func(t *testing.T) { | ||
s := testStruct{} | ||
m := mockFS{ | ||
cache: map[string]cacheEntity{ | ||
"/tmp/key1.json": cacheEntity{[]byte(`{"x":"val1"}`), 0777}, | ||
}, | ||
} | ||
c := FSCache{ | ||
FS: &m, | ||
CachePath: "/tmp/", | ||
} | ||
ok, err := c.Get("key1", &s) | ||
|
||
require.Nil(t, err) | ||
require.True(t, ok) | ||
require.Equal(t, testStruct{"val1"}, s) | ||
}) | ||
|
||
t.Run("Fails when couldn't read the file", func(t *testing.T) { | ||
s := testStruct{} | ||
m := mockFS{ | ||
readFileErr: fmt.Errorf("Some weird FS error"), | ||
} | ||
c := FSCache{ | ||
FS: &m, | ||
CachePath: "/tmp/", | ||
} | ||
ok, err := c.Get("key1", &s) | ||
|
||
require.False(t, ok) | ||
require.EqualError(t, err, "Some weird FS error") | ||
}) | ||
|
||
t.Run("Fails when couldn't unmarshal the contents", func(t *testing.T) { | ||
s := testStruct{} | ||
m := mockFS{ | ||
cache: map[string]cacheEntity{ | ||
"/tmp/key1.json": cacheEntity{[]byte(`incorrect JSON`), 0777}, | ||
}, | ||
} | ||
c := FSCache{ | ||
FS: &m, | ||
CachePath: "/tmp/", | ||
} | ||
ok, err := c.Get("key1", &s) | ||
|
||
require.True(t, ok) | ||
require.Contains(t, err.Error(), "invalid character 'i'") | ||
}) | ||
|
||
t.Run("Doesn't fail when the file didn't exist", func(t *testing.T) { | ||
s := testStruct{} | ||
m := mockFS{ | ||
readFileErr: fmt.Errorf("/tmp/key1.json: no such file or directory"), | ||
} | ||
c := FSCache{ | ||
FS: &m, | ||
CachePath: "/tmp/", | ||
} | ||
ok, err := c.Get("key1", &s) | ||
|
||
require.False(t, ok) | ||
require.Nil(t, err) | ||
}) | ||
} | ||
|
||
type cacheEntity struct { | ||
data []byte | ||
perm os.FileMode | ||
} | ||
|
||
type mockFS struct { | ||
mkdirs []string | ||
cache map[string]cacheEntity | ||
mkdirErr error | ||
writeFileErr error | ||
readFileErr error | ||
} | ||
|
||
func (m *mockFS) Mkdir(path string, perms os.FileMode) error { | ||
m.mkdirs = append(m.mkdirs, path) | ||
return m.mkdirErr | ||
} | ||
|
||
func (m *mockFS) WriteFile(key string, data []byte, perm os.FileMode) error { | ||
if m.writeFileErr == nil { | ||
m.cache[key] = cacheEntity{data, perm} | ||
} | ||
return m.writeFileErr | ||
} | ||
|
||
func (m *mockFS) ReadFile(key string) ([]byte, error) { | ||
entity, ok := m.cache[key] | ||
if !ok { | ||
return nil, m.readFileErr | ||
} | ||
return entity.data, m.readFileErr | ||
} | ||
|
||
type testStruct struct { | ||
X string `json:"x"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package main | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
) | ||
|
||
type RealFS struct{} | ||
|
||
func (f RealFS) Mkdir(path string, perms os.FileMode) error { | ||
return os.MkdirAll(path, perms) | ||
} | ||
func (f RealFS) WriteFile(path string, data []byte, perms os.FileMode) error { | ||
return ioutil.WriteFile(path, data, perms) | ||
} | ||
func (f RealFS) ReadFile(path string) ([]byte, error) { | ||
return ioutil.ReadFile(path) | ||
} |
Oops, something went wrong.