Files
PriceUpdaterAppv2/tests/utils/logger.test.js

462 lines
13 KiB
JavaScript

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")
);
});
});
});
});