github.com/go-kivik/kivik/v4@v4.3.2/couchdb/iter_test.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package couchdb 14 15 import ( 16 "context" 17 "errors" 18 "io" 19 "strings" 20 "testing" 21 "time" 22 23 "gitlab.com/flimzy/testy" 24 ) 25 26 func TestCancelableReadCloser(t *testing.T) { 27 t.Run("no cancellation", func(t *testing.T) { 28 t.Parallel() 29 rc := newCancelableReadCloser( 30 context.Background(), 31 io.NopCloser(strings.NewReader("foo")), 32 ) 33 result, err := io.ReadAll(rc) 34 if !testy.ErrorMatches("", err) { 35 t.Errorf("Unexpected error: %s", err) 36 } 37 if string(result) != "foo" { 38 t.Errorf("Unexpected result: %s", string(result)) 39 } 40 }) 41 t.Run("pre-cancelled", func(t *testing.T) { 42 t.Parallel() 43 ctx, cancel := context.WithCancel(context.Background()) 44 cancel() 45 rc := newCancelableReadCloser( 46 ctx, 47 io.NopCloser(io.MultiReader( 48 testy.DelayReader(100*time.Millisecond), 49 strings.NewReader("foo")), 50 ), 51 ) 52 result, err := io.ReadAll(rc) 53 if !testy.ErrorMatches("context canceled", err) { 54 t.Errorf("Unexpected error: %s", err) 55 } 56 if string(result) != "" { 57 t.Errorf("Unexpected result: %s", string(result)) 58 } 59 }) 60 t.Run("canceled mid-flight", func(t *testing.T) { 61 t.Parallel() 62 ctx, cancel := context.WithTimeout(context.Background(), 150*time.Millisecond) 63 defer cancel() 64 r := io.MultiReader( 65 strings.NewReader("foo"), 66 testy.DelayReader(time.Second), 67 strings.NewReader("bar"), 68 ) 69 rc := newCancelableReadCloser( 70 ctx, 71 io.NopCloser(r), 72 ) 73 _, err := io.ReadAll(rc) 74 if !testy.ErrorMatches("context deadline exceeded", err) { 75 t.Errorf("Unexpected error: %s", err) 76 } 77 }) 78 t.Run("read error, not canceled", func(t *testing.T) { 79 t.Parallel() 80 rc := newCancelableReadCloser( 81 context.Background(), 82 io.NopCloser(testy.ErrorReader("foo", errors.New("read err"))), 83 ) 84 _, err := io.ReadAll(rc) 85 if !testy.ErrorMatches("read err", err) { 86 t.Errorf("Unexpected error: %s", err) 87 } 88 }) 89 t.Run("closed early", func(t *testing.T) { 90 t.Parallel() 91 rc := newCancelableReadCloser( 92 context.Background(), 93 io.NopCloser(testy.NeverReader()), 94 ) 95 _ = rc.Close() 96 result, err := io.ReadAll(rc) 97 if !testy.ErrorMatches("iterator closed", err) { 98 t.Errorf("Unexpected error: %s", err) 99 } 100 if string(result) != "" { 101 t.Errorf("Unexpected result: %s", string(result)) 102 } 103 }) 104 }