test: add additional search test utilities

This commit is contained in:
courtmanr@gmail.com
2025-03-17 16:53:18 +00:00
parent fa68c80641
commit 67303168bf
4 changed files with 819 additions and 0 deletions
+374
View File
@@ -0,0 +1,374 @@
/**
* Exhaustive Search Test Suite
*
* This test suite verifies ALL possible search patterns, combinations, and edge cases.
* It uses both mock data and real-world data structures to ensure accuracy.
*/
const { getSortedAndFilteredData } = require('./networkUtils');
// Create a more extensive mock data set with varied properties
const generateExhaustiveTestData = () => {
// Base test data similar to the real application
const guests = [
// PRIMARY GUESTS
{
id: '101',
name: 'web-server', // Plain name
type: 'qemu',
status: 'running',
node: 'pve-prod-01',
shared: true,
primaryNode: 'pve-prod-01',
tags: ['web', 'production']
},
{
id: '102',
name: 'database-primary', // Has "primary" in name
type: 'qemu',
status: 'running',
node: 'pve-prod-01',
shared: true,
primaryNode: 'pve-prod-01',
tags: ['db', 'primary', 'production']
},
{
id: '103',
name: 'sprint-server', // Has "pri" as substring
type: 'qemu',
status: 'paused',
node: 'pve-prod-01',
shared: true,
primaryNode: 'pve-prod-01',
tags: ['sprint', 'production']
},
// SECONDARY GUESTS
{
id: '201',
name: 'web-secondary',
type: 'qemu',
status: 'running',
node: 'pve-prod-02',
shared: true,
primaryNode: 'pve-prod-01',
tags: ['web', 'secondary']
},
{
id: '202',
name: 'sec-database', // Has "sec" at start of name
type: 'lxc',
status: 'stopped',
node: 'pve-prod-02',
shared: true,
primaryNode: 'pve-prod-01',
tags: ['db', 'backup']
},
{
id: '203',
name: 'prism-backup', // Has "pri" as substring
type: 'lxc',
status: 'running',
node: 'pve-prod-02',
shared: true,
primaryNode: 'pve-prod-01',
tags: ['backup']
},
// NON-SHARED GUESTS
{
id: '301',
name: 'standalone-pri-app', // Has "pri" in name but is NOT primary
type: 'qemu',
status: 'running',
node: 'pve-dev-01',
shared: false,
tags: ['dev', 'private']
},
{
id: '302',
name: 'security-container', // Has "sec" as substring but is NOT secondary
type: 'lxc',
status: 'stopped',
node: 'pve-dev-01',
shared: false,
tags: ['security', 'dev']
},
// EDGE CASES
{
id: '401',
name: 'p', // Single-letter name matching a role search
type: 'qemu',
status: 'running',
node: 'pve-edge-01',
shared: true,
primaryNode: 'pve-edge-01',
tags: ['test']
},
{
id: '402',
name: 's', // Single-letter name matching a role search
type: 'qemu',
status: 'running',
node: 'pve-edge-01',
shared: true,
primaryNode: 'pve-prod-01',
tags: ['test']
}
];
// Add optional metrics data
const metricsData = {
cpu: {},
memory: {},
disk: {},
network: {}
};
guests.forEach(guest => {
const id = guest.id;
metricsData.cpu[id] = { usage: Math.random() * 100 };
metricsData.memory[id] = { usagePercent: Math.random() * 100 };
metricsData.disk[id] = { usagePercent: Math.random() * 100 };
metricsData.network[id] = { inRate: Math.random() * 10000000, outRate: Math.random() * 10000000 };
});
// Node data for name resolution
const nodeData = [
{ id: 'pve-prod-01', name: 'prod-cluster-1' },
{ id: 'pve-prod-02', name: 'prod-cluster-2' },
{ id: 'pve-dev-01', name: 'dev-cluster' },
{ id: 'pve-edge-01', name: 'edge-node' }
];
return { guests, metricsData, nodeData };
};
// Function to run all exhaustive tests
export function runExhaustiveTests() {
console.log('===== EXHAUSTIVE SEARCH TEST SUITE =====');
console.log('Testing all possible search patterns and edge cases');
const { guests, metricsData, nodeData } = generateExhaustiveTestData();
// Collection of all test cases
const testCases = [
// 1. ROLE BASED SEARCHES - STANDALONE TERMS
// Each of these tests a specific role search term
{ category: 'ROLE STANDALONE', term: 'role', description: 'All shared guests',
expectIdsContaining: ['101', '102', '103', '201', '202', '203', '401', '402'] },
{ category: 'ROLE STANDALONE', term: 'shared', description: 'Alternative for all shared guests',
expectIdsContaining: ['101', '102', '103', '201', '202', '203', '401', '402'] },
{ category: 'ROLE STANDALONE', term: 'primary', description: 'Finds primary guests',
expectIdsContaining: ['101', '102', '103', '401'] },
{ category: 'ROLE STANDALONE', term: 'pri', description: 'Short form of primary',
expectIdsContaining: ['101', '102', '103', '401'] },
{ category: 'ROLE STANDALONE', term: 'p', description: 'Shortest form of primary',
expectIdsContaining: ['101', '102', '103', '401'] },
{ category: 'ROLE STANDALONE', term: 'secondary', description: 'Finds secondary guests',
expectIdsContaining: ['201', '202', '203', '402'] },
{ category: 'ROLE STANDALONE', term: 'sec', description: 'Short form of secondary',
expectIdsContaining: ['201', '202', '203', '402'] },
{ category: 'ROLE STANDALONE', term: 's', description: 'Shortest form of secondary',
expectIdsContaining: ['201', '202', '203', '402'] },
// 2. ROLE COLUMN SEARCHES
{ category: 'ROLE COLUMN', term: 'role:primary', description: 'Column search for primary',
expectIdsContaining: ['101', '102', '103', '401'] },
{ category: 'ROLE COLUMN', term: 'role:pri', description: 'Column search with short primary',
expectIdsContaining: ['101', '102', '103', '401'] },
{ category: 'ROLE COLUMN', term: 'role:p', description: 'Column search with shortest primary',
expectIdsContaining: ['101', '102', '103', '401'] },
{ category: 'ROLE COLUMN', term: 'role:secondary', description: 'Column search for secondary',
expectIdsContaining: ['201', '202', '203', '402'] },
{ category: 'ROLE COLUMN', term: 'role:sec', description: 'Column search with short secondary',
expectIdsContaining: ['201', '202', '203', '402'] },
{ category: 'ROLE COLUMN', term: 'role:s', description: 'Column search with shortest secondary',
expectIdsContaining: ['201', '202', '203', '402'] },
{ category: 'ROLE COLUMN', term: 'role:none', description: 'Column search for non-shared',
expectIdsContaining: ['301', '302'] },
{ category: 'ROLE COLUMN', term: 'role:-', description: 'Column search for non-shared (dash)',
expectIdsContaining: ['301', '302'] },
// 3. TEXT SEARCHES WITH ROLE COMPONENTS
{ category: 'TEXT MATCH', term: 'primary-', description: 'Has primary in name but with a suffix',
expectIdsContaining: ['102'] },
{ category: 'TEXT MATCH', term: 'sprint', description: 'Has pri in middle of name but not as a word',
expectIdsContaining: ['103'] },
{ category: 'TEXT MATCH', term: 'secondary', description: 'Has secondary in name and/or is secondary',
expectIdsContaining: ['201', '202', '203', '402'] },
{ category: 'TEXT MATCH', term: 'prism', description: 'Has pri as substring',
expectIdsContaining: ['203'] },
// 4. PARTIAL SEARCHES AND EDGE CASES
// The 'p' search should match shared guests with primary role AND match 'p' in text
{ category: 'SINGLE CHAR', term: 'p', description: 'Single letter p',
expectIdsContaining: ['101', '102', '103', '201', '202', '203', '301', '302', '401', '402'] },
// Same for 's'
{ category: 'SINGLE CHAR', term: 's', description: 'Single letter s',
expectIdsContaining: ['101', '102', '103', '201', '202', '203', '301', '302', '401', '402'] },
// Test pve prefix in node names
{ category: 'SINGLE CHAR', term: 'pve', description: 'Text in all node IDs',
expectIdsContaining: ['101', '102', '103', '201', '202', '203', '301', '302', '401', '402'] },
// Test a mix of properties and values
{ category: 'TYPE SEARCH', term: 'qemu', description: 'VM type search',
expectIdsContaining: ['101', '102', '103', '201', '301', '401', '402'] },
{ category: 'TYPE SEARCH', term: 'vm', description: 'VM alternate search',
expectIdsContaining: ['101', '102', '103', '201', '301', '401', '402'] },
{ category: 'TYPE SEARCH', term: 'lxc', description: 'Container type search',
expectIdsContaining: ['202', '203', '302'] },
// 5. COMBINED SEARCHES (AND LOGIC)
{ category: 'COMBINED', term: 'primary running', description: 'Primary AND running status',
expectIdsContaining: ['101', '102', '401'] },
{ category: 'COMBINED', term: 'sec stopped', description: 'Secondary AND stopped status',
expectIdsContaining: ['202'] },
// 6. NEGATIVE TESTS - these should NOT match
{ category: 'NEGATIVE', term: 'nonexistent', description: 'Term that appears nowhere',
expectIdsContaining: [] }
];
// Run the tests
console.log(`Running ${testCases.length} comprehensive search test cases\n`);
let passCount = 0;
let failCount = 0;
// Keep track of failures by category
const failures = {};
testCases.forEach((testCase, index) => {
console.log(`[${index + 1}/${testCases.length}] Testing "${testCase.term}" - ${testCase.description}`);
// Run the search
const filteredData = getSortedAndFilteredData(
guests,
{ key: 'name', direction: 'asc' }, // Default sort
{}, // No filters
null, // Show all statuses
[testCase.term], // Single search term as an array
'', // No active search term
metricsData,
'all', // Show all guest types
nodeData
);
// Extract result IDs
const resultIds = filteredData.map(guest => guest.id);
// Validate expected IDs are included
const expectedIds = testCase.expectIdsContaining;
const missingIds = expectedIds.filter(id => !resultIds.includes(id));
const unexpectedIds = resultIds.filter(id => !expectedIds.includes(id));
const passed = missingIds.length === 0 &&
(unexpectedIds.length === 0 || expectedIds.length === 0 && resultIds.length === 0);
if (passed) {
console.log(` ✅ PASSED - Found ${resultIds.length} guests as expected`);
passCount++;
} else {
console.log(` ❌ FAILED`);
if (missingIds.length > 0) {
console.log(` Missing IDs: ${missingIds.join(', ')}`);
}
if (unexpectedIds.length > 0) {
console.log(` Unexpected IDs: ${unexpectedIds.join(', ')}`);
}
// Add to failures by category
const category = testCase.category;
if (!failures[category]) {
failures[category] = [];
}
failures[category].push({
term: testCase.term,
description: testCase.description,
expected: expectedIds,
actual: resultIds,
missing: missingIds,
unexpected: unexpectedIds
});
failCount++;
}
console.log(''); // Empty line for readability
});
// Print summary
console.log('===== TEST SUMMARY =====');
console.log(`Total tests: ${testCases.length}`);
console.log(`Passed: ${passCount} (${(passCount/testCases.length*100).toFixed(1)}%)`);
console.log(`Failed: ${failCount} (${(failCount/testCases.length*100).toFixed(1)}%)`);
// Print failures by category
if (failCount > 0) {
console.log('\n===== FAILURES BY CATEGORY =====');
Object.keys(failures).forEach(category => {
console.log(`\n${category} - ${failures[category].length} failures:`);
failures[category].forEach(failure => {
console.log(` "${failure.term}" - ${failure.description}`);
console.log(` Expected: ${failure.expected.join(', ')}`);
console.log(` Actual: ${failure.actual.join(', ')}`);
});
});
// Provide advice for fixing issues
console.log('\n===== TROUBLESHOOTING =====');
// Check specifically for 'pri' issues
if (failures['ROLE STANDALONE']?.some(f => f.term === 'pri')) {
console.log('\nIssue detected with "pri" searches:');
console.log('1. Check if "pri" is being treated as a special case rather than a role indicator');
console.log('2. Ensure proper handling of "pri" at the beginning of the search logic');
console.log('3. Make sure word boundaries are properly enforced for "pri" matches');
}
// Check for single character issues
if (Object.keys(failures).includes('SINGLE CHAR')) {
console.log('\nIssue detected with single character searches:');
console.log('1. Single character searches should perform a full text search across all fields');
console.log('2. Check that single character logic runs BEFORE role-specific logic');
}
}
return {
totalTests: testCases.length,
passed: passCount,
failed: failCount,
failures
};
}
// Run the tests if executed directly
if (typeof require !== 'undefined' && require.main === module) {
runExhaustiveTests();
}
module.exports = { runExhaustiveTests };
+79
View File
@@ -0,0 +1,79 @@
/**
* Comprehensive Search Verification Script
*
* This script runs a battery of tests to verify that ALL search patterns work correctly.
* It combines both standard tests and special test cases for edge conditions.
*/
import { runSearchTests } from './searchTests.js';
console.log('===============================================================');
console.log('= COMPREHENSIVE SEARCH VERIFICATION =');
console.log('===============================================================');
console.log('This script verifies ALL search patterns work correctly, including:');
console.log('- Basic text searches');
console.log('- Role-specific searches (pri, sec, primary, secondary)');
console.log('- Column-based searches (role:pri, type:vm, etc.)');
console.log('- Single character searches (p, s, v, etc.)');
console.log('- Edge cases and potentially problematic patterns');
console.log('\nRunning standard search test suite...');
// Execute all standard tests
const results = runSearchTests();
// Output summary
console.log('\n===============================================================');
console.log('= VERIFICATION RESULTS =');
console.log('===============================================================');
if (results.general.failed === 0 && results.role.filter(r => !r.passed).length === 0) {
console.log('✅ ALL TESTS PASSED!');
console.log(` - ${results.general.passed} general tests passed`);
console.log(` - ${results.role.filter(r => r.passed).length} role-specific tests passed`);
console.log('\nThe search functionality is working correctly for all patterns.');
console.log('Key validations:');
console.log(' - "pri" correctly returns primary guests');
console.log(' - "sec" correctly returns secondary guests');
console.log(' - Single-character searches work properly');
console.log(' - Combined searches apply AND logic correctly');
} else {
console.log('❌ SOME TESTS FAILED!');
console.log(` - ${results.general.failed} general tests failed`);
console.log(` - ${results.role.filter(r => !r.passed).length} role-specific tests failed`);
// Show detailed failures
console.log('\nFailed tests:');
if (results.general.failed > 0) {
results.general.details
.filter(detail => !detail.passed)
.forEach(detail => {
console.log(` - ${detail.name}: ${detail.description}`);
console.log(` Expected: ${JSON.stringify(detail.expectedIds)}`);
console.log(` Actual: ${JSON.stringify(detail.actualIds)}`);
});
}
const failedRoleTests = results.role.filter(r => !r.passed);
if (failedRoleTests.length > 0) {
console.log('\nFailed role-specific tests:');
failedRoleTests.forEach(r => {
console.log(` - "${r.term}": Expected ${JSON.stringify(r.expectedIds)}, got ${JSON.stringify(r.resultIds)}`);
});
}
}
// Execution instructions
console.log('\n===============================================================');
console.log('= HOW TO USE THIS VERIFICATION TOOL =');
console.log('===============================================================');
console.log('Run this script after making any changes to the search functionality:');
console.log(' node frontend/src/utils/VerifyAllSearches.js');
console.log('\nIf you add new search capabilities, update searchTests.js to include tests');
console.log('for the new functionality.');
// Exit with appropriate code
if (results.general.failed === 0 && results.role.filter(r => !r.passed).length === 0) {
process.exit(0);
} else {
process.exit(1);
}
+173
View File
@@ -0,0 +1,173 @@
// Debug script for testing 'pri' search issue
// Mock implementation of matchesTerm function for testing
function matchesTerm(guest, termLower, nodeData) {
// Prevent operations on undefined/null terms
if (!termLower) return true;
// CASE 3: Standard role terminology
// Matches exact role terms (not as part of other words) using standard terminology
if (termLower === 'shared' || termLower === 'role') {
return !!guest.shared;
}
// Primary role terms: match whole words only, requires shared=true and isPrimary=true
if (termLower === 'primary' || termLower === 'pri') {
if (!guest.shared) return false;
console.log(`Checking primary for ${guest.id}: primaryNode=${guest.primaryNode}, node=${guest.node}, matches=${guest.primaryNode === guest.node}`);
return guest.primaryNode === guest.node;
}
// Secondary role terms: match whole words only, requires shared=true and isPrimary=false
if (termLower === 'secondary' || termLower === 'sec') {
if (!guest.shared) return false;
return guest.primaryNode !== guest.node;
}
// CASE 5: Single character searches (including role abbreviations)
if (termLower.length === 1) {
const searchText = getFullSearchableText(guest, nodeData);
console.log(`Single-char search for ${termLower} in ${guest.id}: "${searchText}"`);
return searchText.includes(termLower);
}
// Default: search in all text fields
const searchText = getFullSearchableText(guest, nodeData);
console.log(`Full search for ${termLower} in ${guest.id}: "${searchText}"`);
return searchText.includes(termLower);
}
// Improved function to get complete searchable text for a guest
function getFullSearchableText(guest, nodeData) {
// Include ALL searchable properties
const nodeName = guest.node || '';
// Build full searchable text by concatenating ALL searchable fields
const searchableFields = [
guest.name || '',
guest.id || '',
guest.status || '',
// Add VM/CT descriptive terms
guest.type === 'qemu' ? 'vm virtual machine' : 'ct container',
nodeName,
// Add role descriptive terms if shared
guest.shared ? (guest.primaryNode === guest.node ? 'primary pri p' : 'secondary sec s') : '',
// Add shared indicator if applicable
guest.shared ? 'shared role' : 'none',
// Any other custom properties that should be searchable
guest.description || '',
guest.tags || ''
];
// Join all fields with spaces and convert to lowercase
const fullSearchText = searchableFields.join(' ').toLowerCase();
return fullSearchText;
}
// Mock test data - simplified version of actual app data
const testGuests = [
{
id: '101',
name: 'web-server',
status: 'running',
type: 'qemu',
node: 'pve-prod-01',
shared: true,
primaryNode: 'pve-prod-01' // This is primary on this node
},
{
id: '102',
name: 'database',
status: 'running',
type: 'qemu',
node: 'pve-prod-01',
shared: true,
primaryNode: 'pve-prod-01' // This is primary on this node
},
{
id: '201',
name: 'cache-server',
status: 'stopped',
type: 'qemu',
node: 'pve-prod-02',
shared: true,
primaryNode: 'pve-prod-01' // This is secondary on this node
}
];
// Mock node data
const nodeData = [
{ id: 'pve-prod-01', name: 'Production Node 1' },
{ id: 'pve-prod-02', name: 'Production Node 2' }
];
// Test specific search terms directly
function testSearch(searchTerm) {
console.log(`\n===== TESTING SEARCH TERM: "${searchTerm}" =====`);
const result = testGuests.filter(guest => {
const matches = matchesTerm(guest, searchTerm.toLowerCase(), nodeData);
console.log(` Guest ${guest.id} (${guest.name}) matches ${searchTerm}? ${matches ? 'YES' : 'NO'}`);
return matches;
});
console.log(`\nResults for "${searchTerm}":`);
console.log(` Found ${result.length} guests:`);
if (result.length === 0) {
console.log(" NO RESULTS FOUND!");
} else {
result.forEach(guest => {
console.log(` - ${guest.id}: ${guest.name} (${guest.node}, shared=${guest.shared}, primaryNode=${guest.primaryNode})`);
console.log(` Primary? ${guest.primaryNode === guest.node ? 'YES' : 'NO'}`);
});
}
// Print expected vs actual for primaries
const expectedPrimaries = testGuests.filter(g => g.shared && g.primaryNode === g.node);
console.log(`\nExpected primaries: ${expectedPrimaries.length} guests`);
expectedPrimaries.forEach(g => console.log(` - ${g.id}: ${g.name}`));
// Verify if all primaries were found
const allPrimariesFound = expectedPrimaries.every(
expected => result.some(res => res.id === expected.id)
);
console.log(`\nAll primaries found? ${allPrimariesFound ? 'YES ✅' : 'NO ❌'}`);
if (!allPrimariesFound) {
console.log("Missing primaries:");
expectedPrimaries.forEach(expected => {
if (!result.some(res => res.id === expected.id)) {
console.log(` - ${expected.id}: ${expected.name}`);
}
});
}
}
// Test various forms of the primary search
console.log("\n********** DEBUGGING 'PRI' SEARCH ISSUE **********");
testSearch('pri');
testSearch('primary');
testSearch('p'); // Single character test
// Test role-prefixed searches to check if they work differently
console.log("\n********** TESTING PREFIXED SEARCHES **********");
testSearch('role:pri');
testSearch('role:primary');
console.log("\n\n********** SEARCH IMPLEMENTATION DETAILS **********");
// Print the relevant implementation from networkUtils.js
console.log(`
Search logic for 'pri' is implemented in networkUtils.js:
// Standard role terminology
if (termLower === 'primary' || termLower === 'pri') {
if (!guest.shared) return false;
return guest.primaryNode === guest.node;
}
and in getFullSearchableText:
guest.shared ? (guest.primaryNode === guest.node ? 'primary pri p' : 'secondary sec s') : '',
`);
+193
View File
@@ -0,0 +1,193 @@
// Standalone test for 'pri' search
// Simple script to test that 'pri' search works properly
// Mock implementation of required functions
function matchesTerm(guest, termLower, nodeData) {
// Prevent operations on undefined/null terms
if (!termLower) return true;
// Handle column-specific searches
if (termLower.includes(':')) {
const [prefix, value] = termLower.split(':', 2);
if (prefix.trim().toLowerCase() === 'role') {
const roleValue = (value || '').trim().toLowerCase();
// Non-shared checks
if (roleValue === '-' || roleValue === 'none') {
return !guest.shared;
}
// Need to be shared for other role searches
if (!guest.shared) return false;
const isPrimary = guest.primaryNode === guest.node;
// Primary checks
if (roleValue === 'p' || roleValue.startsWith('pri') || roleValue === 'primary') {
console.log(`Column search ${termLower} for ${guest.id}: isPrimary=${isPrimary}`);
return isPrimary;
}
// Secondary checks
if (roleValue === 's' || roleValue.startsWith('sec') || roleValue === 'secondary') {
return !isPrimary;
}
return false;
}
// Unknown column, just do text search
return getFullSearchableText(guest).includes(termLower);
}
// Standard role terminology
if (termLower === 'primary' || termLower === 'pri') {
console.log(`Standard term "${termLower}" check for ${guest.id}: shared=${guest.shared}, isPrimary=${guest.primaryNode === guest.node}`);
if (!guest.shared) return false;
return guest.primaryNode === guest.node;
}
// Secondary role terms
if (termLower === 'secondary' || termLower === 'sec') {
if (!guest.shared) return false;
return guest.primaryNode !== guest.node;
}
// Single letter searches
if (termLower.length === 1) {
const searchText = getFullSearchableText(guest);
console.log(`Single char search "${termLower}" for ${guest.id} in: "${searchText}"`);
return searchText.includes(termLower);
}
// Default text search
return getFullSearchableText(guest).includes(termLower);
}
function getFullSearchableText(guest) {
// Build full searchable text
const searchableFields = [
guest.name || '',
guest.id || '',
guest.status || '',
guest.type === 'qemu' ? 'vm virtual machine' : 'ct container',
guest.node || '',
guest.shared ? (guest.primaryNode === guest.node ? 'primary pri p' : 'secondary sec s') : '',
guest.shared ? 'shared role' : 'none'
];
return searchableFields.join(' ').toLowerCase();
}
// Mock guests
const guests = [
{
id: '101',
name: 'web-server-primary',
node: 'node1',
shared: true,
primaryNode: 'node1', // Primary role on current node
status: 'running',
type: 'qemu'
},
{
id: '102',
name: 'db-primary',
node: 'node1',
shared: true,
primaryNode: 'node1', // Primary role on current node
status: 'running',
type: 'qemu'
},
{
id: '201',
name: 'web-server-secondary',
node: 'node2',
shared: true,
primaryNode: 'node1', // Secondary role on current node (node2)
status: 'running',
type: 'qemu'
},
{
id: '202',
name: 'db-secondary',
node: 'node2',
shared: true,
primaryNode: 'node1', // Secondary role on current node (node2)
status: 'running',
type: 'qemu'
},
{
id: '301',
name: 'standalone-app',
node: 'node3',
shared: false, // Not shared
status: 'running',
type: 'qemu'
},
{
id: '302',
name: 'prince-app', // Has "pri" in the name, but not primary
node: 'node3',
shared: false,
status: 'running',
type: 'qemu'
}
];
// Run tests for various search terms
function runTest(searchTerm) {
console.log(`\n===== TESTING SEARCH FOR: "${searchTerm}" =====`);
const results = guests.filter(guest => {
const matches = matchesTerm(guest, searchTerm.toLowerCase(), []);
const searchableText = getFullSearchableText(guest);
console.log(`Guest ${guest.id} (${guest.name}): ${matches ? 'MATCH' : 'NO MATCH'}`);
console.log(` Shared: ${guest.shared}, Primary node: ${guest.primaryNode}, Current node: ${guest.node}`);
console.log(` Is Primary on this node? ${guest.shared && guest.primaryNode === guest.node}`);
console.log(` Searchable text: "${searchableText}"`);
console.log(` Contains 'pri'? ${searchableText.includes('pri')}`);
console.log(` Contains 'primary'? ${searchableText.includes('primary')}`);
return matches;
});
console.log(`\nRESULTS FOR "${searchTerm}":`);
console.log(` Found ${results.length} matches:`);
if (results.length > 0) {
results.forEach(r => console.log(` - ${r.id}: ${r.name}`));
} else {
console.log(' NO MATCHES');
}
// Check expectation - primary search should find primary nodes
const expectedPrimaries = guests.filter(g => g.shared && g.primaryNode === g.node);
if (searchTerm === 'pri' || searchTerm === 'primary' || searchTerm === 'role:pri' || searchTerm === 'role:primary') {
const allPrimariesFound = expectedPrimaries.every(p => results.some(r => r.id === p.id));
const onlyPrimariesFound = results.every(r => expectedPrimaries.some(p => p.id === r.id));
console.log(`\nTEST RESULTS:`);
console.log(` Expected ${expectedPrimaries.length} primaries: ${expectedPrimaries.map(p => p.id).join(', ')}`);
console.log(` All primary nodes found? ${allPrimariesFound ? 'YES ✅' : 'NO ❌'}`);
console.log(` Only primary nodes found? ${onlyPrimariesFound ? 'YES ✅' : 'NO ❌'}`);
if (!allPrimariesFound || !onlyPrimariesFound) {
console.log(`\n❌ TEST FAILED`);
} else {
console.log(`\n✅ TEST PASSED`);
}
}
}
console.log("🔍 TESTING 'PRI' SEARCH FUNCTIONALITY");
console.log("===================================");
runTest('pri');
runTest('primary');
runTest('role:pri');
runTest('role:primary');
runTest('p'); // Should match primary guests and others containing 'p'
runTest('prince'); // Should match guest 302 ("prince-app")