▍ humdrum codex / dots v1.0.0
2.6 KB raw
 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
package day

import (
	"reflect"
	"testing"
	"time"
)

func at(h, m int) time.Time {
	return time.Date(2026, 7, 29, h, m, 0, 0, time.UTC)
}

func TestParseTarget(t *testing.T) {
	cases := []struct {
		name      string
		arg       string
		blockSize int
		now       time.Time
		want      []int
		wantErr   bool
	}{
		{"empty is current slot", "", 30, at(14, 47), []int{29}, false},
		{"single time", "14:30", 30, at(0, 0), []int{29}, false},
		{"snap down", "14:47", 30, at(0, 0), []int{29}, false},
		{"single digit hour", "9:00", 30, at(0, 0), []int{18}, false},
		{"leading zero", "09:00", 30, at(0, 0), []int{18}, false},
		{"range end exclusive", "13:00-15:00", 30, at(0, 0), []int{26, 27, 28, 29}, false},
		{"range at 15min blocks", "13:00-14:00", 15, at(0, 0), []int{52, 53, 54, 55}, false},
		{"range at 60min blocks", "13:30-15:00", 60, at(0, 0), []int{13, 14}, false},
		{"midnight slot", "0:00", 30, at(0, 0), []int{0}, false},
		{"last slot", "23:30", 30, at(0, 0), []int{47}, false},
		{"end before start", "15:00-13:00", 30, at(0, 0), nil, true},
		{"end equals start", "13:00-13:00", 30, at(0, 0), nil, true},
		{"hour out of range", "24:00", 30, at(0, 0), nil, true},
		{"minute out of range", "12:60", 30, at(0, 0), nil, true},
		{"garbage", "archer", 30, at(0, 0), nil, true},
		{"single digit minute", "1:5", 30, at(0, 0), nil, true},
		{"three digit hour", "007:00", 30, at(0, 0), nil, true},
		{"leading space", " 9:00", 30, at(0, 0), nil, true},
		{"incomplete range end", "9:00-", 30, at(0, 0), nil, true},
		{"hyphen prefix", "-9:00", 30, at(0, 0), nil, true},
		{"double hyphen in range", "9:00--15:00", 30, at(0, 0), nil, true},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got, err := ParseTarget(c.arg, c.blockSize, c.now)
			if c.wantErr != (err != nil) {
				t.Fatalf("err = %v, wantErr %v", err, c.wantErr)
			}
			if !c.wantErr && !reflect.DeepEqual(got, c.want) {
				t.Fatalf("got %v, want %v", got, c.want)
			}
		})
	}
}

func TestIsTimeSyntax(t *testing.T) {
	yes := []string{"14:30", "9:00", "09:00", "13:00-15:00"}
	no := []string{"archer", "", "14", "14:3", "a-b", "14:30ish", "1:5", " 9:00", "9:00-"}
	for _, s := range yes {
		if !IsTimeSyntax(s) {
			t.Errorf("IsTimeSyntax(%q) = false, want true", s)
		}
	}
	for _, s := range no {
		if IsTimeSyntax(s) {
			t.Errorf("IsTimeSyntax(%q) = true, want false", s)
		}
	}
}

func TestSlotLabel(t *testing.T) {
	if got := SlotLabel(29, 30); got != "14:30" {
		t.Fatalf("got %q", got)
	}
	if got := SlotLabel(0, 30); got != "0:00" {
		t.Fatalf("got %q", got)
	}
	if got := SlotLabel(55, 15); got != "13:45" {
		t.Fatalf("got %q", got)
	}
}