809 lines
22 KiB
JavaScript
809 lines
22 KiB
JavaScript
const ProgressService = require("../../src/services/progress");
|
|
const fs = require("fs").promises;
|
|
const path = require("path");
|
|
|
|
describe("ProgressService", () => {
|
|
let progressService;
|
|
let testFilePath;
|
|
|
|
beforeEach(() => {
|
|
// Use a unique test file for each test to avoid conflicts
|
|
testFilePath = `test-progress-${Date.now()}-${Math.random()
|
|
.toString(36)
|
|
.substr(2, 9)}.md`;
|
|
progressService = new ProgressService(testFilePath);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
// Clean up test file after each test
|
|
try {
|
|
await fs.unlink(testFilePath);
|
|
} catch (error) {
|
|
// File might not exist, that's okay
|
|
}
|
|
});
|
|
|
|
describe("formatTimestamp", () => {
|
|
test("should format timestamp correctly", () => {
|
|
const testDate = new Date("2024-01-15T14:30:45.123Z");
|
|
const formatted = progressService.formatTimestamp(testDate);
|
|
|
|
expect(formatted).toBe("2024-01-15 14:30:45 UTC");
|
|
});
|
|
|
|
test("should use current date when no date provided", () => {
|
|
const formatted = progressService.formatTimestamp();
|
|
|
|
// Should be a valid timestamp format
|
|
expect(formatted).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$/);
|
|
});
|
|
|
|
test("should handle different dates correctly", () => {
|
|
const testCases = [
|
|
{
|
|
input: new Date("2023-12-31T23:59:59.999Z"),
|
|
expected: "2023-12-31 23:59:59 UTC",
|
|
},
|
|
{
|
|
input: new Date("2024-01-01T00:00:00.000Z"),
|
|
expected: "2024-01-01 00:00:00 UTC",
|
|
},
|
|
{
|
|
input: new Date("2024-06-15T12:00:00.500Z"),
|
|
expected: "2024-06-15 12:00:00 UTC",
|
|
},
|
|
];
|
|
|
|
testCases.forEach(({ input, expected }) => {
|
|
expect(progressService.formatTimestamp(input)).toBe(expected);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("logOperationStart", () => {
|
|
test("should create progress file and log operation start", async () => {
|
|
const config = {
|
|
targetTag: "test-tag",
|
|
priceAdjustmentPercentage: 10,
|
|
};
|
|
|
|
await progressService.logOperationStart(config);
|
|
|
|
// Check that file was created
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("# Shopify Price Update Progress Log");
|
|
expect(content).toContain("## Price Update Operation -");
|
|
expect(content).toContain("Target Tag: test-tag");
|
|
expect(content).toContain("Price Adjustment: 10%");
|
|
expect(content).toContain("**Configuration:**");
|
|
expect(content).toContain("**Progress:**");
|
|
});
|
|
|
|
test("should handle negative percentage", async () => {
|
|
const config = {
|
|
targetTag: "clearance",
|
|
priceAdjustmentPercentage: -25,
|
|
};
|
|
|
|
await progressService.logOperationStart(config);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("Target Tag: clearance");
|
|
expect(content).toContain("Price Adjustment: -25%");
|
|
});
|
|
|
|
test("should handle special characters in tag", async () => {
|
|
const config = {
|
|
targetTag: "sale-2024_special!",
|
|
priceAdjustmentPercentage: 15.5,
|
|
};
|
|
|
|
await progressService.logOperationStart(config);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("Target Tag: sale-2024_special!");
|
|
expect(content).toContain("Price Adjustment: 15.5%");
|
|
});
|
|
});
|
|
|
|
describe("logRollbackStart", () => {
|
|
test("should create progress file and log rollback operation start", async () => {
|
|
const config = {
|
|
targetTag: "rollback-tag",
|
|
};
|
|
|
|
await progressService.logRollbackStart(config);
|
|
|
|
// Check that file was created
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("# Shopify Price Update Progress Log");
|
|
expect(content).toContain("## Price Rollback Operation -");
|
|
expect(content).toContain("Target Tag: rollback-tag");
|
|
expect(content).toContain("Operation Mode: rollback");
|
|
expect(content).toContain("**Configuration:**");
|
|
expect(content).toContain("**Progress:**");
|
|
});
|
|
|
|
test("should distinguish rollback from update operations in logs", async () => {
|
|
const updateConfig = {
|
|
targetTag: "update-tag",
|
|
priceAdjustmentPercentage: 10,
|
|
};
|
|
|
|
const rollbackConfig = {
|
|
targetTag: "rollback-tag",
|
|
};
|
|
|
|
await progressService.logOperationStart(updateConfig);
|
|
await progressService.logRollbackStart(rollbackConfig);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("## Price Update Operation -");
|
|
expect(content).toContain("Price Adjustment: 10%");
|
|
expect(content).toContain("## Price Rollback Operation -");
|
|
expect(content).toContain("Operation Mode: rollback");
|
|
});
|
|
});
|
|
|
|
describe("logProductUpdate", () => {
|
|
test("should log successful product update", async () => {
|
|
// First create the file
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 10,
|
|
});
|
|
|
|
const entry = {
|
|
productId: "gid://shopify/Product/123456789",
|
|
productTitle: "Test Product",
|
|
variantId: "gid://shopify/ProductVariant/987654321",
|
|
oldPrice: 29.99,
|
|
newPrice: 32.99,
|
|
};
|
|
|
|
await progressService.logProductUpdate(entry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain(
|
|
"✅ **Test Product** (gid://shopify/Product/123456789)"
|
|
);
|
|
expect(content).toContain(
|
|
"Variant: gid://shopify/ProductVariant/987654321"
|
|
);
|
|
expect(content).toContain("Price: $29.99 → $32.99");
|
|
expect(content).toContain("Updated:");
|
|
});
|
|
|
|
test("should handle products with special characters in title", async () => {
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 10,
|
|
});
|
|
|
|
const entry = {
|
|
productId: "gid://shopify/Product/123",
|
|
productTitle: 'Product with "Quotes" & Special Chars!',
|
|
variantId: "gid://shopify/ProductVariant/456",
|
|
oldPrice: 10.0,
|
|
newPrice: 11.0,
|
|
};
|
|
|
|
await progressService.logProductUpdate(entry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain('**Product with "Quotes" & Special Chars!**');
|
|
});
|
|
|
|
test("should handle decimal prices correctly", async () => {
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 5.5,
|
|
});
|
|
|
|
const entry = {
|
|
productId: "gid://shopify/Product/123",
|
|
productTitle: "Decimal Price Product",
|
|
variantId: "gid://shopify/ProductVariant/456",
|
|
oldPrice: 19.95,
|
|
newPrice: 21.05,
|
|
};
|
|
|
|
await progressService.logProductUpdate(entry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("Price: $19.95 → $21.05");
|
|
});
|
|
});
|
|
|
|
describe("logRollbackUpdate", () => {
|
|
test("should log successful rollback operation", async () => {
|
|
// First create the file
|
|
await progressService.logRollbackStart({
|
|
targetTag: "rollback-test",
|
|
});
|
|
|
|
const entry = {
|
|
productId: "gid://shopify/Product/123456789",
|
|
productTitle: "Rollback Test Product",
|
|
variantId: "gid://shopify/ProductVariant/987654321",
|
|
oldPrice: 1000.0,
|
|
compareAtPrice: 750.0,
|
|
newPrice: 750.0,
|
|
};
|
|
|
|
await progressService.logRollbackUpdate(entry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain(
|
|
"🔄 **Rollback Test Product** (gid://shopify/Product/123456789)"
|
|
);
|
|
expect(content).toContain(
|
|
"Variant: gid://shopify/ProductVariant/987654321"
|
|
);
|
|
expect(content).toContain("Price: $1000 → $750 (from Compare At: $750)");
|
|
expect(content).toContain("Rolled back:");
|
|
});
|
|
|
|
test("should distinguish rollback from update entries", async () => {
|
|
await progressService.logRollbackStart({
|
|
targetTag: "test",
|
|
});
|
|
|
|
const updateEntry = {
|
|
productId: "gid://shopify/Product/123",
|
|
productTitle: "Update Product",
|
|
variantId: "gid://shopify/ProductVariant/456",
|
|
oldPrice: 750.0,
|
|
newPrice: 1000.0,
|
|
};
|
|
|
|
const rollbackEntry = {
|
|
productId: "gid://shopify/Product/789",
|
|
productTitle: "Rollback Product",
|
|
variantId: "gid://shopify/ProductVariant/012",
|
|
oldPrice: 1000.0,
|
|
compareAtPrice: 750.0,
|
|
newPrice: 750.0,
|
|
};
|
|
|
|
await progressService.logProductUpdate(updateEntry);
|
|
await progressService.logRollbackUpdate(rollbackEntry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
// Update entry should use checkmark
|
|
expect(content).toContain("✅ **Update Product**");
|
|
expect(content).toContain("Updated:");
|
|
|
|
// Rollback entry should use rollback emoji
|
|
expect(content).toContain("🔄 **Rollback Product**");
|
|
expect(content).toContain("from Compare At:");
|
|
expect(content).toContain("Rolled back:");
|
|
});
|
|
|
|
test("should handle products with special characters in rollback", async () => {
|
|
await progressService.logRollbackStart({
|
|
targetTag: "test",
|
|
});
|
|
|
|
const entry = {
|
|
productId: "gid://shopify/Product/123",
|
|
productTitle: 'Rollback Product with "Quotes" & Special Chars!',
|
|
variantId: "gid://shopify/ProductVariant/456",
|
|
oldPrice: 500.0,
|
|
compareAtPrice: 400.0,
|
|
newPrice: 400.0,
|
|
};
|
|
|
|
await progressService.logRollbackUpdate(entry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain(
|
|
'**Rollback Product with "Quotes" & Special Chars!**'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("logError", () => {
|
|
test("should log error with all details", async () => {
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 10,
|
|
});
|
|
|
|
const entry = {
|
|
productId: "gid://shopify/Product/123",
|
|
productTitle: "Failed Product",
|
|
variantId: "gid://shopify/ProductVariant/456",
|
|
errorMessage: "Invalid price data",
|
|
};
|
|
|
|
await progressService.logError(entry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain(
|
|
"❌ **Failed Product** (gid://shopify/Product/123)"
|
|
);
|
|
expect(content).toContain("Variant: gid://shopify/ProductVariant/456");
|
|
expect(content).toContain("Error: Invalid price data");
|
|
expect(content).toContain("Failed:");
|
|
});
|
|
|
|
test("should handle error without variant ID", async () => {
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 10,
|
|
});
|
|
|
|
const entry = {
|
|
productId: "gid://shopify/Product/123",
|
|
productTitle: "Failed Product",
|
|
errorMessage: "Product not found",
|
|
};
|
|
|
|
await progressService.logError(entry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain(
|
|
"❌ **Failed Product** (gid://shopify/Product/123)"
|
|
);
|
|
expect(content).not.toContain("Variant:");
|
|
expect(content).toContain("Error: Product not found");
|
|
});
|
|
|
|
test("should handle complex error messages", async () => {
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 10,
|
|
});
|
|
|
|
const entry = {
|
|
productId: "gid://shopify/Product/123",
|
|
productTitle: "Complex Error Product",
|
|
variantId: "gid://shopify/ProductVariant/456",
|
|
errorMessage:
|
|
"GraphQL error: Field 'price' of type 'Money!' must not be null",
|
|
};
|
|
|
|
await progressService.logError(entry);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain(
|
|
"Error: GraphQL error: Field 'price' of type 'Money!' must not be null"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("logCompletionSummary", () => {
|
|
test("should log completion summary with all statistics", async () => {
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 10,
|
|
});
|
|
|
|
const startTime = new Date(Date.now() - 5000); // 5 seconds ago
|
|
const summary = {
|
|
totalProducts: 10,
|
|
successfulUpdates: 8,
|
|
failedUpdates: 2,
|
|
startTime: startTime,
|
|
};
|
|
|
|
await progressService.logCompletionSummary(summary);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("**Summary:**");
|
|
expect(content).toContain("Total Products Processed: 10");
|
|
expect(content).toContain("Successful Updates: 8");
|
|
expect(content).toContain("Failed Updates: 2");
|
|
expect(content).toContain("Duration: 5 seconds");
|
|
expect(content).toContain("Completed:");
|
|
expect(content).toContain("---");
|
|
});
|
|
|
|
test("should handle summary without start time", async () => {
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 10,
|
|
});
|
|
|
|
const summary = {
|
|
totalProducts: 5,
|
|
successfulUpdates: 5,
|
|
failedUpdates: 0,
|
|
};
|
|
|
|
await progressService.logCompletionSummary(summary);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("Duration: Unknown seconds");
|
|
});
|
|
|
|
test("should handle zero statistics", async () => {
|
|
await progressService.logOperationStart({
|
|
targetTag: "test",
|
|
priceAdjustmentPercentage: 10,
|
|
});
|
|
|
|
const summary = {
|
|
totalProducts: 0,
|
|
successfulUpdates: 0,
|
|
failedUpdates: 0,
|
|
startTime: new Date(),
|
|
};
|
|
|
|
await progressService.logCompletionSummary(summary);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("Total Products Processed: 0");
|
|
expect(content).toContain("Successful Updates: 0");
|
|
expect(content).toContain("Failed Updates: 0");
|
|
});
|
|
});
|
|
|
|
describe("logRollbackSummary", () => {
|
|
test("should log rollback completion summary with all statistics", async () => {
|
|
await progressService.logRollbackStart({
|
|
targetTag: "rollback-test",
|
|
});
|
|
|
|
const startTime = new Date(Date.now() - 8000); // 8 seconds ago
|
|
const summary = {
|
|
totalProducts: 5,
|
|
totalVariants: 8,
|
|
eligibleVariants: 6,
|
|
successfulRollbacks: 5,
|
|
failedRollbacks: 1,
|
|
skippedVariants: 2,
|
|
startTime: startTime,
|
|
};
|
|
|
|
await progressService.logRollbackSummary(summary);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("**Rollback Summary:**");
|
|
expect(content).toContain("Total Products Processed: 5");
|
|
expect(content).toContain("Total Variants Processed: 8");
|
|
expect(content).toContain("Eligible Variants: 6");
|
|
expect(content).toContain("Successful Rollbacks: 5");
|
|
expect(content).toContain("Failed Rollbacks: 1");
|
|
expect(content).toContain("Skipped Variants: 2 (no compare-at price)");
|
|
expect(content).toContain("Duration: 8 seconds");
|
|
expect(content).toContain("Completed:");
|
|
expect(content).toContain("---");
|
|
});
|
|
|
|
test("should handle rollback summary without start time", async () => {
|
|
await progressService.logRollbackStart({
|
|
targetTag: "test",
|
|
});
|
|
|
|
const summary = {
|
|
totalProducts: 3,
|
|
totalVariants: 5,
|
|
eligibleVariants: 5,
|
|
successfulRollbacks: 5,
|
|
failedRollbacks: 0,
|
|
skippedVariants: 0,
|
|
};
|
|
|
|
await progressService.logRollbackSummary(summary);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("Duration: Unknown seconds");
|
|
});
|
|
|
|
test("should distinguish rollback summary from update summary", async () => {
|
|
await progressService.logRollbackStart({
|
|
targetTag: "test",
|
|
});
|
|
|
|
const updateSummary = {
|
|
totalProducts: 5,
|
|
successfulUpdates: 4,
|
|
failedUpdates: 1,
|
|
startTime: new Date(Date.now() - 5000),
|
|
};
|
|
|
|
const rollbackSummary = {
|
|
totalProducts: 3,
|
|
totalVariants: 6,
|
|
eligibleVariants: 4,
|
|
successfulRollbacks: 3,
|
|
failedRollbacks: 1,
|
|
skippedVariants: 2,
|
|
startTime: new Date(Date.now() - 3000),
|
|
};
|
|
|
|
await progressService.logCompletionSummary(updateSummary);
|
|
await progressService.logRollbackSummary(rollbackSummary);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
// Should contain both summary types
|
|
expect(content).toContain("**Summary:**");
|
|
expect(content).toContain("Successful Updates: 4");
|
|
expect(content).toContain("**Rollback Summary:**");
|
|
expect(content).toContain("Successful Rollbacks: 3");
|
|
expect(content).toContain("Skipped Variants: 2 (no compare-at price)");
|
|
});
|
|
|
|
test("should handle zero rollback statistics", async () => {
|
|
await progressService.logRollbackStart({
|
|
targetTag: "test",
|
|
});
|
|
|
|
const summary = {
|
|
totalProducts: 0,
|
|
totalVariants: 0,
|
|
eligibleVariants: 0,
|
|
successfulRollbacks: 0,
|
|
failedRollbacks: 0,
|
|
skippedVariants: 0,
|
|
startTime: new Date(),
|
|
};
|
|
|
|
await progressService.logRollbackSummary(summary);
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("Total Products Processed: 0");
|
|
expect(content).toContain("Total Variants Processed: 0");
|
|
expect(content).toContain("Eligible Variants: 0");
|
|
expect(content).toContain("Successful Rollbacks: 0");
|
|
expect(content).toContain("Failed Rollbacks: 0");
|
|
expect(content).toContain("Skipped Variants: 0");
|
|
});
|
|
});
|
|
|
|
describe("categorizeError", () => {
|
|
test("should categorize rate limiting errors", () => {
|
|
const testCases = [
|
|
"Rate limit exceeded",
|
|
"HTTP 429 Too Many Requests",
|
|
"Request was throttled",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe(
|
|
"Rate Limiting"
|
|
);
|
|
});
|
|
});
|
|
|
|
test("should categorize network errors", () => {
|
|
const testCases = [
|
|
"Network connection failed",
|
|
"Connection timeout",
|
|
"Network error occurred",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe(
|
|
"Network Issues"
|
|
);
|
|
});
|
|
});
|
|
|
|
test("should categorize authentication errors", () => {
|
|
const testCases = [
|
|
"Authentication failed",
|
|
"HTTP 401 Unauthorized",
|
|
"Invalid authentication credentials",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe(
|
|
"Authentication"
|
|
);
|
|
});
|
|
});
|
|
|
|
test("should categorize permission errors", () => {
|
|
const testCases = [
|
|
"Permission denied",
|
|
"HTTP 403 Forbidden",
|
|
"Insufficient permissions",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe(
|
|
"Permissions"
|
|
);
|
|
});
|
|
});
|
|
|
|
test("should categorize not found errors", () => {
|
|
const testCases = [
|
|
"Product not found",
|
|
"HTTP 404 Not Found",
|
|
"Resource not found",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe(
|
|
"Resource Not Found"
|
|
);
|
|
});
|
|
});
|
|
|
|
test("should categorize validation errors", () => {
|
|
const testCases = [
|
|
"Validation error: Invalid price",
|
|
"Invalid product data",
|
|
"Price validation failed",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe(
|
|
"Data Validation"
|
|
);
|
|
});
|
|
});
|
|
|
|
test("should categorize server errors", () => {
|
|
const testCases = [
|
|
"Internal server error",
|
|
"HTTP 500 Server Error",
|
|
"HTTP 502 Bad Gateway",
|
|
"HTTP 503 Service Unavailable",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe(
|
|
"Server Errors"
|
|
);
|
|
});
|
|
});
|
|
|
|
test("should categorize Shopify API errors", () => {
|
|
const testCases = [
|
|
"Shopify API error occurred",
|
|
"Shopify API request failed",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe(
|
|
"Shopify API"
|
|
);
|
|
});
|
|
});
|
|
|
|
test("should categorize unknown errors as Other", () => {
|
|
const testCases = [
|
|
"Something went wrong",
|
|
"Unexpected error",
|
|
"Random failure message",
|
|
];
|
|
|
|
testCases.forEach((errorMessage) => {
|
|
expect(progressService.categorizeError(errorMessage)).toBe("Other");
|
|
});
|
|
});
|
|
|
|
test("should handle case insensitive categorization", () => {
|
|
expect(progressService.categorizeError("RATE LIMIT EXCEEDED")).toBe(
|
|
"Rate Limiting"
|
|
);
|
|
expect(progressService.categorizeError("Network Connection Failed")).toBe(
|
|
"Network Issues"
|
|
);
|
|
expect(progressService.categorizeError("AUTHENTICATION FAILED")).toBe(
|
|
"Authentication"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("createProgressEntry", () => {
|
|
test("should create progress entry with timestamp", () => {
|
|
const data = {
|
|
productId: "gid://shopify/Product/123",
|
|
productTitle: "Test Product",
|
|
status: "success",
|
|
};
|
|
|
|
const entry = progressService.createProgressEntry(data);
|
|
|
|
expect(entry).toHaveProperty("timestamp");
|
|
expect(entry.timestamp).toBeInstanceOf(Date);
|
|
expect(entry.productId).toBe("gid://shopify/Product/123");
|
|
expect(entry.productTitle).toBe("Test Product");
|
|
expect(entry.status).toBe("success");
|
|
});
|
|
|
|
test("should preserve all original data", () => {
|
|
const data = {
|
|
productId: "gid://shopify/Product/456",
|
|
productTitle: "Another Product",
|
|
variantId: "gid://shopify/ProductVariant/789",
|
|
oldPrice: 10.0,
|
|
newPrice: 11.0,
|
|
errorMessage: "Some error",
|
|
};
|
|
|
|
const entry = progressService.createProgressEntry(data);
|
|
|
|
expect(entry.productId).toBe(data.productId);
|
|
expect(entry.productTitle).toBe(data.productTitle);
|
|
expect(entry.variantId).toBe(data.variantId);
|
|
expect(entry.oldPrice).toBe(data.oldPrice);
|
|
expect(entry.newPrice).toBe(data.newPrice);
|
|
expect(entry.errorMessage).toBe(data.errorMessage);
|
|
});
|
|
});
|
|
|
|
describe("appendToProgressFile", () => {
|
|
test("should create file with header when file doesn't exist", async () => {
|
|
await progressService.appendToProgressFile("Test content");
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
expect(content).toContain("# Shopify Price Update Progress Log");
|
|
expect(content).toContain(
|
|
"This file tracks the progress of price update operations."
|
|
);
|
|
expect(content).toContain("Test content");
|
|
});
|
|
|
|
test("should append to existing file without adding header", async () => {
|
|
// Create file first
|
|
await progressService.appendToProgressFile("First content");
|
|
|
|
// Append more content
|
|
await progressService.appendToProgressFile("Second content");
|
|
|
|
const content = await fs.readFile(testFilePath, "utf8");
|
|
|
|
// Should only have one header
|
|
const headerCount = (
|
|
content.match(/# Shopify Price Update Progress Log/g) || []
|
|
).length;
|
|
expect(headerCount).toBe(1);
|
|
|
|
expect(content).toContain("First content");
|
|
expect(content).toContain("Second content");
|
|
});
|
|
|
|
test("should handle file write errors gracefully", async () => {
|
|
// Mock fs.appendFile to throw an error
|
|
const originalAppendFile = fs.appendFile;
|
|
const mockAppendFile = jest
|
|
.fn()
|
|
.mockRejectedValue(new Error("Permission denied"));
|
|
fs.appendFile = mockAppendFile;
|
|
|
|
// Should not throw an error, but should log warnings
|
|
const consoleSpy = jest
|
|
.spyOn(console, "warn")
|
|
.mockImplementation(() => {});
|
|
|
|
await progressService.appendToProgressFile("Test content");
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining("Warning: Failed to write to progress file")
|
|
);
|
|
|
|
consoleSpy.mockRestore();
|
|
fs.appendFile = originalAppendFile;
|
|
});
|
|
});
|
|
});
|