feat: add OffsetDateTime, nesting limits and uniform Marshaler dispatch
Test / test (push) Successful in 2m18s
Test / test (push) Successful in 2m18s
Assisted-by: DeepSeek V4.1 Flash
This commit is contained in:
+127
@@ -951,3 +951,130 @@ func TestQuotedStringNeverBecomesDateTime(t *testing.T) {
|
||||
t.Fatal("expected a quoted string to be rejected for LocalDate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderMaxDepth(t *testing.T) {
|
||||
deep := func(n int) []byte {
|
||||
return []byte("v = " + strings.Repeat("[", n) + strings.Repeat("]", n) + "\n")
|
||||
}
|
||||
var cfg struct {
|
||||
V any `toml:"v"`
|
||||
}
|
||||
if err := NewDecoder().MaxDepth(4).Decode(deep(4), &cfg); err != nil {
|
||||
t.Fatalf("at the limit: %v", err)
|
||||
}
|
||||
err := NewDecoder().MaxDepth(4).Decode(deep(5), &cfg)
|
||||
if err == nil {
|
||||
t.Fatal("expected a nesting error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "limit of 4") {
|
||||
t.Errorf("err = %v, want it to name the limit", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderMaxInputSize(t *testing.T) {
|
||||
doc := []byte("v = \"ab\"\n")
|
||||
var cfg struct {
|
||||
V string `toml:"v"`
|
||||
}
|
||||
if err := NewDecoder().MaxInputSize(len(doc)).Decode(doc, &cfg); err != nil {
|
||||
t.Fatalf("at the limit: %v", err)
|
||||
}
|
||||
err := NewDecoder().MaxInputSize(len(doc)-1).Decode(doc, &cfg)
|
||||
if err == nil {
|
||||
t.Fatal("expected a size error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "over the limit of 8") {
|
||||
t.Errorf("err = %v, want it to name the limit", err)
|
||||
}
|
||||
// Parse carries the nesting default and no size limit.
|
||||
if _, err := Parse(doc); err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// --- OffsetDateTime --------------------------------------------------------
|
||||
|
||||
func TestOffsetDateTimeIsTheParsedType(t *testing.T) {
|
||||
// A document's offset date-time arrives as the wrapper, and a plain
|
||||
// time.Time destination still takes it, so a timestamp field needs no
|
||||
// change to keep working.
|
||||
in := []byte("stamp = 2026-06-26T10:00:00-07:00\n")
|
||||
want := time.Date(2026, 6, 26, 10, 0, 0, 0, time.FixedZone("", -7*3600))
|
||||
|
||||
var plain struct {
|
||||
Stamp time.Time `toml:"stamp"`
|
||||
}
|
||||
if err := Unmarshal(in, &plain); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if !plain.Stamp.Equal(want) {
|
||||
t.Errorf("time.Time destination = %v, want %v", plain.Stamp, want)
|
||||
}
|
||||
|
||||
var wrapped struct {
|
||||
Stamp OffsetDateTime `toml:"stamp"`
|
||||
}
|
||||
if err := Unmarshal(in, &wrapped); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if !wrapped.Stamp.Time.Equal(want) {
|
||||
t.Errorf("OffsetDateTime destination = %v, want %v", wrapped.Stamp.Time, want)
|
||||
}
|
||||
if got := wrapped.Stamp.String(); got != "2026-06-26T10:00-07:00" {
|
||||
t.Errorf("String() = %q, want 2026-06-26T10:00-07:00", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOffsetDateTimeFromHandBuiltTree(t *testing.T) {
|
||||
// A tree built by hand may carry a plain time.Time, which is the other
|
||||
// source of the offset kind; both date-time destinations take it.
|
||||
tree := map[string]any{"stamp": time.Date(2026, 6, 26, 10, 0, 0, 0, time.UTC)}
|
||||
want := time.Date(2026, 6, 26, 10, 0, 0, 0, time.UTC)
|
||||
|
||||
var plain struct {
|
||||
Stamp time.Time `toml:"stamp"`
|
||||
}
|
||||
if err := newDecoder().decode(tree, &plain); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if !plain.Stamp.Equal(want) {
|
||||
t.Errorf("time.Time destination = %v, want %v", plain.Stamp, want)
|
||||
}
|
||||
|
||||
var wrapped struct {
|
||||
Stamp OffsetDateTime `toml:"stamp"`
|
||||
}
|
||||
if err := newDecoder().decode(tree, &wrapped); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if !wrapped.Stamp.Time.Equal(want) {
|
||||
t.Errorf("OffsetDateTime destination = %v, want %v", wrapped.Stamp.Time, want)
|
||||
}
|
||||
}
|
||||
|
||||
// dateKindReceiver records the Go type UnmarshalTOML was handed.
|
||||
type dateKindReceiver struct{ Kind string }
|
||||
|
||||
func (r *dateKindReceiver) UnmarshalTOML(data any) error {
|
||||
r.Kind = fmt.Sprintf("%T", data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUnmarshalerReceivesOffsetDateTime(t *testing.T) {
|
||||
// The interface sees the wrapper, which names the date-time kind on its
|
||||
// own; the local kinds keep their own wrappers.
|
||||
var cfg struct {
|
||||
O dateKindReceiver `toml:"o"`
|
||||
L dateKindReceiver `toml:"l"`
|
||||
}
|
||||
in := []byte("o = 2026-06-26T10:00:00Z\nl = 2026-06-26T10:00:00\n")
|
||||
if err := Unmarshal(in, &cfg); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if cfg.O.Kind != "interpres.OffsetDateTime" {
|
||||
t.Errorf("offset kind = %q, want interpres.OffsetDateTime", cfg.O.Kind)
|
||||
}
|
||||
if cfg.L.Kind != "interpres.LocalDateTime" {
|
||||
t.Errorf("local kind = %q, want interpres.LocalDateTime", cfg.L.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user