const fs = require('fs'); const os = require('os'); const path = require('path'); const { closeDatabase, openDatabase, run } = require('../../server/lib/sqlite'); const { ensurePlantSchema, getPlants } = require('../../server/lib/plants'); describe('server plant search ranking', () => { let db; let dbPath; beforeAll(async () => { dbPath = path.join(os.tmpdir(), `greenlns-search-${Date.now()}.sqlite`); db = await openDatabase(dbPath); await ensurePlantSchema(db); const entries = [ { id: '1', name: 'Snake Plant', botanicalName: 'Sansevieria trifasciata', imageUri: '/plants/snake-plant.webp', imageStatus: 'ok', description: 'Very resilient houseplant that handles little light well.', categories: ['easy', 'low_light', 'air_purifier'], careInfo: { waterIntervalDays: 14, light: 'Low to full light', temp: '16-30C' }, confidence: 1, }, { id: '2', name: 'Spider Plant', botanicalName: 'Chlorophytum comosum', imageUri: '/plants/spider-plant.webp', imageStatus: 'ok', description: 'Easy houseplant that is safe for pets and helps clean indoor air.', categories: ['easy', 'pet_friendly', 'air_purifier'], careInfo: { waterIntervalDays: 6, light: 'Bright to partial shade', temp: '16-24C' }, confidence: 1, }, { id: '3', name: 'Monstera', botanicalName: 'Monstera deliciosa', imageUri: '/plants/monstera.webp', imageStatus: 'ok', description: 'Popular indoor plant with large split leaves.', categories: ['easy'], careInfo: { waterIntervalDays: 7, light: 'Bright indirect light', temp: '18-24C' }, confidence: 1, }, { id: '4', name: 'Easy Adan', botanicalName: 'Adan botanica', imageUri: '/plants/easy-adan.webp', imageStatus: 'ok', description: 'Pet friendly plant for low light corners.', categories: ['succulent', 'low_light', 'pet_friendly'], careInfo: { waterIntervalDays: 8, light: 'Partial shade', temp: '18-24C' }, confidence: 1, }, { id: '5', name: 'Boston Fern', botanicalName: 'Nephrolepis exaltata', imageUri: '/plants/boston-fern.webp', imageStatus: 'ok', description: 'Loves steady moisture and humid rooms.', categories: ['high_humidity', 'hanging'], careInfo: { waterIntervalDays: 3, light: 'Partial shade', temp: '16-24C' }, confidence: 1, }, { id: '6', name: 'Aloe Vera', botanicalName: 'Aloe vera', imageUri: '/plants/aloe-vera.webp', imageStatus: 'ok', description: 'Sun-loving succulent for bright windows.', categories: ['succulent', 'sun', 'medicinal'], careInfo: { waterIntervalDays: 12, light: 'Sunny', temp: '18-30C' }, confidence: 1, }, ]; for (const entry of entries) { await run( db, `INSERT INTO plants ( id, name, botanicalName, imageUri, imageStatus, description, categories, careInfo, confidence, createdAt, updatedAt ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))`, [ entry.id, entry.name, entry.botanicalName, entry.imageUri, entry.imageStatus, entry.description, JSON.stringify(entry.categories), JSON.stringify(entry.careInfo), entry.confidence, ], ); } }); afterAll(async () => { if (db) { await closeDatabase(db); } if (dbPath && fs.existsSync(dbPath)) { fs.unlinkSync(dbPath); } }); it('returns exact common name matches first', async () => { const results = await getPlants(db, { query: 'Monstera', limit: 3 }); expect(results[0].name).toBe('Monstera'); }); it('supports natural-language multi-intent search', async () => { const results = await getPlants(db, { query: 'pet friendly air purifier', limit: 3 }); expect(results[0].name).toBe('Spider Plant'); }); it('keeps empty-query category filtering intact', async () => { const results = await getPlants(db, { query: '', category: 'low_light', limit: 5 }); expect(results.length).toBeGreaterThan(0); results.forEach((entry) => { expect(entry.categories).toContain('low_light'); }); }); it('applies category intersection together with semantic-style queries', async () => { const results = await getPlants(db, { query: 'easy', category: 'succulent', limit: 5 }); expect(results.length).toBe(1); expect(results[0].name).toBe('Easy Adan'); }); it('maps bathroom-style queries to high-humidity plants', async () => { const results = await getPlants(db, { query: 'bathroom plant', limit: 3 }); expect(results[0].name).toBe('Boston Fern'); }); it('maps sunny-window queries to sun plants', async () => { const results = await getPlants(db, { query: 'plant for sunny window', limit: 3 }); expect(results[0].name).toBe('Aloe Vera'); }); });