85456d3833
Stalls compared the due instant to now while Rank compares whole calendar days, so a task due at 09:00 was counted overdue from 09:01 while its own row on the same page still read the reason as today. Stalls now reads dayDelta. A row with no capture time also counted as sitting, because the zero time is January of year 1 and every span from it clears ten days. Rank already guarded that and Stalls did not. Folded the open-versus-candidate partition FormatRU and Spoken each carried into one split helper. The two have to agree on where that line falls.
90 lines
3.4 KiB
Go
90 lines
3.4 KiB
Go
package tasks
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func stallNow() time.Time { return time.Date(2026, 8, 5, 12, 0, 0, 0, time.UTC) }
|
|
|
|
func TestStallsCountsTheThreeShapes(t *testing.T) {
|
|
now := stallNow()
|
|
day := 24 * time.Hour
|
|
yesterday := now.Add(-day)
|
|
items := []Item{
|
|
// Overdue and sitting at once: it counts in both, because they are two
|
|
// different things wrong with one task.
|
|
{ID: 1, Text: "оплатить интернет", Status: StatusOpen, Created: now.Add(-20 * day), Due: &yesterday},
|
|
{ID: 2, Text: "купить молоко", Status: StatusOpen, Created: now.Add(-12 * day)},
|
|
{ID: 3, Text: "позвонить маме", Status: StatusOpen, Created: now.Add(-time.Hour)},
|
|
{ID: 4, Text: "продлить домен", Status: StatusCandidate, Created: now.Add(-30 * day), Due: &yesterday},
|
|
}
|
|
got := Stalls(items, now)
|
|
if len(got) != 3 {
|
|
t.Fatalf("shapes = %+v, want overdue, sitting, unconfirmed", got)
|
|
}
|
|
if got[0].N != 1 {
|
|
t.Errorf("overdue = %d, want 1 — a candidate's due date is Maven's reading of a mail", got[0].N)
|
|
}
|
|
if got[1].N != 2 {
|
|
t.Errorf("sitting = %d, want 2", got[1].N)
|
|
}
|
|
if got[2].N != 1 {
|
|
t.Errorf("unconfirmed = %d, want 1", got[2].N)
|
|
}
|
|
line := StallsRU(got)
|
|
for _, want := range []string{"просрочено", "лежит", "подтверждения"} {
|
|
if !strings.Contains(line, want) {
|
|
t.Errorf("StallsRU = %q, want it to mention %q", line, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStallsSaysNothingWhenThereIsNothing(t *testing.T) {
|
|
now := stallNow()
|
|
items := []Item{{ID: 1, Text: "купить молоко", Status: StatusOpen, Created: now.Add(-time.Hour)}}
|
|
if got := Stalls(items, now); len(got) != 0 {
|
|
t.Fatalf("shapes = %+v, want none", got)
|
|
}
|
|
// "ничего не залежалось" appended to every list read is a nag with a
|
|
// friendly face, so the empty case renders as nothing at all.
|
|
if got := StallsRU(nil); got != "" {
|
|
t.Errorf("StallsRU(nil) = %q, want empty", got)
|
|
}
|
|
}
|
|
|
|
func TestStallsCountsOverdueByCalendarDay(t *testing.T) {
|
|
// Same reading as Rank, or the count above the table contradicts the reason
|
|
// in the row: due at 09:00, asked at 12:00, and it is still due today.
|
|
now := stallNow()
|
|
earlier := now.Add(-3 * time.Hour)
|
|
items := []Item{{ID: 1, Text: "оплатить интернет", Status: StatusOpen, Created: now.Add(-time.Hour), Due: &earlier}}
|
|
if got := Stalls(items, now); len(got) != 0 {
|
|
t.Fatalf("shapes = %+v, want none — a task due today is not overdue", got)
|
|
}
|
|
}
|
|
|
|
func TestStallsIgnoresATaskWithNoCaptureTime(t *testing.T) {
|
|
// The zero time is January of year 1, so an unstamped row would count as
|
|
// sitting forever.
|
|
now := stallNow()
|
|
items := []Item{{ID: 1, Text: "купить молоко", Status: StatusOpen}}
|
|
if got := Stalls(items, now); len(got) != 0 {
|
|
t.Fatalf("shapes = %+v, want none", got)
|
|
}
|
|
}
|
|
|
|
func TestStallsCountsNoJudgement(t *testing.T) {
|
|
// The line this shape may not cross. Every sentence states a count; none of
|
|
// them says whether the work matters or should be dropped.
|
|
now := stallNow()
|
|
items := []Item{{ID: 1, Text: "оплатить интернет", Status: StatusOpen, Created: now.Add(-40 * 24 * time.Hour)}}
|
|
line := StallsRU(Stalls(items, now))
|
|
for _, banned := range []string{"стоит", "лучше", "надо", "брось", "важно"} {
|
|
if strings.Contains(line, banned) {
|
|
t.Errorf("StallsRU = %q — %q is an assessment, and counting is the whole licence here", line, banned)
|
|
}
|
|
}
|
|
}
|