Implemented Rollback Functionality
This commit is contained in:
461
tests/utils/logger.test.js
Normal file
461
tests/utils/logger.test.js
Normal file
@@ -0,0 +1,461 @@
|
||||
const Logger = require("../../src/utils/logger");
|
||||
const ProgressService = require("../../src/services/progress");
|
||||
|
||||
// Mock the ProgressService
|
||||
jest.mock("../../src/services/progress");
|
||||
|
||||
describe("Logger", () => {
|
||||
let logger;
|
||||
let mockProgressService;
|
||||
let consoleSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
// Create mock progress service
|
||||
mockProgressService = {
|
||||
logOperationStart: jest.fn(),
|
||||
logRollbackStart: jest.fn(),
|
||||
logProductUpdate: jest.fn(),
|
||||
logRollbackUpdate: jest.fn(),
|
||||
logCompletionSummary: jest.fn(),
|
||||
logRollbackSummary: jest.fn(),
|
||||
logError: jest.fn(),
|
||||
logErrorAnalysis: jest.fn(),
|
||||
};
|
||||
|
||||
// Mock the ProgressService constructor
|
||||
ProgressService.mockImplementation(() => mockProgressService);
|
||||
|
||||
logger = new Logger();
|
||||
|
||||
// Spy on console methods
|
||||
consoleSpy = {
|
||||
log: jest.spyOn(console, "log").mockImplementation(() => {}),
|
||||
warn: jest.spyOn(console, "warn").mockImplementation(() => {}),
|
||||
error: jest.spyOn(console, "error").mockImplementation(() => {}),
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
consoleSpy.log.mockRestore();
|
||||
consoleSpy.warn.mockRestore();
|
||||
consoleSpy.error.mockRestore();
|
||||
});
|
||||
|
||||
describe("Rollback Logging Methods", () => {
|
||||
describe("logRollbackStart", () => {
|
||||
it("should log rollback operation start to console and progress file", async () => {
|
||||
const config = {
|
||||
targetTag: "test-tag",
|
||||
shopDomain: "test-shop.myshopify.com",
|
||||
};
|
||||
|
||||
await logger.logRollbackStart(config);
|
||||
|
||||
// Check console output
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"Starting price rollback operation with configuration:"
|
||||
)
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Target Tag: test-tag")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Operation Mode: rollback")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Shop Domain: test-shop.myshopify.com")
|
||||
);
|
||||
|
||||
// Check progress service was called
|
||||
expect(mockProgressService.logRollbackStart).toHaveBeenCalledWith(
|
||||
config
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("logRollbackUpdate", () => {
|
||||
it("should log successful rollback operations to console and progress file", async () => {
|
||||
const entry = {
|
||||
productTitle: "Test Product",
|
||||
productId: "gid://shopify/Product/123",
|
||||
variantId: "gid://shopify/ProductVariant/456",
|
||||
oldPrice: 1000.0,
|
||||
compareAtPrice: 750.0,
|
||||
newPrice: 750.0,
|
||||
};
|
||||
|
||||
await logger.logRollbackUpdate(entry);
|
||||
|
||||
// Check console output contains rollback-specific formatting
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("🔄")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Rolled back "Test Product"')
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Price: 1000 → 750")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("from Compare At: 750")
|
||||
);
|
||||
|
||||
// Check progress service was called
|
||||
expect(mockProgressService.logRollbackUpdate).toHaveBeenCalledWith(
|
||||
entry
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("logRollbackSummary", () => {
|
||||
it("should log rollback completion summary to console and progress file", async () => {
|
||||
const summary = {
|
||||
totalProducts: 5,
|
||||
totalVariants: 8,
|
||||
eligibleVariants: 6,
|
||||
successfulRollbacks: 5,
|
||||
failedRollbacks: 1,
|
||||
skippedVariants: 2,
|
||||
startTime: new Date(Date.now() - 30000), // 30 seconds ago
|
||||
};
|
||||
|
||||
await logger.logRollbackSummary(summary);
|
||||
|
||||
// Check console output contains rollback-specific formatting
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("ROLLBACK OPERATION COMPLETE")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Total Products Processed: 5")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Total Variants Processed: 8")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Eligible Variants: 6")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Successful Rollbacks: ")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Failed Rollbacks: ")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Skipped Variants: ")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("no compare-at price")
|
||||
);
|
||||
|
||||
// Check progress service was called
|
||||
expect(mockProgressService.logRollbackSummary).toHaveBeenCalledWith(
|
||||
summary
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle zero failed rollbacks without red coloring", async () => {
|
||||
const summary = {
|
||||
totalProducts: 3,
|
||||
totalVariants: 5,
|
||||
eligibleVariants: 5,
|
||||
successfulRollbacks: 5,
|
||||
failedRollbacks: 0,
|
||||
skippedVariants: 0,
|
||||
startTime: new Date(Date.now() - 15000),
|
||||
};
|
||||
|
||||
await logger.logRollbackSummary(summary);
|
||||
|
||||
// Should show failed rollbacks without red coloring when zero
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Failed Rollbacks: 0")
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Skipped Variants: 0")
|
||||
);
|
||||
});
|
||||
|
||||
it("should show colored output for failed rollbacks and skipped variants when greater than zero", async () => {
|
||||
const summary = {
|
||||
totalProducts: 5,
|
||||
totalVariants: 8,
|
||||
eligibleVariants: 6,
|
||||
successfulRollbacks: 4,
|
||||
failedRollbacks: 2,
|
||||
skippedVariants: 2,
|
||||
startTime: new Date(Date.now() - 45000),
|
||||
};
|
||||
|
||||
await logger.logRollbackSummary(summary);
|
||||
|
||||
// Should show colored output for non-zero values
|
||||
const logCalls = consoleSpy.log.mock.calls.map((call) => call[0]);
|
||||
const failedRollbacksCall = logCalls.find((call) =>
|
||||
call.includes("Failed Rollbacks:")
|
||||
);
|
||||
const skippedVariantsCall = logCalls.find((call) =>
|
||||
call.includes("Skipped Variants:")
|
||||
);
|
||||
|
||||
expect(failedRollbacksCall).toContain("\x1b[31m"); // Red color code
|
||||
expect(skippedVariantsCall).toContain("\x1b[33m"); // Yellow color code
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Rollback vs Update Distinction", () => {
|
||||
it("should distinguish rollback logs from update logs in console output", async () => {
|
||||
const updateEntry = {
|
||||
productTitle: "Test Product",
|
||||
oldPrice: 750.0,
|
||||
newPrice: 1000.0,
|
||||
compareAtPrice: 1000.0,
|
||||
};
|
||||
|
||||
const rollbackEntry = {
|
||||
productTitle: "Test Product",
|
||||
oldPrice: 1000.0,
|
||||
compareAtPrice: 750.0,
|
||||
newPrice: 750.0,
|
||||
};
|
||||
|
||||
await logger.logProductUpdate(updateEntry);
|
||||
await logger.logRollbackUpdate(rollbackEntry);
|
||||
|
||||
const logCalls = consoleSpy.log.mock.calls.map((call) => call[0]);
|
||||
|
||||
// Update should use checkmark emoji
|
||||
const updateCall = logCalls.find((call) => call.includes("Updated"));
|
||||
expect(updateCall).toContain("✅");
|
||||
|
||||
// Rollback should use rollback emoji
|
||||
const rollbackCall = logCalls.find((call) =>
|
||||
call.includes("Rolled back")
|
||||
);
|
||||
expect(rollbackCall).toContain("🔄");
|
||||
});
|
||||
|
||||
it("should call different progress service methods for updates vs rollbacks", async () => {
|
||||
const updateEntry = {
|
||||
productTitle: "Test",
|
||||
oldPrice: 750,
|
||||
newPrice: 1000,
|
||||
};
|
||||
const rollbackEntry = {
|
||||
productTitle: "Test",
|
||||
oldPrice: 1000,
|
||||
newPrice: 750,
|
||||
compareAtPrice: 750,
|
||||
};
|
||||
|
||||
await logger.logProductUpdate(updateEntry);
|
||||
await logger.logRollbackUpdate(rollbackEntry);
|
||||
|
||||
expect(mockProgressService.logProductUpdate).toHaveBeenCalledWith(
|
||||
updateEntry
|
||||
);
|
||||
expect(mockProgressService.logRollbackUpdate).toHaveBeenCalledWith(
|
||||
rollbackEntry
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Error Handling", () => {
|
||||
it("should handle progress service errors gracefully", async () => {
|
||||
mockProgressService.logRollbackStart.mockRejectedValue(
|
||||
new Error("Progress service error")
|
||||
);
|
||||
|
||||
const config = {
|
||||
targetTag: "test-tag",
|
||||
shopDomain: "test-shop.myshopify.com",
|
||||
};
|
||||
|
||||
// Should not throw even if progress service fails
|
||||
await expect(logger.logRollbackStart(config)).resolves.not.toThrow();
|
||||
|
||||
// Console output should still work
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Starting price rollback operation")
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle rollback update logging errors gracefully", async () => {
|
||||
mockProgressService.logRollbackUpdate.mockRejectedValue(
|
||||
new Error("Progress service error")
|
||||
);
|
||||
|
||||
const entry = {
|
||||
productTitle: "Test Product",
|
||||
oldPrice: 1000,
|
||||
newPrice: 750,
|
||||
compareAtPrice: 750,
|
||||
};
|
||||
|
||||
// Should not throw even if progress service fails
|
||||
await expect(logger.logRollbackUpdate(entry)).resolves.not.toThrow();
|
||||
|
||||
// Console output should still work
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Rolled back")
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle rollback summary logging errors gracefully", async () => {
|
||||
mockProgressService.logRollbackSummary.mockRejectedValue(
|
||||
new Error("Progress service error")
|
||||
);
|
||||
|
||||
const summary = {
|
||||
totalProducts: 5,
|
||||
successfulRollbacks: 4,
|
||||
failedRollbacks: 1,
|
||||
skippedVariants: 0,
|
||||
startTime: new Date(),
|
||||
};
|
||||
|
||||
// Should not throw even if progress service fails
|
||||
await expect(logger.logRollbackSummary(summary)).resolves.not.toThrow();
|
||||
|
||||
// Console output should still work
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("ROLLBACK OPERATION COMPLETE")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Existing Logger Methods", () => {
|
||||
describe("Basic logging methods", () => {
|
||||
it("should log info messages to console", async () => {
|
||||
await logger.info("Test info message");
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Test info message")
|
||||
);
|
||||
});
|
||||
|
||||
it("should log warning messages to console", async () => {
|
||||
await logger.warning("Test warning message");
|
||||
|
||||
expect(consoleSpy.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Test warning message")
|
||||
);
|
||||
});
|
||||
|
||||
it("should log error messages to console", async () => {
|
||||
await logger.error("Test error message");
|
||||
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Test error message")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Operation start logging", () => {
|
||||
it("should log operation start for update mode", async () => {
|
||||
const config = {
|
||||
targetTag: "test-tag",
|
||||
priceAdjustmentPercentage: 10,
|
||||
shopDomain: "test-shop.myshopify.com",
|
||||
};
|
||||
|
||||
await logger.logOperationStart(config);
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Starting price update operation")
|
||||
);
|
||||
expect(mockProgressService.logOperationStart).toHaveBeenCalledWith(
|
||||
config
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Product update logging", () => {
|
||||
it("should log product updates", async () => {
|
||||
const entry = {
|
||||
productTitle: "Test Product",
|
||||
oldPrice: 100,
|
||||
newPrice: 110,
|
||||
compareAtPrice: 100,
|
||||
};
|
||||
|
||||
await logger.logProductUpdate(entry);
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Updated")
|
||||
);
|
||||
expect(mockProgressService.logProductUpdate).toHaveBeenCalledWith(
|
||||
entry
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Completion summary logging", () => {
|
||||
it("should log completion summary", async () => {
|
||||
const summary = {
|
||||
totalProducts: 5,
|
||||
successfulUpdates: 4,
|
||||
failedUpdates: 1,
|
||||
startTime: new Date(),
|
||||
};
|
||||
|
||||
await logger.logCompletionSummary(summary);
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("OPERATION COMPLETE")
|
||||
);
|
||||
expect(mockProgressService.logCompletionSummary).toHaveBeenCalledWith(
|
||||
summary
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Error logging", () => {
|
||||
it("should log product errors", async () => {
|
||||
const errorEntry = {
|
||||
productTitle: "Test Product",
|
||||
errorMessage: "Test error",
|
||||
};
|
||||
|
||||
await logger.logProductError(errorEntry);
|
||||
|
||||
expect(mockProgressService.logError).toHaveBeenCalledWith(errorEntry);
|
||||
});
|
||||
|
||||
it("should log error analysis", async () => {
|
||||
const errors = [
|
||||
{ errorMessage: "Error 1" },
|
||||
{ errorMessage: "Error 2" },
|
||||
];
|
||||
const summary = { totalProducts: 2 };
|
||||
|
||||
await logger.logErrorAnalysis(errors, summary);
|
||||
|
||||
expect(mockProgressService.logErrorAnalysis).toHaveBeenCalledWith(
|
||||
errors,
|
||||
summary
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Product count logging", () => {
|
||||
it("should log product count", async () => {
|
||||
await logger.logProductCount(5);
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Found 5 products")
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle zero products", async () => {
|
||||
await logger.logProductCount(0);
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Found 0 products")
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user