From b03bfa032ad0dffbe429c8b2e5a1e750b750dc5c Mon Sep 17 00:00:00 2001 From: Tanner Prestegard <tanner.prestegard@ligo.org> Date: Thu, 9 May 2019 14:07:04 -0500 Subject: [PATCH] Add unit tests for searches --- gracedb/search/tests/test_queries.py | 186 +++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 gracedb/search/tests/test_queries.py diff --git a/gracedb/search/tests/test_queries.py b/gracedb/search/tests/test_queries.py new file mode 100644 index 000000000..a9fbd810b --- /dev/null +++ b/gracedb/search/tests/test_queries.py @@ -0,0 +1,186 @@ +try: + from unittest import mock +except ImportError: # python < 3 + import mock +import datetime +import pytest +import pytz + +from django.conf import settings +from django.db.models import Q + +from superevents.models import Superevent +from search.constants import RUN_MAP +from search.query.events import parseQuery +from search.query.superevents import parseSupereventQuery + + +# Get server timezone +SERVER_TZ = pytz.timezone(settings.TIME_ZONE) + +# Helper function for comparing Q objects +# Shouldn't be necessary in Django 2.0+ +def compare_Qs(Q1, Q2): + if hasattr(Q1, 'children') and hasattr(Q2, 'children'): + if (Q1.__class__ == Q2.__class__) and ((Q1.connector, Q1.negated) + == (Q2.connector, Q2.negated)) and (len(Q1.children) == + len(Q2.children)): + # Get children + result = [compare_Qs(Q1.children[i], Q2.children[i]) for i in + range(len(Q1.children))] + return all(result) + return (Q1 == Q2) + +# Label names to use in Label list mock +MOCK_LABEL_LIST = ['LABEL1', 'LABEL2', 'LABEL3'] +# Datetime to use in timezone.now() mock +MOCK_NOW_DT = SERVER_TZ.localize(datetime.datetime(2019, 3, 31, 10, 50, 0)) + +# Tests ----------------------------------------------------------------------- +DEFAULT_Q = ~Q(category=Superevent.SUPEREVENT_CATEGORY_TEST) & \ + ~Q(category=Superevent.SUPEREVENT_CATEGORY_MDC) +SUPEREVENT_QUERY_TEST_DATA = [ + ("", DEFAULT_Q), + ("id: S190509bc", + Q(**Superevent.get_filter_kwargs_for_date_id_lookup("S190509bc"))), + ("superevent_id: S190509bc", + Q(**Superevent.get_filter_kwargs_for_date_id_lookup("S190509bc"))), + ("S190509bc", + Q(**Superevent.get_filter_kwargs_for_date_id_lookup("S190509bc"))), + ("TS190509bc", + Q(**Superevent.get_filter_kwargs_for_date_id_lookup("TS190509bc"))), + ("MS190509bc", + Q(**Superevent.get_filter_kwargs_for_date_id_lookup("MS190509bc"))), + ("GW121203K", + Q(**Superevent.get_filter_kwargs_for_date_id_lookup("GW121203K"))), + ("MGW180923ZZZ", + Q(**Superevent.get_filter_kwargs_for_date_id_lookup("MGW180923ZZZ"))), + # Test case with missing letter suffix + ("GW150914", + Q(**Superevent.get_filter_kwargs_for_date_id_lookup("GW150914A"))), + ("Test", Q(category=Superevent.SUPEREVENT_CATEGORY_TEST)), + ("category: Test", Q(category=Superevent.SUPEREVENT_CATEGORY_TEST)), + ("MDC", Q(category=Superevent.SUPEREVENT_CATEGORY_MDC)), + ("category: MDC", Q(category=Superevent.SUPEREVENT_CATEGORY_MDC)), + ("category: Production", + Q(category=Superevent.SUPEREVENT_CATEGORY_PRODUCTION)), + ("t_0: 1234.5678", Q(t_0=1234.5678) & DEFAULT_Q), + ("t_0: 1234.56 .. 3456.78", Q(t_0__range=[1234.56, 3456.78]) & + DEFAULT_Q), + ("gpstime: 1234.5678", Q(t_0=1234.5678) & DEFAULT_Q), + ("gpstime: 1234.56 .. 3456.78", Q(t_0__range=[1234.56, 3456.78]) & + DEFAULT_Q), + ("t_start: 1234.5678", Q(t_start=1234.5678) & DEFAULT_Q), + ("t_start: 1234.56 .. 3456.78", Q(t_start__range=[1234.56, 3456.78]) & + DEFAULT_Q), + ("t_end: 1234.5678", Q(t_end=1234.5678) & DEFAULT_Q), + ("t_end: 1234.56 .. 3456.78", Q(t_end__range=[1234.56, 3456.78]) & + DEFAULT_Q), + ("submitter: \"test\"", (Q(submitter__username__icontains="test") | + Q(submitter__last_name__icontains="test")) & DEFAULT_Q), + ("\"test\"", (Q(submitter__username__icontains="test") | + Q(submitter__last_name__icontains="test")) & DEFAULT_Q), + ('preferred_event: G1234', Q(preferred_event__id=1234) & DEFAULT_Q), + ('preferred_event: G1234 .. G2345', + Q(preferred_event__id__range=[1234, 2345]) & DEFAULT_Q), + ('event: G1234', Q(events__id=1234) & DEFAULT_Q), + ('event: G1234 .. G2345', Q(events__id__range=[1234, 2345]) & DEFAULT_Q), + ('events: G1234', Q(events__id=1234) & DEFAULT_Q), + ('events: G1234 .. G2345', Q(events__id__range=[1234, 2345]) & DEFAULT_Q), + ('is_gw: True', Q(is_gw=True) & DEFAULT_Q), + ('is_gw: False', Q(is_gw=False) & DEFAULT_Q), + ('is_public: True', Q(is_exposed=True) & DEFAULT_Q), + ('is_public: False', Q(is_exposed=False) & DEFAULT_Q), + ('is_exposed: True', Q(is_exposed=True) & DEFAULT_Q), + ('is_exposed: False', Q(is_exposed=False) & DEFAULT_Q), + ('status: public', Q(is_exposed=True) & DEFAULT_Q), + ('status: internal', Q(is_exposed=False) & DEFAULT_Q), + ('public', Q(is_exposed=True) & DEFAULT_Q), + ('internal', Q(is_exposed=False) & DEFAULT_Q), + ('category: production', + Q(category=Superevent.SUPEREVENT_CATEGORY_PRODUCTION)), + ('Test', + Q(category=Superevent.SUPEREVENT_CATEGORY_TEST)), + ('category: MDC', + Q(category=Superevent.SUPEREVENT_CATEGORY_MDC)), + ('created: 2019-05-04', Q(created=SERVER_TZ.localize(datetime.datetime( + 2019, 5, 4, 0, 0, 0))) & DEFAULT_Q), + ('created: 2019-05-04 01:23:45 .. 2019-05-05 12:34:56', + Q(created__range=[ + SERVER_TZ.localize(datetime.datetime(2019, 5, 4, 1, 23, 45)), + SERVER_TZ.localize(datetime.datetime(2019, 5, 5, 12, 34, 56))]) & + DEFAULT_Q), + ('yesterday .. now', Q(created__range=[ + SERVER_TZ.localize(datetime.datetime(2019, 3, 30, 0, 0, 0)), + MOCK_NOW_DT]) & DEFAULT_Q), + ('noon', Q(created=SERVER_TZ.localize(datetime.datetime(2019, 3, 31, + 12, 0, 0))) & DEFAULT_Q), + ('far <= 1e-10', Q(preferred_event__far__lte=1e-10) & DEFAULT_Q), + ('far < 1e-10', Q(preferred_event__far__lt=1e-10) & DEFAULT_Q), + ('far > 1e-10', Q(preferred_event__far__gt=1e-10) & DEFAULT_Q), + ('far >= 1e-10', Q(preferred_event__far__gte=1e-10) & DEFAULT_Q), + ('far: 0.001', Q(preferred_event__far=0.001) & DEFAULT_Q), + ('far in 1e-5, 1e-4', Q(preferred_event__far__range=[1e-5, 1e-4]) & + DEFAULT_Q), + ('far: 1e-5 .. 1e-4', Q(preferred_event__far__range=[1e-5, 1e-4]) & + DEFAULT_Q), + ('far: 1e-5 - 1e-4', Q(preferred_event__far__range=[1e-5, 1e-4]) & + DEFAULT_Q), + # A few complex queries + ('far in 1e-10, 1e-8 created: yesterday public', + Q(preferred_event__far__range=[1e-10, 1e-8]) & + Q(created=SERVER_TZ.localize(datetime.datetime(2019, 3, 30))) & + Q(is_exposed=True) & DEFAULT_Q), + ('gpstime: 123 .. 456 Test events: G123 .. G129', + Q(t_0__range=[123, 456]) & + Q(category=Superevent.SUPEREVENT_CATEGORY_TEST) & + Q(events__id__range=[123, 129])), +] +# Add tests based on run IDs +RUNID_QUERY_DATA = [(k, Q(t_0__range=v) & (DEFAULT_Q)) + for k,v in RUN_MAP.items()] +SUPEREVENT_QUERY_TEST_DATA.extend(RUNID_QUERY_DATA) + +@pytest.mark.parametrize("query,expected_Q_result", SUPEREVENT_QUERY_TEST_DATA) +def test_superevent_queries(query, expected_Q_result): + # Mock out label queries + with mock.patch('search.query.labels.Label.objects.values_list') as mock_labels_list, \ + mock.patch('events.nltime.timezone.now') as mock_now: + # Set up mocks + mock_labels_list.return_value = MOCK_LABEL_LIST + mock_now.return_value = MOCK_NOW_DT + + # Run query + Q_result = parseSupereventQuery(query) + assert compare_Qs(Q_result, expected_Q_result) + + +# Group, pipeline, search names to use in mocks +MOCK_GROUP_LIST = ['Test', 'External', 'GROUP1', 'GROUP2'] +MOCK_PIPELINE_LIST = ['HardwareInjection', 'PIPELINE1', 'PIPELINE2'] +MOCK_SEARCH_LIST = ['MDC', 'SEARCH1', 'SEARCH2'] + +DEFAULT_EVENT_Q = ~Q(group__name='Test') & ~Q(search__name='MDC') +# NOTE: the event query stuff is just too nasty. Attempts at testing +# are not going well. It needs a full rework. +EVENT_QUERY_TEST_DATA = [ + ("", DEFAULT_EVENT_Q), +] +@pytest.mark.parametrize("query,expected_Q_result", EVENT_QUERY_TEST_DATA) +def test_event_queries(query, expected_Q_result): + # Mock out label queries + with mock.patch('search.query.labels.Label.objects.values_list') as mock_labels_list, \ + mock.patch('search.query.events.Group.objects.values_list') as mock_group_list, \ + mock.patch('search.query.events.Pipeline.objects.values_list') as mock_pipeline_list, \ + mock.patch('search.query.events.Search.objects.values_list') as mock_search_list, \ + mock.patch('events.nltime.timezone.now') as mock_now: + # Set up mocks + mock_labels_list.return_value = MOCK_LABEL_LIST + mock_group_list.return_value = MOCK_GROUP_LIST + mock_pipeline_list.return_value = MOCK_PIPELINE_LIST + mock_search_list.return_value = MOCK_SEARCH_LIST + mock_now.return_value = MOCK_NOW_DT + + # Run query + Q_result = parseQuery(query) + assert compare_Qs(Q_result, expected_Q_result) -- GitLab