Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {BulkOperationRunMutationMutation} from '../../api/graphql/bulk-operation
import {OrganizationApp, OrganizationSource, OrganizationStore} from '../../models/organization.js'
import {renderSuccess, renderWarning, renderError, renderInfo} from '@shopify/cli-kit/node/ui'
import {ensureAuthenticatedAdminAsApp} from '@shopify/cli-kit/node/session'
import {inTemporaryDirectory, writeFile} from '@shopify/cli-kit/node/fs'
import {inTemporaryDirectory, writeFile, readFile} from '@shopify/cli-kit/node/fs'
import {joinPath} from '@shopify/cli-kit/node/path'
import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output'
import {describe, test, expect, vi, beforeEach, afterEach} from 'vitest'
Expand Down Expand Up @@ -39,7 +39,6 @@ vi.mock('@shopify/cli-kit/node/ui', async () => {
renderInfo: vi.fn(),
}
})
vi.mock('@shopify/cli-kit/node/fs')
vi.mock('@shopify/cli-kit/node/session', async () => {
const actual = await vi.importActual('@shopify/cli-kit/node/session')
return {
Expand Down Expand Up @@ -276,6 +275,7 @@ describe('executeBulkOperation', () => {
adminSession: mockAdminSession,
query: mutation,
variablesJsonl: variables.join('\n'),
version: BULK_OPERATIONS_MIN_API_VERSION,
})
})
})
Expand Down Expand Up @@ -552,36 +552,39 @@ describe('executeBulkOperation', () => {
)

test('writes results to file when --output-file flag is provided', async () => {
const query = '{ products { edges { node { id } } } }'
const outputFile = '/tmp/results.jsonl'
const resultsContent =
'{"data":{"productCreate":{"product":{"id":"gid://shopify/Product/123"},"userErrors":[]}},"__lineNumber":0}\n{"data":{"productCreate":{"product":{"id":"gid://shopify/Product/456"},"userErrors":[]}},"__lineNumber":1}'
await inTemporaryDirectory(async (tmpDir) => {
const query = '{ products { edges { node { id } } } }'
const outputFile = joinPath(tmpDir, 'results.jsonl')
const resultsContent =
'{"data":{"productCreate":{"product":{"id":"gid://shopify/Product/123"},"userErrors":[]}},"__lineNumber":0}\n{"data":{"productCreate":{"product":{"id":"gid://shopify/Product/456"},"userErrors":[]}},"__lineNumber":1}'

const initialResponse: BulkOperationRunQueryMutation['bulkOperationRunQuery'] = {
bulkOperation: createdBulkOperation,
userErrors: [],
}
const completedOperation = {
...createdBulkOperation,
status: 'COMPLETED' as const,
url: 'https://example.com/download',
objectCount: '2',
}
const initialResponse: BulkOperationRunQueryMutation['bulkOperationRunQuery'] = {
bulkOperation: createdBulkOperation,
userErrors: [],
}
const completedOperation = {
...createdBulkOperation,
status: 'COMPLETED' as const,
url: 'https://example.com/download',
objectCount: '2',
}

vi.mocked(runBulkOperationQuery).mockResolvedValue(initialResponse)
vi.mocked(watchBulkOperation).mockResolvedValue(completedOperation)
vi.mocked(downloadBulkOperationResults).mockResolvedValue(resultsContent)
vi.mocked(runBulkOperationQuery).mockResolvedValue(initialResponse)
vi.mocked(watchBulkOperation).mockResolvedValue(completedOperation)
vi.mocked(downloadBulkOperationResults).mockResolvedValue(resultsContent)

await executeBulkOperation({
organization: mockOrganization,
remoteApp: mockRemoteApp,
store: mockStore,
query,
watch: true,
outputFile,
})
await executeBulkOperation({
organization: mockOrganization,
remoteApp: mockRemoteApp,
store: mockStore,
query,
watch: true,
outputFile,
})

expect(writeFile).toHaveBeenCalledWith(outputFile, resultsContent)
const content = await readFile(outputFile)
expect(content).toBe(resultsContent)
})
})

test('writes results to stdout when --output-file flag is not provided', async () => {
Expand Down Expand Up @@ -615,7 +618,6 @@ describe('executeBulkOperation', () => {
})

expect(mockOutput.info()).toContain(resultsContent)
expect(writeFile).not.toHaveBeenCalled()
})

test.each(['FAILED', 'CANCELED', 'EXPIRED'] as const)(
Expand Down Expand Up @@ -748,41 +750,45 @@ describe('executeBulkOperation', () => {
})

test('renders warning when results written to file contain userErrors', async () => {
const query = '{ products { edges { node { id } } } }'
const outputFile = '/tmp/results.jsonl'
const resultsWithErrors = '{"data":{"productUpdate":{"userErrors":[{"message":"invalid input"}]}},"__lineNumber":0}'
await inTemporaryDirectory(async (tmpDir) => {
const query = '{ products { edges { node { id } } } }'
const outputFile = joinPath(tmpDir, 'results.jsonl')
const resultsWithErrors =
'{"data":{"productUpdate":{"userErrors":[{"message":"invalid input"}]}},"__lineNumber":0}'

const initialResponse: BulkOperationRunQueryMutation['bulkOperationRunQuery'] = {
bulkOperation: createdBulkOperation,
userErrors: [],
}
const completedOperation = {
...createdBulkOperation,
status: 'COMPLETED' as const,
url: 'https://example.com/download',
objectCount: '1',
}
const initialResponse: BulkOperationRunQueryMutation['bulkOperationRunQuery'] = {
bulkOperation: createdBulkOperation,
userErrors: [],
}
const completedOperation = {
...createdBulkOperation,
status: 'COMPLETED' as const,
url: 'https://example.com/download',
objectCount: '1',
}

vi.mocked(runBulkOperationQuery).mockResolvedValue(initialResponse)
vi.mocked(watchBulkOperation).mockResolvedValue(completedOperation)
vi.mocked(downloadBulkOperationResults).mockResolvedValue(resultsWithErrors)
vi.mocked(runBulkOperationQuery).mockResolvedValue(initialResponse)
vi.mocked(watchBulkOperation).mockResolvedValue(completedOperation)
vi.mocked(downloadBulkOperationResults).mockResolvedValue(resultsWithErrors)

await executeBulkOperation({
organization: mockOrganization,
remoteApp: mockRemoteApp,
store: mockStore,
query,
watch: true,
outputFile,
})
await executeBulkOperation({
organization: mockOrganization,
remoteApp: mockRemoteApp,
store: mockStore,
query,
watch: true,
outputFile,
})

expect(writeFile).toHaveBeenCalledWith(outputFile, resultsWithErrors)
expect(renderWarning).toHaveBeenCalledWith(
expect.objectContaining({
headline: 'Bulk operation completed with errors.',
body: `Results written to ${outputFile}. Check file for error details.`,
}),
)
const content = await readFile(outputFile)
expect(content).toBe(resultsWithErrors)
expect(renderWarning).toHaveBeenCalledWith(
expect.objectContaining({
headline: 'Bulk operation completed with errors.',
body: `Results written to ${outputFile}. Check file for error details.`,
}),
)
})
})

test('calls resolveApiVersion with minimum API version constant', async () => {
Expand Down
Loading