Files
PriceUpdaterAppv2/tests/integration/scheduled-execution-workflow.test.js

410 lines
11 KiB
JavaScript

/**
* Integration Tests for Scheduled Execution Workflow
* These tests verify the complete scheduled execution functionality works together
* Requirements: 1.2, 2.1, 3.1, 3.2, 5.1, 5.2
*/
const ShopifyPriceUpdater = require("../../src/index");
const { getConfig } = require("../../src/config/environment");
// Mock external dependencies but test internal integration
jest.mock("../../src/config/environment");
jest.mock("../../src/services/shopify");
jest.mock("../../src/services/progress");
jest.mock("../../src/utils/logger");
describe("Scheduled Execution Workflow Integration Tests", () => {
let mockConfig;
let mockShopifyService;
beforeEach(() => {
// Mock base configuration
mockConfig = {
shopDomain: "test-shop.myshopify.com",
accessToken: "test-token",
targetTag: "scheduled-test",
priceAdjustmentPercentage: 10,
operationMode: "update",
scheduledExecutionTime: null,
isScheduled: false,
};
// Mock Shopify service responses
mockShopifyService = {
testConnection: jest.fn().mockResolvedValue(true),
executeQuery: jest.fn(),
executeMutation: jest.fn(),
executeWithRetry: jest.fn(),
};
getConfig.mockReturnValue(mockConfig);
// Mock ShopifyService constructor
const ShopifyService = require("../../src/services/shopify");
ShopifyService.mockImplementation(() => mockShopifyService);
});
afterEach(() => {
jest.clearAllMocks();
});
describe("Backward Compatibility", () => {
test("should execute immediately when scheduling is not configured", async () => {
// Configure without scheduling
mockConfig.scheduledExecutionTime = null;
mockConfig.isScheduled = false;
getConfig.mockReturnValue(mockConfig);
// Mock product response
const mockProductsResponse = {
products: {
edges: [
{
node: {
id: "gid://shopify/Product/123",
title: "Immediate Test Product",
tags: ["scheduled-test"],
variants: {
edges: [
{
node: {
id: "gid://shopify/ProductVariant/456",
price: "50.00",
compareAtPrice: null,
title: "Test Variant",
},
},
],
},
},
},
],
pageInfo: { hasNextPage: false, endCursor: null },
},
};
const mockUpdateResponse = {
productVariantsBulkUpdate: {
productVariants: [
{
id: "gid://shopify/ProductVariant/456",
price: "55.00",
compareAtPrice: "50.00",
},
],
userErrors: [],
},
};
mockShopifyService.executeWithRetry
.mockResolvedValueOnce(mockProductsResponse)
.mockResolvedValue(mockUpdateResponse);
const app = new ShopifyPriceUpdater();
const exitCode = await app.run();
// Verify immediate execution
expect(exitCode).toBe(0);
// Verify normal workflow was executed
expect(mockShopifyService.executeWithRetry).toHaveBeenCalledTimes(2);
});
test("should maintain rollback functionality without scheduling", async () => {
// Configure rollback mode without scheduling
const rollbackConfig = {
...mockConfig,
operationMode: "rollback",
scheduledExecutionTime: null,
isScheduled: false,
};
getConfig.mockReturnValue(rollbackConfig);
// Mock rollback product response
const mockProductsResponse = {
products: {
edges: [
{
node: {
id: "gid://shopify/Product/123",
title: "Rollback Test Product",
tags: ["scheduled-test"],
variants: {
edges: [
{
node: {
id: "gid://shopify/ProductVariant/456",
price: "55.00",
compareAtPrice: "50.00",
title: "Test Variant",
},
},
],
},
},
},
],
pageInfo: { hasNextPage: false, endCursor: null },
},
};
const mockRollbackResponse = {
productVariantsBulkUpdate: {
productVariants: [
{
id: "gid://shopify/ProductVariant/456",
price: "50.00",
compareAtPrice: null,
},
],
userErrors: [],
},
};
mockShopifyService.executeWithRetry
.mockResolvedValueOnce(mockProductsResponse)
.mockResolvedValue(mockRollbackResponse);
const app = new ShopifyPriceUpdater();
const exitCode = await app.run();
// Verify immediate rollback execution
expect(exitCode).toBe(0);
// Verify rollback workflow was executed
expect(mockShopifyService.executeWithRetry).toHaveBeenCalledTimes(2);
});
});
describe("Scheduled Update Workflow", () => {
test("should execute complete scheduled update workflow successfully", async () => {
// Set up scheduled execution for immediate execution (past time)
const scheduledTime = new Date(Date.now() - 1000); // 1 second ago
const scheduledConfig = {
...mockConfig,
scheduledExecutionTime: scheduledTime,
isScheduled: true,
operationMode: "update",
};
getConfig.mockReturnValue(scheduledConfig);
// Mock product fetching response
const mockProductsResponse = {
products: {
edges: [
{
node: {
id: "gid://shopify/Product/123",
title: "Scheduled Test Product",
tags: ["scheduled-test"],
variants: {
edges: [
{
node: {
id: "gid://shopify/ProductVariant/456",
price: "50.00",
compareAtPrice: null,
title: "Test Variant",
},
},
],
},
},
},
],
pageInfo: {
hasNextPage: false,
endCursor: null,
},
},
};
// Mock successful update response
const mockUpdateResponse = {
productVariantsBulkUpdate: {
productVariants: [
{
id: "gid://shopify/ProductVariant/456",
price: "55.00",
compareAtPrice: "50.00",
},
],
userErrors: [],
},
};
mockShopifyService.executeWithRetry
.mockResolvedValueOnce(mockProductsResponse)
.mockResolvedValue(mockUpdateResponse);
// Start the application
const app = new ShopifyPriceUpdater();
const exitCode = await app.run();
// Verify successful completion
expect(exitCode).toBe(0);
// Verify Shopify API calls were made
expect(mockShopifyService.executeWithRetry).toHaveBeenCalledTimes(2);
});
test("should execute complete scheduled rollback workflow successfully", async () => {
// Set up scheduled execution for rollback mode
const scheduledTime = new Date(Date.now() - 1000); // 1 second ago
const scheduledConfig = {
...mockConfig,
scheduledExecutionTime: scheduledTime,
isScheduled: true,
operationMode: "rollback",
};
getConfig.mockReturnValue(scheduledConfig);
// Mock product fetching response for rollback
const mockProductsResponse = {
products: {
edges: [
{
node: {
id: "gid://shopify/Product/123",
title: "Rollback Test Product",
tags: ["scheduled-test"],
variants: {
edges: [
{
node: {
id: "gid://shopify/ProductVariant/456",
price: "55.00",
compareAtPrice: "50.00",
title: "Test Variant",
},
},
],
},
},
},
],
pageInfo: {
hasNextPage: false,
endCursor: null,
},
},
};
// Mock successful rollback response
const mockRollbackResponse = {
productVariantsBulkUpdate: {
productVariants: [
{
id: "gid://shopify/ProductVariant/456",
price: "50.00",
compareAtPrice: null,
},
],
userErrors: [],
},
};
mockShopifyService.executeWithRetry
.mockResolvedValueOnce(mockProductsResponse)
.mockResolvedValue(mockRollbackResponse);
// Start the application
const app = new ShopifyPriceUpdater();
const exitCode = await app.run();
// Verify successful completion
expect(exitCode).toBe(0);
// Verify Shopify API calls were made
expect(mockShopifyService.executeWithRetry).toHaveBeenCalledTimes(2);
});
});
describe("Cancellation During Countdown", () => {
test("should handle cancellation during countdown period gracefully", async () => {
// Set up scheduled execution for future time
const scheduledTime = new Date(Date.now() + 5000); // 5 seconds in future
const scheduledConfig = {
...mockConfig,
scheduledExecutionTime: scheduledTime,
isScheduled: true,
};
getConfig.mockReturnValue(scheduledConfig);
// Start the application
const app = new ShopifyPriceUpdater();
// Set up cancellation state management
app.setSchedulingActive = jest.fn();
app.setOperationInProgress = jest.fn();
const runPromise = app.run();
// Simulate cancellation by calling cleanup on schedule service after a short delay
setTimeout(() => {
if (app.scheduleService) {
app.scheduleService.cleanup();
}
}, 50);
const exitCode = await runPromise;
// Verify cancellation was handled (should return 0 for clean cancellation)
expect(exitCode).toBe(0);
// Verify no product operations were executed
expect(mockShopifyService.executeWithRetry).not.toHaveBeenCalled();
});
});
describe("Error Handling in Scheduled Operations", () => {
test("should handle API connection failures during scheduled execution", async () => {
// Set up scheduled execution
const scheduledTime = new Date(Date.now() - 1000); // 1 second ago
const scheduledConfig = {
...mockConfig,
scheduledExecutionTime: scheduledTime,
isScheduled: true,
};
getConfig.mockReturnValue(scheduledConfig);
// Mock connection failure
mockShopifyService.testConnection.mockResolvedValue(false);
const app = new ShopifyPriceUpdater();
const exitCode = await app.run();
// Verify failure handling
expect(exitCode).toBe(1);
// Verify connection was tested
expect(mockShopifyService.testConnection).toHaveBeenCalled();
});
test("should handle product fetching failures during scheduled execution", async () => {
// Set up scheduled execution
const scheduledTime = new Date(Date.now() - 1000); // 1 second ago
const scheduledConfig = {
...mockConfig,
scheduledExecutionTime: scheduledTime,
isScheduled: true,
};
getConfig.mockReturnValue(scheduledConfig);
// Mock product fetching failure
mockShopifyService.executeWithRetry.mockRejectedValue(
new Error("GraphQL API error during scheduled execution")
);
const app = new ShopifyPriceUpdater();
const exitCode = await app.run();
// Verify failure handling
expect(exitCode).toBe(1);
// Verify product fetching was attempted
expect(mockShopifyService.executeWithRetry).toHaveBeenCalled();
});
});
});