1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
package cli
import (
"bytes"
"strings"
"testing"
"time"
"codeberg.org/snonux/timesamurai/internal/worktime"
)
func TestTimerStartAndStopCommands(t *testing.T) {
setupTimerState(t)
var startOut bytes.Buffer
startCmd := NewRootCmd()
startCmd.SetOut(&startOut)
startCmd.SetErr(&startOut)
startCmd.SetArgs([]string{"timer", "start"})
if err := startCmd.Execute(); err != nil {
t.Fatalf("timer start execute error = %v", err)
}
if !strings.Contains(startOut.String(), "Timer started.") {
t.Fatalf("timer start output = %q", startOut.String())
}
var stopOut bytes.Buffer
stopCmd := NewRootCmd()
stopCmd.SetOut(&stopOut)
stopCmd.SetErr(&stopOut)
stopCmd.SetArgs([]string{"timer", "stop"})
if err := stopCmd.Execute(); err != nil {
t.Fatalf("timer stop execute error = %v", err)
}
if !strings.Contains(stopOut.String(), "Timer stopped.") {
t.Fatalf("timer stop output = %q", stopOut.String())
}
}
func TestTimerContinueAtZero(t *testing.T) {
setupTimerState(t)
var out bytes.Buffer
cmd := NewRootCmd()
cmd.SetOut(&out)
cmd.SetErr(&out)
cmd.SetArgs([]string{"timer", "continue"})
if err := cmd.Execute(); err != nil {
t.Fatalf("timer continue execute error = %v", err)
}
if !strings.Contains(out.String(), "Timer is at 0, cannot continue.") {
t.Fatalf("timer continue output = %q", out.String())
}
}
func TestTimerStatusFlagConflict(t *testing.T) {
setupTimerState(t)
var out bytes.Buffer
cmd := NewRootCmd()
cmd.SetOut(&out)
cmd.SetErr(&out)
cmd.SetArgs([]string{"timer", "status", "--raw", "--raw-minutes"})
err := cmd.Execute()
if err == nil {
t.Fatal("timer status conflict error = nil, want error")
}
if !strings.Contains(err.Error(), "--raw") {
t.Fatalf("timer status conflict error = %v", err)
}
}
func TestTimerAutoWorktimeSync(t *testing.T) {
setupTimerState(t)
dbDir := t.TempDir()
cfgPath := writeWorkConfigWithAuto(t, dbDir, "host-auto", true)
out, err := runRootCommand("--config", cfgPath, "timer", "start")
if err != nil {
t.Fatalf("timer start error = %v (output: %q)", err, out)
}
out, err = runRootCommand("--config", cfgPath, "timer", "stop")
if err != nil {
t.Fatalf("timer stop error = %v (output: %q)", err, out)
}
entries, err := worktime.LoadAll(dbDir)
if err != nil {
t.Fatalf("LoadAll() error = %v", err)
}
var hasLogin bool
var hasLogout bool
for _, entry := range entries {
if entry.What != "work" {
continue
}
if entry.Action == "login" {
hasLogin = true
}
if entry.Action == "logout" {
hasLogout = true
}
}
if !hasLogin || !hasLogout {
t.Fatalf("auto worktime sync missing login/logout entries: %+v", entries)
}
}
func TestTimerAutoWorktimeSyncIgnoresAlreadyLoggedIn(t *testing.T) {
setupTimerState(t)
dbDir := t.TempDir()
if _, err := worktime.Login(dbDir, "host-auto", "work", time.Unix(100, 0), "seed"); err != nil {
t.Fatalf("seed Login() error = %v", err)
}
cfgPath := writeWorkConfigWithAuto(t, dbDir, "host-auto", true)
out, err := runRootCommand("--config", cfgPath, "timer", "start")
if err != nil {
t.Fatalf("timer start error = %v (output: %q)", err, out)
}
}
func TestTimerAutoWorktimeSyncIgnoresNotLoggedInOnStop(t *testing.T) {
setupTimerState(t)
dbDir := t.TempDir()
cfgPath := writeWorkConfigWithAuto(t, dbDir, "host-auto", true)
out, err := runRootCommand("--config", cfgPath, "timer", "stop")
if err != nil {
t.Fatalf("timer stop error = %v (output: %q)", err, out)
}
}
func setupTimerState(t *testing.T) {
t.Helper()
tempDir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tempDir)
t.Setenv("HOME", tempDir)
}
|