🔀 Merge branch 'feature/drop' into develop

 Add drop command
This commit is contained in:
Dan Jones 2024-02-25 13:40:09 -06:00
commit b8271227b2
15 changed files with 403 additions and 44 deletions

2
.gitignore vendored
View file

@ -123,3 +123,5 @@ Temporary Items
# End of https://www.toptal.com/developers/gitignore/api/go,linux,emacs,macos
my-log
cover.html
cmd/test.go

View file

@ -17,36 +17,76 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"encoding/json"
"fmt"
"time"
"codeberg.org/danjones000/my-log/files"
"codeberg.org/danjones000/my-log/models"
"codeberg.org/danjones000/my-log/tools"
"github.com/spf13/cobra"
)
var dateStr string
var fields map[string]string
var j Json
// dropCmd represents the drop command
var dropCmd = &cobra.Command{
Use: "drop",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("drop called")
Use: "drop log title",
Short: "Add a new log entry",
// Long: ``,
Args: cobra.ExactArgs(2),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
log := args[0]
title := args[1]
e := models.PartialEntry()
if len(j.RawMessage) > 8 {
err := json.Unmarshal([]byte(j.RawMessage), &e)
if err != nil {
return err
}
}
for k, v := range fields {
e.Fields = append(e.Fields, models.Meta{k, tools.ParseString(v)})
}
e.Title = title
e.Date = time.Now().Local() // @todo parse date
l := models.Log{log, []models.Entry{e}}
err := files.Append(l)
if err != nil {
return err
}
by, err := e.MarshalText()
if err != nil {
return err
}
fmt.Fprintf(cmd.OutOrStdout(), "%s\n", by)
return nil
},
}
func init() {
rootCmd.AddCommand(dropCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// dropCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// dropCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
dropCmd.Flags().StringVarP(&dateStr, "date", "d", time.Now().Local().Format(time.RFC3339), "Date for log entry")
dropCmd.Flags().StringToStringVarP(&fields, "fields", "f", nil, "Fields you add to entry")
dropCmd.Flags().VarP(&j, "json", "j", "Entire entry as json")
}
type Json struct {
json.RawMessage
}
func (j *Json) String() string {
return string(j.RawMessage)
}
func (j *Json) Set(in string) error {
return json.Unmarshal([]byte(in), &j.RawMessage)
}
func (j *Json) Type() string {
return "json"
}

View file

@ -43,7 +43,7 @@ func Execute() {
}
func init() {
cobra.OnInitialize(initConfig)
//cobra.OnInitialize(initConfig)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
@ -56,9 +56,3 @@ func init() {
// when this action is called directly.
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
// @todo
}

View file

@ -14,7 +14,7 @@ import (
)
var ConfigPath string
var Overrides map[string]string
var Overrides = map[string]string{}
func init() {
conf, _ := os.UserConfigDir()

View file

@ -3,7 +3,6 @@ package config
import (
"fmt"
"os"
//fp "path/filepath"
"testing"
"github.com/stretchr/testify/assert"

42
files/append.go Normal file
View file

@ -0,0 +1,42 @@
package files
import (
"fmt"
"os"
fp "path/filepath"
"strings"
"codeberg.org/danjones000/my-log/config"
"codeberg.org/danjones000/my-log/models"
)
func Append(l models.Log) error {
conf, err := config.Load()
if err != nil {
return err
}
filename := fmt.Sprintf("%s.%s", strings.ReplaceAll(l.Name, ".", string(os.PathSeparator)), conf.Input.Ext)
path := fp.Join(conf.Input.Path, filename)
dir := fp.Dir(path)
err = os.MkdirAll(dir, 0750)
if err != nil {
return err
}
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
if err != nil {
return err
}
defer f.Close()
for _, e := range l.Entries {
by, err := e.MarshalText()
if err != nil {
continue
}
f.Write(by)
}
return nil
}

96
files/append_test.go Normal file
View file

@ -0,0 +1,96 @@
package files
import (
"fmt"
"os"
"testing"
"time"
"codeberg.org/danjones000/my-log/config"
"codeberg.org/danjones000/my-log/models"
"github.com/stretchr/testify/suite"
)
func TestAppend(t *testing.T) {
suite.Run(t, new(AppendTestSuite))
}
type AppendTestSuite struct {
suite.Suite
dir string
}
func (s *AppendTestSuite) SetupSuite() {
s.dir, _ = os.MkdirTemp("", "append-test")
config.Overrides["input.path"] = s.dir
config.Overrides["input.ext"] = "log"
}
func (s *AppendTestSuite) TearDownSuite() {
os.RemoveAll(s.dir)
delete(config.Overrides, "input.path")
delete(config.Overrides, "input.ext")
}
func (s *AppendTestSuite) TestSuccess() {
when := time.Now().Local()
e := models.Entry{
Title: "Jimmy",
Date: when,
Fields: []models.Meta{
{"foo", 42},
{"bar", true},
},
}
l := models.Log{
Name: "test",
Entries: []models.Entry{e},
}
err := Append(l)
s.Require().NoError(err)
s.Require().FileExists(s.dir + "/test.log")
by, err := os.ReadFile(s.dir + "/test.log")
st := string(by)
s.Require().NoError(err)
s.Assert().Contains(st, "Jimmy\n")
s.Assert().Contains(st, "\n@foo 42")
s.Assert().Contains(st, "\n@bar true")
}
func (s *AppendTestSuite) TestConfLoadErr() {
currConf := config.ConfigPath
tmp, _ := os.CreateTemp("", "app-conf-*.toml")
fname := tmp.Name()
defer tmp.Close()
defer os.Remove(fname)
fmt.Fprintln(tmp, `{"not":"toml"}`)
config.ConfigPath = fname
defer func(path string) {
config.ConfigPath = path
}(currConf)
err := Append(models.Log{})
s.Assert().ErrorContains(err, "toml")
}
func (s *AppendTestSuite) TestMkdirErr() {
// Don't run this test as root
config.Overrides["input.path"] = "/root/my-logs-test"
defer func(path string) {
config.Overrides["input.path"] = path
}(s.dir)
err := Append(models.Log{})
s.Assert().ErrorContains(err, "permission denied")
}
func (s *AppendTestSuite) TestOpenErr() {
l := models.Log{
Name: "test-open-err",
}
fname := s.dir + "/test-open-err.log"
os.MkdirAll(s.dir, 0750)
f, _ := os.Create(fname)
f.Close()
os.Chmod(fname, 0400) // read only
err := Append(l)
s.Assert().ErrorContains(err, "permission denied")
}

12
go.mod
View file

@ -5,6 +5,7 @@ go 1.21.5
require (
github.com/BurntSushi/toml v1.3.2
github.com/caarlos0/env/v10 v10.0.0
github.com/markusmobius/go-dateparser v1.2.1
github.com/mitchellh/mapstructure v1.5.0
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.8.4
@ -12,10 +13,21 @@ require (
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/elliotchance/pie/v2 v2.7.0 // indirect
github.com/hablullah/go-hijri v1.0.2 // indirect
github.com/hablullah/go-juliandays v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/magefile/mage v1.14.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tetratelabs/wazero v1.2.1 // indirect
github.com/wasilibs/go-re2 v1.3.0 // indirect
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 // indirect
golang.org/x/text v0.10.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/markusmobius/go-dateparser => github.com/goodevilgenius/go-dateparser v1.2.2

22
go.sum
View file

@ -6,12 +6,24 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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/goodevilgenius/go-dateparser v1.2.2 h1:Up9KokPx/h07mesQGAZQg3Xi/8yrDVn1638h3k/lRyk=
github.com/goodevilgenius/go-dateparser v1.2.2/go.mod h1:5xYsZ1h7iB3sE1BSu8bkjYpbFST7EU1/AFxcyO3mgYg=
github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k=
github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ=
github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0=
github.com/hablullah/go-juliandays v1.0.0/go.mod h1:0JOYq4oFOuDja+oospuc61YoX+uNEn7Z6uHYTbBzdGc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 h1:qxLoi6CAcXVzjfvu+KXIXJOAsQB62LXjsfbOaErsVzE=
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958/go.mod h1:Wqfu7mjUHj9WDzSSPI5KfBclTTEnLveRUFr/ujWnTgE=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@ -26,6 +38,16 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tetratelabs/wazero v1.2.1 h1:J4X2hrGzJvt+wqltuvcSjHQ7ujQxA9gb6PeMs4qlUWs=
github.com/tetratelabs/wazero v1.2.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
github.com/wasilibs/go-re2 v1.3.0 h1:LFhBNzoStM3wMie6rN2slD1cuYH2CGiHpvNL3UtcsMw=
github.com/wasilibs/go-re2 v1.3.0/go.mod h1:AafrCXVvGRJJOImMajgJ2M7rVmWyisVK7sFshbxnVrg=
github.com/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2exJQ=
github.com/wasilibs/nottinygc v0.4.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 h1:ba9YlqfDGTTQ5aZ2fwOoQ1hf32QySyQkR6ODGDzHlnE=
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -9,9 +9,11 @@ import (
"strings"
"sync"
"time"
"codeberg.org/danjones000/my-log/tools"
)
const DateFormat = "January 02, 2006 at 03:04:05PM -0700"
const DateFormat = tools.DateFormat
type Entry struct {
Title string
@ -20,6 +22,10 @@ type Entry struct {
skipMissing bool
}
func PartialEntry() Entry {
return Entry{skipMissing: true}
}
type metaRes struct {
out []byte
err error
@ -68,7 +74,7 @@ func (e Entry) MarshalText() ([]byte, error) {
}
ch := e.getFieldMarshalChan()
buff := &bytes.Buffer{}
buff.WriteString("@begin ")
buff.WriteString("\n@begin ")
buff.WriteString(e.Date.Format(DateFormat))
buff.WriteString(" - ")
buff.WriteString(e.Title)
@ -103,12 +109,9 @@ func (m *Entry) UnmarshalText(in []byte) error {
if date == "" {
return ErrorMissingDate
}
d, e := time.Parse(time.RFC3339, date)
d, e := tools.ParseDate(date)
if e != nil {
d, e = time.Parse(DateFormat, date)
if e != nil {
return newParsingError(e)
}
return newParsingError(e)
}
m.Date = d
@ -264,7 +267,7 @@ func (e *Entry) UnmarshalJSON(in []byte) error {
if (!ok || dates == "") && !e.skipMissing {
return ErrorMissingDate
}
date, err := time.Parse(time.RFC3339, dates)
date, err := tools.ParseDate(dates)
if err != nil && !e.skipMissing {
return newParsingError(err)
}
@ -274,7 +277,7 @@ func (e *Entry) UnmarshalJSON(in []byte) error {
if m.Key == "title" || m.Key == "date" {
continue
} else if vs, ok := m.Value.(string); ok {
if vd, err := time.Parse(time.RFC3339, vs); err == nil {
if vd, err := tools.ParseDate(vs); err == nil {
m.Value = vd
} else {
m.Value = vs

View file

@ -9,6 +9,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Type assertions
@ -17,6 +18,19 @@ var _ encoding.TextUnmarshaler = new(Entry)
var _ json.Marshaler = Entry{}
var _ json.Unmarshaler = new(Entry)
func TestPartialEntry(t *testing.T) {
e := PartialEntry()
assert.True(t, e.skipMissing)
err := json.Unmarshal([]byte(`{"a":42}`), &e)
assert.NoError(t, err)
assert.Equal(t, "", e.Title)
assert.Equal(t, time.Time{}, e.Date)
require.Len(t, e.Fields, 1)
f := e.Fields[0]
assert.Equal(t, "a", f.Key)
assert.Equal(t, int64(42), f.Value)
}
func TestEntryMarshal(t *testing.T) {
when := time.Now()
whens := when.Format(DateFormat)
@ -77,12 +91,12 @@ func getEntryMarshalTestRunner(title string, date time.Time, fields []Meta, firs
return
}
if len(lines) == 0 {
assert.Equal(t, first, string(o))
assert.Equal(t, "\n"+first, string(o))
return
}
os := string(o)
assert.Regexp(t, "^"+first, os)
assert.Regexp(t, "^\n"+first, os)
for _, line := range lines {
assert.Regexp(t, "(?m)^"+line, os)
}
@ -266,7 +280,7 @@ func TestEntryJsonUnmarshal(t *testing.T) {
},
{
"date-field",
`{"title":"A Title","date":"` + whens + `","posted":"` + when.Add(-time.Hour).Format(time.RFC3339) + `"}`,
`{"title":"A Title","date":"` + whens + `","posted":"` + when.Add(-time.Hour).In(time.UTC).Format(time.RFC3339) + `"}`,
"A Title",
when,
[]Meta{{"posted", when.Add(-time.Hour)}},
@ -310,6 +324,7 @@ func getEntryJsonUnmarshalTestRunner(in, title string, date time.Time, fields []
assert.Len(t, e.Fields, len(fields))
for _, f := range fields {
got := false
fTime, isTime := f.Value.(time.Time)
for _, m := range e.Fields {
var mVal any = m.Value
var fVal any = f.Value
@ -323,6 +338,13 @@ func getEntryJsonUnmarshalTestRunner(in, title string, date time.Time, fields []
got = true
break
}
if isTime && m.Key == f.Key {
mTime, _ := mVal.(time.Time)
if assert.WithinRange(t, mTime, fTime.Add(-2*time.Second), fTime.Add(2*time.Second)) {
got = true
break
}
}
}
assert.Truef(t, got, "Couldn't find field %+v. We have %+v", f, e.Fields)
}

View file

@ -5,7 +5,6 @@ import (
"regexp"
"strconv"
"strings"
"time"
)
func ParseBytes(in []byte) any {
@ -34,7 +33,7 @@ func ParseString(in string) any {
return i
} else if f, err := strconv.ParseFloat(s, 64); err == nil {
return f
} else if t, err := time.Parse(time.RFC3339, s); err == nil {
} else if t, err := ParseDate(s); err == nil {
return t
} else if err := json.Unmarshal([]byte(s), &j); err == nil {
return j

57
tools/parse_date.go Normal file
View file

@ -0,0 +1,57 @@
package tools
import (
"time"
dp "github.com/markusmobius/go-dateparser"
"github.com/markusmobius/go-dateparser/date"
)
const DateFormat = "January 02, 2006 at 03:04:05PM -0700"
// These are somewhat arbitrary, but reasonably useful min and max times
var (
MinTime = time.Unix(-2208988800, 0) // Jan 1, 1900
MaxTime = MinTime.Add(1<<63 - 1)
)
func ParseDate(in string) (t time.Time, err error) {
if in == "min" {
return MinTime, nil
}
if in == "max" {
return MaxTime, nil
}
conf := dp.Configuration{
CurrentTime: time.Now().Local(),
ReturnTimeAsPeriod: true,
Languages: []string{"en"},
}
d, err := dp.Parse(&conf, in)
t = d.Time
if err != nil {
d, err = dp.Parse(&conf, in, DateFormat)
t = d.Time
return
}
y, mon, day, h, loc := t.Year(), t.Month(), t.Day(), t.Hour(), t.Location()
switch d.Period {
case date.Second:
t = t.Truncate(time.Second)
case date.Minute:
t = t.Truncate(time.Minute)
case date.Hour:
t = time.Date(y, mon, day, h, 0, 0, 0, loc)
case date.Day:
t = time.Date(y, mon, day, 0, 0, 0, 0, loc)
case date.Month:
t = time.Date(y, mon, 1, 0, 0, 0, 0, loc)
case date.Year:
t = time.Date(y, 1, 1, 0, 0, 0, 0, loc)
}
return
}

68
tools/parse_date_test.go Normal file
View file

@ -0,0 +1,68 @@
package tools
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const day = time.Hour * 24
func TestParseDate(t *testing.T) {
now := time.Now().Local()
y, mon, d, h, loc := now.Year(), now.Month(), now.Day(), now.Hour(), now.Location()
sec := now.Truncate(time.Second)
today := time.Date(y, mon, d, 0, 0, 0, 0, loc)
tomorrow := today.Add(day)
yesterday := today.Add(-day)
twoMin := now.Add(2 * time.Minute).Truncate(time.Minute)
twoHour := time.Date(y, mon, d, h+2, 0, 0, 0, loc)
firstMonth := time.Date(y, mon, 1, 0, 0, 0, 0, loc)
firstYear := time.Date(y, 1, 1, 0, 0, 0, 0, loc)
exact := "2075-02-12T12:13:54.536-02:00"
exactd, _ := time.ParseInLocation(time.RFC3339, exact, time.FixedZone("UTC-02:00", -7200))
var ts int64 = 1708876012
tsd := time.Unix(ts, 0)
ent := "February 25, 2024 at 04:00:13AM +0230"
entd, _ := time.Parse(DateFormat, ent)
tests := []struct {
name string
exp time.Time
err string
}{
{"now", sec, ""},
{"today", today, ""},
{"tomorrow", tomorrow, ""},
{"yesterday", yesterday, ""},
{"in two minutes", twoMin, ""},
{"in two hours", twoHour, ""},
{"this month", firstMonth, ""},
{"this year", firstYear, ""},
{"min", MinTime, ""},
{"max", MaxTime, ""},
{exact, exactd, ""},
{fmt.Sprint(ts), tsd, ""},
{ent, entd, ""},
{"not a date", now, fmt.Sprintf(`failed to parse "%s": unknown format`, "not a date")},
}
for _, tt := range tests {
t.Run(tt.name, getDateTest(tt.name, tt.exp, tt.err))
}
}
func getDateTest(in string, exp time.Time, err string) func(t *testing.T) {
return func(t *testing.T) {
out, er := ParseDate(in)
if err != "" {
assert.ErrorContains(t, er, err)
} else {
require.NoError(t, er)
assert.Equal(t, exp, out)
}
}
}

View file

@ -10,6 +10,7 @@ import (
func TestParse(t *testing.T) {
when := time.Now()
now := when.Local().Truncate(time.Second)
tests := []struct {
name string
in string
@ -22,6 +23,8 @@ func TestParse(t *testing.T) {
{"false", "false", false},
{"nil", "nil", nil},
{"time", when.Format(time.RFC3339), when},
{"now", "now", now},
{"DateFormat", now.Format(DateFormat), now},
{"json-obj", `{"foo":"bar","baz":"quux"}`, json.RawMessage(`{"foo":"bar","baz":"quux"}`)},
{"json-arr", `["foo",42,"bar", null,"quux", true]`, json.RawMessage(`["foo",42,"bar", null,"quux", true]`)},
{"empty", "", ""},
@ -48,7 +51,7 @@ func getParseTestRunner(in string, exp any) func(*testing.T) {
if expT, ok := exp.(time.Time); ok {
ti, gotTime := out.(time.Time)
if assert.True(t, gotTime, "Should have gotten a time.Time, but didn't") {
assert.WithinRange(t, expT, ti.Add(-time.Second), ti.Add(time.Second))
assert.WithinRange(t, expT, ti.Add(-2*time.Second), ti.Add(2*time.Second))
}
} else {
assert.Equal(t, exp, out)
@ -57,7 +60,7 @@ func getParseTestRunner(in string, exp any) func(*testing.T) {
if expT, ok := exp.(time.Time); ok {
ti, gotTime := out.(time.Time)
if assert.True(t, gotTime, "Should have gotten a time.Time, but didn't") {
assert.WithinRange(t, expT, ti.Add(-time.Second), ti.Add(time.Second))
assert.WithinRange(t, expT, ti.Add(-2*time.Second), ti.Add(2*time.Second))
}
} else {
assert.Equal(t, exp, out)