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 ,
@@ -355,6 +356,13 @@ describe('executeBulkOperation', () => {
355356 watch : true ,
356357 } )
357358
359+ expect ( watchBulkOperation ) . toHaveBeenCalledWith (
360+ mockAdminSession ,
361+ createdBulkOperation . id ,
362+ expect . any ( Object ) ,
363+ expect . any ( Function ) ,
364+ )
365+ expect ( quickWatchBulkOperation ) . not . toHaveBeenCalled ( )
358366 expect ( renderSuccess ) . toHaveBeenCalledWith (
359367 expect . objectContaining ( {
360368 headline : expect . stringContaining ( 'Bulk operation succeeded:' ) ,
@@ -394,6 +402,27 @@ describe('executeBulkOperation', () => {
394402 expect ( downloadBulkOperationResults ) . not . toHaveBeenCalled ( )
395403 } )
396404
405+ test ( 'uses quickWatchBulkOperation (not watchBulkOperation) when watch flag is false' , async ( ) => {
406+ const query = '{ products { edges { node { id } } } }'
407+ const mockResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
408+ bulkOperation : createdBulkOperation ,
409+ userErrors : [ ] ,
410+ }
411+
412+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( mockResponse )
413+ vi . mocked ( quickWatchBulkOperation ) . mockResolvedValue ( createdBulkOperation )
414+
415+ await executeBulkOperation ( {
416+ remoteApp : mockRemoteApp ,
417+ storeFqdn,
418+ query,
419+ watch : false ,
420+ } )
421+
422+ expect ( quickWatchBulkOperation ) . toHaveBeenCalledWith ( mockAdminSession , createdBulkOperation . id )
423+ expect ( watchBulkOperation ) . not . toHaveBeenCalled ( )
424+ } )
425+
397426 test ( 'writes results to file when --output-file flag is provided' , async ( ) => {
398427 const query = '{ products { edges { node { id } } } }'
399428 const outputFile = '/tmp/results.jsonl'
@@ -512,4 +541,110 @@ describe('executeBulkOperation', () => {
512541
513542 expect ( renderSuccess ) . not . toHaveBeenCalled ( )
514543 } )
544+
545+ test ( 'renders warning when completed operation results contain userErrors' , async ( ) => {
546+ const query = '{ products { edges { node { id } } } }'
547+ const resultsWithErrors = '{"data":{"productUpdate":{"userErrors":[{"message":"invalid input"}]}},"__lineNumber":0}'
548+
549+ const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
550+ bulkOperation : createdBulkOperation ,
551+ userErrors : [ ] ,
552+ }
553+ const completedOperation = {
554+ ...createdBulkOperation ,
555+ status : 'COMPLETED' as const ,
556+ url : 'https://example.com/download' ,
557+ objectCount : '1' ,
558+ }
559+
560+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( initialResponse )
561+ vi . mocked ( watchBulkOperation ) . mockResolvedValue ( completedOperation )
562+ vi . mocked ( downloadBulkOperationResults ) . mockResolvedValue ( resultsWithErrors )
563+
564+ await executeBulkOperation ( {
565+ remoteApp : mockRemoteApp ,
566+ storeFqdn,
567+ query,
568+ watch : true ,
569+ } )
570+
571+ expect ( renderWarning ) . toHaveBeenCalledWith (
572+ expect . objectContaining ( {
573+ headline : 'Bulk operation completed with errors.' ,
574+ body : 'Check results for error details.' ,
575+ } ) ,
576+ )
577+ expect ( renderSuccess ) . not . toHaveBeenCalled ( )
578+ } )
579+
580+ test ( 'renders success when completed operation results have no userErrors' , async ( ) => {
581+ const query = '{ products { edges { node { id } } } }'
582+ const resultsWithoutErrors = '{"data":{"productUpdate":{"product":{"id":"123"},"userErrors":[]}},"__lineNumber":0}'
583+
584+ const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
585+ bulkOperation : createdBulkOperation ,
586+ userErrors : [ ] ,
587+ }
588+ const completedOperation = {
589+ ...createdBulkOperation ,
590+ status : 'COMPLETED' as const ,
591+ url : 'https://example.com/download' ,
592+ objectCount : '1' ,
593+ }
594+
595+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( initialResponse )
596+ vi . mocked ( watchBulkOperation ) . mockResolvedValue ( completedOperation )
597+ vi . mocked ( downloadBulkOperationResults ) . mockResolvedValue ( resultsWithoutErrors )
598+
599+ await executeBulkOperation ( {
600+ remoteApp : mockRemoteApp ,
601+ storeFqdn,
602+ query,
603+ watch : true ,
604+ } )
605+
606+ expect ( renderSuccess ) . toHaveBeenCalledWith (
607+ expect . objectContaining ( {
608+ headline : expect . stringContaining ( 'Bulk operation succeeded' ) ,
609+ } ) ,
610+ )
611+ expect ( renderWarning ) . not . toHaveBeenCalled ( )
612+ } )
613+
614+ test ( 'renders warning when results written to file contain userErrors' , async ( ) => {
615+ const query = '{ products { edges { node { id } } } }'
616+ const outputFile = '/tmp/results.jsonl'
617+ const resultsWithErrors = '{"data":{"productUpdate":{"userErrors":[{"message":"invalid input"}]}},"__lineNumber":0}'
618+
619+ const initialResponse : BulkOperationRunQueryMutation [ 'bulkOperationRunQuery' ] = {
620+ bulkOperation : createdBulkOperation ,
621+ userErrors : [ ] ,
622+ }
623+ const completedOperation = {
624+ ...createdBulkOperation ,
625+ status : 'COMPLETED' as const ,
626+ url : 'https://example.com/download' ,
627+ objectCount : '1' ,
628+ }
629+
630+ vi . mocked ( runBulkOperationQuery ) . mockResolvedValue ( initialResponse )
631+ vi . mocked ( watchBulkOperation ) . mockResolvedValue ( completedOperation )
632+ vi . mocked ( downloadBulkOperationResults ) . mockResolvedValue ( resultsWithErrors )
633+
634+ await executeBulkOperation ( {
635+ remoteApp : mockRemoteApp ,
636+ storeFqdn,
637+ query,
638+ watch : true ,
639+ outputFile,
640+ } )
641+
642+ expect ( writeFile ) . toHaveBeenCalledWith ( outputFile , resultsWithErrors )
643+ expect ( renderWarning ) . toHaveBeenCalledWith (
644+ expect . objectContaining ( {
645+ headline : 'Bulk operation completed with errors.' ,
646+ body : `Results written to ${ outputFile } . Check file for error details.` ,
647+ } ) ,
648+ )
649+ } )
515650} )
0 commit comments