Files
SkyArtShop/website/public/safeguard-tests.html
Local Server f8068ba54c webupdate
2026-01-19 01:17:43 -06:00

665 lines
19 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cart/Wishlist Safeguard Tests</title>
<link rel="icon" type="image/png" href="/favicon.png" />
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
max-width: 1200px;
margin: 40px auto;
padding: 20px;
background: #f5f5f5;
}
h1 {
color: #202023;
border-bottom: 3px solid #fcb1d8;
padding-bottom: 10px;
}
.test-section {
background: white;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.test-button {
background: #fcb1d8;
color: white;
border: none;
padding: 10px 20px;
margin: 5px;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
}
.test-button:hover {
background: #f6ccde;
}
.result {
margin: 10px 0;
padding: 10px;
border-radius: 4px;
font-family: monospace;
}
.success {
background: #d4edda;
color: #155724;
}
.error {
background: #f8d7da;
color: #721c24;
}
.info {
background: #d1ecf1;
color: #0c5460;
}
.status {
display: inline-block;
padding: 2px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
}
.pass {
background: #28a745;
color: white;
}
.fail {
background: #dc3545;
color: white;
}
</style>
</head>
<body>
<h1>🛡️ Cart/Wishlist System - Safeguard Tests</h1>
<div class="test-section">
<h2>1. Invalid Product Tests</h2>
<button class="test-button" onclick="testInvalidProduct()">
Test: No ID
</button>
<button class="test-button" onclick="testInvalidPrice()">
Test: Invalid Price
</button>
<button class="test-button" onclick="testMissingName()">
Test: Missing Name
</button>
<button class="test-button" onclick="testMissingImage()">
Test: Missing Image
</button>
<div id="invalid-results"></div>
</div>
<div class="test-section">
<h2>2. Type Coercion Tests</h2>
<button class="test-button" onclick="testStringId()">
Test: String ID
</button>
<button class="test-button" onclick="testNumberId()">
Test: Number ID
</button>
<button class="test-button" onclick="testMixedIds()">
Test: Mixed IDs
</button>
<div id="type-results"></div>
</div>
<div class="test-section">
<h2>3. Quantity Boundary Tests</h2>
<button class="test-button" onclick="testZeroQuantity()">
Test: Zero Quantity
</button>
<button class="test-button" onclick="testNegativeQuantity()">
Test: Negative Quantity
</button>
<button class="test-button" onclick="testMaxQuantity()">
Test: Max Quantity (999)
</button>
<div id="quantity-results"></div>
</div>
<div class="test-section">
<h2>4. localStorage Corruption Tests</h2>
<button class="test-button" onclick="testCorruptedData()">
Test: Corrupted JSON
</button>
<button class="test-button" onclick="testNonArrayData()">
Test: Non-Array Data
</button>
<button class="test-button" onclick="testRecovery()">
Test: Recovery
</button>
<div id="storage-results"></div>
</div>
<div class="test-section">
<h2>5. Mathematical Safeguard Tests</h2>
<button class="test-button" onclick="testStringPrice()">
Test: String Price
</button>
<button class="test-button" onclick="testNaNPrice()">
Test: NaN Price
</button>
<button class="test-button" onclick="testTotalCalculation()">
Test: Total Calculation
</button>
<div id="math-results"></div>
</div>
<div class="test-section">
<h2>6. Rapid Operation Tests</h2>
<button class="test-button" onclick="testRapidAdd()">
Test: Rapid Add (10x)
</button>
<button class="test-button" onclick="testRapidRemove()">
Test: Rapid Remove
</button>
<button class="test-button" onclick="testSimultaneous()">
Test: Simultaneous Ops
</button>
<div id="rapid-results"></div>
</div>
<div class="test-section">
<h2>Current Cart State</h2>
<button class="test-button" onclick="displayCartState()">
View Cart
</button>
<button class="test-button" onclick="clearCart()">Clear Cart</button>
<div id="cart-state"></div>
</div>
<script>
// Load shop-system.js functionality
function log(message, type = "info", containerId) {
const container = document.getElementById(containerId);
const div = document.createElement("div");
div.className = `result ${type}`;
div.textContent = message;
container.appendChild(div);
}
function logStatus(test, passed, containerId) {
const container = document.getElementById(containerId);
const div = document.createElement("div");
div.className = "result";
div.innerHTML = `${test}: <span class="status ${
passed ? "pass" : "fail"
}">${passed ? "PASS" : "FAIL"}</span>`;
container.appendChild(div);
}
// Initialize simple cart system for testing
const testCart = {
items: [],
addItem(product, quantity = 1) {
// Validation safeguards
if (!product || !product.id) {
return { success: false, error: "Invalid product: missing ID" };
}
const price = parseFloat(product.price);
if (isNaN(price) || price < 0) {
return { success: false, error: "Invalid price" };
}
quantity = Math.max(1, parseInt(quantity) || 1);
const existing = this.items.find(
(item) => String(item.id) === String(product.id)
);
if (existing) {
existing.quantity = Math.min(existing.quantity + quantity, 999);
} else {
this.items.push({
id: product.id,
name: product.name || product.title || "Product",
price: price,
imageurl: product.imageurl || "/assets/images/placeholder.jpg",
quantity: quantity,
});
}
this.save();
return { success: true };
},
removeItem(id) {
this.items = this.items.filter(
(item) => String(item.id) !== String(id)
);
this.save();
return { success: true };
},
getTotal() {
return this.items.reduce((sum, item) => {
const price = parseFloat(item.price) || 0;
const quantity = parseInt(item.quantity) || 0;
return sum + price * quantity;
}, 0);
},
save() {
try {
localStorage.setItem("test_cart", JSON.stringify(this.items));
return true;
} catch (e) {
console.error("Save error:", e);
return false;
}
},
load() {
try {
const data = localStorage.getItem("test_cart");
this.items = data ? JSON.parse(data) : [];
if (!Array.isArray(this.items)) {
this.items = [];
}
// Sanitize
this.items = this.items.filter(
(item) => item && item.id && typeof item.price !== "undefined"
);
return true;
} catch (e) {
console.error("Load error:", e);
localStorage.removeItem("test_cart");
this.items = [];
return false;
}
},
clear() {
this.items = [];
localStorage.removeItem("test_cart");
},
};
// Load cart on page load
testCart.load();
// Test functions
function testInvalidProduct() {
const container = document.getElementById("invalid-results");
container.innerHTML = "";
const result1 = testCart.addItem({ name: "Test Product", price: 10.0 });
logStatus(
"No ID",
!result1.success && result1.error.includes("missing ID"),
"invalid-results"
);
const result2 = testCart.addItem(null);
logStatus("Null product", !result2.success, "invalid-results");
const result3 = testCart.addItem(undefined);
logStatus("Undefined product", !result3.success, "invalid-results");
}
function testInvalidPrice() {
const container = document.getElementById("invalid-results");
const result1 = testCart.addItem({
id: "test1",
name: "Test",
price: "invalid",
});
logStatus(
'String "invalid" price',
!result1.success,
"invalid-results"
);
const result2 = testCart.addItem({
id: "test2",
name: "Test",
price: -10,
});
logStatus("Negative price", !result2.success, "invalid-results");
const result3 = testCart.addItem({ id: "test3", name: "Test" });
logStatus("Missing price", !result3.success, "invalid-results");
}
function testMissingName() {
const container = document.getElementById("invalid-results");
const result = testCart.addItem({ id: "test-name", price: 10.0 });
logStatus(
"Missing name (uses fallback)",
result.success,
"invalid-results"
);
const item = testCart.items.find((i) => i.id === "test-name");
logStatus(
'Fallback name is "Product"',
item && item.name === "Product",
"invalid-results"
);
}
function testMissingImage() {
const container = document.getElementById("invalid-results");
const result = testCart.addItem({
id: "test-img",
name: "Test",
price: 10.0,
});
logStatus(
"Missing image (uses placeholder)",
result.success,
"invalid-results"
);
const item = testCart.items.find((i) => i.id === "test-img");
logStatus(
"Placeholder image set",
item && item.imageurl.includes("placeholder"),
"invalid-results"
);
}
function testStringId() {
const container = document.getElementById("type-results");
container.innerHTML = "";
testCart.clear();
const result = testCart.addItem({
id: "123",
name: "Test",
price: 10.0,
});
logStatus("String ID accepted", result.success, "type-results");
const found = testCart.items.find(
(i) => String(i.id) === String("123")
);
logStatus("String ID comparison works", !!found, "type-results");
}
function testNumberId() {
const container = document.getElementById("type-results");
testCart.clear();
const result = testCart.addItem({ id: 456, name: "Test", price: 10.0 });
logStatus("Number ID accepted", result.success, "type-results");
const found = testCart.items.find((i) => String(i.id) === String(456));
logStatus("Number ID comparison works", !!found, "type-results");
}
function testMixedIds() {
const container = document.getElementById("type-results");
testCart.clear();
testCart.addItem({ id: "789", name: "String ID", price: 10.0 });
testCart.addItem({ id: 789, name: "Number ID", price: 20.0 });
const stringItem = testCart.items.find((i) => String(i.id) === "789");
const numberItem = testCart.items.find(
(i) => String(i.id) === String(789)
);
logStatus(
"Mixed IDs both findable",
stringItem && numberItem,
"type-results"
);
logStatus(
"Treated as same ID (merged)",
testCart.items.length === 1,
"type-results"
);
}
function testZeroQuantity() {
const container = document.getElementById("quantity-results");
container.innerHTML = "";
testCart.clear();
const result = testCart.addItem(
{ id: "q1", name: "Test", price: 10.0 },
0
);
const item = testCart.items.find((i) => i.id === "q1");
logStatus(
"Zero quantity (enforced to 1)",
item && item.quantity === 1,
"quantity-results"
);
}
function testNegativeQuantity() {
const container = document.getElementById("quantity-results");
testCart.clear();
const result = testCart.addItem(
{ id: "q2", name: "Test", price: 10.0 },
-5
);
const item = testCart.items.find((i) => i.id === "q2");
logStatus(
"Negative quantity (enforced to 1)",
item && item.quantity === 1,
"quantity-results"
);
}
function testMaxQuantity() {
const container = document.getElementById("quantity-results");
testCart.clear();
testCart.addItem({ id: "q3", name: "Test", price: 10.0 }, 500);
testCart.addItem({ id: "q3", name: "Test", price: 10.0 }, 500);
const item = testCart.items.find((i) => i.id === "q3");
logStatus(
"Max quantity cap (999)",
item && item.quantity === 999,
"quantity-results"
);
}
function testCorruptedData() {
const container = document.getElementById("storage-results");
container.innerHTML = "";
localStorage.setItem("test_cart", "{invalid json}");
const loaded = testCart.load();
logStatus(
"Corrupted data recovery",
loaded && testCart.items.length === 0,
"storage-results"
);
}
function testNonArrayData() {
const container = document.getElementById("storage-results");
localStorage.setItem("test_cart", '{"not":"an array"}');
testCart.load();
logStatus(
"Non-array data (converted to [])",
Array.isArray(testCart.items),
"storage-results"
);
}
function testRecovery() {
const container = document.getElementById("storage-results");
localStorage.setItem(
"test_cart",
'[{"id":"valid","name":"Test","price":10,"quantity":1}]'
);
testCart.load();
logStatus(
"Valid data recovery",
testCart.items.length === 1,
"storage-results"
);
}
function testStringPrice() {
const container = document.getElementById("math-results");
container.innerHTML = "";
testCart.clear();
testCart.addItem({ id: "m1", name: "Test", price: "10.50" });
const total = testCart.getTotal();
logStatus("String price calculation", total === 10.5, "math-results");
}
function testNaNPrice() {
const container = document.getElementById("math-results");
// This should be rejected during add
const result = testCart.addItem({ id: "m2", name: "Test", price: NaN });
logStatus("NaN price rejected", !result.success, "math-results");
}
function testTotalCalculation() {
const container = document.getElementById("math-results");
testCart.clear();
testCart.addItem({ id: "t1", name: "Item 1", price: "10.50" }, 2);
testCart.addItem({ id: "t2", name: "Item 2", price: 5.25 }, 3);
const total = testCart.getTotal();
const expected = 10.5 * 2 + 5.25 * 3;
logStatus(
"Total calculation accurate",
Math.abs(total - expected) < 0.01,
"math-results"
);
log(
`Expected: $${expected.toFixed(2)}, Got: $${total.toFixed(2)}`,
"info",
"math-results"
);
}
function testRapidAdd() {
const container = document.getElementById("rapid-results");
container.innerHTML = "";
testCart.clear();
const start = Date.now();
for (let i = 0; i < 10; i++) {
testCart.addItem({ id: `rapid${i}`, name: `Item ${i}`, price: 10.0 });
}
const duration = Date.now() - start;
logStatus(
"Rapid 10x add operations",
testCart.items.length === 10,
"rapid-results"
);
log(`Completed in ${duration}ms`, "info", "rapid-results");
}
function testRapidRemove() {
const container = document.getElementById("rapid-results");
testCart.clear();
for (let i = 0; i < 5; i++) {
testCart.addItem({ id: `rem${i}`, name: `Item ${i}`, price: 10.0 });
}
const start = Date.now();
for (let i = 0; i < 5; i++) {
testCart.removeItem(`rem${i}`);
}
const duration = Date.now() - start;
logStatus(
"Rapid 5x remove operations",
testCart.items.length === 0,
"rapid-results"
);
log(`Completed in ${duration}ms`, "info", "rapid-results");
}
function testSimultaneous() {
const container = document.getElementById("rapid-results");
testCart.clear();
// Simulate simultaneous operations
Promise.all([
Promise.resolve(
testCart.addItem({ id: "s1", name: "Item 1", price: 10.0 })
),
Promise.resolve(
testCart.addItem({ id: "s2", name: "Item 2", price: 20.0 })
),
Promise.resolve(
testCart.addItem({ id: "s3", name: "Item 3", price: 30.0 })
),
]).then(() => {
logStatus(
"Simultaneous operations",
testCart.items.length === 3,
"rapid-results"
);
});
}
function displayCartState() {
const container = document.getElementById("cart-state");
container.innerHTML = "";
testCart.load();
log(`Total Items: ${testCart.items.length}`, "info", "cart-state");
log(
`Total Value: $${testCart.getTotal().toFixed(2)}`,
"info",
"cart-state"
);
if (testCart.items.length > 0) {
testCart.items.forEach((item) => {
log(
`${item.name} (ID: ${item.id}) - $${item.price} x ${
item.quantity
} = $${(item.price * item.quantity).toFixed(2)}`,
"info",
"cart-state"
);
});
} else {
log("Cart is empty", "info", "cart-state");
}
}
function clearCart() {
testCart.clear();
displayCartState();
}
// Auto-display cart on load
displayCartState();
</script>
</body>
</html>