@@ -80,7 +80,7 @@ vi.mock("./helpers.js", () => ({
80
80
} ) ) ;
81
81
82
82
vi . mock ( "node:child_process" , ( ) => ( {
83
- spawnSync : vi . fn ( ( ) => ( { status : 0 } ) ) ,
83
+ spawnSync : vi . fn ( ( ) => ( { status : 0 , stderr : Buffer . from ( "" ) } ) ) ,
84
84
} ) ) ;
85
85
86
86
describe ( "populateCache" , ( ) => {
@@ -177,13 +177,72 @@ describe("populateCache", () => {
177
177
// Verify batch upload was used with correct parameters and temporary config
178
178
expect ( spawnSync ) . toHaveBeenCalledWith (
179
179
"rclone" ,
180
- expect . arrayContaining ( [ "copy" , expect . any ( String ) , "r2:test-bucket" ] ) ,
180
+ expect . arrayContaining ( [ "copy" , expect . any ( String ) , "r2:test-bucket" , "--error-on-no-transfer" ] ) ,
181
181
expect . objectContaining ( {
182
+ stdio : [ "inherit" , "inherit" , "pipe" ] ,
182
183
env : expect . objectContaining ( {
183
184
RCLONE_CONFIG : expect . stringMatching ( / r c l o n e - c o n f i g - \d + \. c o n f $ / ) ,
184
185
} ) ,
185
186
} )
186
187
) ;
187
188
} ) ;
189
+
190
+ test ( "handles rclone errors with status > 0" , async ( ) => {
191
+ const { runWrangler } = await import ( "../utils/run-wrangler.js" ) ;
192
+
193
+ // Set R2 credentials
194
+ vi . stubEnv ( "R2_ACCESS_KEY_ID" , "test_access_key" ) ;
195
+ vi . stubEnv ( "R2_SECRET_ACCESS_KEY" , "test_secret_key" ) ;
196
+ vi . stubEnv ( "R2_ACCOUNT_ID" , "test_account_id" ) ;
197
+
198
+ setupMockFileSystem ( ) ;
199
+
200
+ // Mock rclone failure without stderr output
201
+ vi . mocked ( spawnSync ) . mockReturnValueOnce ( {
202
+ status : 7 , // Fatal error exit code
203
+ stderr : "" , // No stderr output
204
+ } as any ) ; // eslint-disable-line @typescript-eslint/no-explicit-any
205
+
206
+ vi . mocked ( runWrangler ) . mockClear ( ) ;
207
+
208
+ await populateCache (
209
+ createTestBuildOptions ( ) ,
210
+ createTestOpenNextConfig ( ) as any , // eslint-disable-line @typescript-eslint/no-explicit-any
211
+ createTestWranglerConfig ( ) as any , // eslint-disable-line @typescript-eslint/no-explicit-any
212
+ createTestPopulateCacheOptions ( )
213
+ ) ;
214
+
215
+ // Should fall back to standard upload when batch upload fails
216
+ expect ( runWrangler ) . toHaveBeenCalled ( ) ;
217
+ } ) ;
218
+ } ) ;
219
+
220
+ test ( "handles rclone errors with status = 0 and stderr output" , async ( ) => {
221
+ const { runWrangler } = await import ( "../utils/run-wrangler.js" ) ;
222
+
223
+ // Set R2 credentials
224
+ vi . stubEnv ( "R2_ACCESS_KEY_ID" , "test_access_key" ) ;
225
+ vi . stubEnv ( "R2_SECRET_ACCESS_KEY" , "test_secret_key" ) ;
226
+ vi . stubEnv ( "R2_ACCOUNT_ID" , "test_account_id" ) ;
227
+
228
+ setupMockFileSystem ( ) ;
229
+
230
+ // Mock rclone error in stderr
231
+ vi . mocked ( spawnSync ) . mockReturnValueOnce ( {
232
+ status : 0 , // non-error exit code
233
+ stderr : Buffer . from ( "ERROR : Failed to copy: AccessDenied: Access Denied (403)" ) ,
234
+ } as any ) ; // eslint-disable-line @typescript-eslint/no-explicit-any
235
+
236
+ vi . mocked ( runWrangler ) . mockClear ( ) ;
237
+
238
+ await populateCache (
239
+ createTestBuildOptions ( ) ,
240
+ createTestOpenNextConfig ( ) as any , // eslint-disable-line @typescript-eslint/no-explicit-any
241
+ createTestWranglerConfig ( ) as any , // eslint-disable-line @typescript-eslint/no-explicit-any
242
+ createTestPopulateCacheOptions ( )
243
+ ) ;
244
+
245
+ // Should fall back to standard upload when batch upload fails
246
+ expect ( runWrangler ) . toHaveBeenCalled ( ) ;
188
247
} ) ;
189
248
} ) ;
0 commit comments