// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package interpres import ( "errors" "strings" "testing" ) // errReader fails every read with a fixed error. type errReader struct{ err error } func (r errReader) Read([]byte) (int, error) { return 0, r.err } // TestUnmarshalRead covers the streaming entry: the happy path with options, // a failing reader, and MaxInputSize bounding what a reader is drained into. func TestUnmarshalRead(t *testing.T) { var got struct { Name string `toml:"name"` N int `toml:"n"` } err := UnmarshalRead(strings.NewReader("name = \"x\"\n"), &got, RejectUnknownFields(true)) if err != nil { t.Fatalf("UnmarshalRead: %v", err) } if got.Name != "x" { t.Errorf("Name = %q", got.Name) } readErr := errors.New("boom") if err := UnmarshalRead(errReader{readErr}, &got); !errors.Is(err, readErr) { t.Errorf("err = %v, want the read error wrapped", err) } err = UnmarshalRead(strings.NewReader("name = \"x\"\n"), &got, MaxInputSize(4)) if err == nil || !strings.Contains(err.Error(), "over the limit") { t.Errorf("err = %v, want the size limit", err) } // The limit bounds the read itself: a reader that would supply far more // than the limit is not drained into memory first. big := strings.Repeat("x", 1<<20) if err := UnmarshalRead(strings.NewReader(big), &got, MaxInputSize(16)); err == nil || !strings.Contains(err.Error(), "over the limit") { t.Errorf("err = %v, want the size limit before the read completes", err) } } // TestParseAsWithOptions covers the generic shorthand carrying options. func TestParseAsWithOptions(t *testing.T) { type cfg struct { Name string `toml:"name"` } got, err := ParseAs[cfg]([]byte("name = \"x\"\nrogue = 1\n"), RejectUnknownFields(true)) if err == nil || !strings.Contains(err.Error(), "unknown field") { t.Errorf("err = %v, want the strict failure", err) } // The statements before the failure stay written, the contract the // targeted path documents and encoding/json follows. if got.Name != "x" { t.Errorf("Name = %q, want the statement before the failure kept", got.Name) } } // TestStatementsValueArrays pins that a value array is one statement, a // scalar array and an array of inline tables alike; only an array of tables // yields per element. func TestStatementsValueArrays(t *testing.T) { src := strings.NewReader("port = [8080, 9090]\nmix = [{y = 1, x = 2}]\n[[items]]\nn = 1\n") var got []Statement for stmt, err := range Statements(src) { if err != nil { t.Fatal(err) } got = append(got, stmt) } if len(got) != 3 { t.Fatalf("got %d statements, want 3", len(got)) } if got[0].Index != -1 || got[0].Table != nil { t.Errorf("port statement = %+v, want one plain key/value", got[0]) } if got[1].Index != -1 || got[1].Table != nil { t.Errorf("mix statement = %+v, want one plain key/value", got[1]) } if got[2].Index != 0 || got[2].Table == nil { t.Errorf("items statement = %+v, want the element with its node", got[2]) } }