410 lines
11 KiB
JavaScript
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();
|
|
});
|
|
});
|
|
});
|