| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | "time" |
| 7 | |
| 8 | "github.com/carlosframework/platform/internal/dbdoor" |
| 9 | ) |
| 10 | |
| 11 | func TestParseDBAt(t *testing.T) { |
| 12 | now := time.Date(2026, 10, 8, 12, 0, 0, 0, time.UTC) |
| 13 | for _, ok := range []string{"2026-10-07T14:00:00Z", "2026-10-07T15:00:00+01:00", "2026-10-07T14:00:00.5Z"} { |
| 14 | got, err := parseDBAt(ok, now) |
| 15 | if err != nil { |
| 16 | t.Errorf("parseDBAt(%q) = %v, want a time", ok, err) |
| 17 | } else if !got.Before(now) { |
| 18 | t.Errorf("parseDBAt(%q) = %v, want before %v", ok, got, now) |
| 19 | } |
| 20 | } |
| 21 | // An explicit time that is Go's zero time must not read as "no --at" |
| 22 | // (found in Astra's review). |
| 23 | for _, early := range []string{"0001-01-01T00:00:00Z", "0001-01-01T01:00:00+01:00"} { |
| 24 | if got, err := parseDBAt(early, now); err != nil || got.IsZero() { |
| 25 | t.Errorf("parseDBAt(%q) = %v, %v, want a non-zero time before any history", early, got, err) |
| 26 | } |
| 27 | } |
| 28 | if got, _ := parseDBAt("", now); !got.IsZero() { |
| 29 | t.Errorf("parseDBAt(\"\") = %v, want zero (the newest copy)", got) |
| 30 | } |
| 31 | bad := map[string]string{ |
| 32 | "2026-10-07 14:00": `--at "2026-10-07 14:00" is not a time. Write it in RFC 3339 with a time zone, for example 2026-10-07T14:00:00Z for UTC`, |
| 33 | "2026-10-07T14:00:00": `--at "2026-10-07T14:00:00" is not a time. Write it in RFC 3339 with a time zone, for example 2026-10-07T14:00:00Z for UTC`, |
| 34 | "yesterday": `--at "yesterday" is not a time. Write it in RFC 3339 with a time zone, for example 2026-10-07T14:00:00Z for UTC`, |
| 35 | "2026-10-09T00:00:00Z": `--at 2026-10-09T00:00:00Z is in the future. Leave --at out to read the newest copy`, |
| 36 | } |
| 37 | for in, want := range bad { |
| 38 | if _, err := parseDBAt(in, now); err == nil || err.Error() != want { |
| 39 | t.Errorf("parseDBAt(%q) = %v, want %q", in, err, want) |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | func TestDBRestoreErrorsForAnAsOfRead(t *testing.T) { |
| 45 | oldest := time.Date(2026, 10, 1, 9, 12, 44, 0, time.UTC) |
| 46 | cases := map[error]string{ |
| 47 | &dbdoor.BeforeHistoryError{Oldest: oldest}: "a.go.tito.io's replica does not go back that far. The oldest point it keeps is 2026-10-01 09:12:44 UTC", |
| 48 | dbdoor.ErrNoTimes: "a.go.tito.io's replica can't be read as of a time yet. Leave --at out to read the newest copy", |
| 49 | } |
| 50 | for err, want := range cases { |
| 51 | if got := dbRestoreError("a.go.tito.io", err).Error(); got != want { |
| 52 | t.Errorf("%v -> %q, want %q", err, got, want) |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | func TestDBAsOfLines(t *testing.T) { |
| 58 | r := &dbdoor.Restored{TXID: 42, At: time.Date(2026, 10, 7, 13, 58, 2, 0, time.UTC)} |
| 59 | at := time.Date(2026, 10, 7, 15, 0, 0, 0, time.FixedZone("", 3600)) |
| 60 | if got, want := dbPullHolds(r, at), "It holds every change up to transaction 42, written 2026-10-07 13:58:02 UTC, the last one replicated before 2026-10-07 14:00:00 UTC.\n"; got != want { |
| 61 | t.Errorf("pull with --at:\n got %q\nwant %q", got, want) |
| 62 | } |
| 63 | if got, want := dbPullHolds(r, time.Time{}), "It holds every change up to transaction 42, written 2026-10-07 13:58:02 UTC.\n"; got != want { |
| 64 | t.Errorf("pull without --at:\n got %q\nwant %q", got, want) |
| 65 | } |
| 66 | if got, want := dbQueryStatus("a.go.tito.io", r, at), "Querying a copy of a.go.tito.io's database as it was before 2026-10-07 14:00:00 UTC, up to transaction 42, written 2026-10-07 13:58:02 UTC. Nothing you run here reaches a.go.tito.io.\n"; got != want { |
| 67 | t.Errorf("query with --at:\n got %q\nwant %q", got, want) |
| 68 | } |
| 69 | if got, want := dbQueryStatus("a.go.tito.io", r, time.Time{}), "Querying a copy of a.go.tito.io's database, up to transaction 42, written 2026-10-07 13:58:02 UTC. Nothing you run here reaches a.go.tito.io.\n"; got != want { |
| 70 | t.Errorf("query without --at:\n got %q\nwant %q", got, want) |
| 71 | } |
| 72 | if !strings.Contains(dbUsage, "Add --at <time> to either verb") { |
| 73 | t.Error("dbUsage does not explain --at") |
| 74 | } |
| 75 | } |
| 76 | |