go-hep.org/x/hep@v0.38.1/groot/internal/httpio/reader_test.go (about) 1 // Copyright ©2022 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package httpio 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "fmt" 12 "io" 13 "net/http" 14 "net/http/httptest" 15 "os" 16 "strings" 17 "testing" 18 "testing/iotest" 19 ) 20 21 func TestOpen(t *testing.T) { 22 srv := httptest.NewServer(http.FileServer(http.Dir("./testdata"))) 23 24 for _, tc := range []struct { 25 url string 26 opts []Option 27 err error 28 }{ 29 { 30 url: srv.URL + "/data.txt", 31 opts: []Option{ 32 func(c *config) error { return fmt.Errorf("option error") }, 33 }, 34 err: fmt.Errorf("httpio: could not open %q: %w", srv.URL+"/data.txt", fmt.Errorf("option error")), 35 }, 36 { 37 url: srv.URL + "xxx/not-there.txt", 38 err: fmt.Errorf("httpio: could not create HTTP request: "), 39 }, 40 { 41 url: srv.URL + "000/not-there.txt", 42 err: fmt.Errorf("httpio: could not send HEAD request: "), 43 }, 44 { 45 url: srv.URL + "/not-there.txt", 46 err: fmt.Errorf("httpio: invalid HEAD response code=404"), 47 }, 48 } { 49 t.Run(tc.url, func(t *testing.T) { 50 _, err := Open(tc.url, tc.opts...) 51 switch { 52 case err == nil: 53 t.Fatalf("expected an error") 54 default: 55 if got, want := err.Error(), tc.err.Error(); !strings.HasPrefix(got, want) { 56 t.Fatalf("invalid error:\ngot= %s\nwant=%s", got, want) 57 } 58 } 59 }) 60 } 61 62 t.Run("no-range", func(t *testing.T) { 63 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 64 w.Header().Set("accept-range", "none") 65 })) 66 67 want := fmt.Errorf("httpio: invalid HEAD response: httpio: accept-range not supported") 68 69 _, err := Open(srv.URL) 70 if got, want := err.Error(), want.Error(); got != want { 71 t.Fatalf("invalid error:\ngot= %s\nwant=%s", got, want) 72 } 73 }) 74 } 75 76 func TestReader(t *testing.T) { 77 dir := "./testdata" 78 srv := httptest.NewServer(http.FileServer(http.Dir(dir))) 79 80 want, err := os.ReadFile("./testdata/data.txt") 81 if err != nil { 82 t.Fatal(err) 83 } 84 85 r, err := Open( 86 srv.URL+"/data.txt", 87 WithBasicAuth("gopher", "s3cr3t"), 88 WithContext(context.Background()), 89 WithClient(http.DefaultClient), 90 ) 91 if err != nil { 92 t.Fatal(err) 93 } 94 defer r.Close() 95 96 if got, want := r.Name(), srv.URL+"/data.txt"; got != want { 97 t.Fatalf("invalid name:\ngot= %q\nwant=%q", got, want) 98 } 99 100 if got, want := r.Size(), int64(len(want)); got != want { 101 t.Fatalf("invalid size: got=%d, want=%d", got, want) 102 } 103 104 got := make([]byte, len(want)) 105 n, err := r.ReadAt(got, 0) 106 if err != nil && !errors.Is(err, io.EOF) { 107 t.Fatalf("could not read-at: %+v", err) 108 } 109 if !bytes.Equal(got, want) { 110 t.Fatalf("invalid read-at:\ngot= %q\nwant=%q", got, want) 111 } 112 if len(want) != n { 113 t.Fatalf("invalid nbytes: got=%d, want=%d", n, len(want)) 114 } 115 116 t.Run("iotest", func(t *testing.T) { 117 r, err := Open(srv.URL + "/data.txt") 118 if err != nil { 119 t.Fatal(err) 120 } 121 defer r.Close() 122 123 err = iotest.TestReader(r, want) 124 if err != nil { 125 t.Fatal(err) 126 } 127 }) 128 129 t.Run("etag", func(t *testing.T) { 130 r, err := Open(srv.URL + "/data.txt") 131 if err != nil { 132 t.Fatal(err) 133 } 134 defer r.Close() 135 136 // fake changing resource 137 r.etag += "XXX" 138 139 p := make([]byte, len(want)) 140 n, err := r.ReadAt(p, 0) 141 switch { 142 case err == nil: 143 t.Fatalf("expected an Etag-related error") 144 default: 145 if got, want := err.Error(), "httpio: resource changed"; got != want { 146 t.Fatalf("invalid error:\ngot= %q\nwant=%q", got, want) 147 } 148 } 149 if n != len(want) { 150 t.Fatalf("invalid size: got=%d, want=%d", n, len(want)) 151 } 152 }) 153 }