/** * 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(); }); }); });