11import { executeBulkOperation } from './execute-bulk-operation.js'
22import { runBulkOperationQuery } from './run-query.js'
33import { runBulkOperationMutation } from './run-mutation.js'
4- import { watchBulkOperation } from './watch-bulk-operation.js'
4+ import { watchBulkOperation , quickWatchBulkOperation } from './watch-bulk-operation.js'
55import { downloadBulkOperationResults } from './download-bulk-operation-results.js'
66import { BulkOperationRunQueryMutation } from '../../api/graphql/bulk-operations/generated/bulk-operation-run-query.js'
77import { BulkOperationRunMutationMutation } from '../../api/graphql/bulk-operations/generated/bulk-operation-run-mutation.js'
@@ -52,6 +52,7 @@ describe('executeBulkOperation', () => {
5252
5353 beforeEach ( ( ) => {
5454 vi . mocked ( ensureAuthenticatedAdminAsApp ) . mockResolvedValue ( mockAdminSession )
55+ vi . mocked ( quickWatchBulkOperation ) . mockResolvedValue ( createdBulkOperation )
5556 } )
5657
5758 afterEach ( ( ) => {
@@ -331,7 +332,7 @@ describe('executeBulkOperation', () => {
331332 } )
332333 } )
333334
334- test ( 'waits for operation to finish and renders success when watch is provided and operation finishes with COMPLETED status ' , async ( ) => {
335+ test ( 'uses watchBulkOperation (not quickWatchBulkOperation) when watch flag is true ' , async ( ) => {
335336 const query = '{ products { edges { node { id } } } }'
336337 const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
337338 bulkOperation : createdBulkOperation ,
@@ -346,7 +347,9 @@ describe('executeBulkOperation', () => {
346347
347348 vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( initialResponse )
348349 vi . mocked ( watchBulkOperation ) . mockResolvedValue ( completedOperation )
349- vi . mocked ( downloadBulkOperationResults ) . mockResolvedValue ( '{"id":"gid://shopify/Product/123"}' )
350+ vi . mocked ( downloadBulkOperationResults ) . mockResolvedValue (
351+ '{"data":{"products":{"edges":[{"node":{"id":"gid://shopify/Product/123"}}],"userErrors":[]}},"__lineNumber":0}' ,
352+ )
350353
351354 await executeBulkOperation ( {
352355 remoteApp : mockRemoteApp ,
@@ -355,6 +358,13 @@ describe('executeBulkOperation', () => {
355358 watch : true ,
356359 } )
357360
361+ expect ( watchBulkOperation ) . toHaveBeenCalledWith (
362+ mockAdminSession ,
363+ createdBulkOperation . id ,
364+ expect . any ( Object ) ,
365+ expect . any ( Function ) ,
366+ )
367+ expect ( quickWatchBulkOperation ) . not . toHaveBeenCalled ( )
358368 expect ( renderSuccess ) . toHaveBeenCalledWith (
359369 expect . objectContaining ( {
360370 headline : expect . stringContaining ( 'Bulk operation succeeded:' ) ,
@@ -394,10 +404,62 @@ describe('executeBulkOperation', () => {
394404 expect ( downloadBulkOperationResults ) . not . toHaveBeenCalled ( )
395405 } )
396406
407+ test ( 'uses quickWatchBulkOperation (not watchBulkOperation) when watch flag is false' , async ( ) => {
408+ const query = '{ products { edges { node { id } } } }'
409+ const mockResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
410+ bulkOperation : createdBulkOperation ,
411+ userErrors : [ ] ,
412+ }
413+
414+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( mockResponse )
415+ vi . mocked ( quickWatchBulkOperation ) . mockResolvedValue ( createdBulkOperation )
416+
417+ await executeBulkOperation ( {
418+ remoteApp : mockRemoteApp ,
419+ storeFqdn,
420+ query,
421+ watch : false ,
422+ } )
423+
424+ expect ( quickWatchBulkOperation ) . toHaveBeenCalledWith ( mockAdminSession , createdBulkOperation . id )
425+ expect ( watchBulkOperation ) . not . toHaveBeenCalled ( )
426+ } )
427+
428+ test ( 'renders info message when quickWatchBulkOperation returns RUNNING status' , async ( ) => {
429+ const query = '{ products { edges { node { id } } } }'
430+ const runningOperation = {
431+ ...createdBulkOperation ,
432+ status : 'RUNNING' as const ,
433+ objectCount : '50' ,
434+ }
435+ const mockResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
436+ bulkOperation : createdBulkOperation ,
437+ userErrors : [ ] ,
438+ }
439+
440+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( mockResponse )
441+ vi . mocked ( quickWatchBulkOperation ) . mockResolvedValue ( runningOperation )
442+
443+ await executeBulkOperation ( {
444+ remoteApp : mockRemoteApp ,
445+ storeFqdn,
446+ query,
447+ watch : false ,
448+ } )
449+
450+ expect ( renderSuccess ) . toHaveBeenCalledWith (
451+ expect . objectContaining ( {
452+ headline : 'Bulk operation is running.' ,
453+ body : [ 'Monitor its progress with:' , { command : expect . stringContaining ( 'shopify app bulk status' ) } ] ,
454+ } ) ,
455+ )
456+ } )
457+
397458 test ( 'writes results to file when --output-file flag is provided' , async ( ) => {
398459 const query = '{ products { edges { node { id } } } }'
399460 const outputFile = '/tmp/results.jsonl'
400- const resultsContent = '{"id":"gid://shopify/Product/123"}\n{"id":"gid://shopify/Product/456"}'
461+ const resultsContent =
462+ '{"data":{"productCreate":{"product":{"id":"gid://shopify/Product/123"},"userErrors":[]}},"__lineNumber":0}\n{"data":{"productCreate":{"product":{"id":"gid://shopify/Product/456"},"userErrors":[]}},"__lineNumber":1}'
401463
402464 const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
403465 bulkOperation : createdBulkOperation ,
@@ -427,7 +489,8 @@ describe('executeBulkOperation', () => {
427489
428490 test ( 'writes results to stdout when --output-file flag is not provided' , async ( ) => {
429491 const query = '{ products { edges { node { id } } } }'
430- const resultsContent = '{"id":"gid://shopify/Product/123"}\n{"id":"gid://shopify/Product/456"}'
492+ const resultsContent =
493+ '{"data":{"productCreate":{"product":{"id":"gid://shopify/Product/123"},"userErrors":[]}},"__lineNumber":0}\n{"data":{"productCreate":{"product":{"id":"gid://shopify/Product/456"},"userErrors":[]}},"__lineNumber":1}'
431494
432495 const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
433496 bulkOperation : createdBulkOperation ,
@@ -512,4 +575,110 @@ describe('executeBulkOperation', () => {
512575
513576 expect ( renderSuccess ) . not . toHaveBeenCalled ( )
514577 } )
578+
579+ test ( 'renders warning when completed operation results contain userErrors' , async ( ) => {
580+ const query = '{ products { edges { node { id } } } }'
581+ const resultsWithErrors = '{"data":{"productUpdate":{"userErrors":[{"message":"invalid input"}]}},"__lineNumber":0}'
582+
583+ const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
584+ bulkOperation : createdBulkOperation ,
585+ userErrors : [ ] ,
586+ }
587+ const completedOperation = {
588+ ...createdBulkOperation ,
589+ status : 'COMPLETED' as const ,
590+ url : 'https://example.com/download' ,
591+ objectCount : '1' ,
592+ }
593+
594+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( initialResponse )
595+ vi . mocked ( watchBulkOperation ) . mockResolvedValue ( completedOperation )
596+ vi . mocked ( downloadBulkOperationResults ) . mockResolvedValue ( resultsWithErrors )
597+
598+ await executeBulkOperation ( {
599+ remoteApp : mockRemoteApp ,
600+ storeFqdn,
601+ query,
602+ watch : true ,
603+ } )
604+
605+ expect ( renderWarning ) . toHaveBeenCalledWith (
606+ expect . objectContaining ( {
607+ headline : 'Bulk operation completed with errors.' ,
608+ body : 'Check results for error details.' ,
609+ } ) ,
610+ )
611+ expect ( renderSuccess ) . not . toHaveBeenCalled ( )
612+ } )
613+
614+ test ( 'renders success when completed operation results have no userErrors' , async ( ) => {
615+ const query = '{ products { edges { node { id } } } }'
616+ const resultsWithoutErrors = '{"data":{"productUpdate":{"product":{"id":"123"},"userErrors":[]}},"__lineNumber":0}'
617+
618+ const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
619+ bulkOperation : createdBulkOperation ,
620+ userErrors : [ ] ,
621+ }
622+ const completedOperation = {
623+ ...createdBulkOperation ,
624+ status : 'COMPLETED' as const ,
625+ url : 'https://example.com/download' ,
626+ objectCount : '1' ,
627+ }
628+
629+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( initialResponse )
630+ vi . mocked ( watchBulkOperation ) . mockResolvedValue ( completedOperation )
631+ vi . mocked ( downloadBulkOperationResults ) . mockResolvedValue ( resultsWithoutErrors )
632+
633+ await executeBulkOperation ( {
634+ remoteApp : mockRemoteApp ,
635+ storeFqdn,
636+ query,
637+ watch : true ,
638+ } )
639+
640+ expect ( renderSuccess ) . toHaveBeenCalledWith (
641+ expect . objectContaining ( {
642+ headline : expect . stringContaining ( 'Bulk operation succeeded' ) ,
643+ } ) ,
644+ )
645+ expect ( renderWarning ) . not . toHaveBeenCalled ( )
646+ } )
647+
648+ test ( 'renders warning when results written to file contain userErrors' , async ( ) => {
649+ const query = '{ products { edges { node { id } } } }'
650+ const outputFile = '/tmp/results.jsonl'
651+ const resultsWithErrors = '{"data":{"productUpdate":{"userErrors":[{"message":"invalid input"}]}},"__lineNumber":0}'
652+
653+ const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
654+ bulkOperation : createdBulkOperation ,
655+ userErrors : [ ] ,
656+ }
657+ const completedOperation = {
658+ ...createdBulkOperation ,
659+ status : 'COMPLETED' as const ,
660+ url : 'https://example.com/download' ,
661+ objectCount : '1' ,
662+ }
663+
664+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( initialResponse )
665+ vi . mocked ( watchBulkOperation ) . mockResolvedValue ( completedOperation )
666+ vi . mocked ( downloadBulkOperationResults ) . mockResolvedValue ( resultsWithErrors )
667+
668+ await executeBulkOperation ( {
669+ remoteApp : mockRemoteApp ,
670+ storeFqdn,
671+ query,
672+ watch : true ,
673+ outputFile,
674+ } )
675+
676+ expect ( writeFile ) . toHaveBeenCalledWith ( outputFile , resultsWithErrors )
677+ expect ( renderWarning ) . toHaveBeenCalledWith (
678+ expect . objectContaining ( {
679+ headline : 'Bulk operation completed with errors.' ,
680+ body : `Results written to ${ outputFile } . Check file for error details.` ,
681+ } ) ,
682+ )
683+ } )
515684} )
0 commit comments