costco-grocery-list/backend/public/test-definitions.js

827 lines
25 KiB
JavaScript

// Test definitions - 108 tests across 14 categories
const tests = [
{
category: "Authentication",
tests: [
{
name: "Login with valid credentials",
method: "POST",
endpoint: "/auth/login",
auth: false,
body: () => ({ username: document.getElementById('username').value, password: document.getElementById('password').value }),
expect: (res) => res.token && res.role,
expectedFields: ['token', 'username', 'role'],
onSuccess: (res) => { authToken = res.token; }
},
{
name: "Login with invalid credentials",
method: "POST",
endpoint: "/auth/login",
auth: false,
body: { username: "wronguser", password: "wrongpass" },
expectFail: true,
expect: (res, status) => status === 401,
expectedFields: ['message']
},
{
name: "Access protected route without token",
method: "GET",
endpoint: "/households",
auth: false,
expectFail: true,
expect: (res, status) => status === 401
}
]
},
{
category: "Households",
tests: [
{
name: "Get user's households",
method: "GET",
endpoint: "/households",
auth: true,
expect: (res) => Array.isArray(res),
onSuccess: (res) => { if (res.length > 0) householdId = res[0].id; }
},
{
name: "Create new household",
method: "POST",
endpoint: "/households",
auth: true,
body: { name: `Test Household ${Date.now()}` },
expect: (res) => res.household && res.household.invite_code,
expectedFields: ['message', 'household', 'household.id', 'household.name', 'household.invite_code']
},
{
name: "Get household details",
method: "GET",
endpoint: () => `/households/${householdId}`,
auth: true,
skip: () => !householdId,
expect: (res) => res.id === householdId,
expectedFields: ['id', 'name', 'invite_code', 'created_at']
},
{
name: "Update household name",
method: "PATCH",
endpoint: () => `/households/${householdId}`,
auth: true,
skip: () => !householdId,
body: { name: `Updated Household ${Date.now()}` },
expect: (res) => res.household,
expectedFields: ['message', 'household', 'household.id', 'household.name']
},
{
name: "Refresh invite code",
method: "POST",
endpoint: () => `/households/${householdId}/invite/refresh`,
auth: true,
skip: () => !householdId,
expect: (res) => res.household && res.household.invite_code,
expectedFields: ['message', 'household', 'household.invite_code']
},
{
name: "Join household with invalid code",
method: "POST",
endpoint: "/households/join/INVALID123",
auth: true,
expectFail: true,
expect: (res, status) => status === 404
},
{
name: "Create household with empty name (validation)",
method: "POST",
endpoint: "/households",
auth: true,
body: { name: "" },
expectFail: true,
expect: (res, status) => status === 400,
expectedFields: ['error']
}
]
},
{
category: "Members",
tests: [
{
name: "Get household members",
method: "GET",
endpoint: () => `/households/${householdId}/members`,
auth: true,
skip: () => !householdId,
expect: (res) => Array.isArray(res) && res.length > 0,
onSuccess: (res) => { testUserId = res[0].user_id; }
},
{
name: "Update member role (non-admin attempting)",
method: "PATCH",
endpoint: () => `/households/${householdId}/members/${testUserId}/role`,
auth: true,
skip: () => !householdId || !testUserId,
body: { role: "user" },
expectFail: true,
expect: (res, status) => status === 400 || status === 403
}
]
},
{
category: "Stores",
tests: [
{
name: "Get all stores catalog",
method: "GET",
endpoint: "/stores",
auth: true,
expect: (res) => Array.isArray(res),
onSuccess: (res) => { if (res.length > 0) storeId = res[0].id; }
},
{
name: "Get household stores",
method: "GET",
endpoint: () => `/stores/household/${householdId}`,
auth: true,
skip: () => !householdId,
expect: (res) => Array.isArray(res)
},
{
name: "Add store to household",
method: "POST",
endpoint: () => `/stores/household/${householdId}`,
auth: true,
skip: () => !householdId || !storeId,
body: () => ({ storeId: storeId, isDefault: true }),
expect: (res) => res.store,
expectedFields: ['message', 'store', 'store.id', 'store.name']
},
{
name: "Set default store",
method: "PATCH",
endpoint: () => `/stores/household/${householdId}/${storeId}/default`,
auth: true,
skip: () => !householdId || !storeId,
expect: (res) => res.message
},
{
name: "Add invalid store to household",
method: "POST",
endpoint: () => `/stores/household/${householdId}`,
auth: true,
skip: () => !householdId,
body: { storeId: 99999 },
expectFail: true,
expect: (res, status) => status === 404
}
]
},
{
category: "Advanced Household Tests",
tests: [
{
name: "Create household for complex workflows",
method: "POST",
endpoint: "/households",
auth: true,
body: { name: `Workflow Test ${Date.now()}` },
expect: (res) => res.household && res.household.id,
onSuccess: (res) => {
createdHouseholdId = res.household.id;
inviteCode = res.household.invite_code;
}
},
{
name: "Verify invite code format (7 chars)",
method: "GET",
endpoint: () => `/households/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId,
expect: (res) => res.invite_code && res.invite_code.length === 7 && res.invite_code.startsWith('H')
},
{
name: "Get household with no stores added yet",
method: "GET",
endpoint: () => `/stores/household/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId,
expect: (res) => Array.isArray(res) && res.length === 0
},
{
name: "Update household with very long name (validation)",
method: "PATCH",
endpoint: () => `/households/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId,
body: { name: "A".repeat(101) },
expectFail: true,
expect: (res, status) => status === 400
},
{
name: "Refresh invite code changes value",
method: "POST",
endpoint: () => `/households/${createdHouseholdId}/invite/refresh`,
auth: true,
skip: () => !createdHouseholdId || !inviteCode,
expect: (res) => res.household && res.household.invite_code !== inviteCode,
onSuccess: (res) => { inviteCode = res.household.invite_code; }
},
{
name: "Join same household twice (idempotent)",
method: "POST",
endpoint: () => `/households/join/${inviteCode}`,
auth: true,
skip: () => !inviteCode,
expect: (res, status) => status === 200 && res.message.includes("already a member")
},
{
name: "Get non-existent household",
method: "GET",
endpoint: "/households/99999",
auth: true,
expectFail: true,
expect: (res, status) => status === 404
},
{
name: "Update non-existent household",
method: "PATCH",
endpoint: "/households/99999",
auth: true,
body: { name: "Test" },
expectFail: true,
expect: (res, status) => status === 403 || status === 404
}
]
},
{
category: "Member Management Edge Cases",
tests: [
{
name: "Get members for created household",
method: "GET",
endpoint: () => `/households/${createdHouseholdId}/members`,
auth: true,
skip: () => !createdHouseholdId,
expect: (res) => Array.isArray(res) && res.length >= 1 && res[0].role === 'admin'
},
{
name: "Update own role (should fail)",
method: "PATCH",
endpoint: () => `/households/${createdHouseholdId}/members/${testUserId}/role`,
auth: true,
skip: () => !createdHouseholdId || !testUserId,
body: { role: "user" },
expectFail: true,
expect: (res, status) => status === 400 && res.error && res.error.includes("own role")
},
{
name: "Update role with invalid value",
method: "PATCH",
endpoint: () => `/households/${createdHouseholdId}/members/1/role`,
auth: true,
skip: () => !createdHouseholdId,
body: { role: "superadmin" },
expectFail: true,
expect: (res, status) => status === 400
},
{
name: "Remove non-existent member",
method: "DELETE",
endpoint: () => `/households/${createdHouseholdId}/members/99999`,
auth: true,
skip: () => !createdHouseholdId,
expectFail: true,
expect: (res, status) => status === 404 || status === 500
}
]
},
{
category: "Store Management Advanced",
tests: [
{
name: "Add multiple stores to household",
method: "POST",
endpoint: () => `/stores/household/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId || !storeId,
body: () => ({ storeId: storeId, isDefault: false }),
expect: (res) => res.store
},
{
name: "Add same store twice (duplicate check)",
method: "POST",
endpoint: () => `/stores/household/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId || !storeId,
body: () => ({ storeId: storeId, isDefault: false }),
expectFail: true,
expect: (res, status) => status === 400 || status === 409 || status === 500
},
{
name: "Set default store for household",
method: "PATCH",
endpoint: () => `/stores/household/${createdHouseholdId}/${storeId}/default`,
auth: true,
skip: () => !createdHouseholdId || !storeId,
expect: (res) => res.message
},
{
name: "Verify default store is first in list",
method: "GET",
endpoint: () => `/stores/household/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId || !storeId,
expect: (res) => Array.isArray(res) && res.length > 0 && res[0].is_default === true
},
{
name: "Set non-existent store as default",
method: "PATCH",
endpoint: () => `/stores/household/${createdHouseholdId}/99999/default`,
auth: true,
skip: () => !createdHouseholdId,
expectFail: true,
expect: (res, status) => status === 404 || status === 500
},
{
name: "Remove store from household",
method: "DELETE",
endpoint: () => `/stores/household/${createdHouseholdId}/${storeId}`,
auth: true,
skip: () => !createdHouseholdId || !storeId,
expect: (res) => res.message
},
{
name: "Verify store removed from household",
method: "GET",
endpoint: () => `/stores/household/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId,
expect: (res) => Array.isArray(res) && res.length === 0
}
]
},
{
category: "Data Integrity & Cleanup",
tests: [
{
name: "Create second household for testing",
method: "POST",
endpoint: "/households",
auth: true,
body: { name: `Second Test ${Date.now()}` },
expect: (res) => res.household && res.household.id,
onSuccess: (res) => { secondHouseholdId = res.household.id; }
},
{
name: "Verify user belongs to multiple households",
method: "GET",
endpoint: "/households",
auth: true,
expect: (res) => Array.isArray(res) && res.length >= 3
},
{
name: "Delete created test household",
method: "DELETE",
endpoint: () => `/households/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId,
expect: (res) => res.message
},
{
name: "Verify deleted household is gone",
method: "GET",
endpoint: () => `/households/${createdHouseholdId}`,
auth: true,
skip: () => !createdHouseholdId,
expectFail: true,
expect: (res, status) => status === 404 || status === 403
},
{
name: "Delete second test household",
method: "DELETE",
endpoint: () => `/households/${secondHouseholdId}`,
auth: true,
skip: () => !secondHouseholdId,
expect: (res) => res.message
},
{
name: "Verify households list updated",
method: "GET",
endpoint: "/households",
auth: true,
expect: (res) => Array.isArray(res)
}
]
},
{
category: "List Operations",
tests: [
{
name: "Get grocery list for household+store",
method: "GET",
endpoint: () => `/households/${householdId}/stores/${storeId}/list`,
auth: true,
skip: () => !householdId || !storeId,
expect: (res) => Array.isArray(res),
expectedFields: ['items']
},
{
name: "Add item to list",
method: "POST",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/add`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test API Item",
quantity: "2 units"
},
expect: (res) => res.item,
expectedFields: ['item', 'item.id', 'item.item_name', 'item.quantity']
},
{
name: "Add duplicate item (should update quantity)",
method: "POST",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/add`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test API Item",
quantity: "3 units"
},
expect: (res) => res.item && res.item.quantity === "3 units"
},
{
name: "Mark item as bought",
method: "PATCH",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test API Item",
bought: true
},
expect: (res) => res.message,
expectedFields: ['message']
},
{
name: "Unmark item (set bought to false)",
method: "PATCH",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test API Item",
bought: false
},
expect: (res) => res.message
},
{
name: "Update item details",
method: "PUT",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test API Item",
quantity: "5 units",
notes: "Updated via API test"
},
expect: (res) => res.item,
expectedFields: ['item', 'item.quantity', 'item.notes']
},
{
name: "Get suggestions based on history",
method: "GET",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/suggestions?query=test`,
auth: true,
skip: () => !householdId || !storeId,
expect: (res) => Array.isArray(res)
},
{
name: "Get recently bought items",
method: "GET",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/recent`,
auth: true,
skip: () => !householdId || !storeId,
expect: (res) => Array.isArray(res)
},
{
name: "Delete item from list",
method: "DELETE",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test API Item"
},
expect: (res) => res.message
},
{
name: "Try to add item with empty name",
method: "POST",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/add`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "",
quantity: "1"
},
expectFail: true,
expect: (res, status) => status === 400
}
]
},
{
category: "Item Classifications",
tests: [
{
name: "Get item classification",
method: "GET",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/classification?item_name=Milk`,
auth: true,
skip: () => !householdId || !storeId,
expect: (res) => res.classification !== undefined,
expectedFields: ['classification']
},
{
name: "Set item classification",
method: "POST",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/classification`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test Classified Item",
classification: "dairy"
},
expect: (res) => res.message || res.classification
},
{
name: "Update item classification",
method: "POST",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/classification`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test Classified Item",
classification: "produce"
},
expect: (res) => res.message || res.classification
},
{
name: "Verify classification persists",
method: "GET",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/classification?item_name=Test Classified Item`,
auth: true,
skip: () => !householdId || !storeId,
expect: (res) => res.classification === "produce"
}
]
},
{
category: "Account Management",
tests: [
{
name: "Get current user profile",
method: "GET",
endpoint: "/users/me",
auth: true,
expect: (res) => res.username,
expectedFields: ['id', 'username', 'name', 'display_name', 'role'],
onSuccess: (res) => { testUserId = res.id; }
},
{
name: "Update display name",
method: "PATCH",
endpoint: "/users/me/display-name",
auth: true,
body: {
display_name: "Test Display Name"
},
expect: (res) => res.message,
expectedFields: ['message']
},
{
name: "Verify display name updated",
method: "GET",
endpoint: "/users/me",
auth: true,
expect: (res) => res.display_name === "Test Display Name"
},
{
name: "Clear display name (set to null)",
method: "PATCH",
endpoint: "/users/me/display-name",
auth: true,
body: {
display_name: null
},
expect: (res) => res.message
},
{
name: "Update password",
method: "PATCH",
endpoint: "/users/me/password",
auth: true,
body: () => ({
currentPassword: document.getElementById('password').value,
newPassword: document.getElementById('password').value
}),
expect: (res) => res.message
},
{
name: "Try to update password with wrong current password",
method: "PATCH",
endpoint: "/users/me/password",
auth: true,
body: {
currentPassword: "wrongpassword",
newPassword: "newpass123"
},
expectFail: true,
expect: (res, status) => status === 401
}
]
},
{
category: "Config Endpoints",
tests: [
{
name: "Get classifications list",
method: "GET",
endpoint: "/config/classifications",
auth: false,
expect: (res) => Array.isArray(res),
expectedFields: ['[0].value', '[0].label', '[0].color']
},
{
name: "Get system config",
method: "GET",
endpoint: "/config",
auth: false,
expect: (res) => res.classifications,
expectedFields: ['classifications']
}
]
},
{
category: "Advanced List Scenarios",
tests: [
{
name: "Add multiple items rapidly",
method: "POST",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/add`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Rapid Test Item 1",
quantity: "1"
},
expect: (res) => res.item
},
{
name: "Add second rapid item",
method: "POST",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/add`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Rapid Test Item 2",
quantity: "1"
},
expect: (res) => res.item
},
{
name: "Verify list contains both items",
method: "GET",
endpoint: () => `/households/${householdId}/stores/${storeId}/list`,
auth: true,
skip: () => !householdId || !storeId,
expect: (res) => res.items && res.items.length >= 2
},
{
name: "Mark both items as bought",
method: "PATCH",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Rapid Test Item 1",
bought: true
},
expect: (res) => res.message
},
{
name: "Mark second item as bought",
method: "PATCH",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Rapid Test Item 2",
bought: true
},
expect: (res) => res.message
},
{
name: "Verify recent items includes bought items",
method: "GET",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/recent`,
auth: true,
skip: () => !householdId || !storeId,
expect: (res) => Array.isArray(res) && res.length > 0
},
{
name: "Delete first rapid test item",
method: "DELETE",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Rapid Test Item 1"
},
expect: (res) => res.message
},
{
name: "Delete second rapid test item",
method: "DELETE",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Rapid Test Item 2"
},
expect: (res) => res.message
}
]
},
{
category: "Edge Cases & Error Handling",
tests: [
{
name: "Access non-existent household",
method: "GET",
endpoint: "/households/99999",
auth: true,
expectFail: true,
expect: (res, status) => status === 403 || status === 404
},
{
name: "Access non-existent store in household",
method: "GET",
endpoint: () => `/households/${householdId}/stores/99999/list`,
auth: true,
skip: () => !householdId,
expectFail: true,
expect: (res, status) => status === 403 || status === 404
},
{
name: "Try to update non-existent item",
method: "PUT",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Non Existent Item 999",
quantity: "1"
},
expectFail: true,
expect: (res, status) => status === 404
},
{
name: "Try to delete non-existent item",
method: "DELETE",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/item`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Non Existent Item 999"
},
expectFail: true,
expect: (res, status) => status === 404
},
{
name: "Invalid classification value",
method: "POST",
endpoint: () => `/households/${householdId}/stores/${storeId}/list/classification`,
auth: true,
skip: () => !householdId || !storeId,
body: {
item_name: "Test Item",
classification: "invalid_category_xyz"
},
expectFail: true,
expect: (res, status) => status === 400
},
{
name: "Empty household name on creation",
method: "POST",
endpoint: "/households",
auth: true,
body: {
name: ""
},
expectFail: true,
expect: (res, status) => status === 400
}
]
}
];