mirror of https://github.com/mickael-menu/zk
Refactor and test note creation
parent
e3f95cd1e1
commit
f2025b3a60
@ -0,0 +1,171 @@
|
||||
package note
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/mickael-menu/zk/core"
|
||||
"github.com/mickael-menu/zk/core/zk"
|
||||
"github.com/mickael-menu/zk/util/assert"
|
||||
"github.com/mickael-menu/zk/util/opt"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
filenameTemplate := spyTemplateString("filename")
|
||||
bodyTemplate := spyTemplateString("body")
|
||||
|
||||
res, err := create(
|
||||
CreateOpts{
|
||||
Dir: zk.Dir{
|
||||
Name: "log",
|
||||
Path: "/test/log",
|
||||
Config: zk.DirConfig{
|
||||
Extra: map[string]string{
|
||||
"hello": "world",
|
||||
},
|
||||
},
|
||||
},
|
||||
Title: opt.NewString("Note title"),
|
||||
Content: opt.NewString("Note content"),
|
||||
},
|
||||
createDeps{
|
||||
filenameTemplate: &filenameTemplate,
|
||||
bodyTemplate: &bodyTemplate,
|
||||
genId: func() string { return "abc" },
|
||||
validatePath: func(path string) (bool, error) { return true, nil },
|
||||
},
|
||||
)
|
||||
|
||||
// Check the created note.
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, &createdNote{
|
||||
path: "/test/log/filename.md",
|
||||
content: "body",
|
||||
})
|
||||
|
||||
// Check that the templates received the proper render contexts.
|
||||
assert.Equal(t, filenameTemplate.Contexts, []renderContext{{
|
||||
ID: "abc",
|
||||
Title: "Note title",
|
||||
Content: "Note content",
|
||||
Dir: "log",
|
||||
Extra: map[string]string{
|
||||
"hello": "world",
|
||||
},
|
||||
}})
|
||||
assert.Equal(t, bodyTemplate.Contexts, []renderContext{{
|
||||
ID: "abc",
|
||||
Title: "Note title",
|
||||
Content: "Note content",
|
||||
Dir: "log",
|
||||
Filename: "filename.md",
|
||||
FilenameStem: "filename",
|
||||
Extra: map[string]string{
|
||||
"hello": "world",
|
||||
},
|
||||
}})
|
||||
}
|
||||
|
||||
func TestCreateTriesUntilValidPath(t *testing.T) {
|
||||
filenameTemplate := spyTemplate(func(context renderContext) string {
|
||||
return context.ID
|
||||
})
|
||||
bodyTemplate := spyTemplateString("body")
|
||||
|
||||
res, err := create(
|
||||
CreateOpts{
|
||||
Dir: zk.Dir{
|
||||
Name: "log",
|
||||
Path: "/test/log",
|
||||
},
|
||||
Title: opt.NewString("Note title"),
|
||||
},
|
||||
createDeps{
|
||||
filenameTemplate: &filenameTemplate,
|
||||
bodyTemplate: &bodyTemplate,
|
||||
genId: incrementingID(),
|
||||
validatePath: func(path string) (bool, error) {
|
||||
return path == "/test/log/3.md", nil
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Check the created note.
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, &createdNote{
|
||||
path: "/test/log/3.md",
|
||||
content: "body",
|
||||
})
|
||||
|
||||
assert.Equal(t, filenameTemplate.Contexts, []renderContext{
|
||||
{
|
||||
ID: "1",
|
||||
Title: "Note title",
|
||||
Dir: "log",
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
Title: "Note title",
|
||||
Dir: "log",
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
Title: "Note title",
|
||||
Dir: "log",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateErrorWhenNoValidPaths(t *testing.T) {
|
||||
_, err := create(
|
||||
CreateOpts{
|
||||
Dir: zk.Dir{
|
||||
Name: "log",
|
||||
Path: "/test/log",
|
||||
},
|
||||
},
|
||||
createDeps{
|
||||
filenameTemplate: core.TemplateFunc(func(context interface{}) (string, error) {
|
||||
return "filename", nil
|
||||
}),
|
||||
bodyTemplate: core.NullTemplate,
|
||||
genId: func() string { return "abc" },
|
||||
validatePath: func(path string) (bool, error) { return false, nil },
|
||||
},
|
||||
)
|
||||
|
||||
assert.Err(t, err, "/test/log/filename.md: note already exists")
|
||||
}
|
||||
|
||||
func spyTemplate(result func(renderContext) string) TemplateSpy {
|
||||
return TemplateSpy{
|
||||
Contexts: make([]renderContext, 0),
|
||||
Result: result,
|
||||
}
|
||||
}
|
||||
|
||||
func spyTemplateString(result string) TemplateSpy {
|
||||
return TemplateSpy{
|
||||
Contexts: make([]renderContext, 0),
|
||||
Result: func(_ renderContext) string { return result },
|
||||
}
|
||||
}
|
||||
|
||||
type TemplateSpy struct {
|
||||
Result func(renderContext) string
|
||||
Contexts []renderContext
|
||||
}
|
||||
|
||||
func (m *TemplateSpy) Render(context interface{}) (string, error) {
|
||||
renderContext := context.(renderContext)
|
||||
m.Contexts = append(m.Contexts, renderContext)
|
||||
return m.Result(renderContext), nil
|
||||
}
|
||||
|
||||
func incrementingID() func() string {
|
||||
i := 0
|
||||
return func() string {
|
||||
i++
|
||||
return fmt.Sprintf("%d", i)
|
||||
}
|
||||
}
|
@ -1,12 +1,28 @@
|
||||
package core
|
||||
|
||||
// TemplateLoader parses a given string template.
|
||||
type TemplateLoader interface {
|
||||
Load(template string) (Template, error)
|
||||
LoadFile(path string) (Template, error)
|
||||
}
|
||||
|
||||
// Template renders strings using a given context.
|
||||
type Template interface {
|
||||
Render(context interface{}) (string, error)
|
||||
}
|
||||
|
||||
// TemplateLoader parses a given string template.
|
||||
type TemplateLoader interface {
|
||||
Load(template string) (Template, error)
|
||||
LoadFile(path string) (Template, error)
|
||||
// TemplateFunc is an adapter to use a function as a Template.
|
||||
type TemplateFunc func(context interface{}) (string, error)
|
||||
|
||||
func (f TemplateFunc) Render(context interface{}) (string, error) {
|
||||
return f(context)
|
||||
}
|
||||
|
||||
// NullTemplate is a Template returning always an empty string.
|
||||
var NullTemplate = nullTemplate{}
|
||||
|
||||
type nullTemplate struct{}
|
||||
|
||||
func (t nullTemplate) Render(context interface{}) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/mickael-menu/zk/util/opt"
|
||||
)
|
||||
|
||||
// ReadStdinPipe returns the content of any piped input.
|
||||
func ReadStdinPipe() (opt.String, error) {
|
||||
fi, err := os.Stdin.Stat()
|
||||
if err != nil {
|
||||
return opt.NullString, err
|
||||
}
|
||||
if fi.Mode()&os.ModeNamedPipe == 0 {
|
||||
// Not a pipe
|
||||
return opt.NullString, nil
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
bytes, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return opt.NullString, err
|
||||
}
|
||||
|
||||
return opt.NewNotEmptyString(string(bytes)), nil
|
||||
}
|
Loading…
Reference in New Issue