package store import ( "context" "testing" "time" ) func openTypesTestStore(t *testing.T) *Store { t.Helper() s := openTestStore(t) if _, err := s.Writer.Exec("CREATE TABLE v (val TEXT)"); err != nil { t.Fatal(err) } return s } // roundTrip writes in through the writer, then reads it back into out and // returns the stored SQLite type and raw text. func roundTrip(t *testing.T, s *Store, in, out any) (typ, raw string) { t.Helper() ctx := context.Background() if _, err := s.Writer.ExecContext(ctx, "DELETE FROM v"); err != nil { t.Fatal(err) } if _, err := s.Writer.ExecContext(ctx, "INSERT INTO v (val) VALUES (?)", in); err != nil { t.Fatal(err) } if err := s.Reader.QueryRowContext(ctx, "SELECT typeof(val), val, val FROM v").Scan(&typ, &raw, out); err != nil { t.Fatal(err) } return typ, raw } func TestTimestampRoundTrip(t *testing.T) { s := openTypesTestStore(t) zone := time.FixedZone("EDT", -4*60*60) in := Timestamp{time.Date(2026, 10, 3, 8, 37, 5, 123456789, zone)} var out Timestamp typ, raw := roundTrip(t, s, in, &out) if typ != "text" || raw != "2026-10-03T12:37:05.123Z" { t.Fatalf("stored %s %q, want text %q", typ, raw, "2026-10-03T12:37:05.123Z") } if want := in.Truncate(time.Millisecond); !out.Equal(want) { t.Fatalf("read back %v, want %v", out, want) } if out.Location() != time.UTC { t.Fatalf("read back location %v, want UTC", out.Location()) } now := Now() roundTrip(t, s, now, &out) if out != now { t.Fatalf("Now() round trip = %#v, want %#v", out, now) } } func TestTimestampRejects(t *testing.T) { if _, err := (Timestamp{}).Value(); err == nil { t.Error("zero Timestamp Value succeeded") } for _, src := range []any{nil, int64(0), []byte("2026-10-03T12:37:05.123Z"), "2026-10-03T12:37:05Z", "2026-10-03T08:37:05.123-04:00", "garbage"} { var ts Timestamp if err := ts.Scan(src); err == nil { t.Errorf("Scan(%#v) succeeded", src) } } } func TestLocalDateRoundTrip(t *testing.T) { s := openTypesTestStore(t) for _, str := range []string{"2026-10-03", "2028-02-29", "0001-01-01", "9999-12-31"} { in, err := ParseLocalDate(str) if err != nil { t.Fatalf("ParseLocalDate(%q): %v", str, err) } if in.String() != str { t.Fatalf("String() = %q, want %q", in.String(), str) } var out LocalDate typ, raw := roundTrip(t, s, in, &out) if typ != "text" || raw != str { t.Fatalf("stored %s %q, want text %q", typ, raw, str) } if out != in { t.Fatalf("read back %v, want %v", out, in) } } } func TestLocalDateRejectsInvalid(t *testing.T) { for _, str := range []string{ "", "2026-02-30", "2026-02-29", "2026-13-01", "2026-00-10", "2026-10-00", "2026-10-32", "2026-1-03", "2026-10-3", "26-10-03", "2026/10/03", "2026-10-03T00:00:00Z", " 2026-10-03", "20261003", "not-a-date", } { if d, err := ParseLocalDate(str); err == nil { t.Errorf("ParseLocalDate(%q) = %v, want error", str, d) } var d LocalDate if err := d.Scan(str); err == nil { t.Errorf("Scan(%q) succeeded", str) } } if _, err := (LocalDate{}).Value(); err == nil { t.Error("zero LocalDate Value succeeded") } var d LocalDate if err := d.Scan(nil); err == nil { t.Error("Scan(nil) succeeded") } }