diff --git a/CHANGELOG.md b/CHANGELOG.md index 2855efa..79ef17d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [0.2.0] - 2026-03-07 + +- Add ID field to Entry struct with auto-generation on marshal/unmarshal +- Upgrade mapstructure to maintained fork + ## [0.1.1] - 2026-03-01 - Add Time method to Date struct for accessing internal timestamp diff --git a/config/load.go b/config/load.go index 3de0a19..9b0341f 100644 --- a/config/load.go +++ b/config/load.go @@ -10,7 +10,7 @@ import ( "codeberg.org/danjones000/my-log/tools" "github.com/BurntSushi/toml" "github.com/caarlos0/env/v10" - mapst "github.com/mitchellh/mapstructure" + mapst "github.com/go-viper/mapstructure/v2" ) var ConfigPath string diff --git a/go.mod b/go.mod index 0c747b1..b856302 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.26.0 require ( github.com/BurntSushi/toml v1.3.2 github.com/caarlos0/env/v10 v10.0.0 + github.com/go-viper/mapstructure/v2 v2.5.0 github.com/google/uuid v1.6.0 github.com/markusmobius/go-dateparser v1.2.3 - github.com/mitchellh/mapstructure v1.5.0 github.com/nalgeon/be v0.3.0 github.com/spf13/cobra v1.8.0 ) diff --git a/go.sum b/go.sum index bc1f80a..c9ae79b 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elliotchance/pie/v2 v2.7.0 h1:FqoIKg4uj0G/CrLGuMS9ejnFKa92lxE1dEgBD3pShXg= github.com/elliotchance/pie/v2 v2.7.0/go.mod h1:18t0dgGFH006g4eVdDtWfgFZPQEgl10IoEO8YWEq3Og= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k= @@ -21,8 +23,6 @@ github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/markusmobius/go-dateparser v1.2.3 h1:TvrsIvr5uk+3v6poDjaicnAFJ5IgtFHgLiuMY2Eb7Nw= github.com/markusmobius/go-dateparser v1.2.3/go.mod h1:cMwQRrBUQlK1UI5TIFHEcvpsMbkWrQLXuaPNMFzuYLk= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/nalgeon/be v0.3.0 h1:QsPANqEtcOD5qT2S3KAtIkDBBn8SXUf/Lb5Bi/z4UqM= github.com/nalgeon/be v0.3.0/go.mod h1:PMwMuBLopwKJkSHnr2qHyLcZYUTqNejN7A8RAqNWO3E= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/models/entry.go b/models/entry.go index efd58b6..46190e5 100644 --- a/models/entry.go +++ b/models/entry.go @@ -20,18 +20,47 @@ import ( const DateFormat = tools.DateFormat type Entry struct { + // ID is an optional, but recommended field. + // When marshaling/unmarshaling, if ID is empty, one is generated. + ID string Title string Date time.Time Fields Metas } +func GenerateID() string { + host, hostErr := os.Hostname() + if hostErr != nil { + host = "localhost" + } + return fmt.Sprintf( + "tag:%s,%s:my-log/%s", + host, + time.Now().Local().Format("2006"), + uuid.NewString(), + ) +} + +func EnsureID(id *string, fields Metas) { + if *id != "" { + return + } + + metaID, hasMetaID := fields.Get("id") + var isStringID bool + *id, isStringID = metaID.(string) + if !hasMetaID || !isStringID { + *id = GenerateID() + } +} + type metaRes struct { out []byte err error } -func (e Entry) getFieldMarshalChan() chan metaRes { - size := len(e.Fields) +func getFieldMarshalChan(fields Metas) chan metaRes { + size := len(fields) ch := make(chan metaRes, size) var wg sync.WaitGroup @@ -53,7 +82,7 @@ func (e Entry) getFieldMarshalChan() chan metaRes { ch <- metaRes{o, er} } - }(e.Fields[i]) + }(fields[i]) } go func() { @@ -71,16 +100,10 @@ func (e Entry) MarshalText() ([]byte, error) { if e.Date.IsZero() { return []byte{}, ErrorMissingDate } + EnsureID(&e.ID, e.Fields) + fields := e.Fields.Set("id", e.ID) - if _, hasId := e.Fields.Get("id"); !hasId { - host, hostErr := os.Hostname() - if hostErr != nil { - host = "localhost" - } - e.Fields = e.Fields.Set("id", fmt.Sprintf("tag:%s,%s:my-log/%s", host, time.Now().Format("2006"), uuid.NewString())) - } - - ch := e.getFieldMarshalChan() + ch := getFieldMarshalChan(fields) buff := &bytes.Buffer{} buff.WriteString("@begin ") buff.WriteString(e.Date.Format(DateFormat)) @@ -124,8 +147,14 @@ func (m *Entry) UnmarshalText(in []byte) error { m.Date = d for meta := range ch { + if meta.Key == "id" { + if id, idIsString := meta.Value.(string); idIsString { + m.ID = id + } + } m.Fields = append(m.Fields, meta) } + EnsureID(&m.ID, m.Fields) return nil } @@ -195,11 +224,13 @@ func (e Entry) MarshalJSON() ([]byte, error) { if e.Date == (time.Time{}) { return []byte{}, ErrorMissingDate } + EnsureID(&e.ID, e.Fields) + fields := e.Fields.Set("id", e.ID) out := map[string]any{} out["title"] = e.Title out["date"] = e.Date.Format(time.RFC3339) - maps.Copy(out, e.Fields.Map()) + maps.Copy(out, fields.Map()) return json.Marshal(out) } @@ -281,5 +312,6 @@ func (e *Entry) UnmarshalJSON(in []byte) error { } e.Fields = append(e.Fields, m) } + EnsureID(&e.ID, e.Fields) return nil } diff --git a/models/entry_test.go b/models/entry_test.go index 6372897..4baabbe 100644 --- a/models/entry_test.go +++ b/models/entry_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - "codeberg.org/danjones000/my-log/internal/testutil/bep" "github.com/nalgeon/be" ) @@ -126,7 +125,7 @@ func TestEntryMarshal(t *testing.T) { func getEntryMarshalTestRunner(title string, date time.Time, fields []Meta, first string, lines []string, err error) func(*testing.T) { return func(t *testing.T) { - en := Entry{title, date, fields} + en := Entry{Title: title, Date: date, Fields: fields} o, er := en.MarshalText() be.Err(t, er, err) if first == "" { @@ -154,22 +153,24 @@ func TestEntryUnmarshal(t *testing.T) { in string title string date time.Time + id string fields []Meta err error }{ - {"one-line", "@begin " + whens + " - A Title @end", "A Title", when, 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}, + {"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}, { "two-fields", "@begin " + whens + " - A Title\n@age 41\n@cool true @end", "A Title", - when, + when, "", []Meta{{"age", 41}, {"cool", true}}, nil, }, @@ -177,7 +178,7 @@ func TestEntryUnmarshal(t *testing.T) { "obj-field", "@begin " + whens + " - A Title\n" + `@me {"name":"Dan","coder":true} @end`, "A Title", - when, + when, "", []Meta{{"me", json.RawMessage(`{"name":"Dan","coder":true}`)}}, nil, }, @@ -185,7 +186,7 @@ func TestEntryUnmarshal(t *testing.T) { "nested-field", "@begin " + whens + " - A Title\n@me:name Dan\n@me:coder true @end", "A Title", - when, + when, "", []Meta{{"me:name", "Dan"}, {"me:coder", true}}, nil, }, @@ -193,7 +194,7 @@ func TestEntryUnmarshal(t *testing.T) { "nested-field-dot", "@begin " + whens + " - A Title\n@me:name Dan Jones\n@me:name:nick Danny\n@me:coder true @end", "A Title", - when, + when, "", []Meta{{"me:name", "Dan Jones"}, {"me:name:nick", "Danny"}, {"me:coder", true}}, nil, }, @@ -201,18 +202,18 @@ func TestEntryUnmarshal(t *testing.T) { "json-field", "@begin " + whens + " - Some Guy\n" + `@json {"name":"Dan","coder":true} @end`, "Some Guy", - when, + when, "", []Meta{{"name", "Dan"}, {"coder", true}}, nil, }, } for _, tt := range tests { - t.Run(tt.name, getEntryUnmarshalTestRunner(tt.in, tt.title, tt.date, tt.fields, tt.err)) + t.Run(tt.name, getEntryUnmarshalTestRunner(tt.in, tt.title, tt.date, tt.id, tt.fields, tt.err)) } } -func getEntryUnmarshalTestRunner(in string, title string, date time.Time, fields []Meta, err error) func(*testing.T) { +func getEntryUnmarshalTestRunner(in string, title string, date time.Time, id string, fields []Meta, err error) func(*testing.T) { return func(t *testing.T) { e := &Entry{} er := e.UnmarshalText([]byte(in)) @@ -223,6 +224,9 @@ func getEntryUnmarshalTestRunner(in string, title string, date time.Time, fields be.Equal(t, e.Title, title) be.True(t, e.Date.After(date.Add(-time.Second)) && e.Date.Before(date.Add(time.Second))) + if id != "" { + be.Equal(t, e.ID, id) + } for _, f := range fields { got := false for _, m := range e.Fields { @@ -263,46 +267,282 @@ func TestEntryJsonMarshal(t *testing.T) { whens := when.Format(time.RFC3339) simple := []Meta{} tests := []struct { - name string - title string - date time.Time - fields []Meta - out string - err error + name string + title string + date time.Time + fields []Meta + check func(*testing.T, []byte) + wantErr error }{ - {"simple", "A Title", when, simple, `{"title":"A Title","date":"` + whens + `"}`, nil}, - {"one-field", "A Title 2", when, []Meta{{"age", 41}}, `{"title":"A Title 2","date":"` + whens + `","age":41}`, nil}, - {"skip-title-field", "A Title", when, []Meta{{"title", "Different title"}}, `{"title":"A Title","date":"` + whens + `"}`, nil}, - {"skip-date-field", "A Title", when, []Meta{{"date", when.Add(time.Hour)}}, `{"title":"A Title","date":"` + whens + `"}`, nil}, - {"skip-dupe-field", "A Title", when, []Meta{{"foo", "bar"}, {"foo", "baz"}}, `{"title":"A Title","date":"` + whens + `","foo": "bar"}`, nil}, - {"two-fields", "A Title", when, []Meta{{"foo", "bar"}, {"baz", 42}}, `{"title":"A Title","date":"` + whens + `","foo": "bar","baz":42}`, nil}, - {"empty-title", "", when, simple, "", ErrorMissingTitle}, - {"empty-date", "A Title", time.Time{}, simple, "", ErrorMissingDate}, - {"obj-field", "A Title", when, []Meta{{"obj", json.RawMessage(`{"foo":"bar","title":"Sub-title"}`)}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","title":"Sub-title"}}`, nil}, - {"date-field", "A Title", when, []Meta{{"when", when.Add(time.Hour)}}, `{"title":"A Title","date":"` + whens + `","when":"` + when.Add(time.Hour).Format(time.RFC3339) + `"}`, nil}, - {"json-field", "A Title", when, []Meta{{"json", json.RawMessage(`{"age": 41, "cool": true, "name": "Jim"}`)}}, `{"title":"A Title","date":"` + whens + `","age":41,"cool": true, "name": "Jim"}`, nil}, - {"nested-field", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:title", "Sub-title"}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","title":"Sub-title"}}`, nil}, - {"double-nested-field", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me:name", "Dan"}, {"obj:me:age", 27}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{"name":"Dan","age":27}}}`, nil}, - {"nested-plus-json", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me", json.RawMessage(`{"name":"Dan","age":27}`)}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{"name":"Dan","age":27}}}`, nil}, - {"nested-part", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me", "Dan"}, {"obj:me:age", 27}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{".":"Dan","age":27}}}`, nil}, - {"nested-part-order", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me:age", 27}, {"obj:me", "Dan"}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{".":"Dan","age":27}}}`, nil}, - {"nested-part-order-two", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me:age", 27}, {"obj:me", "Dan"}, {"obj:me:cool", true}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{".":"Dan","age":27,"cool":true}}}`, nil}, + { + "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, + }, } for _, tt := range tests { - t.Run(tt.name, getEntryJsonMarshalTestRunner(tt.title, tt.date, tt.fields, tt.out, tt.err)) - } -} - -func getEntryJsonMarshalTestRunner(title string, date time.Time, fields []Meta, out string, err error) func(t *testing.T) { - return func(t *testing.T) { - e := Entry{title, date, fields} - o, er := json.Marshal(e) - if err == nil { - bep.JSON(t, o, []byte(out)) - } else { - be.Err(t, er, err) - } + 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) + } + }) } }