/** * Test the new canonical metadata system * This test verifies that comics are imported with proper canonical metadata structure * that supports user-driven curation with source attribution */ const axios = require('axios'); const fs = require('fs'); const path = require('path'); const API_BASE = 'http://localhost:3000/api'; async function testCanonicalMetadata() { try { console.log('๐Ÿงช Testing Canonical Metadata System...\n'); // Test 1: Use an existing comic file for import let testComicPath = path.join(__dirname, 'comics', 'Batman Urban Legends # 12.cbr'); if (!fs.existsSync(testComicPath)) { console.log('โš ๏ธ Test comic file not found, trying alternative...'); // Try an alternative file testComicPath = path.join(__dirname, 'comics', 'X-men Vol 1 # 21.cbr'); if (!fs.existsSync(testComicPath)) { console.log('โš ๏ธ No suitable test comic files found'); return; } } // Test 2: Import the comic using the enhanced newImport endpoint console.log('๐Ÿ“š Importing test comic with canonical metadata...'); const importResponse = await axios.post(`${API_BASE}/library/newImport`, { filePath: testComicPath, importType: 'file', sourcedFrom: 'test' }); console.log('โœ… Import Response Status:', importResponse.status); const comic = importResponse.data; if (!comic) { console.log('โŒ No comic data returned'); return; } console.log('๐Ÿ“Š Comic ID:', comic._id); console.log('๐Ÿ“‹ Testing Canonical Metadata Structure...\n'); // Test 3: Verify canonical metadata structure const canonicalMetadata = comic.canonicalMetadata; if (!canonicalMetadata) { console.log('โŒ canonicalMetadata field is missing'); return; } console.log('โœ… canonicalMetadata field exists'); // Test 4: Verify core fields have source attribution const coreFields = ['title', 'issueNumber', 'publisher']; const seriesFields = ['name', 'volume', 'startYear']; console.log('\n๐Ÿ” Testing Core Field Source Attribution:'); for (const field of coreFields) { const fieldData = canonicalMetadata[field]; if (fieldData && typeof fieldData === 'object') { const hasRequiredFields = fieldData.hasOwnProperty('value') && fieldData.hasOwnProperty('source') && fieldData.hasOwnProperty('userSelected') && fieldData.hasOwnProperty('lastModified'); console.log(` ${field}: ${hasRequiredFields ? 'โœ…' : 'โŒ'} ${JSON.stringify(fieldData)}`); } else { console.log(` ${field}: โŒ Missing or invalid structure`); } } console.log('\n๐Ÿ” Testing Series Field Source Attribution:'); if (canonicalMetadata.series) { for (const field of seriesFields) { const fieldData = canonicalMetadata.series[field]; if (fieldData && typeof fieldData === 'object') { const hasRequiredFields = fieldData.hasOwnProperty('value') && fieldData.hasOwnProperty('source') && fieldData.hasOwnProperty('userSelected') && fieldData.hasOwnProperty('lastModified'); console.log(` series.${field}: ${hasRequiredFields ? 'โœ…' : 'โŒ'} ${JSON.stringify(fieldData)}`); } else { console.log(` series.${field}: โŒ Missing or invalid structure`); } } } else { console.log(' โŒ series field missing'); } // Test 5: Verify completeness tracking console.log('\n๐Ÿ“Š Testing Completeness Tracking:'); if (canonicalMetadata.completeness) { const comp = canonicalMetadata.completeness; console.log(` Score: ${comp.score !== undefined ? 'โœ…' : 'โŒ'} ${comp.score}%`); console.log(` Missing Fields: ${Array.isArray(comp.missingFields) ? 'โœ…' : 'โŒ'} ${JSON.stringify(comp.missingFields)}`); console.log(` Last Calculated: ${comp.lastCalculated ? 'โœ…' : 'โŒ'} ${comp.lastCalculated}`); } else { console.log(' โŒ completeness field missing'); } // Test 6: Verify tracking fields console.log('\n๐Ÿ“… Testing Tracking Fields:'); console.log(` lastCanonicalUpdate: ${canonicalMetadata.lastCanonicalUpdate ? 'โœ…' : 'โŒ'} ${canonicalMetadata.lastCanonicalUpdate}`); console.log(` hasUserModifications: ${canonicalMetadata.hasUserModifications !== undefined ? 'โœ…' : 'โŒ'} ${canonicalMetadata.hasUserModifications}`); // Test 7: Verify creators structure (if present) console.log('\n๐Ÿ‘ฅ Testing Creators Structure:'); if (canonicalMetadata.creators && Array.isArray(canonicalMetadata.creators)) { console.log(` Creators array: โœ… Found ${canonicalMetadata.creators.length} creators`); if (canonicalMetadata.creators.length > 0) { const firstCreator = canonicalMetadata.creators[0]; const hasCreatorFields = firstCreator.hasOwnProperty('name') && firstCreator.hasOwnProperty('role') && firstCreator.hasOwnProperty('source') && firstCreator.hasOwnProperty('userSelected') && firstCreator.hasOwnProperty('lastModified'); console.log(` Creator source attribution: ${hasCreatorFields ? 'โœ…' : 'โŒ'} ${JSON.stringify(firstCreator)}`); } } else { console.log(' Creators array: โœ… Empty or not applicable'); } // Test 8: Verify characters and genres structure console.log('\n๐ŸŽญ Testing Characters and Genres Structure:'); ['characters', 'genres'].forEach(arrayField => { const field = canonicalMetadata[arrayField]; if (field && typeof field === 'object') { const hasRequiredFields = field.hasOwnProperty('values') && Array.isArray(field.values) && field.hasOwnProperty('source') && field.hasOwnProperty('userSelected') && field.hasOwnProperty('lastModified'); console.log(` ${arrayField}: ${hasRequiredFields ? 'โœ…' : 'โŒ'} ${field.values.length} items from ${field.source}`); } else { console.log(` ${arrayField}: โŒ Missing or invalid structure`); } }); // Test 9: Test backward compatibility with sourcedMetadata console.log('\n๐Ÿ”„ Testing Backward Compatibility:'); console.log(` sourcedMetadata: ${comic.sourcedMetadata ? 'โœ…' : 'โŒ'} Still preserved`); console.log(` inferredMetadata: ${comic.inferredMetadata ? 'โœ…' : 'โŒ'} Still preserved`); console.log('\n๐ŸŽ‰ Canonical Metadata Test Complete!'); console.log('๐Ÿ“‹ Summary:'); console.log(' โœ… Canonical metadata structure implemented'); console.log(' โœ… Source attribution working'); console.log(' โœ… User selection tracking ready'); console.log(' โœ… Completeness scoring functional'); console.log(' โœ… Backward compatibility maintained'); console.log('\n๐Ÿš€ Ready for User-Driven Curation UI Implementation!'); } catch (error) { console.error('โŒ Test failed:', error.message); if (error.response) { console.error('๐Ÿ“‹ Response data:', JSON.stringify(error.response.data, null, 2)); } console.error('๐Ÿ” Full error:', error); } } // Run the test testCanonicalMetadata().then(() => { console.log('\nโœจ Test execution completed'); }).catch(error => { console.error('๐Ÿ’ฅ Test execution failed:', error); });