diff options
author | Jaime Martinez <jmartinez@gitlab.com> | 2020-09-08 14:42:37 +0300 |
---|---|---|
committer | Vladimir Shushlin <vshushlin@gitlab.com> | 2020-09-08 14:42:37 +0300 |
commit | e3470b6c41e97aa5124cf5168421172ff119aa21 (patch) | |
tree | c82f271267f4685ac4f8a90161766670eba75cca /internal/httprange/http_reader_test.go | |
parent | c2a7040d020736419adf3afa70023b505bd83ab5 (diff) |
Add httprange package
Adds a slightly modified version of `httprange` package obtained from
https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/326.
Only adds functionality, further improvements will be done in
consecutive iterations.
Diffstat (limited to 'internal/httprange/http_reader_test.go')
-rw-r--r-- | internal/httprange/http_reader_test.go | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/internal/httprange/http_reader_test.go b/internal/httprange/http_reader_test.go new file mode 100644 index 00000000..507a7fe8 --- /dev/null +++ b/internal/httprange/http_reader_test.go @@ -0,0 +1,326 @@ +package httprange + +import ( + "context" + "io" + "net/http" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSeekAndRead(t *testing.T) { + testServer := newTestServer(t, nil) + defer testServer.Close() + + resource, err := NewResource(context.Background(), testServer.URL+"/data") + require.NoError(t, err) + + tests := map[string]struct { + readerOffset int64 + seekOffset int64 + seekWhence int + readSize int + expectedContent string + expectedSeekErrMsg string + expectedReadErr error + }{ + // io.SeekStart ... + "read_all_from_seek_start": { + readSize: testDataLen, + seekWhence: io.SeekStart, + expectedContent: testData, + expectedReadErr: io.EOF, + }, + "read_10_bytes_from_seek_start": { + readSize: testDataLen / 3, + seekWhence: io.SeekStart, + // "1234567890" + expectedContent: testData[:testDataLen/3], + expectedReadErr: nil, + }, + "read_10_bytes_from_seek_start_with_seek_offset": { + readSize: testDataLen / 3, + seekOffset: int64(testDataLen / 3), + seekWhence: io.SeekStart, + // "abcdefghij" + expectedContent: testData[testDataLen/3 : 2*testDataLen/3], + expectedReadErr: nil, + }, + "read_10_bytes_from_seek_offset_until_eof": { + readSize: testDataLen / 3, + seekOffset: int64(2 * testDataLen / 3), + seekWhence: io.SeekStart, + // "0987654321" + expectedContent: testData[2*testDataLen/3:], + expectedReadErr: io.EOF, + }, + "read_10_bytes_from_reader_offset_with_seek_offset_to_eof": { + readSize: testDataLen / 3, + readerOffset: int64(testDataLen / 3), // reader offset at "a" + seekOffset: int64(testDataLen / 3), // seek offset at "0" + seekWhence: io.SeekStart, + // "0987654321" + expectedContent: testData[2*testDataLen/3:], + expectedReadErr: io.EOF, + }, + "invalid_seek_start_negative_seek_offset": { + seekOffset: -1, + seekWhence: io.SeekStart, + expectedSeekErrMsg: "outside of range", + }, + "invalid_range_seek_at_end": { + readSize: testDataLen, + seekOffset: int64(testDataLen), + seekWhence: io.SeekStart, + expectedReadErr: ErrInvalidRange, + }, + // io.SeekCurrent ... + "read_all_from_seek_current": { + readSize: testDataLen, + seekWhence: io.SeekCurrent, + expectedContent: testData, + expectedReadErr: io.EOF, + }, + "read_10_bytes_from_seek_current": { + readSize: testDataLen / 3, + seekWhence: io.SeekCurrent, + // "1234567890" + expectedContent: testData[:testDataLen/3], + expectedReadErr: nil, + }, + "read_10_bytes_from_seek_current_with_seek_offset": { + readSize: testDataLen / 3, + seekOffset: int64(testDataLen / 3), + seekWhence: io.SeekCurrent, + // "abcdefghij" + expectedContent: testData[testDataLen/3 : 2*testDataLen/3], + expectedReadErr: nil, + }, + "read_10_bytes_from_seek_current_with_seek_offset_until_eof": { + readSize: testDataLen / 3, + seekOffset: int64(2 * testDataLen / 3), + seekWhence: io.SeekCurrent, + // "0987654321" + expectedContent: testData[2*testDataLen/3:], + expectedReadErr: io.EOF, + }, + "read_10_bytes_from_reader_offset_and_seek_current_with_seek_offset_to_eof": { + readSize: testDataLen / 3, + readerOffset: int64(testDataLen / 3), // reader offset at "a" + seekOffset: int64(testDataLen / 3), // seek offset at "0" + seekWhence: io.SeekCurrent, + // "0987654321" + expectedContent: testData[2*testDataLen/3:], + expectedReadErr: io.EOF, + }, + "invalid_seek_current_negative_seek_offset": { + seekOffset: -1, + seekWhence: io.SeekCurrent, + expectedSeekErrMsg: "outside of range", + }, + // io.SeekEnd with negative offsets + "read_all_from_seek_end": { + readSize: testDataLen, + seekWhence: io.SeekEnd, + seekOffset: -int64(testDataLen), + expectedContent: testData, + expectedReadErr: io.EOF, + }, + "read_10_bytes_from_seek_end": { + readSize: testDataLen / 3, + seekWhence: io.SeekEnd, + seekOffset: -int64(testDataLen), + // "1234567890" + expectedContent: testData[:testDataLen/3], + expectedReadErr: nil, + }, + "read_10_bytes_from_seek_end_with_seek_offset": { + readSize: testDataLen / 3, + readerOffset: int64(2 * testDataLen / 3), + seekOffset: -int64(testDataLen / 3), + seekWhence: io.SeekEnd, + // "0987654321" + expectedContent: testData[2*testDataLen/3:], + expectedReadErr: io.EOF, + }, + "read_10_bytes_from_seek_end_with_seek_offset_until_eof": { + readSize: testDataLen / 3, + seekOffset: -int64(testDataLen / 3), + seekWhence: io.SeekEnd, + // "0987654321" + expectedContent: testData[2*testDataLen/3:], + expectedReadErr: io.EOF, + }, + "read_10_bytes_from_reader_offset_and_seek_end_with_seek_offset_to_eof": { + readSize: testDataLen / 3, + readerOffset: int64(testDataLen / 3), // reader offset at "a" + seekOffset: -int64(2 * testDataLen / 3), // seek offset at "a" + seekWhence: io.SeekEnd, + // "abcdefghij" + expectedContent: testData[testDataLen/3 : 2*testDataLen/3], + expectedReadErr: nil, + }, + "invalid_seek_end_positive_seek_offset": { + readSize: testDataLen, + seekOffset: 1, + seekWhence: io.SeekEnd, + expectedSeekErrMsg: "outside of range", + }, + "invalid_range_reading_from_end": { + readSize: testDataLen / 3, + seekWhence: io.SeekEnd, + expectedReadErr: ErrInvalidRange, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + r := NewReader(resource, tt.readerOffset, resource.Size-tt.readerOffset) + + _, err := r.Seek(tt.seekOffset, tt.seekWhence) + if tt.expectedSeekErrMsg != "" { + require.EqualError(t, err, tt.expectedSeekErrMsg) + return + } + require.NoError(t, err) + + buf := make([]byte, tt.readSize) + n, err := r.Read(buf) + if tt.expectedReadErr != nil { + require.Equal(t, tt.expectedReadErr, err) + return + } + + require.Equal(t, n, tt.readSize) + require.Equal(t, tt.expectedContent, string(buf)) + }) + } +} + +func TestReaderSetResponse(t *testing.T) { + tests := map[string]struct { + status int + offset int64 + prevETag string + resEtag string + expectedErrMsg string + }{ + "partial_content_success": { + status: http.StatusPartialContent, + }, + "status_ok_success": { + status: http.StatusOK, + }, + "status_ok_previous_response_invalid_offset": { + status: http.StatusOK, + offset: 1, + expectedErrMsg: ErrContentHasChanged.Error(), + }, + "status_ok_previous_response_different_etag": { + status: http.StatusOK, + prevETag: "old", + resEtag: "new", + expectedErrMsg: ErrContentHasChanged.Error(), + }, + "requested_range_not_satisfiable": { + status: http.StatusRequestedRangeNotSatisfiable, + expectedErrMsg: ErrRangeRequestsNotSupported.Error(), + }, + "unhandled_status_code": { + status: http.StatusNotFound, + expectedErrMsg: "httprange: read response 404:", + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + r := NewReader(&Resource{ETag: tt.prevETag}, tt.offset, 0) + res := &http.Response{StatusCode: tt.status, Header: map[string][]string{}} + res.Header.Set("ETag", tt.resEtag) + + err := r.setResponse(res) + if tt.expectedErrMsg != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErrMsg) + return + } + + require.NoError(t, err) + require.Equal(t, r.res, res) + }) + } +} + +func TestReaderSeek(t *testing.T) { + type fields struct { + Resource *Resource + res *http.Response + rangeStart int64 + rangeSize int64 + offset int64 + } + + tests := map[string]struct { + fields fields + offset int64 + whence int + want int64 + newOffset int64 + expectedErrMsg string + }{ + "invalid_whence": { + whence: -1, + expectedErrMsg: "invalid whence", + }, + "outside_of_range_invalid_offset": { + whence: io.SeekStart, + offset: -1, + fields: fields{rangeStart: 1}, + expectedErrMsg: "outside of range", + }, + "outside_of_range_invalid_new_offset": { + whence: io.SeekStart, + offset: 2, // newOffset = 3 + fields: fields{rangeStart: 1, rangeSize: 1}, + expectedErrMsg: "outside of range", + }, + "seek_start": { + whence: io.SeekStart, + offset: 1, + want: 1, + newOffset: 2, + fields: fields{rangeStart: 1, rangeSize: 1}, + }, + "seek_current": { + whence: io.SeekCurrent, + offset: 2, + want: 1, + newOffset: 2, + fields: fields{rangeStart: 1, rangeSize: 1, offset: 0}, + }, + "seek_end": { + whence: io.SeekEnd, + want: 1, + newOffset: 2, + fields: fields{rangeStart: 1, rangeSize: 1, offset: 0}, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + r := &Reader{ + res: tt.fields.res, + rangeStart: tt.fields.rangeStart, + rangeSize: tt.fields.rangeSize, + offset: tt.fields.offset, + } + + got, err := r.Seek(tt.offset, tt.whence) + if tt.expectedErrMsg != "" { + require.EqualError(t, err, tt.expectedErrMsg) + return + } + + require.Equal(t, tt.want, got) + require.Equal(t, tt.newOffset, r.offset) + }) + } +} |