Skip to content

Commit

Permalink
improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tillkuhn committed Nov 16, 2024
1 parent 514f1fa commit 60707e0
Show file tree
Hide file tree
Showing 13 changed files with 120 additions and 68 deletions.
31 changes: 10 additions & 21 deletions cmd/punch.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,37 @@ package cmd

import (
"context"
"errors"
"fmt"
"log"
"strconv"
"time"

"github.com/spf13/cobra"
"github.com/tillkuhn/billy-idle/pkg/tracker"
)

const secsPerHour = 60 * 60

var errArg = errors.New("argument error")

// punchCmd represents the busy command
var punchCmd = &cobra.Command{
Use: "punch",
Short: "Punch busy time",
Long: ``,
Use: "punch",
Short: "Punch the clock - enter actual busy time",
Example: "punch 10h5m 2024-11-07",
Args: cobra.MatchAll(cobra.MinimumNArgs(1), cobra.MaximumNArgs(2)),
Long: ``,
RunE: func(_ *cobra.Command, args []string) error {
var err error
var day time.Time
switch len(args) {
case 0:
return fmt.Errorf("%w: expected a duration and optional day argument", errArg)
case 1:
day = time.Now()
case 2:
day = time.Now()
if len(args) == 2 {
day, err = time.Parse("2006-01-02", args[1])
if err != nil {
return err
}
default:
return fmt.Errorf("%w: to many arguments", errArg)
}
fmt.Println("enter busy time=" + args[0])
t := tracker.New(&trackOpts)
bt, err := strconv.Atoi(args[0])
dur, err := time.ParseDuration(args[0])
fmt.Printf("🕰️ Punching busy time=%s for day %s\n", dur, day.Format("2006-01-02 (Monday)"))
if err != nil {
return err
}
dur := time.Second * time.Duration(bt) * secsPerHour
t := tracker.New(&trackOpts)
if err := t.UpsertPunchRecord(context.Background(), dur, day); err != nil {
log.Println(err)
}
Expand Down
51 changes: 47 additions & 4 deletions cmd/punch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,57 @@ package cmd

import (
"bytes"
"slices"
"testing"

"github.com/stretchr/testify/assert"
)

func TestSum(t *testing.T) {
tests := map[string]struct {
args []string
out string
error string
}{
"missing-args": {
[]string{},
"",
"requires at least 1 arg(s)",
},
"too-many-args": {
[]string{"1", "2", "3"},
"",
"accepts at most 2 arg(s)",
},
"invalid-time": {
[]string{"morning"},
"",
"invalid duration",
},
"invalid-date": {
[]string{"4h5m", "last year"},
"",
"cannot parse",
},
}

for name, te := range tests {
t.Run(name, func(t *testing.T) {
actual := new(bytes.Buffer)
rootCmd.SetOut(actual)
rootCmd.SetErr(actual)
rootCmd.SetArgs(slices.Insert(te.args, 0, punchCmd.Use))
err := rootCmd.Execute()
if te.error != "" {
assert.ErrorContains(t, err, te.error)
}
assert.Contains(t, actual.String(), te.out)
})
}
}

/*
func Test_ExecuteTrackCommandMissingArg(t *testing.T) {
actual := new(bytes.Buffer)
rootCmd.SetOut(actual)
rootCmd.SetErr(actual)
rootCmd.SetArgs([]string{punchCmd.Use})
assert.ErrorContains(t, rootCmd.Execute(), "expected a duration")
}
Expand All @@ -22,3 +63,5 @@ func Test_ExecuteTrackCommand(t *testing.T) {
rootCmd.SetArgs([]string{punchCmd.Use, "2"})
assert.NoError(t, rootCmd.Execute())
}
*/
2 changes: 1 addition & 1 deletion cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ func Test_DefaultAppRoot(t *testing.T) {
assert.Equal(t, defaultAppRoot(), h+"/.billy-idle")
}

func Test_DefaultEnd(t *testing.T) {
func Test_DefaultEnv(t *testing.T) {
assert.Equal(t, defaultEnv(), "test")
}
2 changes: 1 addition & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func Test_Tracker(t *testing.T) {
CheckInterval: 100 * time.Millisecond,
IdleTolerance: 150 * time.Millisecond, // fixed mock value is 125ms
AppRoot: dir, // overwrite with tempdir
Cmd: "pkg/tracker/ioreg_mock.sh",
Cmd: "testdata/ioreg_mock.sh",
Debug: true,
DropCreate: true,
}
Expand Down
37 changes: 3 additions & 34 deletions pkg/tracker/db_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package tracker

import (
"context"
"sync"
"testing"
"time"

"github.com/DATA-DOG/go-sqlmock"
"github.com/jmoiron/sqlx"

"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
)

Expand All @@ -17,39 +15,10 @@ func DBMock(t *testing.T) (*Tracker, sqlmock.Sqlmock) {
mockDB, sqlMock, err := sqlmock.New()
assert.NoError(t, err)
sqlxDB := sqlx.NewDb(mockDB, "sqlmock")
tr := &Tracker{
opts: &Options{ClientID: "test"},
db: sqlxDB,
wg: sync.WaitGroup{},
}
tr := NewWithDB(&Options{ClientID: "test"}, sqlxDB)
return tr, sqlMock
}

func Test_UpsertBusyUpdate(t *testing.T) {
tracker, mock := DBMock(t)
day := time.Now().Round(time.Hour) // Mon Jan 2 15:04:05 MST 2006
sql1 := wildcardStatement("UPDATE " + tablePunch + " SET")
// mock.ExpectPrepare(sql1)
mock.ExpectExec(sql1).WithArgs(day, float64(3600), "test").
WillReturnResult(sqlmock.NewResult(0, 88))
err := tracker.UpsertPunchRecord(context.Background(), time.Second*3600, day)
assert.NoError(t, err)
}

func Test_UpsertBusyInsert(t *testing.T) {
tracker, mock := DBMock(t)
day := time.Now().Round(time.Hour) // Mon Jan 2 15:04:05 MST 2006
sql1 := wildcardStatement("UPDATE " + tablePunch + " SET")
// mock.ExpectPrepare(sql1)
mock.ExpectExec(sql1).WithArgs(day, float64(3600), "test").
WillReturnResult(sqlmock.NewResult(0, 0))
mock.ExpectQuery("INSERT INTO "+tablePunch).
WithArgs(day, float64(3600), "test").
WillReturnRows(mock.NewRows([]string{"id"}).
AddRow("44"))
err := tracker.UpsertPunchRecord(context.Background(), time.Second*3600, day)
assert.NoError(t, err)
}
func wildcardStatement(stmt string) string {
return stmt + " (.+)"
}
5 changes: 3 additions & 2 deletions pkg/tracker/init-db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ CREATE TABLE IF NOT EXISTS punch (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"day" DATE NOT NULL DEFAULT (current_date),
"busy_secs" INTEGER NOT NULL DEFAULT 0,
"created" DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')),
"client" TEXT );
"updated" DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')),
"client" TEXT,
"note" TEXT );
-- SQLite does not support add column if not exists https://stackoverflow.com/q/3604310/4292075
-- ALTER TABLE track ADD COLUMN IF NOT EXISTS task TEXT;
15 changes: 11 additions & 4 deletions pkg/tracker/punch.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ import (
"github.com/pkg/errors"
)

const tablePunch = "punch"
const (
tablePunch = "punch"
hoursPerDay = 24
)

func (t *Tracker) UpsertPunchRecord(ctx context.Context, busyDuration time.Duration, day time.Time) error {
uQuery := `UPDATE ` + tablePunch + `
SET busy_secs=$2,client=$3
WHERE day=$1`
day = day.Round(time.Hour)
day = trucateDay(day) // https://stackoverflow.com/a/38516536/4292075
uRes, err := t.db.ExecContext(ctx, uQuery, day, busyDuration.Seconds(), t.opts.ClientID)
if err != nil {
return errors.Wrap(err, "unable to update busy table")
}
if updated, _ := uRes.RowsAffected(); updated > 0 {
log.Printf("🥫 updated existing busy record for day %s duraction %ds", day, busyDuration)
log.Printf("🥫 Updated existing busy record for day %v duraction %v", day, busyDuration)
return nil // record was already present, insert not required
}

Expand All @@ -30,6 +33,10 @@ func (t *Tracker) UpsertPunchRecord(ctx context.Context, busyDuration time.Durat
if err := t.db.QueryRowContext(ctx, iQuery, day, busyDuration.Seconds(), t.opts.ClientID).Scan(&id); err != nil {
return errors.Wrap(err, "unable to insert new record in busy table")
}
log.Printf("🥫new busy record for day %s duraction %ds created with id=%d", day, busyDuration, id)
log.Printf("🥫 New busy record for day %v duraction %v created with id=%d", day, busyDuration, id)
return nil
}

func trucateDay(t time.Time) time.Time {
return t.Truncate(hoursPerDay * time.Hour).UTC()
}
36 changes: 36 additions & 0 deletions pkg/tracker/punch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package tracker

import (
"context"
"testing"
"time"

"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
)

func Test_UpsertPunchUpdate(t *testing.T) {
tracker, mock := DBMock(t)
day := trucateDay(time.Now())
sql1 := wildcardStatement("UPDATE " + tablePunch + " SET")
// mock.ExpectPrepare(sql1)
mock.ExpectExec(sql1).WithArgs(day, float64(3600), "test").
WillReturnResult(sqlmock.NewResult(0, 88))
err := tracker.UpsertPunchRecord(context.Background(), time.Second*3600, day)
assert.NoError(t, err)
}

func Test_UpsertPunchInsert(t *testing.T) {
tracker, mock := DBMock(t)
day := trucateDay(time.Now())
sql1 := wildcardStatement("UPDATE " + tablePunch + " SET")
// mock.ExpectPrepare(sql1)
mock.ExpectExec(sql1).WithArgs(day, float64(3600), "test").
WillReturnResult(sqlmock.NewResult(0, 0))
mock.ExpectQuery("INSERT INTO "+tablePunch).
WithArgs(day, float64(3600), "test").
WillReturnRows(mock.NewRows([]string{"id"}).
AddRow("44"))
err := tracker.UpsertPunchRecord(context.Background(), time.Second*3600, day)
assert.NoError(t, err)
}
5 changes: 5 additions & 0 deletions pkg/tracker/tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func New(opts *Options) *Tracker {
if err != nil {
log.Fatal(err)
}
return NewWithDB(opts, db)
}

// NewWithDB returns a new Tracker configured with the given Options and DB, good for testing
func NewWithDB(opts *Options, db *sqlx.DB) *Tracker {
return &Tracker{
opts: opts,
db: db,
Expand Down
1 change: 0 additions & 1 deletion sqlite/.gitignore

This file was deleted.

Empty file removed sqlite/.gitkeep
Empty file.
3 changes: 3 additions & 0 deletions testdata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# testdata

folder for test files, ignored by the go tool
File renamed without changes.

0 comments on commit 60707e0

Please sign in to comment.