carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package dbdoor
2
3import (
4 "strings"
5 "testing"
6)
7
8// Range semantics must match litestream's S3 client exactly
9// (s3/replica_client.go:646-651), or a resumed read returns the wrong bytes.
10func TestRangeHeaderMatchesTheS3Client(t *testing.T) {
11 cases := []struct {
12 name string
13 offset, size int64
14 want string
15 }{
16 {"whole file", 0, 0, "bytes=0-"},
17 {"open-ended from an offset", 512, 0, "bytes=512-"},
18 {"sized read", 0, 100, "bytes=0-99"},
19 {"sized read at an offset", 512, 100, "bytes=512-611"},
20 }
21 for _, tc := range cases {
22 t.Run(tc.name, func(t *testing.T) {
23 if got := RangeHeader(tc.offset, tc.size); got != tc.want {
24 t.Errorf("RangeHeader(%d, %d) = %q, want %q", tc.offset, tc.size, got, tc.want)
25 }
26 })
27 }
28}
29
30// ParseRange is RangeHeader's inverse, used by the door. Anything it does not
31// produce is refused rather than guessed at.
32func TestParseRangeInvertsRangeHeader(t *testing.T) {
33 for _, c := range []struct{ offset, size int64 }{{0, 0}, {512, 0}, {0, 100}, {512, 100}} {
34 off, size, err := ParseRange(RangeHeader(c.offset, c.size))
35 if err != nil || off != c.offset || size != c.size {
36 t.Errorf("ParseRange(RangeHeader(%d, %d)) = %d, %d, %v", c.offset, c.size, off, size, err)
37 }
38 }
39 if off, size, err := ParseRange(""); err != nil || off != 0 || size != 0 {
40 t.Errorf("ParseRange(\"\") = %d, %d, %v, want the whole file", off, size, err)
41 }
42 for _, bad := range []string{"bytes=-5", "bytes=5-2", "bytes=0-1,3-4", "items=0-", "bytes=x-", "bytes=0-y"} {
43 if _, _, err := ParseRange(bad); err == nil {
44 t.Errorf("ParseRange(%q) accepted", bad)
45 }
46 }
47}
48
49// Coordinates, never keys: the caller names a level and a TXID range, and
50// the server decides what object that is. lock.json has no coordinate.
51func TestGetPathIsCoordinateAddressed(t *testing.T) {
52 got := GetPath("bab", "titogo", "a.bab.oncarlos.com", 3, 16, 32)
53 for _, want := range []string{"bab", "titogo", "a.bab.oncarlos.com", "/3/", "16", "32"} {
54 if !strings.Contains(got, want) {
55 t.Fatalf("GetPath = %q, missing %q", got, want)
56 }
57 }
58 if strings.Contains(got, ".ltx") || strings.Contains(got, "lock") {
59 t.Fatalf("GetPath = %q leaks the key layout", got)
60 }
61 if !strings.HasSuffix(got, GetTail(3, 16, 32)) || !strings.HasSuffix(ListPath("bab", "titogo", "h.example.com", 3), ListTail(3)) {
62 t.Fatalf("full paths and tails disagree")
63 }
64}
65
66// The span segment round-trips, and nothing that is not a span parses.
67func TestParseSpanRoundTripsAndRefusesTheRest(t *testing.T) {
68 min, max, err := ParseSpan(Span(16, 32))
69 if err != nil || min != 16 || max != 32 {
70 t.Fatalf("ParseSpan(Span(16, 32)) = %d, %d, %v", min, max, err)
71 }
72 for _, bad := range []string{"", "16", "16-", "-32", "32-16", "0-1", "a-b", "lock.json", "16-32-48", "+1-2"} {
73 if _, _, err := ParseSpan(bad); err == nil {
74 t.Errorf("ParseSpan(%q) accepted", bad)
75 }
76 }
77}
78