@@ -67,6 +67,15 @@ func (m *mockExternalStorage) WriteFile(ctx context.Context, name string, data [
67
67
return nil
68
68
}
69
69
70
+ // Implement Open so tests can simulate readers that bind to the Open() context.
71
+ func (m * mockExternalStorage ) Open (ctx context.Context , path string , option * storage.ReaderOption ) (storage.ExternalFileReader , error ) {
72
+ return & ctxBoundReader {ctx : ctx }, nil
73
+ }
74
+
75
+ func (m * mockExternalStorage ) URI () string { return "mock://" }
76
+
77
+ func (m * mockExternalStorage ) Close () {}
78
+
70
79
func TestExtStorageWithTimeoutWriteFileTimeout (t * testing.T ) {
71
80
testTimeout := 50 * time .Millisecond
72
81
@@ -123,3 +132,62 @@ func TestExtStorageWithTimeoutWriteFileSuccess(t *testing.T) {
123
132
// Assert success
124
133
require .NoError (t , err , "Expected no error for successful write within timeout" )
125
134
}
135
+
136
+ // ctxBoundReader is a reader that checks the context passed to Open().
137
+ // It simulates backends (e.g., Azure) that bind reader lifetime to the Open() context.
138
+ type ctxBoundReader struct {
139
+ ctx context.Context
140
+ }
141
+
142
+ func (r * ctxBoundReader ) Read (p []byte ) (int , error ) {
143
+ if err := r .ctx .Err (); err != nil {
144
+ return 0 , err
145
+ }
146
+ if len (p ) == 0 {
147
+ return 0 , nil
148
+ }
149
+ p [0 ] = 'x'
150
+ return 1 , nil
151
+ }
152
+
153
+ func (r * ctxBoundReader ) Seek (offset int64 , whence int ) (int64 , error ) {
154
+ return 0 , nil
155
+ }
156
+
157
+ func (r * ctxBoundReader ) Close () error { return nil }
158
+
159
+ func (r * ctxBoundReader ) GetFileSize () (int64 , error ) { return 1 , nil }
160
+
161
+ func TestExtStorageOpenDoesNotCancelReaderContext (t * testing.T ) {
162
+ timedStore := & extStorageWithTimeout {
163
+ ExternalStorage : & mockExternalStorage {},
164
+ timeout : 100 * time .Millisecond ,
165
+ }
166
+
167
+ rd , err := timedStore .Open (context .Background (), "file" , nil )
168
+ require .NoError (t , err )
169
+ defer rd .Close ()
170
+
171
+ // If Open() had used a derived context with immediate cancel, this would fail with context canceled.
172
+ _ , err = rd .Read (make ([]byte , 1 ))
173
+ require .NoError (t , err )
174
+ }
175
+
176
+ func TestExtStorageOpenReaderRespectsCallerCancel (t * testing.T ) {
177
+ timedStore := & extStorageWithTimeout {
178
+ ExternalStorage : & mockExternalStorage {},
179
+ timeout : 10 * time .Millisecond ,
180
+ }
181
+
182
+ ctx , cancel := context .WithCancel (context .Background ())
183
+ rd , err := timedStore .Open (ctx , "file" , nil )
184
+ require .NoError (t , err )
185
+ defer rd .Close ()
186
+
187
+ // This should cause the reader to fail with context canceled.
188
+ cancel ()
189
+ _ , err = rd .Read (make ([]byte , 1 ))
190
+
191
+ require .Error (t , err )
192
+ require .True (t , errors .Is (err , context .Canceled ) || errors .Is (err , context .DeadlineExceeded ))
193
+ }
0 commit comments