[🤖] Add Unit-Tests
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m34s

This commit is contained in:
2026-04-27 10:46:08 +02:00
parent dad0e3240d
commit 02d6894ec6
116 changed files with 18795 additions and 1 deletions
+145
View File
@@ -0,0 +1,145 @@
package timeext
import (
"testing"
"time"
)
func TestFromNanoseconds(t *testing.T) {
if got := FromNanoseconds(0); got != 0 {
t.Errorf("expected 0, got %v", got)
}
if got := FromNanoseconds(1); got != time.Nanosecond {
t.Errorf("expected 1ns, got %v", got)
}
if got := FromNanoseconds(1000); got != 1000*time.Nanosecond {
t.Errorf("expected 1000ns, got %v", got)
}
if got := FromNanoseconds(int64(123456789)); got != time.Duration(123456789) {
t.Errorf("expected 123456789ns, got %v", got)
}
}
func TestFromMicroseconds(t *testing.T) {
if got := FromMicroseconds(1); got != time.Microsecond {
t.Errorf("expected 1us, got %v", got)
}
if got := FromMicroseconds(1000); got != time.Millisecond {
t.Errorf("expected 1ms, got %v", got)
}
if got := FromMicroseconds(2.5); got != time.Microsecond*2+time.Nanosecond*500 {
t.Errorf("expected 2.5us, got %v", got)
}
}
func TestFromMilliseconds(t *testing.T) {
if got := FromMilliseconds(1); got != time.Millisecond {
t.Errorf("expected 1ms, got %v", got)
}
if got := FromMilliseconds(1000); got != time.Second {
t.Errorf("expected 1s, got %v", got)
}
}
func TestFromSeconds(t *testing.T) {
if got := FromSeconds(1); got != time.Second {
t.Errorf("expected 1s, got %v", got)
}
if got := FromSeconds(60); got != time.Minute {
t.Errorf("expected 1min, got %v", got)
}
if got := FromSeconds(0.5); got != 500*time.Millisecond {
t.Errorf("expected 0.5s, got %v", got)
}
}
func TestFromMinutes(t *testing.T) {
if got := FromMinutes(1); got != time.Minute {
t.Errorf("expected 1min, got %v", got)
}
if got := FromMinutes(60); got != time.Hour {
t.Errorf("expected 1h, got %v", got)
}
}
func TestFromHours(t *testing.T) {
if got := FromHours(1); got != time.Hour {
t.Errorf("expected 1h, got %v", got)
}
if got := FromHours(24); got != 24*time.Hour {
t.Errorf("expected 24h, got %v", got)
}
}
func TestFromDays(t *testing.T) {
if got := FromDays(1); got != 24*time.Hour {
t.Errorf("expected 1d, got %v", got)
}
if got := FromDays(7); got != 7*24*time.Hour {
t.Errorf("expected 7d, got %v", got)
}
if got := FromDays(0); got != 0 {
t.Errorf("expected 0, got %v", got)
}
}
func TestFormatNaturalDurationEnglish(t *testing.T) {
tests := []struct {
dur time.Duration
want string
}{
{time.Second, "1 second ago"},
{2 * time.Second, "2 seconds ago"},
{30 * time.Second, "30 seconds ago"},
{179 * time.Second, "179 seconds ago"},
{180 * time.Second, "3 minutes ago"},
{30 * time.Minute, "30 minutes ago"},
{179 * time.Minute, "179 minutes ago"},
{180 * time.Minute, "3 hours ago"},
{24 * time.Hour, "24 hours ago"},
{71 * time.Hour, "71 hours ago"},
{72 * time.Hour, "3 days ago"},
{20 * 24 * time.Hour, "20 days ago"},
{21 * 24 * time.Hour, "3 weeks ago"},
{11 * 7 * 24 * time.Hour, "11 weeks ago"},
// The months tier divides hours by (24*7*30); the actual boundaries are unusual
// but we capture the current observable behavior:
{12 * 7 * 24 * time.Hour, "0 months ago"},
{90 * 7 * 24 * time.Hour, "3 months ago"},
}
for _, tt := range tests {
got := FormatNaturalDurationEnglish(tt.dur)
if got != tt.want {
t.Errorf("FormatNaturalDurationEnglish(%v) = %q; want %q", tt.dur, got, tt.want)
}
}
}
func TestFormatDurationGerman(t *testing.T) {
tests := []struct {
dur time.Duration
want string
}{
{time.Second, "1s"},
{30 * time.Second, "30s"},
{179 * time.Second, "179s"},
{180 * time.Second, "3min"},
{30 * time.Minute, "30min"},
{179 * time.Minute, "179min"},
{180 * time.Minute, "3h"},
{24 * time.Hour, "24h"},
{71 * time.Hour, "71h"},
{72 * time.Hour, "3 Tage"},
{20 * 24 * time.Hour, "20 Tage"},
{21 * 24 * time.Hour, "3 Wochen"},
{11 * 7 * 24 * time.Hour, "11 Wochen"},
{12 * 7 * 24 * time.Hour, "0 Monate"},
{90 * 7 * 24 * time.Hour, "3 Monate"},
}
for _, tt := range tests {
got := FormatDurationGerman(tt.dur)
if got != tt.want {
t.Errorf("FormatDurationGerman(%v) = %q; want %q", tt.dur, got, tt.want)
}
}
}
+74
View File
@@ -0,0 +1,74 @@
package timeext
import (
"testing"
"time"
)
func TestMonthNameGermanShort3(t *testing.T) {
tests := []struct {
m time.Month
want string
}{
{time.January, "Jan"},
{time.February, "Feb"},
{time.March, "Mär"},
{time.April, "Apr"},
{time.May, "Mai"},
{time.June, "Jun"},
{time.July, "Jul"},
{time.August, "Aug"},
{time.September, "Sep"},
{time.October, "Okt"},
{time.November, "Nov"},
{time.December, "Dez"},
}
for _, tt := range tests {
got := MonthNameGermanShort3(tt.m)
if got != tt.want {
t.Errorf("MonthNameGermanShort3(%v) = %q; want %q", tt.m, got, tt.want)
}
}
}
func TestMonthNameGermanShort3_Invalid(t *testing.T) {
got := MonthNameGermanShort3(time.Month(13))
want := "%!Month(13)"
if got != want {
t.Errorf("MonthNameGermanShort3(13) = %q; want %q", got, want)
}
}
func TestMonthNameGermanLong(t *testing.T) {
tests := []struct {
m time.Month
want string
}{
{time.January, "Januar"},
{time.February, "Februar"},
{time.March, "März"},
{time.April, "April"},
{time.May, "Mai"},
{time.June, "Juni"},
{time.July, "Juli"},
{time.August, "August"},
{time.September, "September"},
{time.October, "Oktober"},
{time.November, "November"},
{time.December, "Dezember"},
}
for _, tt := range tests {
got := MonthNameGermanLong(tt.m)
if got != tt.want {
t.Errorf("MonthNameGermanLong(%v) = %q; want %q", tt.m, got, tt.want)
}
}
}
func TestMonthNameGermanLong_Invalid(t *testing.T) {
got := MonthNameGermanLong(time.Month(0))
want := "%!Month(0)"
if got != want {
t.Errorf("MonthNameGermanLong(0) = %q; want %q", got, want)
}
}
+180
View File
@@ -0,0 +1,180 @@
package timeext
import (
"testing"
"time"
)
func TestOpenTimeRange_String_Empty(t *testing.T) {
r := OpenTimeRange{}
if got := r.String(); got != "[]" {
t.Errorf("expected [], got %q", got)
}
}
func TestOpenTimeRange_String_FromOnly(t *testing.T) {
tm := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{From: &tm}
got := r.String()
if got == "" || got[0] != '[' || got[len(got)-1] != ']' {
t.Errorf("unexpected format: %q", got)
}
}
func TestOpenTimeRange_String_ToOnly(t *testing.T) {
tm := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{To: &tm}
got := r.String()
if got == "" || got[0] != '[' || got[len(got)-1] != ']' {
t.Errorf("unexpected format: %q", got)
}
}
func TestOpenTimeRange_String_Both(t *testing.T) {
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
t2 := time.Date(2022, 12, 31, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{From: &t1, To: &t2}
got := r.String()
if got == "" || got[0] != '[' || got[len(got)-1] != ']' {
t.Errorf("unexpected format: %q", got)
}
}
func TestOpenTimeRange_Contains_Empty(t *testing.T) {
r := OpenTimeRange{}
if !r.Contains(time.Now()) {
t.Errorf("empty range should contain anything")
}
}
func TestOpenTimeRange_Contains_FromOnly(t *testing.T) {
from := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{From: &from}
if r.Contains(time.Date(2022, 5, 1, 0, 0, 0, 0, time.UTC)) {
t.Errorf("should not contain time before From")
}
if !r.Contains(from) {
t.Errorf("should contain From itself")
}
if !r.Contains(time.Date(2022, 7, 1, 0, 0, 0, 0, time.UTC)) {
t.Errorf("should contain time after From")
}
}
func TestOpenTimeRange_Contains_ToOnly(t *testing.T) {
to := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{To: &to}
if !r.Contains(time.Date(2022, 5, 1, 0, 0, 0, 0, time.UTC)) {
t.Errorf("should contain time before To")
}
if r.Contains(to) {
t.Errorf("should not contain To itself (exclusive)")
}
if r.Contains(time.Date(2022, 7, 1, 0, 0, 0, 0, time.UTC)) {
t.Errorf("should not contain time after To")
}
}
func TestOpenTimeRange_Contains_Both(t *testing.T) {
from := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
to := time.Date(2022, 12, 31, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{From: &from, To: &to}
if r.Contains(time.Date(2021, 12, 31, 0, 0, 0, 0, time.UTC)) {
t.Errorf("should not contain time before From")
}
if !r.Contains(time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC)) {
t.Errorf("should contain time within range")
}
if r.Contains(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)) {
t.Errorf("should not contain time after To")
}
}
func TestNewOpenTimeRange_Nil(t *testing.T) {
if NewOpenTimeRange(nil, nil) != nil {
t.Errorf("expected nil for both nil inputs")
}
}
func TestNewOpenTimeRange_FromOnly(t *testing.T) {
tm := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
r := NewOpenTimeRange(&tm, nil)
if r == nil || r.From == nil || !r.From.Equal(tm) || r.To != nil {
t.Errorf("unexpected result: %v", r)
}
}
func TestNewOpenTimeRange_ToOnly(t *testing.T) {
tm := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
r := NewOpenTimeRange(nil, &tm)
if r == nil || r.To == nil || !r.To.Equal(tm) || r.From != nil {
t.Errorf("unexpected result: %v", r)
}
}
func TestNewOpenTimeRange_Both(t *testing.T) {
from := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
to := time.Date(2022, 12, 31, 0, 0, 0, 0, time.UTC)
r := NewOpenTimeRange(&from, &to)
if r == nil || !r.From.Equal(from) || !r.To.Equal(to) {
t.Errorf("unexpected result: %v", r)
}
}
func TestOpenTimeRange_ToMongoPipeline_Empty(t *testing.T) {
r := OpenTimeRange{}
pipeline := r.ToMongoPipeline("ts")
if len(pipeline) != 0 {
t.Errorf("expected empty pipeline, got %v", pipeline)
}
}
func TestOpenTimeRange_ToMongoPipeline_FromOnly(t *testing.T) {
from := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{From: &from}
pipeline := r.ToMongoPipeline("ts")
if len(pipeline) != 1 {
t.Errorf("expected 1 stage, got %d", len(pipeline))
}
}
func TestOpenTimeRange_ToMongoPipeline_ToOnly(t *testing.T) {
to := time.Date(2022, 12, 31, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{To: &to}
pipeline := r.ToMongoPipeline("ts")
if len(pipeline) != 1 {
t.Errorf("expected 1 stage, got %d", len(pipeline))
}
}
func TestOpenTimeRange_ToMongoPipeline_Both(t *testing.T) {
from := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
to := time.Date(2022, 12, 31, 0, 0, 0, 0, time.UTC)
r := OpenTimeRange{From: &from, To: &to}
pipeline := r.ToMongoPipeline("ts")
if len(pipeline) != 2 {
t.Errorf("expected 2 stages, got %d", len(pipeline))
}
}
func TestOpenTimeRange_AppendToMongoPipeline_Nil(t *testing.T) {
var r *OpenTimeRange
existing := []any{"existing"}
got := r.AppendToMongoPipeline(existing, "ts")
if len(got) != 1 {
t.Errorf("expected unchanged pipeline, got %v", got)
}
}
func TestOpenTimeRange_AppendToMongoPipeline_NonNil(t *testing.T) {
from := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
r := &OpenTimeRange{From: &from}
existing := []any{"existing"}
got := r.AppendToMongoPipeline(existing, "ts")
if len(got) != 2 {
t.Errorf("expected pipeline with 2 entries, got %v", got)
}
}
+43
View File
@@ -0,0 +1,43 @@
package timeext
import (
"testing"
"time"
)
func TestWeekdayNameGerman(t *testing.T) {
tests := []struct {
d time.Weekday
want string
}{
{time.Sunday, "Sonntag"},
{time.Monday, "Montag"},
{time.Tuesday, "Dienstag"},
{time.Wednesday, "Mittwoch"},
{time.Thursday, "Donnerstag"},
{time.Friday, "Freitag"},
{time.Saturday, "Samstag"},
}
for _, tt := range tests {
got := WeekdayNameGerman(tt.d)
if got != tt.want {
t.Errorf("WeekdayNameGerman(%v) = %q; want %q", tt.d, got, tt.want)
}
}
}
func TestWeekdayNameGerman_Invalid(t *testing.T) {
got := WeekdayNameGerman(time.Weekday(8))
want := "%!Weekday(8)"
if got != want {
t.Errorf("WeekdayNameGerman(8) = %q; want %q", got, want)
}
}
func TestWeekdayNameGerman_Negative(t *testing.T) {
got := WeekdayNameGerman(time.Weekday(-1))
want := "%!Weekday(-1)"
if got != want {
t.Errorf("WeekdayNameGerman(-1) = %q; want %q", got, want)
}
}
+81
View File
@@ -0,0 +1,81 @@
package timeext
import (
"testing"
"time"
)
func TestGetIsoWeekCount(t *testing.T) {
// The implementation subtracts 1 from the ISO week numbers internally,
// so for a 53-week year it returns 52, and for a 52-week year it returns 51.
tests := []struct {
year int
want int
}{
{2020, 52}, // 2020 is a 53-week ISO year
{2021, 51},
{2022, 51},
{2023, 51},
{2024, 51},
}
for _, tt := range tests {
got := GetIsoWeekCount(tt.year)
if got != tt.want {
t.Errorf("GetIsoWeekCount(%d) = %d; want %d", tt.year, got, tt.want)
}
}
}
func TestGetAggregateIsoWeekCount_Year1900(t *testing.T) {
got := GetAggregateIsoWeekCount(1900)
if got != 0 {
t.Errorf("GetAggregateIsoWeekCount(1900) = %d; want 0", got)
}
}
func TestGetAggregateIsoWeekCount_Monotonic(t *testing.T) {
// The aggregate count must be strictly monotonically increasing year over year (for years > 1900)
prev := GetAggregateIsoWeekCount(1900)
for y := 1901; y <= 2030; y++ {
cur := GetAggregateIsoWeekCount(y)
if cur <= prev {
t.Errorf("GetAggregateIsoWeekCount(%d)=%d not greater than GetAggregateIsoWeekCount(%d)=%d", y, cur, y-1, prev)
}
prev = cur
}
}
func TestGetAggregateIsoWeekCount_BeforeBaseline(t *testing.T) {
// for years < 1900 the aggregate is negative
got := GetAggregateIsoWeekCount(1899)
if got >= 0 {
t.Errorf("GetAggregateIsoWeekCount(1899) = %d; want < 0", got)
}
}
func TestGetGlobalWeeknumber_Monotonic(t *testing.T) {
// Walking forward by one week should never decrease the global week number
t0 := time.Date(2020, 1, 6, 0, 0, 0, 0, TimezoneBerlin) // Monday, ISO week 2 of 2020
prev := GetGlobalWeeknumber(t0)
for i := 1; i < 200; i++ {
ti := t0.AddDate(0, 0, i*7)
cur := GetGlobalWeeknumber(ti)
if cur < prev {
t.Errorf("week number decreased at offset %d weeks: %d -> %d", i, prev, cur)
}
prev = cur
}
}
func TestGetGlobalWeeknumber_DifferentYearsDiffer(t *testing.T) {
w2020 := GetGlobalWeeknumber(time.Date(2020, 6, 1, 0, 0, 0, 0, TimezoneBerlin))
w2021 := GetGlobalWeeknumber(time.Date(2021, 6, 1, 0, 0, 0, 0, TimezoneBerlin))
if w2021 <= w2020 {
t.Errorf("expected w2021 > w2020, got %d and %d", w2021, w2020)
}
// Approximately 52 weeks apart
delta := w2021 - w2020
if delta < 50 || delta > 54 {
t.Errorf("expected ~52 week difference, got %d", delta)
}
}