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 "context"
5 "encoding/json"
6 "io"
7 "net/http"
8 "strconv"
9 "strings"
10 "testing"
11
12 "github.com/superfly/ltx"
13)
14
15func TestHandleProtocolCompatibility(t *testing.T) {
16 for _, mode := range []string{"legacy", "pinned", "downgraded-get"} {
17 t.Run(mode, func(t *testing.T) {
18 c := NewClient(func(ctx context.Context, tail string, h http.Header) (*http.Response, error) {
19 out := &http.Response{StatusCode: 200, Header: http.Header{}}
20 if tail == ListTail(0) {
21 list := ListResponse{Files: []LTXFile{{Level: 0, MinTXID: 1, MaxTXID: 1, Size: 3}}}
22 if mode != "legacy" {
23 list.Protocol = 2
24 list.Files[0].Handle = "opaque"
25 }
26 b, _ := json.Marshal(list)
27 out.Body = io.NopCloser(strings.NewReader(string(b)))
28 } else {
29 want := ""
30 if mode != "legacy" {
31 want = "opaque"
32 }
33 if h.Get(HandleHeader) != want || h.Get("Range") != "bytes=1-2" {
34 t.Fatal(tail, h)
35 }
36 if mode == "pinned" {
37 out.Header.Set(HandleHeader, want)
38 }
39 out.Body = io.NopCloser(strings.NewReader("bc"))
40 }
41 return out, nil
42 })
43 it, err := c.LTXFiles(t.Context(), 0, 0, false)
44 if err != nil {
45 t.Fatal(err)
46 }
47 it.Close()
48 rc, err := c.OpenLTXFile(t.Context(), 0, ltx.TXID(1), ltx.TXID(1), 1, 2)
49 if mode == "downgraded-get" {
50 if err == nil {
51 rc.Close()
52 t.Fatal("accepted unpinned body")
53 }
54 return
55 }
56 if err != nil {
57 t.Fatal(err)
58 }
59 b, err := io.ReadAll(rc)
60 rc.Close()
61 if err != nil || string(b) != "bc" {
62 t.Fatal(string(b), err)
63 }
64 })
65 }
66}
67
68func TestHandleProtocolNeverFallsBack(t *testing.T) {
69 c := NewClient(nil)
70 first := ListResponse{Protocol: 2, Files: []LTXFile{{Level: 0, MinTXID: 1, MaxTXID: 1, Size: 3, Handle: "first"}}}
71 if _, err := c.rememberFiles(0, first); err != nil {
72 t.Fatal(err)
73 }
74 again := ListResponse{Protocol: 2, Files: []LTXFile{{Level: 0, MinTXID: 1, MaxTXID: 1, Size: 3, Handle: "second"}}}
75 files, err := c.rememberFiles(0, again)
76 if err != nil || files[0].Handle != "first" {
77 t.Fatal(files, err)
78 }
79 again.Files[0].Size = 4
80 if _, err := c.rememberFiles(0, again); err == nil {
81 t.Fatal("metadata changed")
82 }
83 if _, err := c.rememberFiles(0, ListResponse{}); err == nil {
84 t.Fatal("listing downgraded")
85 }
86 if _, err := c.fileHandle(0, 2, 2); err == nil {
87 t.Fatal("missing handle fell back")
88 }
89 for _, bad := range []ListResponse{{Protocol: 3}, {Protocol: 2, Files: []LTXFile{{Level: 0, MinTXID: 1, MaxTXID: 1}}}} {
90 if _, err := NewClient(nil).rememberFiles(0, bad); err == nil {
91 t.Fatal("bad listing", bad)
92 }
93 }
94}
95
96func TestHandleProtocolCompletePlan(t *testing.T) {
97 lists := 0
98 c := NewClient(func(ctx context.Context, tail string, h http.Header) (*http.Response, error) {
99 if strings.Count(tail, "/") == 2 {
100 lists++
101 if lists != 1 {
102 t.Fatal("relisted during one restore")
103 }
104 return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(`{"protocol":2,"plan":true,"files":[{"level":9,"min_txid":1,"max_txid":11,"size":100,"handle":"root"},{"level":1,"min_txid":10,"max_txid":13,"size":100,"handle":"continuation"}]}`))}, nil
105 }
106 if h.Get(HandleHeader) != "continuation" {
107 t.Fatal("lost selected handle", h)
108 }
109 return &http.Response{StatusCode: 206, Header: h.Clone(), Body: io.NopCloser(strings.NewReader("bytes"))}, nil
110 })
111 for _, level := range []int{9, 8, 1, 0, 9} {
112 it, err := c.LTXFiles(t.Context(), level, 0, false)
113 if err != nil {
114 t.Fatal(err)
115 }
116 var count int
117 for it.Next() {
118 count++
119 if it.Item().Level != level {
120 t.Fatal(it.Item())
121 }
122 }
123 if err := it.Close(); err != nil {
124 t.Fatal(err)
125 }
126 want := 0
127 if level == 9 || level == 1 {
128 want = 1
129 }
130 if count != want {
131 t.Fatalf("level %d: %d files, want %d", level, count, want)
132 }
133 }
134 rc, err := c.OpenLTXFile(t.Context(), 1, 10, 13, 0, 0)
135 if err != nil {
136 t.Fatal(err)
137 }
138 rc.Close()
139}
140
141func TestHandleProtocolRetainsSnapshotCRC(t *testing.T) {
142 for _, first := range []string{"0", "12345678901234567890"} {
143 t.Run(first, func(t *testing.T) {
144 c := NewClient(nil)
145 for _, crc := range []string{first, "7", "null"} {
146 var list ListResponse
147 raw := `{"protocol":2,"files":[{"level":9,"min_txid":1,"max_txid":11,"size":100,"handle":"` + crc + `","snapshot_crc64":` + crc + `}]}`
148 if err := json.Unmarshal([]byte(raw), &list); err != nil {
149 t.Fatal(err)
150 }
151 files, err := c.rememberFiles(9, list)
152 if err != nil {
153 t.Fatal(err)
154 }
155 if files[0].Handle != first || files[0].SnapshotCRC64 == nil {
156 t.Fatalf("lost first handle metadata: %+v", files[0])
157 }
158 want, err := strconv.ParseUint(first, 10, 64)
159 if err != nil {
160 t.Fatal(err)
161 }
162 if *files[0].SnapshotCRC64 != want {
163 t.Fatalf("CRC changed to %d", *files[0].SnapshotCRC64)
164 }
165 }
166 })
167 }
168}
169