687 lines
19 KiB
JavaScript
687 lines
19 KiB
JavaScript
const ShopifyPriceUpdater = require("../src/index");
|
|
const { getConfig } = require("../src/config/environment");
|
|
const ProductService = require("../src/services/product");
|
|
const Logger = require("../src/utils/logger");
|
|
|
|
// Mock dependencies
|
|
jest.mock("../src/config/environment");
|
|
jest.mock("../src/services/product");
|
|
jest.mock("../src/utils/logger");
|
|
|
|
describe("ShopifyPriceUpdater - Rollback Functionality", () => {
|
|
let app;
|
|
let mockConfig;
|
|
let mockProductService;
|
|
let mockLogger;
|
|
|
|
beforeEach(() => {
|
|
// Mock configuration
|
|
mockConfig = {
|
|
shopDomain: "test-shop.myshopify.com",
|
|
accessToken: "test-token",
|
|
targetTag: "test-tag",
|
|
priceAdjustmentPercentage: 10,
|
|
operationMode: "rollback",
|
|
};
|
|
|
|
// Mock product service
|
|
mockProductService = {
|
|
shopifyService: {
|
|
testConnection: jest.fn(),
|
|
},
|
|
fetchProductsByTag: jest.fn(),
|
|
validateProductsForRollback: jest.fn(),
|
|
rollbackProductPrices: jest.fn(),
|
|
getProductSummary: jest.fn(),
|
|
};
|
|
|
|
// Mock logger
|
|
mockLogger = {
|
|
logRollbackStart: jest.fn(),
|
|
logOperationStart: jest.fn(),
|
|
logProductCount: jest.fn(),
|
|
logRollbackSummary: jest.fn(),
|
|
logCompletionSummary: jest.fn(),
|
|
logErrorAnalysis: jest.fn(),
|
|
info: jest.fn(),
|
|
warning: jest.fn(),
|
|
error: jest.fn(),
|
|
};
|
|
|
|
// Mock constructors
|
|
getConfig.mockReturnValue(mockConfig);
|
|
ProductService.mockImplementation(() => mockProductService);
|
|
Logger.mockImplementation(() => mockLogger);
|
|
|
|
app = new ShopifyPriceUpdater();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe("Rollback Mode Initialization", () => {
|
|
test("should initialize with rollback configuration", async () => {
|
|
const result = await app.initialize();
|
|
|
|
expect(result).toBe(true);
|
|
expect(getConfig).toHaveBeenCalled();
|
|
expect(mockLogger.logRollbackStart).toHaveBeenCalledWith(mockConfig);
|
|
expect(mockLogger.logOperationStart).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("should handle initialization failure", async () => {
|
|
getConfig.mockImplementation(() => {
|
|
throw new Error("Configuration error");
|
|
});
|
|
|
|
const result = await app.initialize();
|
|
|
|
expect(result).toBe(false);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
"Initialization failed: Configuration error"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Rollback Product Fetching and Validation", () => {
|
|
test("should fetch and validate products for rollback", async () => {
|
|
const mockProducts = [
|
|
{
|
|
id: "gid://shopify/Product/123",
|
|
title: "Test Product",
|
|
variants: [
|
|
{
|
|
id: "gid://shopify/ProductVariant/456",
|
|
price: 50.0,
|
|
compareAtPrice: 75.0,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const mockEligibleProducts = [mockProducts[0]];
|
|
const mockSummary = {
|
|
totalProducts: 1,
|
|
totalVariants: 1,
|
|
priceRange: { min: 50, max: 50 },
|
|
};
|
|
|
|
mockProductService.fetchProductsByTag.mockResolvedValue(mockProducts);
|
|
mockProductService.validateProductsForRollback.mockResolvedValue(
|
|
mockEligibleProducts
|
|
);
|
|
mockProductService.getProductSummary.mockReturnValue(mockSummary);
|
|
|
|
// Initialize app first
|
|
await app.initialize();
|
|
const result = await app.fetchAndValidateProductsForRollback();
|
|
|
|
expect(result).toEqual(mockEligibleProducts);
|
|
expect(mockProductService.fetchProductsByTag).toHaveBeenCalledWith(
|
|
"test-tag"
|
|
);
|
|
expect(
|
|
mockProductService.validateProductsForRollback
|
|
).toHaveBeenCalledWith(mockProducts);
|
|
expect(mockLogger.logProductCount).toHaveBeenCalledWith(1);
|
|
expect(mockLogger.info).toHaveBeenCalledWith("Rollback Product Summary:");
|
|
});
|
|
|
|
test("should handle empty product results", async () => {
|
|
mockProductService.fetchProductsByTag.mockResolvedValue([]);
|
|
|
|
// Initialize app first
|
|
await app.initialize();
|
|
const result = await app.fetchAndValidateProductsForRollback();
|
|
|
|
expect(result).toEqual([]);
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
"No products found with the specified tag. Operation completed."
|
|
);
|
|
});
|
|
|
|
test("should handle product fetching errors", async () => {
|
|
mockProductService.fetchProductsByTag.mockRejectedValue(
|
|
new Error("API error")
|
|
);
|
|
|
|
// Initialize app first
|
|
await app.initialize();
|
|
const result = await app.fetchAndValidateProductsForRollback();
|
|
|
|
expect(result).toBe(null);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
"Failed to fetch products for rollback: API error"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Rollback Price Operations", () => {
|
|
test("should execute rollback operations successfully", async () => {
|
|
const mockProducts = [
|
|
{
|
|
id: "gid://shopify/Product/123",
|
|
title: "Test Product",
|
|
variants: [
|
|
{
|
|
id: "gid://shopify/ProductVariant/456",
|
|
price: 50.0,
|
|
compareAtPrice: 75.0,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const mockResults = {
|
|
totalProducts: 1,
|
|
totalVariants: 1,
|
|
eligibleVariants: 1,
|
|
successfulRollbacks: 1,
|
|
failedRollbacks: 0,
|
|
skippedVariants: 0,
|
|
errors: [],
|
|
};
|
|
|
|
mockProductService.rollbackProductPrices.mockResolvedValue(mockResults);
|
|
|
|
const result = await app.rollbackPrices(mockProducts);
|
|
|
|
expect(result).toEqual(mockResults);
|
|
expect(mockProductService.rollbackProductPrices).toHaveBeenCalledWith(
|
|
mockProducts
|
|
);
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
"Starting price rollback operations"
|
|
);
|
|
});
|
|
|
|
test("should handle empty products array", async () => {
|
|
const result = await app.rollbackPrices([]);
|
|
|
|
expect(result).toEqual({
|
|
totalProducts: 0,
|
|
totalVariants: 0,
|
|
eligibleVariants: 0,
|
|
successfulRollbacks: 0,
|
|
failedRollbacks: 0,
|
|
skippedVariants: 0,
|
|
errors: [],
|
|
});
|
|
});
|
|
|
|
test("should handle rollback operation errors", async () => {
|
|
const mockProducts = [
|
|
{
|
|
id: "gid://shopify/Product/123",
|
|
title: "Test Product",
|
|
variants: [{ price: 50.0, compareAtPrice: 75.0 }],
|
|
},
|
|
];
|
|
|
|
mockProductService.rollbackProductPrices.mockRejectedValue(
|
|
new Error("Rollback failed")
|
|
);
|
|
|
|
const result = await app.rollbackPrices(mockProducts);
|
|
|
|
expect(result).toBe(null);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
"Price rollback failed: Rollback failed"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Rollback Summary Display", () => {
|
|
test("should display successful rollback summary", async () => {
|
|
const mockResults = {
|
|
totalProducts: 5,
|
|
totalVariants: 8,
|
|
eligibleVariants: 6,
|
|
successfulRollbacks: 6,
|
|
failedRollbacks: 0,
|
|
skippedVariants: 2,
|
|
errors: [],
|
|
};
|
|
|
|
app.startTime = new Date();
|
|
|
|
const exitCode = await app.displayRollbackSummary(mockResults);
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(mockLogger.logRollbackSummary).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
totalProducts: 5,
|
|
totalVariants: 8,
|
|
eligibleVariants: 6,
|
|
successfulRollbacks: 6,
|
|
failedRollbacks: 0,
|
|
skippedVariants: 2,
|
|
})
|
|
);
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
"🎉 All rollback operations completed successfully!"
|
|
);
|
|
});
|
|
|
|
test("should display partial success rollback summary", async () => {
|
|
const mockResults = {
|
|
totalProducts: 5,
|
|
totalVariants: 8,
|
|
eligibleVariants: 6,
|
|
successfulRollbacks: 5,
|
|
failedRollbacks: 1,
|
|
skippedVariants: 2,
|
|
errors: [
|
|
{
|
|
productId: "gid://shopify/Product/123",
|
|
errorMessage: "Test error",
|
|
},
|
|
],
|
|
};
|
|
|
|
app.startTime = new Date();
|
|
|
|
const exitCode = await app.displayRollbackSummary(mockResults);
|
|
|
|
expect(exitCode).toBe(1); // Moderate success rate (83.3%)
|
|
expect(mockLogger.logRollbackSummary).toHaveBeenCalled();
|
|
expect(mockLogger.logErrorAnalysis).toHaveBeenCalledWith(
|
|
mockResults.errors,
|
|
expect.any(Object)
|
|
);
|
|
expect(mockLogger.warning).toHaveBeenCalledWith(
|
|
expect.stringContaining("moderate success rate")
|
|
);
|
|
});
|
|
|
|
test("should display moderate success rollback summary", async () => {
|
|
const mockResults = {
|
|
totalProducts: 5,
|
|
totalVariants: 8,
|
|
eligibleVariants: 6,
|
|
successfulRollbacks: 3,
|
|
failedRollbacks: 3,
|
|
skippedVariants: 2,
|
|
errors: [
|
|
{ productId: "1", errorMessage: "Error 1" },
|
|
{ productId: "2", errorMessage: "Error 2" },
|
|
{ productId: "3", errorMessage: "Error 3" },
|
|
],
|
|
};
|
|
|
|
app.startTime = new Date();
|
|
|
|
const exitCode = await app.displayRollbackSummary(mockResults);
|
|
|
|
expect(exitCode).toBe(1); // Moderate success rate (50%)
|
|
expect(mockLogger.warning).toHaveBeenCalledWith(
|
|
expect.stringContaining("moderate success rate")
|
|
);
|
|
});
|
|
|
|
test("should display low success rollback summary", async () => {
|
|
const mockResults = {
|
|
totalProducts: 5,
|
|
totalVariants: 8,
|
|
eligibleVariants: 6,
|
|
successfulRollbacks: 1,
|
|
failedRollbacks: 5,
|
|
skippedVariants: 2,
|
|
errors: Array.from({ length: 5 }, (_, i) => ({
|
|
productId: `${i}`,
|
|
errorMessage: `Error ${i}`,
|
|
})),
|
|
};
|
|
|
|
app.startTime = new Date();
|
|
|
|
const exitCode = await app.displayRollbackSummary(mockResults);
|
|
|
|
expect(exitCode).toBe(2); // Low success rate (16.7%)
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
expect.stringContaining("low success rate")
|
|
);
|
|
});
|
|
|
|
test("should display complete failure rollback summary", async () => {
|
|
const mockResults = {
|
|
totalProducts: 5,
|
|
totalVariants: 8,
|
|
eligibleVariants: 6,
|
|
successfulRollbacks: 0,
|
|
failedRollbacks: 6,
|
|
skippedVariants: 2,
|
|
errors: Array.from({ length: 6 }, (_, i) => ({
|
|
productId: `${i}`,
|
|
errorMessage: `Error ${i}`,
|
|
})),
|
|
};
|
|
|
|
app.startTime = new Date();
|
|
|
|
const exitCode = await app.displayRollbackSummary(mockResults);
|
|
|
|
expect(exitCode).toBe(2);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
"❌ All rollback operations failed. Please check your configuration and try again."
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Operation Mode Header Display", () => {
|
|
test("should display rollback mode header", async () => {
|
|
const consoleSpy = jest.spyOn(console, "log").mockImplementation();
|
|
|
|
// Initialize app first
|
|
await app.initialize();
|
|
await app.displayOperationModeHeader();
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining("SHOPIFY PRICE ROLLBACK MODE")
|
|
);
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining(
|
|
"Reverting prices from compare-at to main price"
|
|
)
|
|
);
|
|
expect(mockLogger.info).toHaveBeenCalledWith("Operation Mode: ROLLBACK");
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
|
|
test("should display update mode header when not in rollback mode", async () => {
|
|
mockConfig.operationMode = "update";
|
|
app.config = mockConfig;
|
|
|
|
const consoleSpy = jest.spyOn(console, "log").mockImplementation();
|
|
|
|
await app.displayOperationModeHeader();
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining("SHOPIFY PRICE UPDATE MODE")
|
|
);
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining("Adjusting prices by 10%")
|
|
);
|
|
expect(mockLogger.info).toHaveBeenCalledWith("Operation Mode: UPDATE");
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
});
|
|
|
|
describe("Complete Rollback Workflow", () => {
|
|
test("should execute complete rollback workflow successfully", async () => {
|
|
// Mock successful initialization
|
|
mockProductService.shopifyService.testConnection.mockResolvedValue(true);
|
|
|
|
// Mock successful product fetching and validation
|
|
const mockProducts = [
|
|
{
|
|
id: "gid://shopify/Product/123",
|
|
title: "Test Product",
|
|
variants: [
|
|
{
|
|
id: "gid://shopify/ProductVariant/456",
|
|
price: 50.0,
|
|
compareAtPrice: 75.0,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
mockProductService.fetchProductsByTag.mockResolvedValue(mockProducts);
|
|
mockProductService.validateProductsForRollback.mockResolvedValue(
|
|
mockProducts
|
|
);
|
|
mockProductService.getProductSummary.mockReturnValue({
|
|
totalProducts: 1,
|
|
totalVariants: 1,
|
|
priceRange: { min: 50, max: 50 },
|
|
});
|
|
|
|
// Mock successful rollback
|
|
const mockResults = {
|
|
totalProducts: 1,
|
|
totalVariants: 1,
|
|
eligibleVariants: 1,
|
|
successfulRollbacks: 1,
|
|
failedRollbacks: 0,
|
|
skippedVariants: 0,
|
|
errors: [],
|
|
};
|
|
|
|
mockProductService.rollbackProductPrices.mockResolvedValue(mockResults);
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(mockLogger.logRollbackStart).toHaveBeenCalledWith(mockConfig);
|
|
expect(mockProductService.fetchProductsByTag).toHaveBeenCalledWith(
|
|
"test-tag"
|
|
);
|
|
expect(
|
|
mockProductService.validateProductsForRollback
|
|
).toHaveBeenCalledWith(mockProducts);
|
|
expect(mockProductService.rollbackProductPrices).toHaveBeenCalledWith(
|
|
mockProducts
|
|
);
|
|
expect(mockLogger.logRollbackSummary).toHaveBeenCalled();
|
|
});
|
|
|
|
test("should handle rollback workflow with initialization failure", async () => {
|
|
getConfig.mockImplementation(() => {
|
|
throw new Error("Config error");
|
|
});
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(1);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
expect.stringContaining("Initialization failed")
|
|
);
|
|
});
|
|
|
|
test("should handle rollback workflow with connection failure", async () => {
|
|
mockProductService.shopifyService.testConnection.mockResolvedValue(false);
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(1);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
expect.stringContaining("API connection failed")
|
|
);
|
|
});
|
|
|
|
test("should handle rollback workflow with product fetching failure", async () => {
|
|
mockProductService.shopifyService.testConnection.mockResolvedValue(true);
|
|
mockProductService.fetchProductsByTag.mockRejectedValue(
|
|
new Error("Fetch error")
|
|
);
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(1);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
expect.stringContaining("Product fetching for rollback failed")
|
|
);
|
|
});
|
|
|
|
test("should handle rollback workflow with rollback operation failure", async () => {
|
|
mockProductService.shopifyService.testConnection.mockResolvedValue(true);
|
|
mockProductService.fetchProductsByTag.mockResolvedValue([
|
|
{
|
|
id: "gid://shopify/Product/123",
|
|
variants: [{ price: 50, compareAtPrice: 75 }],
|
|
},
|
|
]);
|
|
mockProductService.validateProductsForRollback.mockResolvedValue([
|
|
{
|
|
id: "gid://shopify/Product/123",
|
|
variants: [{ price: 50, compareAtPrice: 75 }],
|
|
},
|
|
]);
|
|
mockProductService.getProductSummary.mockReturnValue({
|
|
totalProducts: 1,
|
|
totalVariants: 1,
|
|
priceRange: { min: 50, max: 50 },
|
|
});
|
|
mockProductService.rollbackProductPrices.mockRejectedValue(
|
|
new Error("Rollback error")
|
|
);
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(1);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
expect.stringContaining("Price rollback process failed")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Dual Operation Mode Support", () => {
|
|
test("should route to update workflow when operation mode is update", async () => {
|
|
mockConfig.operationMode = "update";
|
|
app.config = mockConfig;
|
|
|
|
// Mock update-specific methods
|
|
app.safeFetchAndValidateProducts = jest.fn().mockResolvedValue([]);
|
|
app.safeUpdatePrices = jest.fn().mockResolvedValue({
|
|
totalProducts: 0,
|
|
totalVariants: 0,
|
|
successfulUpdates: 0,
|
|
failedUpdates: 0,
|
|
errors: [],
|
|
});
|
|
app.displaySummaryAndGetExitCode = jest.fn().mockResolvedValue(0);
|
|
|
|
mockProductService.shopifyService.testConnection.mockResolvedValue(true);
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(mockLogger.logOperationStart).toHaveBeenCalledWith(mockConfig);
|
|
expect(mockLogger.logRollbackStart).not.toHaveBeenCalled();
|
|
expect(app.safeFetchAndValidateProducts).toHaveBeenCalled();
|
|
expect(app.safeUpdatePrices).toHaveBeenCalled();
|
|
expect(app.displaySummaryAndGetExitCode).toHaveBeenCalled();
|
|
});
|
|
|
|
test("should route to rollback workflow when operation mode is rollback", async () => {
|
|
mockConfig.operationMode = "rollback";
|
|
app.config = mockConfig;
|
|
|
|
mockProductService.shopifyService.testConnection.mockResolvedValue(true);
|
|
mockProductService.fetchProductsByTag.mockResolvedValue([]);
|
|
mockProductService.validateProductsForRollback.mockResolvedValue([]);
|
|
mockProductService.getProductSummary.mockReturnValue({
|
|
totalProducts: 0,
|
|
totalVariants: 0,
|
|
priceRange: { min: 0, max: 0 },
|
|
});
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(mockLogger.logRollbackStart).toHaveBeenCalledWith(mockConfig);
|
|
expect(mockLogger.logOperationStart).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("Error Handling and Recovery", () => {
|
|
test("should handle unexpected errors gracefully", async () => {
|
|
mockProductService.shopifyService.testConnection.mockResolvedValue(true);
|
|
mockProductService.fetchProductsByTag.mockImplementation(() => {
|
|
throw new Error("Unexpected error");
|
|
});
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(1); // Critical failure exit code
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
expect.stringContaining("Product fetching for rollback failed")
|
|
);
|
|
});
|
|
|
|
test("should handle critical failures with proper logging", async () => {
|
|
// Initialize app first
|
|
await app.initialize();
|
|
const exitCode = await app.handleCriticalFailure("Test failure", 1);
|
|
|
|
expect(exitCode).toBe(1);
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
"Critical failure in rollback mode: Test failure"
|
|
);
|
|
expect(mockLogger.logRollbackSummary).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
totalProducts: 0,
|
|
errors: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
errorMessage: "Test failure",
|
|
}),
|
|
]),
|
|
})
|
|
);
|
|
});
|
|
|
|
test("should handle unexpected errors with partial results", async () => {
|
|
const partialResults = {
|
|
totalProducts: 2,
|
|
totalVariants: 3,
|
|
eligibleVariants: 2,
|
|
successfulRollbacks: 1,
|
|
failedRollbacks: 1,
|
|
skippedVariants: 1,
|
|
errors: [{ errorMessage: "Previous error" }],
|
|
};
|
|
|
|
const error = new Error("Unexpected error");
|
|
error.stack = "Error stack trace";
|
|
|
|
// Initialize app first
|
|
await app.initialize();
|
|
await app.handleUnexpectedError(error, partialResults);
|
|
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
"Unexpected error occurred in rollback mode: Unexpected error"
|
|
);
|
|
expect(mockLogger.logRollbackSummary).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
totalProducts: 2,
|
|
totalVariants: 3,
|
|
eligibleVariants: 2,
|
|
successfulRollbacks: 1,
|
|
failedRollbacks: 1,
|
|
skippedVariants: 1,
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Backward Compatibility", () => {
|
|
test("should default to update mode when operation mode is not specified", async () => {
|
|
mockConfig.operationMode = "update";
|
|
app.config = mockConfig;
|
|
|
|
// Mock update workflow methods
|
|
app.safeFetchAndValidateProducts = jest.fn().mockResolvedValue([]);
|
|
app.safeUpdatePrices = jest.fn().mockResolvedValue({
|
|
totalProducts: 0,
|
|
totalVariants: 0,
|
|
successfulUpdates: 0,
|
|
failedUpdates: 0,
|
|
errors: [],
|
|
});
|
|
app.displaySummaryAndGetExitCode = jest.fn().mockResolvedValue(0);
|
|
|
|
mockProductService.shopifyService.testConnection.mockResolvedValue(true);
|
|
|
|
const exitCode = await app.run();
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(mockLogger.logOperationStart).toHaveBeenCalledWith(mockConfig);
|
|
expect(mockLogger.logRollbackStart).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|