Skip to content

Commit

Permalink
test: add tests for push
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbrdn committed Nov 13, 2023
1 parent eaf9306 commit 59a37e3
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 30 deletions.
41 changes: 27 additions & 14 deletions internal/commands/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ func PushToRemote(filename string, remoteName string, optPath string, optTree bo
return err
}
if stat.IsDir() {
return pushDirectory(abs, remote, optPath, optTree, log)
return pushDirectory(abs, remote, optPath, optTree)
} else {
return PushFile(filename, remote, optPath, log)
return pushFile(filename, remote, optPath)
}
}

Expand All @@ -61,7 +61,7 @@ func sanitizePath(path string) string {
return p
}

func pushDirectory(absDirname string, remote remotes.Remote, optPath string, optTree bool, log *slog.Logger) error {
func pushDirectory(absDirname string, remote remotes.Remote, optPath string, optTree bool) error {
err := filepath.WalkDir(absDirname, func(path string, d fs.DirEntry, err error) error {
if d.IsDir() || !strings.HasSuffix(d.Name(), ".json") {
return nil
Expand All @@ -74,7 +74,7 @@ func pushDirectory(absDirname string, remote remotes.Remote, optPath string, opt
optPath = filepath.Dir(strings.TrimPrefix(path, absDirname))
}

err = PushFile(path, remote, optPath, log)
err = pushFile(path, remote, optPath)

return err
})
Expand All @@ -83,31 +83,44 @@ func pushDirectory(absDirname string, remote remotes.Remote, optPath string, opt

}

func PushFile(filename string, remote remotes.Remote, optPath string, log *slog.Logger) error {
abs, raw, err := internal.ReadRequiredFile(filename)
func pushFile(filename string, remote remotes.Remote, optPath string) error {
log := slog.Default()
_, raw, err := internal.ReadRequiredFile(filename)
if err != nil {
log.Error("couldn't read file", "error", err)
log.Error("couldn't read file", "filename", filename, "error", err)
return err
}

_, err = PushFile(raw, remote, optPath)
if err != nil {
return err
}
return nil
}
func PushFile(raw []byte, remote remotes.Remote, optPath string) (model.TMID, error) {
log := slog.Default()
tm, err := ValidateThingModel(raw)
if err != nil {
log.Error("validation failed", "error", err)
return err
return model.TMID{}, err
}

versioned, id, err := prepareToImport(tm, raw, optPath)
if err != nil {
return err
return model.TMID{}, err
}

err = remote.Push(tm, id, versioned)
err = remote.Push(id, versioned)
if err != nil {
log.Error("error pushing to remote", "filename", abs, "error", err)
return err
var errExists *remotes.ErrTMExists
if errors.As(err, &errExists) {
log.Info("Thing Model already exists", "existing-id", errExists.ExistingId)
return errExists.ExistingId, nil
}
log.Error("error pushing to remote", "error", err)
return id, err
}
log.Info("pushed successfully")
return nil
return id, nil
}

func prepareToImport(tm *model.ThingModel, raw []byte, optPath string) ([]byte, model.TMID, error) {
Expand Down
113 changes: 100 additions & 13 deletions internal/commands/push_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package commands

import (
"bytes"
"encoding/json"
"fmt"
"github.com/stretchr/testify/assert"
"github.com/web-of-things-open-source/tm-catalog-cli/internal"
"github.com/web-of-things-open-source/tm-catalog-cli/internal/model"
"io/fs"
"log"
"github.com/web-of-things-open-source/tm-catalog-cli/internal/remotes"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
Expand Down Expand Up @@ -110,14 +112,99 @@ func TestGenerateNewID(t *testing.T) {
assert.Equal(t, "author/omnicorp/senseall/opt/dir/v3.2.1-20231110123243-bf21a9e8fbc5.tm.json", id.String())
}

func TestName(t *testing.T) {
root, _ := os.Getwd()
fileSystem := os.DirFS(root)
_ = fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
log.Fatal(err)
}
fmt.Println(path + " " + d.Name())
return nil
})
func TestPushToRemoteUnversioned(t *testing.T) {
wd, _ := os.Getwd()
root, err := os.MkdirTemp(wd, "tm-catalog")
assert.NoError(t, err)
t.Logf("test root: %s", root)
defer func() { _ = os.RemoveAll(root) }()

remote, err := remotes.NewFileRemote(
map[string]any{
"type": "file",
"url": "file:" + root,
})
assert.NoError(t, err)

// write first TM
_, raw, err := internal.ReadRequiredFile("../../test/data/push/omnilamp.json")
assert.NoError(t, err)
id, err := PushFile(raw, remote, "")
assert.NoError(t, err)
testTMDir := filepath.Join(root, filepath.Dir(id.String()))
t.Logf("test TM dir: %s", testTMDir)
entries, _ := os.ReadDir(testTMDir)
assert.Len(t, entries, 1)
firstSaved := entries[0].Name()

// attempt overwriting with the same content
time.Sleep(1050 * time.Millisecond)
id, err = PushFile(raw, remote, "")
assert.NoError(t, err)
entries, _ = os.ReadDir(filepath.Join(root, filepath.Dir(id.String())))
assert.Len(t, entries, 1)
assert.Equal(t, firstSaved, entries[0].Name())

// write a changed file - saves new version
time.Sleep(1050 * time.Millisecond)
raw = bytes.Replace(raw, []byte("Lamp Thing Model"), []byte("Lamp Thing"), 1)
id, err = PushFile(raw, remote, "")
assert.NoError(t, err)
entries, _ = os.ReadDir(filepath.Join(root, filepath.Dir(id.String())))
assert.Len(t, entries, 2)
assert.Equal(t, firstSaved, entries[0].Name())

// change the file back and write - saves new version
time.Sleep(1050 * time.Millisecond)
raw = bytes.Replace(raw, []byte("Lamp Thing"), []byte("Lamp Thing Model"), 1)
id, err = PushFile(raw, remote, "")
assert.NoError(t, err)
entries, _ = os.ReadDir(filepath.Join(root, filepath.Dir(id.String())))
assert.Len(t, entries, 3)
assert.Equal(t, firstSaved, entries[0].Name())

}
func TestPushToRemoteVersioned(t *testing.T) {
root, err := os.MkdirTemp(os.TempDir(), "tm-catalog")
assert.NoError(t, err)
defer func() { _ = os.RemoveAll(root) }()

remote, err := remotes.NewFileRemote(
map[string]any{
"type": "file",
"url": "file:" + root,
})
assert.NoError(t, err)

// write first TM
_, raw, err := internal.ReadRequiredFile("../../test/data/push/omnilamp-versioned.json")
assert.NoError(t, err)

id, err := PushFile(raw, remote, "")
assert.NoError(t, err)
entries, _ := os.ReadDir(filepath.Join(root, filepath.Dir(id.String())))
assert.Len(t, entries, 1)
assert.True(t, strings.HasPrefix(entries[0].Name(), "v3.2.1"))

// write a new version of ThingModel - saves new version
time.Sleep(1050 * time.Millisecond)
raw = bytes.Replace(raw, []byte("\"v3.2.1\""), []byte("\"v4.0.0\""), 1)
id, err = PushFile(raw, remote, "")
assert.NoError(t, err)
entries, _ = os.ReadDir(filepath.Join(root, filepath.Dir(id.String())))
assert.Len(t, entries, 2)
assert.True(t, strings.HasPrefix(entries[1].Name(), "v4.0.0"))

// change an older version and push - saves new version
_, raw, err = internal.ReadRequiredFile("../../test/data/push/omnilamp-versioned.json")
time.Sleep(1050 * time.Millisecond)
raw = bytes.Replace(raw, []byte("Lamp Thing Model"), []byte("Lamp Thing"), 1)
id, err = PushFile(raw, remote, "")
assert.NoError(t, err)
entries, _ = os.ReadDir(filepath.Join(root, filepath.Dir(id.String())))
assert.Len(t, entries, 3)
assert.True(t, strings.HasPrefix(entries[0].Name(), "v3.2.1"))
assert.True(t, strings.HasPrefix(entries[1].Name(), "v3.2.1"))
assert.True(t, strings.HasPrefix(entries[2].Name(), "v4.0.0"))

}
12 changes: 10 additions & 2 deletions internal/remotes/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ type FileRemote struct {
root string
}

type ErrTMExists struct {
ExistingId model.TMID
}

func (e *ErrTMExists) Error() string {
return fmt.Sprintf("Thing Model already exists under id: %v", e.ExistingId)
}

func NewFileRemote(config map[string]any) (*FileRemote, error) {
urlString := config["url"].(string)
rootUrl, err := url2.Parse(urlString)
Expand All @@ -44,7 +52,7 @@ func NewFileRemote(config map[string]any) (*FileRemote, error) {
}, nil
}

func (f *FileRemote) Push(_ *model.ThingModel, id model.TMID, raw []byte) error {
func (f *FileRemote) Push(id model.TMID, raw []byte) error {
if len(raw) == 0 {
return errors.New("nothing to write")
}
Expand All @@ -56,7 +64,7 @@ func (f *FileRemote) Push(_ *model.ThingModel, id model.TMID, raw []byte) error

if found, existingId := f.getExistingID(id); found {
slog.Default().Info(fmt.Sprintf("TM already exists under ID %v", existingId))
return nil
return &ErrTMExists{ExistingId: existingId}
}

err = os.WriteFile(fullPath, raw, os.ModePerm) //fixme: review permissions
Expand Down
4 changes: 3 additions & 1 deletion internal/remotes/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
)

type Remote interface {
Push(model *model.ThingModel, id model.TMID, raw []byte) error
// Push writes the Thing Model file into the path under root that corresponds to id.
// Returns ErrTMExists if the same file is already stored with a different timestamp
Push(id model.TMID, raw []byte) error
Fetch(id model.TMID) ([]byte, error)
CreateToC() error
List(filter string) (model.Toc, error)
Expand Down
38 changes: 38 additions & 0 deletions test/data/push/omnilamp-versioned.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"@context": [
"https://www.w3.org/2022/wot/td/v1.1",
"https://schema.org/docs/jsonldcontext.json"
],
"@type": "tm:ThingModel",
"title": "Lamp Thing Model",
"schema:manufacturer": {
"name": "omnicorp"
},
"schema:mpn": "omnilamp",
"schema:author": {
"name": "omnicorp TM department"
},
"properties": {
"status": {
"description": "current status of the lamp (on|off)",
"type": "string",
"readOnly": true
}
},
"actions": {
"toggle": {
"description": "Turn the lamp on or off"
}
},
"events": {
"overheating": {
"description": "Lamp reaches a critical temperature (overheating)",
"data": {
"type": "string"
}
}
},
"version": {
"model": "v3.2.1"
}
}
35 changes: 35 additions & 0 deletions test/data/push/omnilamp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"@context": [
"https://www.w3.org/2022/wot/td/v1.1",
"https://schema.org/docs/jsonldcontext.json"
],
"@type": "tm:ThingModel",
"title": "Lamp Thing Model",
"schema:manufacturer": {
"name": "omnicorp"
},
"schema:mpn": "omnilamp",
"schema:author": {
"name": "omnicorp TM department"
},
"properties": {
"status": {
"description": "current status of the lamp (on|off)",
"type": "string",
"readOnly": true
}
},
"actions": {
"toggle": {
"description": "Turn the lamp on or off"
}
},
"events": {
"overheating": {
"description": "Lamp reaches a critical temperature (overheating)",
"data": {
"type": "string"
}
}
}
}

0 comments on commit 59a37e3

Please sign in to comment.