99 lines
5.0 KiB
Python
99 lines
5.0 KiB
Python
import json
|
|
import os
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
|
|
from test_catalog_api import fixture, load_service
|
|
|
|
|
|
class BannerTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory(dir=os.environ.get('TMPDIR'))
|
|
self.path = fixture(self.temp.name)
|
|
self.api = load_service()
|
|
self.catalog = self.api.Catalog(self.path)
|
|
|
|
def tearDown(self):
|
|
self.catalog.close()
|
|
self.temp.cleanup()
|
|
|
|
def test_local_config_reloads_safe_bounded_ads_without_admin_endpoint(self):
|
|
config = Path(self.temp.name) / 'banners.json'
|
|
self.catalog.close()
|
|
self.catalog = self.api.Catalog(self.path, banner_config=config)
|
|
self.assertEqual(self.catalog.dispatch('/api/banners/'), {'banners': []})
|
|
ad = {'title_en': 'Fixture sponsor', 'title_fa': 'حامی آزمایشی',
|
|
'subtitle_en': 'Test only', 'target_url': 'https://sponsor.example/offer',
|
|
'image_url': 'https://sponsor.example/banner.jpg', 'secret': 'not public'}
|
|
config.write_text(json.dumps({'banners': [ad]}))
|
|
result = self.catalog.dispatch('/api/banners/')['banners']
|
|
self.assertEqual(len(result), 1)
|
|
self.assertEqual(result[0]['title_fa'], ad['title_fa'])
|
|
self.assertEqual(result[0]['image_url'], ad['image_url'])
|
|
self.assertNotIn('secret', result[0])
|
|
config.write_text('{invalid')
|
|
self.assertEqual(self.catalog.dispatch('/api/banners/'), {'banners': []})
|
|
for target in ['http://evil.example/', 'javascript:alert(1)', '//evil.example/',
|
|
'https://a:b@evil.example/', 'https://evil.example:444/',
|
|
'https://evil.example/ bad', 'https://evil.example/\\\\bad',
|
|
'https://evil.example/%0abad', 'https://',
|
|
'https://-bad.example/', 'https://bad..example/', 'https://bad.example./',
|
|
'https://evil.example/%zz', 'https://evil.example/' + 'x' * 2048]:
|
|
config.write_text(json.dumps({'banners': [{**ad, 'target_url': target}, ad]}))
|
|
self.assertEqual(len(self.catalog.dispatch('/api/banners/')['banners']), 1, target)
|
|
for row in [None, True, 5, {}, {**ad, 'title_en': 'x' * 81},
|
|
{**ad, 'subtitle_en': 'x' * 181}, {**ad, 'title_fa': 'bad\u202etext'},
|
|
{**ad, 'image_url': 'http://evil.example/banner.jpg'}]:
|
|
config.write_text(json.dumps({'banners': [row, ad]}))
|
|
self.assertEqual(len(self.catalog.dispatch('/api/banners/')['banners']), 1, row)
|
|
config.write_text(json.dumps({'banners': [ad] * 30}))
|
|
self.assertEqual(len(self.catalog.dispatch('/api/banners/')['banners']), 5)
|
|
config.write_text(json.dumps({'banners': [{}] * 20 + [ad]}))
|
|
self.assertEqual(self.catalog.dispatch('/api/banners/'), {'banners': []})
|
|
config.write_text(' ' * 65537)
|
|
self.assertEqual(self.catalog.dispatch('/api/banners/'), {'banners': []})
|
|
for value in [None, [], {'banners': {}}, {'banners': []}]:
|
|
config.write_text(json.dumps(value))
|
|
self.assertEqual(self.catalog.dispatch('/api/banners/'), {'banners': []})
|
|
for route in ['/api/banners/admin/', '/api/banners/config/']:
|
|
with self.assertRaises(self.api.ApiError) as error:
|
|
self.catalog.dispatch(route)
|
|
self.assertEqual(error.exception.status, 404)
|
|
|
|
def test_http_banner_endpoint_is_json_no_store_and_rejects_writes(self):
|
|
from http.client import HTTPConnection
|
|
import threading
|
|
server = self.api.make_server(self.catalog, port=0)
|
|
worker = threading.Thread(target=server.serve_forever, daemon=True)
|
|
worker.start()
|
|
try:
|
|
for method, expected in [('GET', 200), ('HEAD', 200), ('POST', 405), ('PUT', 405)]:
|
|
client = HTTPConnection('127.0.0.1', server.server_port, timeout=5)
|
|
client.request(method, '/api/banners/')
|
|
response = client.getresponse()
|
|
self.assertEqual(response.status, expected)
|
|
self.assertEqual(response.getheader('Cache-Control'), 'no-store')
|
|
self.assertIn('application/json', response.getheader('Content-Type'))
|
|
body = response.read()
|
|
if method == 'GET':
|
|
self.assertEqual(json.loads(body), {'banners': []})
|
|
if method == 'HEAD':
|
|
self.assertEqual(body, b'')
|
|
client.close()
|
|
finally:
|
|
server.shutdown()
|
|
server.server_close()
|
|
worker.join(timeout=5)
|
|
|
|
def test_read_only_banner_route_defaults_to_empty_ad_list(self):
|
|
self.assertEqual(self.catalog.dispatch('/api/banners/'), {'banners': []})
|
|
self.assertEqual(self.catalog.dispatch('/api/banners'), {'banners': []})
|
|
with self.assertRaises(self.api.ApiError) as error:
|
|
self.catalog.dispatch('/api/banners/?url=https://evil.example/')
|
|
self.assertEqual(error.exception.status, 400)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|