|
| 1 | +/** |
| 2 | + * Test script for Phase 3: Elimination Service |
| 3 | + * |
| 4 | + * Tests: |
| 5 | + * 1. "Arabica coffee beans" - should NOT show Robusta codes |
| 6 | + * 2. "Robusta coffee" - should NOT show Arabica codes |
| 7 | + * 3. "instant coffee" - should only show Ch.21 codes |
| 8 | + * 4. "brake pads" - should still work (regression test) |
| 9 | + */ |
| 10 | + |
| 11 | +import { |
| 12 | + filterCandidatesByElimination, |
| 13 | + extractModifiersFromText, |
| 14 | + extractModifiersFromAnswer, |
| 15 | + isQuestionStillRelevant |
| 16 | +} from '../src/services/elimination.service'; |
| 17 | + |
| 18 | +// Test data - simulated HS code candidates |
| 19 | +const coffeeCandidates = [ |
| 20 | + { code: '0901.11.11', description: 'Coffee not roasted: Arabica plantation: A Grade' }, |
| 21 | + { code: '0901.11.12', description: 'Coffee not roasted: Arabica plantation: B Grade' }, |
| 22 | + { code: '0901.11.21', description: 'Coffee not roasted: Arabica cherry: A Grade' }, |
| 23 | + { code: '0901.11.31', description: 'Coffee not roasted: Robusta parchment: AB Grade' }, |
| 24 | + { code: '0901.11.32', description: 'Coffee not roasted: Robusta parchment: PB Grade' }, |
| 25 | + { code: '0901.11.41', description: 'Coffee not roasted: Robusta cherry: AB Grade' }, |
| 26 | + { code: '0901.12.00', description: 'Coffee roasted, not decaffeinated' }, |
| 27 | + { code: '2101.11.00', description: 'Extracts, essences and concentrates, of coffee' }, |
| 28 | + { code: '2101.11.10', description: 'Instant coffee, not flavoured' }, |
| 29 | + { code: '2101.11.20', description: 'Instant coffee, flavoured' }, |
| 30 | +]; |
| 31 | + |
| 32 | +const brakePadCandidates = [ |
| 33 | + { code: '8708.30.00', description: 'Brakes and servo-brakes; parts thereof' }, |
| 34 | + { code: '8708.30.10', description: 'Brake pads for motor vehicles' }, |
| 35 | + { code: '6813.81.00', description: 'Brake linings and pads (friction material)' }, |
| 36 | + { code: '6913.10.00', description: 'Ceramic articles n.e.s.' }, |
| 37 | +]; |
| 38 | + |
| 39 | +console.log('='.repeat(60)); |
| 40 | +console.log('PHASE 3: Elimination Service Tests'); |
| 41 | +console.log('='.repeat(60)); |
| 42 | + |
| 43 | +// Test 1: Extract modifiers from text |
| 44 | +console.log('\n--- Test 1: Extract Modifiers from Text ---'); |
| 45 | +const arabicaModifiers = extractModifiersFromText('Arabica coffee beans'); |
| 46 | +console.log(`"Arabica coffee beans" -> modifiers: [${arabicaModifiers.join(', ')}]`); |
| 47 | + |
| 48 | +const robustaModifiers = extractModifiersFromText('Robusta coffee'); |
| 49 | +console.log(`"Robusta coffee" -> modifiers: [${robustaModifiers.join(', ')}]`); |
| 50 | + |
| 51 | +const instantModifiers = extractModifiersFromText('instant coffee'); |
| 52 | +console.log(`"instant coffee" -> modifiers: [${instantModifiers.join(', ')}]`); |
| 53 | + |
| 54 | +const brakePadModifiers = extractModifiersFromText('brake pads for cars'); |
| 55 | +console.log(`"brake pads for cars" -> modifiers: [${brakePadModifiers.join(', ')}]`); |
| 56 | + |
| 57 | +// Test 2: Filter Arabica coffee (should exclude Robusta) |
| 58 | +console.log('\n--- Test 2: Filter "Arabica coffee beans" ---'); |
| 59 | +const arabicaResult = filterCandidatesByElimination(coffeeCandidates, { |
| 60 | + productTypeModifiers: ['arabica', 'beans'], |
| 61 | + originalQuery: 'Arabica coffee beans' |
| 62 | +}); |
| 63 | +console.log(`Input: ${coffeeCandidates.length} candidates`); |
| 64 | +console.log(`Output: ${arabicaResult.filteredCodes.length} candidates`); |
| 65 | +console.log(`Eliminated: ${arabicaResult.eliminatedCount}`); |
| 66 | +console.log(`Applied rules: ${arabicaResult.appliedRules.join(', ')}`); |
| 67 | +console.log('Remaining codes:'); |
| 68 | +arabicaResult.filteredCodes.forEach(c => console.log(` - ${c.code}: ${c.description.substring(0, 50)}...`)); |
| 69 | + |
| 70 | +// Verify: Should NOT contain Robusta |
| 71 | +const hasRobusta = arabicaResult.filteredCodes.some(c => |
| 72 | + c.description.toLowerCase().includes('robusta') |
| 73 | +); |
| 74 | +console.log(`\nVERIFY: Contains Robusta? ${hasRobusta ? 'FAIL' : 'PASS'}`); |
| 75 | + |
| 76 | +// Test 3: Filter Robusta coffee (should exclude Arabica) |
| 77 | +console.log('\n--- Test 3: Filter "Robusta coffee" ---'); |
| 78 | +const robustaResult = filterCandidatesByElimination(coffeeCandidates, { |
| 79 | + productTypeModifiers: ['robusta'], |
| 80 | + originalQuery: 'Robusta coffee' |
| 81 | +}); |
| 82 | +console.log(`Input: ${coffeeCandidates.length} candidates`); |
| 83 | +console.log(`Output: ${robustaResult.filteredCodes.length} candidates`); |
| 84 | +console.log(`Eliminated: ${robustaResult.eliminatedCount}`); |
| 85 | +console.log('Remaining codes:'); |
| 86 | +robustaResult.filteredCodes.forEach(c => console.log(` - ${c.code}: ${c.description.substring(0, 50)}...`)); |
| 87 | + |
| 88 | +// Verify: Should NOT contain Arabica |
| 89 | +const hasArabica = robustaResult.filteredCodes.some(c => |
| 90 | + c.description.toLowerCase().includes('arabica') |
| 91 | +); |
| 92 | +console.log(`\nVERIFY: Contains Arabica? ${hasArabica ? 'FAIL' : 'PASS'}`); |
| 93 | + |
| 94 | +// Test 4: Filter instant coffee (should only be Ch.21) |
| 95 | +console.log('\n--- Test 4: Filter "instant coffee" ---'); |
| 96 | +const instantResult = filterCandidatesByElimination(coffeeCandidates, { |
| 97 | + productTypeModifiers: ['instant'], |
| 98 | + originalQuery: 'instant coffee' |
| 99 | +}); |
| 100 | +console.log(`Input: ${coffeeCandidates.length} candidates`); |
| 101 | +console.log(`Output: ${instantResult.filteredCodes.length} candidates`); |
| 102 | +console.log(`Eliminated: ${instantResult.eliminatedCount}`); |
| 103 | +console.log('Remaining codes:'); |
| 104 | +instantResult.filteredCodes.forEach(c => console.log(` - ${c.code}: ${c.description.substring(0, 50)}...`)); |
| 105 | + |
| 106 | +// Verify: All remaining should be Ch.21 |
| 107 | +const allCh21 = instantResult.filteredCodes.every(c => c.code.startsWith('21')); |
| 108 | +console.log(`\nVERIFY: All Ch.21? ${allCh21 ? 'PASS' : 'FAIL'}`); |
| 109 | + |
| 110 | +// Test 5: Brake pads (should not eliminate relevant codes) |
| 111 | +console.log('\n--- Test 5: Filter "brake pads for cars" ---'); |
| 112 | +const brakePadResult = filterCandidatesByElimination(brakePadCandidates, { |
| 113 | + productTypeModifiers: [], |
| 114 | + originalQuery: 'brake pads for cars' |
| 115 | +}); |
| 116 | +console.log(`Input: ${brakePadCandidates.length} candidates`); |
| 117 | +console.log(`Output: ${brakePadResult.filteredCodes.length} candidates`); |
| 118 | +console.log(`Eliminated: ${brakePadResult.eliminatedCount}`); |
| 119 | +console.log('Remaining codes:'); |
| 120 | +brakePadResult.filteredCodes.forEach(c => console.log(` - ${c.code}: ${c.description.substring(0, 50)}...`)); |
| 121 | + |
| 122 | +// Verify: Should contain 8708.30 codes |
| 123 | +const has8708 = brakePadResult.filteredCodes.some(c => c.code.startsWith('8708')); |
| 124 | +console.log(`\nVERIFY: Contains 8708 brake codes? ${has8708 ? 'PASS' : 'FAIL'}`); |
| 125 | + |
| 126 | +// Test 6: Extract modifiers from answer |
| 127 | +console.log('\n--- Test 6: Extract Modifiers from Answer ---'); |
| 128 | +const arabicaAnswer = extractModifiersFromAnswer({ |
| 129 | + code: '0901.11.11', |
| 130 | + description: 'Coffee not roasted: Arabica plantation: A Grade' |
| 131 | +}); |
| 132 | +console.log(`Answer "Arabica plantation A Grade" -> modifiers: [${arabicaAnswer.join(', ')}]`); |
| 133 | + |
| 134 | +const robustaAnswer = extractModifiersFromAnswer({ |
| 135 | + code: '0901.11.31', |
| 136 | + description: 'Coffee not roasted: Robusta parchment: AB Grade' |
| 137 | +}); |
| 138 | +console.log(`Answer "Robusta parchment AB Grade" -> modifiers: [${robustaAnswer.join(', ')}]`); |
| 139 | + |
| 140 | +// Test 7: Is question still relevant |
| 141 | +console.log('\n--- Test 7: Is Question Still Relevant ---'); |
| 142 | +const arabicaOptions = [ |
| 143 | + { code: '0901.11.11', description: 'Arabica plantation A Grade' }, |
| 144 | + { code: '0901.11.12', description: 'Arabica plantation B Grade' }, |
| 145 | + { code: '0901.11.31', description: 'Robusta parchment AB Grade' }, |
| 146 | +]; |
| 147 | +const relevanceCheck = isQuestionStillRelevant(arabicaOptions, { |
| 148 | + productTypeModifiers: ['arabica'], |
| 149 | + originalQuery: 'Arabica coffee' |
| 150 | +}); |
| 151 | +console.log(`With "arabica" modifier:`); |
| 152 | +console.log(` Relevant: ${relevanceCheck.relevant}`); |
| 153 | +console.log(` Remaining options: ${relevanceCheck.remainingOptions}`); |
| 154 | +if (relevanceCheck.autoSelectCode) { |
| 155 | + console.log(` Auto-select code: ${relevanceCheck.autoSelectCode}`); |
| 156 | +} |
| 157 | + |
| 158 | +// Summary |
| 159 | +console.log('\n' + '='.repeat(60)); |
| 160 | +console.log('SUMMARY'); |
| 161 | +console.log('='.repeat(60)); |
| 162 | +console.log(`Test 2 (Arabica excludes Robusta): ${!hasRobusta ? 'PASS' : 'FAIL'}`); |
| 163 | +console.log(`Test 3 (Robusta excludes Arabica): ${!hasArabica ? 'PASS' : 'FAIL'}`); |
| 164 | +console.log(`Test 4 (Instant -> Ch.21 only): ${allCh21 ? 'PASS' : 'FAIL'}`); |
| 165 | +console.log(`Test 5 (Brake pads -> 8708): ${has8708 ? 'PASS' : 'FAIL'}`); |
| 166 | + |
| 167 | +const allPassed = !hasRobusta && !hasArabica && allCh21 && has8708; |
| 168 | +console.log(`\nOVERALL: ${allPassed ? 'ALL TESTS PASSED' : 'SOME TESTS FAILED'}`); |
0 commit comments