search-api-handler.spec.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import type { IncomingMessage } from 'http';
  2. import { beforeEach, describe, expect, it } from 'vitest';
  3. import { searchApiModule } from './search-api-handler';
  4. describe('searchApiModule', () => {
  5. const mockRequest = {} as IncomingMessage;
  6. beforeEach(() => {
  7. // No mocks needed - test actual behavior
  8. });
  9. describe('canHandle', () => {
  10. it.each`
  11. description | url | expected
  12. ${'search API endpoint'} | ${'/_api/search?q=test'} | ${true}
  13. ${'search API without query'} | ${'/_api/search'} | ${true}
  14. ${'search endpoint'} | ${'/_search?q=keyword'} | ${true}
  15. ${'search endpoint without q'} | ${'/_search'} | ${true}
  16. ${'nested search API'} | ${'/admin/_api/search?q=admin'} | ${true}
  17. ${'nested search endpoint'} | ${'/docs/_search?q=documentation'} | ${true}
  18. ${'other API endpoint'} | ${'/_api/pages'} | ${false}
  19. ${'regular page path'} | ${'/search/results'} | ${false}
  20. ${'similar but different'} | ${'/_api/search-results'} | ${false}
  21. ${'root path'} | ${'/'} | ${false}
  22. ${'empty URL'} | ${''} | ${false}
  23. `('should return $expected for $description: $url', ({ url, expected }) => {
  24. const result = searchApiModule.canHandle(url);
  25. expect(result).toBe(expected);
  26. });
  27. });
  28. describe('handle', () => {
  29. describe('search API with query parameter', () => {
  30. it('should anonymize search query when q parameter is present', () => {
  31. const originalUrl = '/_api/search?q=sensitive search term&limit=10';
  32. // Ensure canHandle returns true for this URL
  33. expect(searchApiModule.canHandle(originalUrl)).toBe(true);
  34. const result = searchApiModule.handle(mockRequest, originalUrl);
  35. expect(result).toEqual({
  36. 'http.target': '/_api/search?q=%5BANONYMIZED%5D&limit=10',
  37. });
  38. });
  39. it('should handle encoded query parameters', () => {
  40. const originalUrl = '/_search?q=encoded%20search%20term&sort=relevance';
  41. // Ensure canHandle returns true for this URL
  42. expect(searchApiModule.canHandle(originalUrl)).toBe(true);
  43. const result = searchApiModule.handle(mockRequest, originalUrl);
  44. expect(result).toEqual({
  45. 'http.target': '/_search?q=%5BANONYMIZED%5D&sort=relevance',
  46. });
  47. });
  48. it('should handle empty query parameter', () => {
  49. const originalUrl = '/_api/search?q=&page=1';
  50. // Ensure canHandle returns true for this URL
  51. expect(searchApiModule.canHandle(originalUrl)).toBe(true);
  52. const result = searchApiModule.handle(mockRequest, originalUrl);
  53. expect(result).toEqual({
  54. 'http.target': '/_api/search?q=%5BANONYMIZED%5D&page=1',
  55. });
  56. });
  57. it('should handle complex query with special characters', () => {
  58. const originalUrl = '/_search?q=user:john+tag:important&format=json';
  59. // Ensure canHandle returns true for this URL
  60. expect(searchApiModule.canHandle(originalUrl)).toBe(true);
  61. const result = searchApiModule.handle(mockRequest, originalUrl);
  62. expect(result).toEqual({
  63. 'http.target': '/_search?q=%5BANONYMIZED%5D&format=json',
  64. });
  65. });
  66. });
  67. describe('search API without query parameter', () => {
  68. it('should return null when no q parameter is present', () => {
  69. const url = '/_api/search?limit=20&sort=date';
  70. // Ensure canHandle returns true for this URL
  71. expect(searchApiModule.canHandle(url)).toBe(true);
  72. const result = searchApiModule.handle(mockRequest, url);
  73. expect(result).toBeNull();
  74. });
  75. it('should return null for search endpoint without query', () => {
  76. const url = '/_search?page=2&format=json';
  77. // Ensure canHandle returns true for this URL
  78. expect(searchApiModule.canHandle(url)).toBe(true);
  79. const result = searchApiModule.handle(mockRequest, url);
  80. expect(result).toBeNull();
  81. });
  82. it('should return null for search API without any parameters', () => {
  83. const url = '/_api/search';
  84. // Ensure canHandle returns true for this URL
  85. expect(searchApiModule.canHandle(url)).toBe(true);
  86. const result = searchApiModule.handle(mockRequest, url);
  87. expect(result).toBeNull();
  88. });
  89. });
  90. describe('edge cases', () => {
  91. it('should handle multiple q parameters', () => {
  92. const originalUrl = '/_api/search?q=first&q=second&limit=5';
  93. // Ensure canHandle returns true for this URL
  94. expect(searchApiModule.canHandle(originalUrl)).toBe(true);
  95. const result = searchApiModule.handle(mockRequest, originalUrl);
  96. expect(result).toEqual({
  97. 'http.target': '/_api/search?q=%5BANONYMIZED%5D&limit=5',
  98. });
  99. });
  100. it('should preserve other parameters while anonymizing q', () => {
  101. const originalUrl = '/_search?category=docs&q=secret&page=1&sort=date';
  102. // Ensure canHandle returns true for this URL
  103. expect(searchApiModule.canHandle(originalUrl)).toBe(true);
  104. const result = searchApiModule.handle(mockRequest, originalUrl);
  105. // The actual output may have different parameter order due to URL parsing
  106. expect(result).toEqual({
  107. 'http.target':
  108. '/_search?category=docs&q=%5BANONYMIZED%5D&page=1&sort=date',
  109. });
  110. });
  111. it('should handle URLs with fragments', () => {
  112. const originalUrl = '/_api/search?q=test#results';
  113. // Ensure canHandle returns true for this URL
  114. expect(searchApiModule.canHandle(originalUrl)).toBe(true);
  115. const result = searchApiModule.handle(mockRequest, originalUrl);
  116. expect(result).toEqual({
  117. 'http.target': '/_api/search?q=%5BANONYMIZED%5D#results',
  118. });
  119. });
  120. });
  121. });
  122. });