1
0

search.test.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /**
  2. * Search Query Utilities Tests
  3. *
  4. * Tests multi-signal scoring, kind bonuses, and path relevance.
  5. */
  6. import { describe, it, expect } from 'vitest';
  7. import {
  8. extractSearchTerms,
  9. scorePathRelevance,
  10. kindBonus,
  11. STOP_WORDS,
  12. } from '../src/search/query-utils';
  13. describe('Search Query Utilities', () => {
  14. describe('extractSearchTerms', () => {
  15. it('should extract meaningful terms from a query', () => {
  16. const terms = extractSearchTerms('find the login handler');
  17. expect(terms).toContain('login');
  18. expect(terms).toContain('handler');
  19. // 'find' and 'the' are stop words
  20. expect(terms).not.toContain('find');
  21. expect(terms).not.toContain('the');
  22. });
  23. it('should filter stop words', () => {
  24. const terms = extractSearchTerms('how does the authentication work');
  25. expect(terms).not.toContain('how');
  26. expect(terms).not.toContain('does');
  27. expect(terms).not.toContain('the');
  28. expect(terms).toContain('authentication');
  29. expect(terms).toContain('work');
  30. });
  31. it('should handle camelCase by lowercasing', () => {
  32. const terms = extractSearchTerms('UserService');
  33. expect(terms).toContain('userservice');
  34. });
  35. it('should strip punctuation', () => {
  36. const terms = extractSearchTerms('payment.process()');
  37. expect(terms).toContain('payment');
  38. expect(terms).toContain('process');
  39. });
  40. it('should return empty for all stop words', () => {
  41. const terms = extractSearchTerms('how do I get the');
  42. expect(terms).toHaveLength(0);
  43. });
  44. it('should filter single-character terms', () => {
  45. const terms = extractSearchTerms('a b c auth');
  46. expect(terms).toEqual(['auth']);
  47. });
  48. });
  49. describe('scorePathRelevance', () => {
  50. it('should score filename matches highest', () => {
  51. const score = scorePathRelevance('src/auth/login.ts', 'login');
  52. expect(score).toBeGreaterThanOrEqual(10);
  53. });
  54. it('should score directory matches', () => {
  55. const score = scorePathRelevance('src/auth/index.ts', 'auth');
  56. expect(score).toBeGreaterThanOrEqual(5);
  57. });
  58. it('should return 0 for unrelated paths', () => {
  59. const score = scorePathRelevance('src/utils/format.ts', 'payment');
  60. expect(score).toBe(0);
  61. });
  62. it('should accumulate scores for multiple matching terms', () => {
  63. const score = scorePathRelevance('src/auth/login.ts', 'auth login');
  64. // Both 'auth' (dir match) and 'login' (filename match)
  65. expect(score).toBeGreaterThanOrEqual(15);
  66. });
  67. it('should return 0 for empty query terms', () => {
  68. const score = scorePathRelevance('src/auth/login.ts', 'the a an');
  69. expect(score).toBe(0);
  70. });
  71. });
  72. describe('kindBonus', () => {
  73. it('should give functions and methods highest bonus', () => {
  74. expect(kindBonus('function')).toBe(10);
  75. expect(kindBonus('method')).toBe(10);
  76. });
  77. it('should rank functions > classes > variables > imports', () => {
  78. expect(kindBonus('function')).toBeGreaterThan(kindBonus('class'));
  79. expect(kindBonus('class')).toBeGreaterThan(kindBonus('variable'));
  80. expect(kindBonus('variable')).toBeGreaterThan(kindBonus('import'));
  81. });
  82. it('should give routes high priority', () => {
  83. expect(kindBonus('route')).toBeGreaterThanOrEqual(9);
  84. });
  85. it('should give components high priority', () => {
  86. expect(kindBonus('component')).toBeGreaterThanOrEqual(8);
  87. });
  88. it('should return 0 for parameter and file kinds', () => {
  89. expect(kindBonus('parameter')).toBe(0);
  90. expect(kindBonus('file')).toBe(0);
  91. });
  92. it('should return 0 for unknown kinds', () => {
  93. expect(kindBonus('unknown_kind' as any)).toBe(0);
  94. });
  95. });
  96. describe('STOP_WORDS', () => {
  97. it('should contain common English stop words', () => {
  98. expect(STOP_WORDS.has('the')).toBe(true);
  99. expect(STOP_WORDS.has('and')).toBe(true);
  100. expect(STOP_WORDS.has('or')).toBe(true);
  101. });
  102. it('should contain action verbs used in queries', () => {
  103. expect(STOP_WORDS.has('find')).toBe(true);
  104. expect(STOP_WORDS.has('show')).toBe(true);
  105. expect(STOP_WORDS.has('get')).toBe(true);
  106. expect(STOP_WORDS.has('list')).toBe(true);
  107. });
  108. it('should not contain technical terms', () => {
  109. expect(STOP_WORDS.has('function')).toBe(false);
  110. expect(STOP_WORDS.has('class')).toBe(false);
  111. expect(STOP_WORDS.has('auth')).toBe(false);
  112. });
  113. });
  114. });