Skip to content

Commit

Permalink
Fix JSONArtifact registry tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jwillp committed Aug 22, 2024
1 parent 2e38e1a commit 782d328
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 67 deletions.
32 changes: 21 additions & 11 deletions artefactregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,48 @@ import (

// JSONArtifactRegistry implementation of a ArtifactRegistry that is saved as a JSON file.
type JSONArtifactRegistry struct {
GeneratedAt time.Time `json:"generatedAt"`
ArtifactMap map[string]*JSONArtifactRegistryProcessor `json:"files"`
FilePath string
mu sync.RWMutex // Mutex to protect concurrent access
GeneratedAt time.Time `json:"generatedAt"`
ArtifactMap map[string]*JSONArtifactRegistryProcessor `json:"files"`
FilePath string `json:"-"`
FileSystem FileSystem `json:"-"`
mu sync.RWMutex // Mutex to protect concurrent access
CurrentTimeProvider func() time.Time
}

type JSONArtifactRegistryProcessor struct {
Artifacts []string `json:"files"`
}

// NewJSONArtifactRegistry returns a new artifact file registry.
func NewJSONArtifactRegistry(fileName string) *JSONArtifactRegistry {
func NewJSONArtifactRegistry(fileName string, fs FileSystem) *JSONArtifactRegistry {
return &JSONArtifactRegistry{
GeneratedAt: time.Now(),
ArtifactMap: nil,
FilePath: fileName,
CurrentTimeProvider: func() time.Time {
return time.Now()
},
FileSystem: fs,
}
}

func (r *JSONArtifactRegistry) Load() error {
r.mu.Lock()
defer r.mu.Unlock()

bytes, err := os.ReadFile(r.FilePath)
bytes, err := r.FileSystem.ReadFile(r.FilePath)

if err != nil {
if os.IsNotExist(err) {
return nil
}
return errors.WrapWithMessage(err, errors.InternalErrorCode, "failed loading artifact file registry")
}

// empty file is okay
if len(bytes) == 0 {
return nil
}

if err := json.Unmarshal(bytes, r); err != nil {
return errors.WrapWithMessage(err, errors.InternalErrorCode, "failed loading artifact file registry")
}
Expand All @@ -52,15 +63,14 @@ func (r *JSONArtifactRegistry) Save() error {
r.mu.RLock()
defer r.mu.RUnlock()

if r.ArtifactMap == nil {
return nil
}
r.GeneratedAt = r.CurrentTimeProvider()

// Generate a JSON file containing all artifact files for clean up later on
js, err := json.MarshalIndent(r, "", " ")
if err != nil {
return errors.Wrap(err, "failed generating artifact file registry")
}
if err := os.WriteFile(r.FilePath, js, os.ModePerm); err != nil {
if err := r.FileSystem.WriteFile(r.FilePath, js, os.ModePerm); err != nil {
return errors.Wrap(err, "failed generating artifact file registry")
}

Expand Down
136 changes: 84 additions & 52 deletions artefactregistry_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package specter

import (
"fmt"
"github.com/stretchr/testify/require"
"os"
"testing"
"time"
Expand All @@ -9,37 +11,55 @@ import (
)

func TestJSONArtifactRegistry_Load(t *testing.T) {
tests := []struct {
name string
fileContent string
expectError bool
type given struct {
jsonFileContent string
}
type then struct {
expectedError error
expectedValue *JSONArtifactRegistry
}
tests := []struct {
name string
given given
then then
}{
{
name: "Successful Load",
fileContent: `{"generatedAt":"2024-01-01T00:00:00Z","files":{"processor1":{"files":["file1.txt"]}}}`,
expectError: false,
expectedValue: &JSONArtifactRegistry{
GeneratedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
ArtifactMap: map[string]*JSONArtifactRegistryProcessor{"processor1": {Artifacts: []string{"file1.txt"}}},
name: "Successful Load",
given: given{
jsonFileContent: `{"generatedAt":"2024-01-01T00:00:00Z","files":{"processor1":{"files":["file1.txt"]}}}`,
},
then: then{
expectedError: nil,
expectedValue: &JSONArtifactRegistry{
GeneratedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
ArtifactMap: map[string]*JSONArtifactRegistryProcessor{"processor1": {Artifacts: []string{"file1.txt"}}},
},
},
},
{
name: "File Not Exist",
fileContent: "",
expectError: false,
expectedValue: &JSONArtifactRegistry{
GeneratedAt: time.Time{},
ArtifactMap: nil,
name: "File Not Exist",
given: given{
jsonFileContent: "",
},
then: then{
expectedError: nil,
expectedValue: &JSONArtifactRegistry{
GeneratedAt: time.Time{},
ArtifactMap: nil,
},
},
},
{
name: "Malformed JSON",
fileContent: `{"files":{`,
expectError: true,
expectedValue: &JSONArtifactRegistry{
GeneratedAt: time.Time{},
ArtifactMap: nil,
name: "Malformed JSON",
given: given{
jsonFileContent: `{"files":{`,
},
then: then{
expectedError: fmt.Errorf("failed loading artifact file registry: unexpected end of JSON input"),
expectedValue: &JSONArtifactRegistry{
GeneratedAt: time.Time{},
ArtifactMap: nil,
},
},
},
}
Expand All @@ -48,47 +68,54 @@ func TestJSONArtifactRegistry_Load(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
// Setup
filePath := "test_registry.json"
err := os.WriteFile(filePath, []byte(tt.fileContent), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
defer os.Remove(filePath)

registry := &JSONArtifactRegistry{
FilePath: filePath,
fs := &mockFileSystem{}
err := fs.WriteFile(filePath, []byte(tt.given.jsonFileContent), os.ModePerm)
require.NoError(t, err)

registry := NewJSONArtifactRegistry(filePath, fs)
registry.CurrentTimeProvider = func() time.Time {
return time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
}

// Act
err = registry.Load()

// Assert
if tt.expectError {
if tt.then.expectedError != nil {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expectedValue, registry)
assert.Equal(t, tt.then.expectedValue.GeneratedAt, registry.GeneratedAt)
}
})
}
}

func TestJSONArtifactRegistry_Save(t *testing.T) {
type then struct {
expectedJSON string
expectedError error
}

tests := []struct {
name string
initialState *JSONArtifactRegistry
expectedJSON string
name string
given *JSONArtifactRegistry
then then
}{
{
name: "Successful Save",
initialState: &JSONArtifactRegistry{
given: &JSONArtifactRegistry{
ArtifactMap: map[string]*JSONArtifactRegistryProcessor{
"processor1": {
Artifacts: []string{"file1.txt"},
},
},
},
expectedJSON: `{
"generatedAt": "0001-01-01T00:00:00Z",
then: then{
expectedError: nil,
expectedJSON: `{
"generatedAt": "2024-01-01T00:00:00Z",
"files": {
"processor1": {
"files": [
Expand All @@ -97,36 +124,41 @@ func TestJSONArtifactRegistry_Save(t *testing.T) {
}
}
}`,
},
},
{
name: "Empty Registry",
initialState: &JSONArtifactRegistry{},
expectedJSON: `{
"generatedAt": "0001-01-01T00:00:00Z",
"files": {}
name: "Empty Registry",
given: &JSONArtifactRegistry{},
then: then{
expectedJSON: `{
"generatedAt": "2024-01-01T00:00:00Z",
"files": null
}`,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup
filePath := "test_registry.json"
registry := &JSONArtifactRegistry{
FilePath: filePath,
fs := &mockFileSystem{}
registry := NewJSONArtifactRegistry(filePath, fs)
registry.ArtifactMap = tt.given.ArtifactMap
registry.CurrentTimeProvider = func() time.Time {
return time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
}
registry.ArtifactMap = tt.initialState.ArtifactMap

// Act
err := registry.Save()

// Assert
assert.NoError(t, err)
if tt.then.expectedError != nil {
require.ErrorIs(t, err, tt.then.expectedError)
} else {
require.NoError(t, err)
}

// Read back and verify
data, err := os.ReadFile(filePath)
assert.NoError(t, err)
assert.JSONEq(t, tt.expectedJSON, string(data))
actualJSON, err := fs.ReadFile(filePath)
assert.JSONEq(t, tt.then.expectedJSON, string(actualJSON))
})
}
}
Expand Down
5 changes: 5 additions & 0 deletions source.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,18 @@ type FileSystem interface {
StatPath(location string) (os.FileInfo, error)
WalkDir(dirPath string, f func(path string, d fs.DirEntry, err error) error) error
ReadFile(filePath string) ([]byte, error)
WriteFile(filePath string, data []byte, perm fs.FileMode) error
}

var _ FileSystem = LocalFileSystem{}

// LocalFileSystem is an implementation of a FileSystem that works on the local file system where this program is running.
type LocalFileSystem struct{}

func (l LocalFileSystem) WriteFile(filePath string, data []byte, perm fs.FileMode) error {
return os.WriteFile(filePath, data, perm)
}

func (l LocalFileSystem) ReadFile(filePath string) ([]byte, error) {
return os.ReadFile(filePath)
}
Expand Down
24 changes: 20 additions & 4 deletions source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,30 @@ func (m mockFileInfo) Mode() os.FileMode { return m.mode }
func (m mockFileInfo) IsDir() bool { return m.isDir }
func (m mockFileInfo) Sys() interface{} { return nil }

var _ FileSystem = (*mockFileSystem)(nil)

type mockFileSystem struct {
files map[string][]byte
dirs map[string]bool

absErr error
statErr error
walkDirErr error
readFileErr error
absErr error
statErr error
walkDirErr error
readFileErr error
writeFileErr error
}

func (m *mockFileSystem) WriteFile(filePath string, data []byte, _ fs.FileMode) error {
if m.writeFileErr != nil {
return m.writeFileErr
}

if m.files == nil {
m.files = map[string][]byte{}
}

m.files[filePath] = data
return nil
}

func (m *mockFileSystem) Abs(location string) (string, error) {
Expand Down

0 comments on commit 782d328

Please sign in to comment.