mirror of
https://github.com/mickael-menu/zk
synced 2024-11-15 12:12:56 +00:00
0eaf26483f
Finalise transfer from old repo (github.com/mickael-menu/zk) to new (github.com/zk-org/zk) Co-authored-by: tjex <tjex@tjex.net>
603 lines
15 KiB
Go
603 lines
15 KiB
Go
package core
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/zk-org/zk/internal/util/opt"
|
|
"github.com/zk-org/zk/internal/util/test/assert"
|
|
)
|
|
|
|
func TestParseDefaultConfig(t *testing.T) {
|
|
conf, err := ParseConfig([]byte(""), ".zk/config.toml", NewDefaultConfig(), true)
|
|
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, conf, Config{
|
|
Notebook: NotebookConfig{
|
|
Dir: opt.NullString,
|
|
},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "{{id}}",
|
|
Extension: "md",
|
|
BodyTemplatePath: opt.NullString,
|
|
IDOptions: IDOptions{
|
|
Length: 4,
|
|
Charset: CharsetAlphanum,
|
|
Case: CaseLower,
|
|
},
|
|
DefaultTitle: "Untitled",
|
|
Lang: "en",
|
|
Exclude: []string{},
|
|
},
|
|
Groups: make(map[string]GroupConfig),
|
|
Format: FormatConfig{
|
|
Markdown: MarkdownConfig{
|
|
Hashtags: true,
|
|
ColonTags: false,
|
|
MultiwordTags: false,
|
|
LinkFormat: "markdown",
|
|
LinkEncodePath: true,
|
|
LinkDropExtension: true,
|
|
},
|
|
},
|
|
Tool: ToolConfig{
|
|
Editor: opt.NullString,
|
|
Shell: opt.NullString,
|
|
Pager: opt.NullString,
|
|
FzfPreview: opt.NullString,
|
|
FzfLine: opt.NullString,
|
|
},
|
|
LSP: LSPConfig{
|
|
Diagnostics: LSPDiagnosticConfig{
|
|
WikiTitle: LSPDiagnosticNone,
|
|
DeadLink: LSPDiagnosticError,
|
|
},
|
|
},
|
|
Filters: make(map[string]string),
|
|
Aliases: make(map[string]string),
|
|
Extra: make(map[string]string),
|
|
})
|
|
}
|
|
|
|
func TestParseInvalidConfig(t *testing.T) {
|
|
_, err := ParseConfig([]byte(`;`), ".zk/config.toml", NewDefaultConfig(), false)
|
|
assert.NotNil(t, err)
|
|
}
|
|
|
|
func TestParseComplete(t *testing.T) {
|
|
conf, err := ParseConfig([]byte(`
|
|
# Comment
|
|
|
|
[notebook]
|
|
dir = "~/notebook"
|
|
|
|
[note]
|
|
filename = "{{id}}.note"
|
|
extension = "txt"
|
|
template = "default.note"
|
|
language = "fr"
|
|
default-title = "Sans titre"
|
|
id-charset = "alphanum"
|
|
id-length = 4
|
|
id-case = "lower"
|
|
exclude = ["ignored", ".git"]
|
|
|
|
[format.markdown]
|
|
hashtags = false
|
|
colon-tags = true
|
|
multiword-tags = true
|
|
link-format = "custom"
|
|
link-encode-path = true
|
|
link-drop-extension = false
|
|
|
|
[tool]
|
|
editor = "vim"
|
|
shell = "/bin/bash"
|
|
pager = "less"
|
|
fzf-preview = "bat {1}"
|
|
fzf-line = "{{title}}"
|
|
fzf-options = "--border --height 40%"
|
|
fzf-bind-new = "Ctrl-C"
|
|
|
|
[extra]
|
|
hello = "world"
|
|
salut = "le monde"
|
|
|
|
[filter]
|
|
recents = "--created-after '2 weeks ago'"
|
|
journal = "journal --sort created"
|
|
|
|
[alias]
|
|
ls = "zk list $@"
|
|
ed = "zk edit $@"
|
|
|
|
[group.log]
|
|
paths = ["journal/daily", "journal/weekly"]
|
|
|
|
[group.log.note]
|
|
filename = "{{date}}.md"
|
|
extension = "note"
|
|
template = "log.md"
|
|
language = "de"
|
|
default-title = "Ohne Titel"
|
|
id-charset = "letters"
|
|
id-length = 8
|
|
id-case = "mixed"
|
|
exclude = ["new-ignored"]
|
|
|
|
[group.log.extra]
|
|
log-ext = "value"
|
|
|
|
[group.ref.note]
|
|
filename = "{{slug title}}.md"
|
|
|
|
[group."without path"]
|
|
paths = []
|
|
|
|
[lsp.completion]
|
|
use-additional-text-edits = true
|
|
note-label = "notelabel"
|
|
note-filter-text = "notefiltertext"
|
|
note-detail = "notedetail"
|
|
|
|
[lsp.diagnostics]
|
|
wiki-title = "hint"
|
|
dead-link = "none"
|
|
`), ".zk/config.toml", NewDefaultConfig(), true)
|
|
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, conf, Config{
|
|
Notebook: NotebookConfig{
|
|
Dir: opt.NewString("~/notebook"),
|
|
},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "{{id}}.note",
|
|
Extension: "txt",
|
|
BodyTemplatePath: opt.NewString("default.note"),
|
|
IDOptions: IDOptions{
|
|
Length: 4,
|
|
Charset: CharsetAlphanum,
|
|
Case: CaseLower,
|
|
},
|
|
Lang: "fr",
|
|
DefaultTitle: "Sans titre",
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
Groups: map[string]GroupConfig{
|
|
"log": {
|
|
Paths: []string{"journal/daily", "journal/weekly"},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "{{date}}.md",
|
|
Extension: "note",
|
|
BodyTemplatePath: opt.NewString("log.md"),
|
|
IDOptions: IDOptions{
|
|
Length: 8,
|
|
Charset: CharsetLetters,
|
|
Case: CaseMixed,
|
|
},
|
|
Lang: "de",
|
|
DefaultTitle: "Ohne Titel",
|
|
Exclude: []string{"ignored", ".git", "new-ignored"},
|
|
},
|
|
Extra: map[string]string{
|
|
"hello": "world",
|
|
"salut": "le monde",
|
|
"log-ext": "value",
|
|
},
|
|
},
|
|
"ref": {
|
|
Paths: []string{"ref"},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "{{slug title}}.md",
|
|
Extension: "txt",
|
|
BodyTemplatePath: opt.NewString("default.note"),
|
|
IDOptions: IDOptions{
|
|
Length: 4,
|
|
Charset: CharsetAlphanum,
|
|
Case: CaseLower,
|
|
},
|
|
Lang: "fr",
|
|
DefaultTitle: "Sans titre",
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
Extra: map[string]string{
|
|
"hello": "world",
|
|
"salut": "le monde",
|
|
},
|
|
},
|
|
"without path": {
|
|
Paths: []string{},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "{{id}}.note",
|
|
Extension: "txt",
|
|
BodyTemplatePath: opt.NewString("default.note"),
|
|
IDOptions: IDOptions{
|
|
Length: 4,
|
|
Charset: CharsetAlphanum,
|
|
Case: CaseLower,
|
|
},
|
|
Lang: "fr",
|
|
DefaultTitle: "Sans titre",
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
Extra: map[string]string{
|
|
"hello": "world",
|
|
"salut": "le monde",
|
|
},
|
|
},
|
|
},
|
|
Format: FormatConfig{
|
|
Markdown: MarkdownConfig{
|
|
Hashtags: false,
|
|
ColonTags: true,
|
|
MultiwordTags: true,
|
|
LinkFormat: "custom",
|
|
LinkEncodePath: true,
|
|
LinkDropExtension: false,
|
|
},
|
|
},
|
|
Tool: ToolConfig{
|
|
Editor: opt.NewString("vim"),
|
|
Shell: opt.NewString("/bin/bash"),
|
|
Pager: opt.NewString("less"),
|
|
FzfPreview: opt.NewString("bat {1}"),
|
|
FzfLine: opt.NewString("{{title}}"),
|
|
FzfOptions: opt.NewString("--border --height 40%"),
|
|
FzfBindNew: opt.NewString("Ctrl-C"),
|
|
},
|
|
LSP: LSPConfig{
|
|
Completion: LSPCompletionConfig{
|
|
Note: LSPCompletionTemplates{
|
|
Label: opt.NewString("notelabel"),
|
|
FilterText: opt.NewString("notefiltertext"),
|
|
Detail: opt.NewString("notedetail"),
|
|
},
|
|
UseAdditionalTextEdits: opt.True,
|
|
},
|
|
Diagnostics: LSPDiagnosticConfig{
|
|
WikiTitle: LSPDiagnosticHint,
|
|
DeadLink: LSPDiagnosticNone,
|
|
},
|
|
},
|
|
Filters: map[string]string{
|
|
"recents": "--created-after '2 weeks ago'",
|
|
"journal": "journal --sort created",
|
|
},
|
|
Aliases: map[string]string{
|
|
"ls": "zk list $@",
|
|
"ed": "zk edit $@",
|
|
},
|
|
Extra: map[string]string{
|
|
"hello": "world",
|
|
"salut": "le monde",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestParseMergesGroupConfig(t *testing.T) {
|
|
conf, err := ParseConfig([]byte(`
|
|
[note]
|
|
filename = "root-filename"
|
|
extension = "txt"
|
|
template = "root-template"
|
|
language = "fr"
|
|
default-title = "Sans titre"
|
|
id-charset = "letters"
|
|
id-length = 42
|
|
id-case = "upper"
|
|
exclude = ["ignored", ".git"]
|
|
|
|
[extra]
|
|
hello = "world"
|
|
salut = "le monde"
|
|
|
|
[group.log.note]
|
|
filename = "log-filename"
|
|
template = "log-template"
|
|
id-charset = "numbers"
|
|
id-length = 8
|
|
id-case = "mixed"
|
|
|
|
[group.log.extra]
|
|
hello = "override"
|
|
log-ext = "value"
|
|
|
|
[group.inherited]
|
|
`), ".zk/config.toml", NewDefaultConfig(), false)
|
|
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, conf, Config{
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "root-filename",
|
|
Extension: "txt",
|
|
BodyTemplatePath: opt.NewString("root-template"),
|
|
IDOptions: IDOptions{
|
|
Length: 42,
|
|
Charset: CharsetLetters,
|
|
Case: CaseUpper,
|
|
},
|
|
Lang: "fr",
|
|
DefaultTitle: "Sans titre",
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
Groups: map[string]GroupConfig{
|
|
"log": {
|
|
Paths: []string{"log"},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "log-filename",
|
|
Extension: "txt",
|
|
BodyTemplatePath: opt.NewString("log-template"),
|
|
IDOptions: IDOptions{
|
|
Length: 8,
|
|
Charset: CharsetNumbers,
|
|
Case: CaseMixed,
|
|
},
|
|
Lang: "fr",
|
|
DefaultTitle: "Sans titre",
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
Extra: map[string]string{
|
|
"hello": "override",
|
|
"salut": "le monde",
|
|
"log-ext": "value",
|
|
},
|
|
},
|
|
"inherited": {
|
|
Paths: []string{"inherited"},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "root-filename",
|
|
Extension: "txt",
|
|
BodyTemplatePath: opt.NewString("root-template"),
|
|
IDOptions: IDOptions{
|
|
Length: 42,
|
|
Charset: CharsetLetters,
|
|
Case: CaseUpper,
|
|
},
|
|
Lang: "fr",
|
|
DefaultTitle: "Sans titre",
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
Extra: map[string]string{
|
|
"hello": "world",
|
|
"salut": "le monde",
|
|
},
|
|
},
|
|
},
|
|
Format: FormatConfig{
|
|
Markdown: MarkdownConfig{
|
|
Hashtags: true,
|
|
ColonTags: false,
|
|
MultiwordTags: false,
|
|
LinkFormat: "markdown",
|
|
LinkEncodePath: true,
|
|
LinkDropExtension: true,
|
|
},
|
|
},
|
|
LSP: LSPConfig{
|
|
Completion: LSPCompletionConfig{
|
|
Note: LSPCompletionTemplates{
|
|
Label: opt.NullString,
|
|
FilterText: opt.NullString,
|
|
Detail: opt.NullString,
|
|
},
|
|
},
|
|
Diagnostics: LSPDiagnosticConfig{
|
|
WikiTitle: LSPDiagnosticNone,
|
|
DeadLink: LSPDiagnosticError,
|
|
},
|
|
},
|
|
Filters: make(map[string]string),
|
|
Aliases: make(map[string]string),
|
|
Extra: map[string]string{
|
|
"hello": "world",
|
|
"salut": "le monde",
|
|
},
|
|
})
|
|
}
|
|
|
|
// Some properties like `pager` and `fzf.preview` differentiate between not
|
|
// being set and an empty string.
|
|
func TestParsePreservePropertiesAllowingEmptyValues(t *testing.T) {
|
|
conf, err := ParseConfig([]byte(`
|
|
[tool]
|
|
pager = ""
|
|
fzf-preview = ""
|
|
`), ".zk/config.toml", NewDefaultConfig(), false)
|
|
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, conf.Tool.Pager.IsNull(), false)
|
|
assert.Equal(t, conf.Tool.Pager, opt.NewString(""))
|
|
assert.Equal(t, conf.Tool.FzfPreview.IsNull(), false)
|
|
assert.Equal(t, conf.Tool.FzfPreview, opt.NewString(""))
|
|
}
|
|
|
|
func TestParseNotebook(t *testing.T) {
|
|
toml := `
|
|
[notebook]
|
|
dir = "/home/user/folder"
|
|
`
|
|
// Should parse notebook if isGlobal == true
|
|
conf, err := ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig(), true)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, conf.Notebook.Dir, opt.NewString("/home/user/folder"))
|
|
|
|
// Should not parse notebook if isGlobal == false
|
|
conf, err = ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig(), false)
|
|
assert.NotNil(t, err)
|
|
assert.Err(t, err, "notebook.dir should not be set on local configuration")
|
|
}
|
|
|
|
func TestParseIDCharset(t *testing.T) {
|
|
test := func(charset string, expected Charset) {
|
|
toml := fmt.Sprintf(`
|
|
[note]
|
|
id-charset = "%v"
|
|
`, charset)
|
|
conf, err := ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig(), false)
|
|
assert.Nil(t, err)
|
|
if !cmp.Equal(conf.Note.IDOptions.Charset, expected) {
|
|
t.Errorf("Didn't parse ID charset `%v` as expected", charset)
|
|
}
|
|
}
|
|
|
|
test("alphanum", CharsetAlphanum)
|
|
test("hex", CharsetHex)
|
|
test("letters", CharsetLetters)
|
|
test("numbers", CharsetNumbers)
|
|
test("HEX", []rune("HEX")) // case sensitive
|
|
test("custom", []rune("custom"))
|
|
}
|
|
|
|
func TestParseIDCase(t *testing.T) {
|
|
test := func(letterCase string, expected Case) {
|
|
toml := fmt.Sprintf(`
|
|
[note]
|
|
id-case = "%v"
|
|
`, letterCase)
|
|
conf, err := ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig(), false)
|
|
assert.Nil(t, err)
|
|
if !cmp.Equal(conf.Note.IDOptions.Case, expected) {
|
|
t.Errorf("Didn't parse ID case `%v` as expected", letterCase)
|
|
}
|
|
}
|
|
|
|
test("lower", CaseLower)
|
|
test("upper", CaseUpper)
|
|
test("mixed", CaseMixed)
|
|
test("unknown", CaseLower)
|
|
}
|
|
|
|
// If link-encode-path is not set explicitly, it defaults to true for
|
|
// "markdown" format and false for anything else.
|
|
func TestParseMarkdownLinkEncodePath(t *testing.T) {
|
|
test := func(format string, expected bool) {
|
|
toml := fmt.Sprintf(`
|
|
[format.markdown]
|
|
link-format = "%s"
|
|
`, format)
|
|
conf, err := ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig(), false)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, conf.Format.Markdown.LinkEncodePath, expected)
|
|
}
|
|
|
|
test("", true)
|
|
test("markdown", true)
|
|
test("wiki", false)
|
|
test("custom", false)
|
|
}
|
|
|
|
func TestParseLSPDiagnosticsSeverity(t *testing.T) {
|
|
test := func(value string, expected LSPDiagnosticSeverity) {
|
|
toml := fmt.Sprintf(`
|
|
[lsp.diagnostics]
|
|
wiki-title = "%s"
|
|
dead-link = "%s"
|
|
`, value, value)
|
|
conf, err := ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig(), false)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, conf.LSP.Diagnostics.WikiTitle, expected)
|
|
assert.Equal(t, conf.LSP.Diagnostics.DeadLink, expected)
|
|
}
|
|
|
|
test("", LSPDiagnosticNone)
|
|
test("none", LSPDiagnosticNone)
|
|
test("error", LSPDiagnosticError)
|
|
test("warning", LSPDiagnosticWarning)
|
|
test("info", LSPDiagnosticInfo)
|
|
test("hint", LSPDiagnosticHint)
|
|
|
|
toml := `
|
|
[lsp.diagnostics]
|
|
wiki-title = "foobar"
|
|
`
|
|
_, err := ParseConfig([]byte(toml), ".zk/config.toml", NewDefaultConfig(), false)
|
|
assert.Err(t, err, "foobar: unknown LSP diagnostic severity - may be none, hint, info, warning or error")
|
|
}
|
|
|
|
func TestGroupConfigExcludeGlobs(t *testing.T) {
|
|
// empty globs
|
|
config := GroupConfig{
|
|
Paths: []string{"path"},
|
|
Note: NoteConfig{Exclude: []string{}},
|
|
}
|
|
assert.Equal(t, config.ExcludeGlobs(), []string{})
|
|
|
|
// empty paths
|
|
config = GroupConfig{
|
|
Paths: []string{},
|
|
Note: NoteConfig{
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
}
|
|
assert.Equal(t, config.ExcludeGlobs(), []string{"ignored", ".git"})
|
|
|
|
// several paths
|
|
config = GroupConfig{
|
|
Paths: []string{"log", "drafts"},
|
|
Note: NoteConfig{
|
|
Exclude: []string{"ignored", "*.git"},
|
|
},
|
|
}
|
|
assert.Equal(t, config.ExcludeGlobs(), []string{"log/ignored", "log/*.git", "drafts/ignored", "drafts/*.git"})
|
|
}
|
|
|
|
func TestGroupConfigClone(t *testing.T) {
|
|
original := GroupConfig{
|
|
Paths: []string{"original"},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "{{id}}.note",
|
|
Extension: "md",
|
|
BodyTemplatePath: opt.NewString("default.note"),
|
|
IDOptions: IDOptions{
|
|
Length: 4,
|
|
Charset: CharsetAlphanum,
|
|
Case: CaseLower,
|
|
},
|
|
Lang: "fr",
|
|
DefaultTitle: "Sans titre",
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
Extra: map[string]string{
|
|
"hello": "world",
|
|
},
|
|
}
|
|
|
|
clone := original.Clone()
|
|
// Check that the clone is equivalent
|
|
assert.Equal(t, clone, original)
|
|
|
|
clone.Paths = []string{"cloned"}
|
|
clone.Note.FilenameTemplate = "modified"
|
|
clone.Note.Extension = "txt"
|
|
clone.Note.BodyTemplatePath = opt.NewString("modified")
|
|
clone.Note.IDOptions.Length = 41
|
|
clone.Note.IDOptions.Charset = CharsetNumbers
|
|
clone.Note.IDOptions.Case = CaseUpper
|
|
clone.Note.Lang = "de"
|
|
clone.Note.DefaultTitle = "Ohne Titel"
|
|
clone.Note.Exclude = []string{"other-ignored"}
|
|
clone.Extra["test"] = "modified"
|
|
|
|
// Check that we didn't modify the original
|
|
assert.Equal(t, original, GroupConfig{
|
|
Paths: []string{"original"},
|
|
Note: NoteConfig{
|
|
FilenameTemplate: "{{id}}.note",
|
|
Extension: "md",
|
|
BodyTemplatePath: opt.NewString("default.note"),
|
|
IDOptions: IDOptions{
|
|
Length: 4,
|
|
Charset: CharsetAlphanum,
|
|
Case: CaseLower,
|
|
},
|
|
Lang: "fr",
|
|
DefaultTitle: "Sans titre",
|
|
Exclude: []string{"ignored", ".git"},
|
|
},
|
|
Extra: map[string]string{
|
|
"hello": "world",
|
|
},
|
|
})
|
|
}
|