# SPDX-License-Identifier: AGPL-3.0-or-later # pylint: disable=missing-module-docstring import mock from lxml import html from urllib.parse import parse_qs from searx.engines import kagi from searx.exceptions import SearxEngineAPIException from tests import SearxTestCase class TestKagiEngine(SearxTestCase): def setUp(self): self.test_html = """
example1.com
Content 1
example2.com
Content 2
""" def test_request(self): # Test with missing API token kagi.token = None params = {'pageno': 1, 'headers': {}} self.assertRaises(SearxEngineAPIException, kagi.request, 'test query', params) # Test with valid API token but no cookie kagi.token = 'test_token' params = {'pageno': 1, 'headers': {}, 'cookies': {}} query = 'test query' request_params = kagi.request(query, params) self.assertIn('url', request_params) self.assertIn('token=test_token', request_params['url']) self.assertIn('q=test+query', request_params['url']) self.assertEqual(request_params['max_redirects'], 1) self.assertTrue(request_params['allow_redirects']) # Test with both required cookies params['cookies']['kagi_session'] = 'test_session' params['cookies']['_kagi_search_'] = 'test_search' request_params = kagi.request(query, params) self.assertNotIn('token=', request_params['url']) self.assertIn('q=test+query', request_params['url']) self.assertEqual(request_params['max_redirects'], 1) self.assertTrue(request_params['allow_redirects']) # Test with missing search cookie params['cookies'] = {'kagi_session': 'test_session'} request_params = kagi.request(query, params) self.assertIn('token=', request_params['url']) # Test with missing session cookie params['cookies'] = {'_kagi_search_': 'test_search'} request_params = kagi.request(query, params) self.assertIn('token=', request_params['url']) # Test pagination params['pageno'] = 2 request_params = kagi.request(query, params) self.assertIn('batch=2', request_params['url']) self.assertEqual(request_params['max_redirects'], 1) def test_response(self): def verify_cookie_capture(cookie_headers, expected_session, expected_search): mock_headers = mock.Mock() mock_headers.get_list = mock.Mock(return_value=cookie_headers) mock_headers.__contains__ = mock.Mock(return_value=True) response = mock.Mock( text=self.test_html, status_code=200, headers=mock_headers, search_params={'cookies': {}} ) results = kagi.response(response) self.assertEqual(response.search_params['cookies'].get('kagi_session'), expected_session) self.assertEqual(response.search_params['cookies'].get('_kagi_search_'), expected_search) return results # Test cookie capture with standard attributes results = verify_cookie_capture( ['kagi_session=test_session; Path=/; HttpOnly', '_kagi_search_=test_search; Path=/; HttpOnly'], 'test_session', 'test_search', ) # Test cookie capture with additional attributes results = verify_cookie_capture( [ 'kagi_session=test_session2; Path=/; HttpOnly; SameSite=Lax', '_kagi_search_=test_search2; Domain=.kagi.com; Path=/; SameSite=Lax', ], 'test_session2', 'test_search2', ) self.assertEqual(type(results), list) self.assertEqual(len(results), 2) # 2 search results # Check first result self.assertEqual(results[0]['title'], 'Result 1') self.assertEqual(results[0]['url'], 'https://example1.com') self.assertEqual(results[0]['content'], 'Content 1') self.assertEqual(results[0]['domain'], 'example1.com') # Check second result self.assertEqual(results[1]['title'], 'Result 2') self.assertEqual(results[1]['url'], 'https://example2.com') self.assertEqual(results[1]['content'], 'Content 2') self.assertEqual(results[1]['domain'], 'example2.com') def test_response_error_handling(self): # Test invalid token/cookie response response = mock.Mock( text='', status_code=401, search_params={'cookies': {'kagi_session': 'invalid_session'}}, headers={} ) self.assertRaises(SearxEngineAPIException, kagi.response, response) # Verify invalid cookie was cleared self.assertNotIn('kagi_session', response.search_params['cookies']) # Test rate limit response response = mock.Mock(text='', status_code=429, search_params={'cookies': {}}, headers={}) self.assertRaises(SearxEngineAPIException, kagi.response, response) # Test other error response response = mock.Mock(text='', status_code=500, search_params={'cookies': {}}, headers={}) self.assertRaises(SearxEngineAPIException, kagi.response, response)