- Fix 'string(d)' int-to-rune conversion in rpg_test.go by using fmt.Sprintf - Fix ComputeDailyTotals dayStart logic: initial state should be StateWorking when dayStart > 0, correctly counting work from dayStart to first out event - Update README project structure to reflect new domain/handler/repo layout - Implement missing achievements: rpg_pioneer (on RPG enable) and salary_setter (on first rate set) - Add tryUnlockAchievement helper to Handler
210 lines
5.3 KiB
Go
210 lines
5.3 KiB
Go
package domain
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func mkEvent(typ string, ts int64) Event {
|
|
return Event{EventType: typ, OccurredAt: ts}
|
|
}
|
|
|
|
func TestComputeDailyTotals_Empty(t *testing.T) {
|
|
got := ComputeDailyTotals(nil, 0, 0, 0)
|
|
if got.TotalSeconds != 0 || got.BreakSeconds != 0 {
|
|
t.Errorf("empty input: got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_NoEvents(t *testing.T) {
|
|
got := ComputeDailyTotals([]Event{}, 0, 0, 0)
|
|
if got.TotalSeconds != 0 || got.BreakSeconds != 0 {
|
|
t.Errorf("no events: got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_SingleInOut(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("out", 200),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 0)
|
|
if got.TotalSeconds != 100 {
|
|
t.Errorf("single in/out: TotalSeconds=%d, want 100", got.TotalSeconds)
|
|
}
|
|
if got.BreakSeconds != 0 {
|
|
t.Errorf("single in/out: BreakSeconds=%d, want 0", got.BreakSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_MissingFirstIn(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("out", 200),
|
|
mkEvent("in", 300),
|
|
mkEvent("out", 400),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 100, 500)
|
|
if got.TotalSeconds != 200 {
|
|
t.Errorf("missing first in: TotalSeconds=%d, want 200", got.TotalSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_MissingLastOut(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("out", 200),
|
|
mkEvent("in", 300),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 500)
|
|
if got.TotalSeconds != 300 {
|
|
t.Errorf("missing last out: TotalSeconds=%d, want 300", got.TotalSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_BreakUnderThreshold(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("out", 200),
|
|
mkEvent("in", 250),
|
|
mkEvent("out", 400),
|
|
}
|
|
got := ComputeDailyTotals(events, 60, 0, 0)
|
|
if got.TotalSeconds != 300 {
|
|
t.Errorf("break under threshold: TotalSeconds=%d, want 300", got.TotalSeconds)
|
|
}
|
|
if got.BreakSeconds != 0 {
|
|
t.Errorf("break under threshold: BreakSeconds=%d, want 0", got.BreakSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_BreakOverThreshold(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("out", 200),
|
|
mkEvent("in", 300),
|
|
mkEvent("out", 400),
|
|
}
|
|
got := ComputeDailyTotals(events, 60, 0, 0)
|
|
if got.TotalSeconds != 200 {
|
|
t.Errorf("break over threshold: TotalSeconds=%d, want 200", got.TotalSeconds)
|
|
}
|
|
if got.BreakSeconds != 100 {
|
|
t.Errorf("break over threshold: BreakSeconds=%d, want 100", got.BreakSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_ConsecutiveOuts(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("out", 200),
|
|
mkEvent("out", 250),
|
|
mkEvent("in", 300),
|
|
mkEvent("out", 400),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 0)
|
|
if got.TotalSeconds != 200 {
|
|
t.Errorf("consecutive outs: TotalSeconds=%d, want 200", got.TotalSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_ConsecutiveIns(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("in", 150),
|
|
mkEvent("out", 300),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 0)
|
|
if got.TotalSeconds != 200 {
|
|
t.Errorf("consecutive ins: TotalSeconds=%d, want 200", got.TotalSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_SingleInEndOfDay(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 500)
|
|
if got.TotalSeconds != 400 {
|
|
t.Errorf("single in with dayEnd: TotalSeconds=%d, want 400", got.TotalSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_SingleOutStartOfDay(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("out", 200),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 100, 500)
|
|
if got.TotalSeconds != 100 {
|
|
t.Errorf("single out with dayStart: TotalSeconds=%d, want 100", got.TotalSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_ZeroThreshold(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("out", 200),
|
|
mkEvent("in", 250),
|
|
mkEvent("out", 400),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 0)
|
|
if got.TotalSeconds != 250 {
|
|
t.Errorf("zero threshold: TotalSeconds=%d, want 250", got.TotalSeconds)
|
|
}
|
|
if got.BreakSeconds != 50 {
|
|
t.Errorf("zero threshold: BreakSeconds=%d, want 50", got.BreakSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_LargeBreak(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("out", 200),
|
|
mkEvent("in", 10000),
|
|
mkEvent("out", 10100),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 0)
|
|
if got.TotalSeconds != 200 {
|
|
t.Errorf("large break: TotalSeconds=%d, want 200", got.TotalSeconds)
|
|
}
|
|
if got.BreakSeconds != 9800 {
|
|
t.Errorf("large break: BreakSeconds=%d, want 9800", got.BreakSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_MultipleSessions(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
mkEvent("out", 200),
|
|
mkEvent("in", 300),
|
|
mkEvent("out", 400),
|
|
mkEvent("in", 500),
|
|
mkEvent("out", 600),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 0)
|
|
if got.TotalSeconds != 300 {
|
|
t.Errorf("multiple sessions: TotalSeconds=%d, want 300", got.TotalSeconds)
|
|
}
|
|
if got.BreakSeconds != 200 {
|
|
t.Errorf("multiple sessions: BreakSeconds=%d, want 200", got.BreakSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_OnlyOut(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("out", 200),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 0)
|
|
if got.TotalSeconds != 0 {
|
|
t.Errorf("only out no dayStart: TotalSeconds=%d, want 0", got.TotalSeconds)
|
|
}
|
|
}
|
|
|
|
func TestComputeDailyTotals_OnlyIn(t *testing.T) {
|
|
events := []Event{
|
|
mkEvent("in", 100),
|
|
}
|
|
got := ComputeDailyTotals(events, 0, 0, 0)
|
|
if got.TotalSeconds != 0 {
|
|
t.Errorf("only in no dayEnd: TotalSeconds=%d, want 0", got.TotalSeconds)
|
|
}
|
|
}
|