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  }