2024-01-28 01:13:25 -06:00
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
import (
|
2024-01-28 17:39:42 -06:00
|
|
|
"bufio"
|
2024-01-28 01:13:25 -06:00
|
|
|
"encoding"
|
2024-01-28 08:56:34 -06:00
|
|
|
"encoding/json"
|
2026-02-13 14:12:30 -06:00
|
|
|
"regexp"
|
2024-01-28 17:39:42 -06:00
|
|
|
"strings"
|
2024-01-28 01:13:25 -06:00
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
2026-02-13 14:12:30 -06:00
|
|
|
"github.com/nalgeon/be"
|
2024-01-28 01:13:25 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Type assertions
|
|
|
|
|
var _ encoding.TextMarshaler = Entry{}
|
2024-01-28 08:56:34 -06:00
|
|
|
var _ encoding.TextUnmarshaler = new(Entry)
|
2024-01-28 19:23:51 -06:00
|
|
|
var _ json.Marshaler = Entry{}
|
2024-01-28 22:02:57 -06:00
|
|
|
var _ json.Unmarshaler = new(Entry)
|
2024-01-28 01:13:25 -06:00
|
|
|
|
2024-01-28 08:56:34 -06:00
|
|
|
func TestEntryMarshal(t *testing.T) {
|
2024-01-28 01:13:25 -06:00
|
|
|
when := time.Now()
|
|
|
|
|
whens := when.Format(DateFormat)
|
|
|
|
|
simple := []Meta{}
|
|
|
|
|
nolines := []string{}
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
title string
|
|
|
|
|
date time.Time
|
|
|
|
|
fields []Meta
|
|
|
|
|
first string
|
|
|
|
|
lines []string
|
|
|
|
|
err error
|
|
|
|
|
}{
|
2024-01-28 12:41:55 -06:00
|
|
|
{"no-title", "", when, simple, "", nolines, ErrorMissingTitle},
|
|
|
|
|
{"zero-date", "Empty title", time.Time{}, simple, "", nolines, ErrorMissingDate},
|
2026-02-10 15:43:23 -06:00
|
|
|
{"one-line", "A Title", when, simple, "@begin " + whens + " - A Title", []string{"@id .+ @end"}, nil},
|
2024-01-28 01:13:25 -06:00
|
|
|
{
|
|
|
|
|
"one-field",
|
|
|
|
|
"Title 2",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"age", 41}},
|
|
|
|
|
"@begin " + whens + " - Title 2",
|
2026-02-10 15:43:23 -06:00
|
|
|
[]string{"@age 41", "@id .*"},
|
2024-01-28 01:13:25 -06:00
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"three-fields",
|
|
|
|
|
"Title 3",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"age", 41}, {"cool", true}, {"name", "Jim"}},
|
|
|
|
|
"@begin " + whens + " - Title 3",
|
|
|
|
|
[]string{"@age 41", "@cool true", "@name Jim"},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"json-field",
|
|
|
|
|
"Title J",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"json", json.RawMessage(`{"age": 41, "cool": true, "name": "Jim"}`)}},
|
|
|
|
|
"@begin " + whens + " - Title J",
|
|
|
|
|
[]string{"@age 41", "@cool true", "@name Jim"},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2026-02-10 18:15:07 -06:00
|
|
|
{
|
|
|
|
|
"nested-json",
|
|
|
|
|
"Title N",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"me", json.RawMessage(`{"age": 43, "cool": true}`)}},
|
|
|
|
|
"@begin " + whens + " - Title N",
|
|
|
|
|
[]string{"@me:age 43", "@me:cool true"},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"nested-map",
|
|
|
|
|
"Title M",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"me", map[string]any{"age": 43, "cool": true}}},
|
|
|
|
|
"@begin " + whens + " - Title M",
|
|
|
|
|
[]string{"@me:age 43", "@me:cool true"},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"double-nested-map",
|
|
|
|
|
"Title DM",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"me", map[string]any{"age": 43, "name": map[string]any{"first": "Dan", "last": "Jones"}}}},
|
|
|
|
|
"@begin " + whens + " - Title DM",
|
|
|
|
|
[]string{"@me:age 43", "@me:name:first Dan", "@me:name:last Jones"},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2026-02-12 10:41:35 -06:00
|
|
|
{
|
|
|
|
|
"double-nested-map-dot",
|
|
|
|
|
"Title DM",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"me", map[string]any{"age": 43, "name": map[string]any{".": "Dan Jones", "nick": "Danny"}}}},
|
|
|
|
|
"@begin " + whens + " - Title DM",
|
|
|
|
|
[]string{"@me:age 43", "@me:name Dan Jones", "@me:name:nick Danny"},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"double-nested-map-blank",
|
|
|
|
|
"Title DM",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"me", map[string]any{"age": 43, "name": map[string]any{"": "Dan Jones", "nick": "Danny"}}}},
|
|
|
|
|
"@begin " + whens + " - Title DM",
|
|
|
|
|
[]string{"@me:age 43", "@me:name Dan Jones", "@me:name:nick Danny"},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2026-02-10 18:15:07 -06:00
|
|
|
{
|
|
|
|
|
"nested-keys-in-json",
|
|
|
|
|
"Title NKJ",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"me", json.RawMessage(`{"name:first": "Dan", "name:last": "Jones"}`)}},
|
|
|
|
|
"@begin " + whens + " - Title NKJ",
|
|
|
|
|
[]string{"@me:name:first Dan", "@me:name:last Jones"},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2024-01-28 01:13:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
2024-01-28 08:56:34 -06:00
|
|
|
t.Run(tt.name, getEntryMarshalTestRunner(tt.title, tt.date, tt.fields, tt.first, tt.lines, tt.err))
|
2024-01-28 01:13:25 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-28 08:56:34 -06:00
|
|
|
func getEntryMarshalTestRunner(title string, date time.Time, fields []Meta, first string, lines []string, err error) func(*testing.T) {
|
2024-01-28 01:13:25 -06:00
|
|
|
return func(t *testing.T) {
|
2026-03-07 18:57:33 -06:00
|
|
|
en := Entry{Title: title, Date: date, Fields: fields}
|
2024-01-28 01:13:25 -06:00
|
|
|
o, er := en.MarshalText()
|
2026-02-13 14:12:30 -06:00
|
|
|
be.Err(t, er, err)
|
2024-01-28 01:13:25 -06:00
|
|
|
if first == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-03-09 15:38:34 -06:00
|
|
|
os := string(o)
|
2024-01-28 01:13:25 -06:00
|
|
|
if len(lines) == 0 {
|
2026-02-13 14:12:30 -06:00
|
|
|
be.Equal(t, os, first)
|
2024-01-28 01:13:25 -06:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 14:12:30 -06:00
|
|
|
be.True(t, regexp.MustCompile(first).MatchString(os))
|
2024-01-28 01:13:25 -06:00
|
|
|
for _, line := range lines {
|
2026-02-13 14:12:30 -06:00
|
|
|
be.True(t, regexp.MustCompile("(?m)^"+line).MatchString(os))
|
2024-01-28 01:13:25 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-28 08:56:34 -06:00
|
|
|
|
|
|
|
|
func TestEntryUnmarshal(t *testing.T) {
|
|
|
|
|
when := time.Now()
|
|
|
|
|
whens := when.Format(DateFormat)
|
|
|
|
|
simple := []Meta{}
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
in string
|
|
|
|
|
title string
|
|
|
|
|
date time.Time
|
2026-03-07 18:57:33 -06:00
|
|
|
id string
|
2024-01-28 08:56:34 -06:00
|
|
|
fields []Meta
|
|
|
|
|
err error
|
|
|
|
|
}{
|
2026-03-07 18:57:33 -06:00
|
|
|
{"one-line", "@begin " + whens + " - A Title @end", "A Title", when, "", simple, nil},
|
|
|
|
|
{"with-id", "@begin " + whens + " - A Title\n@id jimmy-john @end", "A Title", when, "jimmy-john", simple, nil},
|
|
|
|
|
{"rfc3999-date", "@begin " + when.Format(time.RFC3339) + " - A Title @end", "A Title", when, "", simple, nil},
|
|
|
|
|
{"multi-title", "@begin " + whens + " - A Title\nwith break @end", "A Title\nwith break", when, "", simple, nil},
|
|
|
|
|
{"no-title", "@begin " + whens + " - @end", "", when, "", simple, ErrorMissingTitle},
|
|
|
|
|
{"parse-error", "this is no good", "", when, "", simple, ErrorParsing},
|
|
|
|
|
{"no-date", "@begin - A Title @end", "A Title", when, "", simple, ErrorMissingDate},
|
|
|
|
|
{"bad-date", "@begin not-a-real date - A Title @end", "A Title", when, "", simple, ErrorParsing},
|
|
|
|
|
{"one-field", "@begin " + whens + " - A Title\n@age 41 @end", "A Title", when, "", []Meta{{"age", 41}}, nil},
|
2024-01-28 08:56:34 -06:00
|
|
|
{
|
|
|
|
|
"two-fields",
|
|
|
|
|
"@begin " + whens + " - A Title\n@age 41\n@cool true @end",
|
|
|
|
|
"A Title",
|
2026-03-07 18:57:33 -06:00
|
|
|
when, "",
|
2024-01-28 08:56:34 -06:00
|
|
|
[]Meta{{"age", 41}, {"cool", true}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"obj-field",
|
|
|
|
|
"@begin " + whens + " - A Title\n" + `@me {"name":"Dan","coder":true} @end`,
|
|
|
|
|
"A Title",
|
2026-03-07 18:57:33 -06:00
|
|
|
when, "",
|
2024-01-28 08:56:34 -06:00
|
|
|
[]Meta{{"me", json.RawMessage(`{"name":"Dan","coder":true}`)}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2026-02-10 16:13:39 -06:00
|
|
|
{
|
|
|
|
|
"nested-field",
|
|
|
|
|
"@begin " + whens + " - A Title\n@me:name Dan\n@me:coder true @end",
|
|
|
|
|
"A Title",
|
2026-03-07 18:57:33 -06:00
|
|
|
when, "",
|
2026-02-10 16:13:39 -06:00
|
|
|
[]Meta{{"me:name", "Dan"}, {"me:coder", true}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2026-02-12 10:41:35 -06:00
|
|
|
{
|
|
|
|
|
"nested-field-dot",
|
|
|
|
|
"@begin " + whens + " - A Title\n@me:name Dan Jones\n@me:name:nick Danny\n@me:coder true @end",
|
|
|
|
|
"A Title",
|
2026-03-07 18:57:33 -06:00
|
|
|
when, "",
|
2026-02-12 10:41:35 -06:00
|
|
|
[]Meta{{"me:name", "Dan Jones"}, {"me:name:nick", "Danny"}, {"me:coder", true}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2024-01-28 08:56:34 -06:00
|
|
|
{
|
|
|
|
|
"json-field",
|
|
|
|
|
"@begin " + whens + " - Some Guy\n" + `@json {"name":"Dan","coder":true} @end`,
|
2024-01-30 23:40:47 -06:00
|
|
|
"Some Guy",
|
2026-03-07 18:57:33 -06:00
|
|
|
when, "",
|
2024-01-28 08:56:34 -06:00
|
|
|
[]Meta{{"name", "Dan"}, {"coder", true}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
2026-03-07 18:57:33 -06:00
|
|
|
t.Run(tt.name, getEntryUnmarshalTestRunner(tt.in, tt.title, tt.date, tt.id, tt.fields, tt.err))
|
2024-01-28 08:56:34 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 18:57:33 -06:00
|
|
|
func getEntryUnmarshalTestRunner(in string, title string, date time.Time, id string, fields []Meta, err error) func(*testing.T) {
|
2024-01-28 08:56:34 -06:00
|
|
|
return func(t *testing.T) {
|
|
|
|
|
e := &Entry{}
|
|
|
|
|
er := e.UnmarshalText([]byte(in))
|
|
|
|
|
if err != nil {
|
2026-02-13 14:12:30 -06:00
|
|
|
be.Err(t, er, err)
|
2024-01-28 08:56:34 -06:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 14:12:30 -06:00
|
|
|
be.Equal(t, e.Title, title)
|
|
|
|
|
be.True(t, e.Date.After(date.Add(-time.Second)) && e.Date.Before(date.Add(time.Second)))
|
2026-03-07 18:57:33 -06:00
|
|
|
if id != "" {
|
|
|
|
|
be.Equal(t, e.ID, id)
|
|
|
|
|
}
|
2024-01-28 08:56:34 -06:00
|
|
|
for _, f := range fields {
|
|
|
|
|
got := false
|
|
|
|
|
for _, m := range e.Fields {
|
2024-01-28 17:39:42 -06:00
|
|
|
var mVal any = m.Value
|
|
|
|
|
var fVal any = f.Value
|
|
|
|
|
if mJ, ok := m.Value.(json.RawMessage); ok {
|
|
|
|
|
mVal = string(mJ)
|
|
|
|
|
}
|
|
|
|
|
if fJ, ok := f.Value.(json.RawMessage); ok {
|
|
|
|
|
fVal = string(fJ)
|
|
|
|
|
}
|
|
|
|
|
if m.Key == f.Key && mVal == fVal {
|
2024-01-28 08:56:34 -06:00
|
|
|
got = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-13 14:12:30 -06:00
|
|
|
be.True(t, got)
|
2024-01-28 08:56:34 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-28 17:39:42 -06:00
|
|
|
|
|
|
|
|
func TestScan(t *testing.T) {
|
|
|
|
|
in := "@begin date - Title\nlong\n@foo john\njones\n@bar 42@nobody @end"
|
|
|
|
|
read := strings.NewReader(in)
|
|
|
|
|
scan := bufio.NewScanner(read)
|
|
|
|
|
scan.Split(scanEntry)
|
2026-02-13 14:12:30 -06:00
|
|
|
be.True(t, scan.Scan())
|
|
|
|
|
be.Equal(t, scan.Text(), "@begin date - Title\nlong")
|
|
|
|
|
be.True(t, scan.Scan())
|
|
|
|
|
be.Equal(t, scan.Text(), "@foo john\njones")
|
|
|
|
|
be.True(t, scan.Scan())
|
|
|
|
|
be.Equal(t, scan.Text(), "@bar 42@nobody")
|
|
|
|
|
be.True(t, !scan.Scan())
|
2024-01-28 17:39:42 -06:00
|
|
|
}
|
2024-01-28 19:23:51 -06:00
|
|
|
|
|
|
|
|
func TestEntryJsonMarshal(t *testing.T) {
|
|
|
|
|
when := time.Now()
|
2024-01-28 19:41:30 -06:00
|
|
|
whens := when.Format(time.RFC3339)
|
2024-01-28 19:23:51 -06:00
|
|
|
simple := []Meta{}
|
|
|
|
|
tests := []struct {
|
2026-03-07 18:57:33 -06:00
|
|
|
name string
|
|
|
|
|
title string
|
|
|
|
|
date time.Time
|
|
|
|
|
fields []Meta
|
|
|
|
|
check func(*testing.T, []byte)
|
|
|
|
|
wantErr error
|
2024-01-28 19:23:51 -06:00
|
|
|
}{
|
2026-03-07 18:57:33 -06:00
|
|
|
{
|
|
|
|
|
"simple",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
simple,
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
be.Equal(t, m["title"].(string), "A Title")
|
|
|
|
|
be.Equal(t, m["date"].(string), whens)
|
|
|
|
|
_, hasID := m["id"].(string)
|
|
|
|
|
be.True(t, hasID)
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"one-field",
|
|
|
|
|
"A Title 2",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"age", 41}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
be.Equal(t, m["title"].(string), "A Title 2")
|
|
|
|
|
be.Equal(t, m["date"].(string), whens)
|
|
|
|
|
be.Equal(t, m["age"].(float64), float64(41))
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"skip-title-field",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"title", "Different title"}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
be.Equal(t, m["title"].(string), "A Title")
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"skip-date-field",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"date", when.Add(time.Hour)}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
be.Equal(t, m["date"].(string), whens)
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"skip-dupe-field",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"foo", "bar"}, {"foo", "baz"}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
be.Equal(t, m["foo"].(string), "bar")
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"two-fields",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"foo", "bar"}, {"baz", 42}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
be.Equal(t, m["foo"].(string), "bar")
|
|
|
|
|
be.Equal(t, m["baz"].(float64), float64(42))
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"empty-title",
|
|
|
|
|
"",
|
|
|
|
|
when,
|
|
|
|
|
simple,
|
|
|
|
|
nil,
|
|
|
|
|
ErrorMissingTitle,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"empty-date",
|
|
|
|
|
"A Title",
|
|
|
|
|
time.Time{},
|
|
|
|
|
simple,
|
|
|
|
|
nil,
|
|
|
|
|
ErrorMissingDate,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"obj-field",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"obj", json.RawMessage(`{"foo":"bar","title":"Sub-title"}`)}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
obj, ok := m["obj"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, obj["foo"].(string), "bar")
|
|
|
|
|
be.Equal(t, obj["title"].(string), "Sub-title")
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"date-field",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"when", when.Add(time.Hour)}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
be.Equal(t, m["when"].(string), when.Add(time.Hour).Format(time.RFC3339))
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"json-field",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"json", json.RawMessage(`{"age": 41, "cool": true, "name": "Jim"}`)}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
be.Equal(t, m["age"].(float64), float64(41))
|
|
|
|
|
be.Equal(t, m["cool"].(bool), true)
|
|
|
|
|
be.Equal(t, m["name"].(string), "Jim")
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"nested-field",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"obj:foo", "bar"}, {"obj:title", "Sub-title"}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
obj, ok := m["obj"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, obj["foo"].(string), "bar")
|
|
|
|
|
be.Equal(t, obj["title"].(string), "Sub-title")
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"double-nested-field",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"obj:foo", "bar"}, {"obj:me:name", "Dan"}, {"obj:me:age", 27}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
obj, ok := m["obj"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, obj["foo"].(string), "bar")
|
|
|
|
|
me, ok := obj["me"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, me["name"].(string), "Dan")
|
|
|
|
|
be.Equal(t, me["age"].(float64), float64(27))
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"nested-plus-json",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"obj:foo", "bar"}, {"obj:me", json.RawMessage(`{"name":"Dan","age":27}`)}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
obj, ok := m["obj"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, obj["foo"].(string), "bar")
|
|
|
|
|
me, ok := obj["me"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, me["name"].(string), "Dan")
|
|
|
|
|
be.Equal(t, me["age"].(float64), float64(27))
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"nested-part",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"obj:foo", "bar"}, {"obj:me", "Dan"}, {"obj:me:age", 27}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
obj, ok := m["obj"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, obj["foo"].(string), "bar")
|
|
|
|
|
me, ok := obj["me"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, me["."].(string), "Dan")
|
|
|
|
|
be.Equal(t, me["age"].(float64), float64(27))
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"nested-part-order",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"obj:foo", "bar"}, {"obj:me:age", 27}, {"obj:me", "Dan"}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
obj, ok := m["obj"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, obj["foo"].(string), "bar")
|
|
|
|
|
me, ok := obj["me"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, me["."].(string), "Dan")
|
|
|
|
|
be.Equal(t, me["age"].(float64), float64(27))
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"nested-part-order-two",
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"obj:foo", "bar"}, {"obj:me:age", 27}, {"obj:me", "Dan"}, {"obj:me:cool", true}},
|
|
|
|
|
func(t *testing.T, o []byte) {
|
|
|
|
|
var m map[string]any
|
|
|
|
|
err := json.Unmarshal(o, &m)
|
|
|
|
|
be.Err(t, err, nil)
|
|
|
|
|
obj, ok := m["obj"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, obj["foo"].(string), "bar")
|
|
|
|
|
me, ok := obj["me"].(map[string]any)
|
|
|
|
|
be.True(t, ok)
|
|
|
|
|
be.Equal(t, me["."].(string), "Dan")
|
|
|
|
|
be.Equal(t, me["age"].(float64), float64(27))
|
|
|
|
|
be.Equal(t, me["cool"].(bool), true)
|
|
|
|
|
},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2024-01-28 19:23:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
2026-03-07 18:57:33 -06:00
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
e := Entry{Title: tt.title, Date: tt.date, Fields: tt.fields}
|
|
|
|
|
o, er := json.Marshal(e)
|
|
|
|
|
if tt.wantErr == nil {
|
|
|
|
|
be.Err(t, er, nil)
|
|
|
|
|
tt.check(t, o)
|
|
|
|
|
} else {
|
|
|
|
|
be.Err(t, er, tt.wantErr)
|
|
|
|
|
}
|
|
|
|
|
})
|
2024-01-28 19:23:51 -06:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-28 22:02:57 -06:00
|
|
|
|
|
|
|
|
func TestEntryJsonUnmarshal(t *testing.T) {
|
2024-01-29 22:07:18 -06:00
|
|
|
when := time.Now().Truncate(time.Second)
|
2024-01-28 22:02:57 -06:00
|
|
|
whens := when.Format(time.RFC3339)
|
|
|
|
|
simple := []Meta{}
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
in string
|
|
|
|
|
title string
|
|
|
|
|
date time.Time
|
|
|
|
|
fields []Meta
|
|
|
|
|
err error
|
|
|
|
|
}{
|
|
|
|
|
{"simple", `{"title":"A Title","date":"` + whens + `"}`, "A Title", when, simple, nil},
|
|
|
|
|
{"missing-title", `{"date":"` + whens + `"}`, "", when, simple, ErrorMissingTitle},
|
|
|
|
|
{"missing-date", `{"title":"A Title"}`, "", when, simple, ErrorMissingDate},
|
|
|
|
|
{"empty-title", `{"title":"","date":"` + whens + `"}`, "", when, simple, ErrorMissingTitle},
|
|
|
|
|
{"empty-date", `{"title":"A Title","date":""}`, "", when, simple, ErrorMissingDate},
|
|
|
|
|
{"bad-date", `{"title":"A Title","date":"bad"}`, "", when, simple, ErrorParsing},
|
2024-01-29 22:07:18 -06:00
|
|
|
{"bad-json", `{"title":"A Title","date":"`, "", when, simple, ErrorParsing},
|
2024-01-28 22:02:57 -06:00
|
|
|
{
|
|
|
|
|
"single-field",
|
|
|
|
|
`{"title":"A Title","date":"` + whens + `","hello":"Hi"}`,
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"hello", "Hi"}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"many-fields",
|
2024-01-29 22:07:18 -06:00
|
|
|
`{"title":"A Title","date":"` + whens + `","hello":"Hi","bye":42,"b":true,"fl":42.13}`,
|
2024-01-28 22:02:57 -06:00
|
|
|
"A Title",
|
|
|
|
|
when,
|
2024-01-29 22:07:18 -06:00
|
|
|
[]Meta{{"hello", "Hi"}, {"bye", int64(42)}, {"b", true}, {"fl", float64(42.13)}},
|
2024-01-28 22:02:57 -06:00
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"date-field",
|
2024-02-25 13:39:12 -06:00
|
|
|
`{"title":"A Title","date":"` + whens + `","posted":"` + when.Add(-time.Hour).In(time.UTC).Format(time.RFC3339) + `"}`,
|
2024-01-28 22:02:57 -06:00
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"posted", when.Add(-time.Hour)}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2024-01-30 23:40:47 -06:00
|
|
|
{
|
|
|
|
|
"json-field",
|
|
|
|
|
`{"title":"A Title","date":"` + whens + `","json":{"age": 41, "cool": true, "name": "Jim"}}`,
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"age", int64(41)}, {"cool", true}, {"name", "Jim"}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"json-field-embed",
|
|
|
|
|
`{"title":"A Title","date":"` + whens + `","json":"{\"age\": 41, \"cool\": true, \"name\": \"Jim\"}"}`,
|
|
|
|
|
"A Title",
|
|
|
|
|
when,
|
|
|
|
|
[]Meta{{"age", int64(41)}, {"cool", true}, {"name", "Jim"}},
|
|
|
|
|
nil,
|
|
|
|
|
},
|
2024-01-28 22:02:57 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, getEntryJsonUnmarshalTestRunner(tt.in, tt.title, tt.date, tt.fields, tt.err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getEntryJsonUnmarshalTestRunner(in, title string, date time.Time, fields []Meta, err error) func(t *testing.T) {
|
|
|
|
|
return func(t *testing.T) {
|
|
|
|
|
e := new(Entry)
|
2024-01-29 22:07:18 -06:00
|
|
|
er := e.UnmarshalJSON([]byte(in))
|
2024-01-28 22:02:57 -06:00
|
|
|
if err != nil {
|
2026-02-13 14:12:30 -06:00
|
|
|
be.Err(t, er, err)
|
2024-01-28 22:02:57 -06:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 14:12:30 -06:00
|
|
|
be.Equal(t, er, nil)
|
|
|
|
|
be.Equal(t, e.Title, title)
|
|
|
|
|
be.True(t, e.Date.After(date.Add(-time.Second)) && e.Date.Before(date.Add(time.Second)))
|
|
|
|
|
be.Equal(t, len(e.Fields), len(fields))
|
2024-01-28 22:02:57 -06:00
|
|
|
for _, f := range fields {
|
|
|
|
|
got := false
|
2024-02-25 13:39:12 -06:00
|
|
|
fTime, isTime := f.Value.(time.Time)
|
2024-01-28 22:02:57 -06:00
|
|
|
for _, m := range e.Fields {
|
|
|
|
|
var mVal any = m.Value
|
|
|
|
|
var fVal any = f.Value
|
|
|
|
|
if mJ, ok := m.Value.(json.RawMessage); ok {
|
|
|
|
|
mVal = string(mJ)
|
|
|
|
|
}
|
|
|
|
|
if fJ, ok := f.Value.(json.RawMessage); ok {
|
|
|
|
|
fVal = string(fJ)
|
|
|
|
|
}
|
|
|
|
|
if m.Key == f.Key && mVal == fVal {
|
|
|
|
|
got = true
|
|
|
|
|
break
|
|
|
|
|
}
|
2024-02-25 13:39:12 -06:00
|
|
|
if isTime && m.Key == f.Key {
|
|
|
|
|
mTime, _ := mVal.(time.Time)
|
2026-02-13 14:12:30 -06:00
|
|
|
if mTime.After(fTime.Add(-2*time.Second)) && mTime.Before(fTime.Add(2*time.Second)) {
|
2024-02-25 13:39:12 -06:00
|
|
|
got = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-28 22:02:57 -06:00
|
|
|
}
|
2026-02-13 14:12:30 -06:00
|
|
|
be.True(t, got)
|
2024-01-28 22:02:57 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|