Files
nofx/market/timeframe_test.go
tinkle-community 41c2625bb2 test(manager,market,trader): cover previously untested core paths
- manager: 15 tests incl. concurrent map access under -race (was 0 tests)
- market: timeframe normalization regression tests
- trader: FIFO position rebuild tests (partial closes, hedge/one-way mode,
  PnL-fallback entry price, invalid input)
2026-06-11 00:37:45 +08:00

124 lines
3.7 KiB
Go

package market
import (
"slices"
"testing"
"time"
)
func TestNormalizeTimeframe(t *testing.T) {
tests := []struct {
name string
input string
want string
wantErr bool
}{
{name: "valid lowercase minute", input: "1m", want: "1m"},
{name: "valid lowercase hour", input: "4h", want: "4h"},
{name: "valid lowercase day", input: "1d", want: "1d"},
{name: "uppercase normalized", input: "1H", want: "1h"},
{name: "mixed case normalized", input: "15M", want: "15m"},
{name: "uppercase day", input: "1D", want: "1d"},
{name: "leading and trailing whitespace", input: " 30m ", want: "30m"},
{name: "whitespace and uppercase", input: " 12H ", want: "12h"},
{name: "empty string", input: "", wantErr: true},
{name: "whitespace only", input: " ", wantErr: true},
{name: "unsupported value", input: "7m", wantErr: true},
{name: "unsupported week", input: "1w", wantErr: true},
{name: "garbage input", input: "abc", wantErr: true},
{name: "internal whitespace not trimmed", input: "1 m", wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NormalizeTimeframe(tt.input)
if tt.wantErr {
if err == nil {
t.Fatalf("NormalizeTimeframe(%q) = %q, want error", tt.input, got)
}
return
}
if err != nil {
t.Fatalf("NormalizeTimeframe(%q) unexpected error: %v", tt.input, err)
}
if got != tt.want {
t.Errorf("NormalizeTimeframe(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}
func TestTFDuration(t *testing.T) {
tests := []struct {
name string
input string
want time.Duration
wantErr bool
}{
{name: "one minute", input: "1m", want: time.Minute},
{name: "three minutes", input: "3m", want: 3 * time.Minute},
{name: "five minutes", input: "5m", want: 5 * time.Minute},
{name: "fifteen minutes", input: "15m", want: 15 * time.Minute},
{name: "thirty minutes", input: "30m", want: 30 * time.Minute},
{name: "one hour", input: "1h", want: time.Hour},
{name: "two hours", input: "2h", want: 2 * time.Hour},
{name: "four hours", input: "4h", want: 4 * time.Hour},
{name: "six hours", input: "6h", want: 6 * time.Hour},
{name: "twelve hours", input: "12h", want: 12 * time.Hour},
{name: "one day", input: "1d", want: 24 * time.Hour},
{name: "uppercase with whitespace", input: " 1D ", want: 24 * time.Hour},
{name: "empty string", input: "", wantErr: true},
{name: "unsupported value", input: "2d", wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := TFDuration(tt.input)
if tt.wantErr {
if err == nil {
t.Fatalf("TFDuration(%q) = %v, want error", tt.input, got)
}
return
}
if err != nil {
t.Fatalf("TFDuration(%q) unexpected error: %v", tt.input, err)
}
if got != tt.want {
t.Errorf("TFDuration(%q) = %v, want %v", tt.input, got, tt.want)
}
})
}
}
func TestSupportedTimeframes(t *testing.T) {
got := SupportedTimeframes()
if len(got) == 0 {
t.Fatal("SupportedTimeframes() returned empty slice")
}
if !slices.IsSorted(got) {
t.Errorf("SupportedTimeframes() not sorted: %v", got)
}
for _, required := range []string{"1m", "1d"} {
if !slices.Contains(got, required) {
t.Errorf("SupportedTimeframes() missing %q: %v", required, got)
}
}
// Every advertised timeframe must round-trip through NormalizeTimeframe and TFDuration.
for _, tf := range got {
norm, err := NormalizeTimeframe(tf)
if err != nil {
t.Errorf("NormalizeTimeframe(%q) unexpected error: %v", tf, err)
}
if norm != tf {
t.Errorf("NormalizeTimeframe(%q) = %q, want identity", tf, norm)
}
if d, err := TFDuration(tf); err != nil || d <= 0 {
t.Errorf("TFDuration(%q) = %v, %v; want positive duration and nil error", tf, d, err)
}
}
}