def setUp(self):
     self.cookie_jar = InMemoryCookieJar()
     self.rules = [Rule(lambda *args: False, MagicMock(), RULE_IDENTIFIER) for _ in range(10)]
     self.cookie_jar.enrich_cookie(
         COOKIE_IDENTIFIER, Enrichment("first", datetime(year=1, month=1, day=1, tzinfo=timezone.utc), Metadata()))
     self.cookie = self.cookie_jar.fetch_cookie(COOKIE_IDENTIFIER)
     self.processor = BasicProcessor(self.cookie_jar, [], [])
 def setUp(self):
     self.cookie_jar = InMemoryCookieJar()
     self.rules = [Rule(lambda *args: False, MagicMock()) for _ in range(10)]
     self.cookie = Cookie(COOKIE_IDENTIFIER)
     self.processor = BasicProcessor(self.cookie_jar, [], [])
class TestBasicProcessor(unittest.TestCase):
    """
    Tests for `BasicProcessor`.
    """
    def setUp(self):
        self.cookie_jar = InMemoryCookieJar()
        self.rules = [Rule(lambda *args: False, MagicMock(), RULE_IDENTIFIER) for _ in range(10)]
        self.cookie_jar.enrich_cookie(
            COOKIE_IDENTIFIER, Enrichment("first", datetime(year=1, month=1, day=1, tzinfo=timezone.utc), Metadata()))
        self.cookie = self.cookie_jar.fetch_cookie(COOKIE_IDENTIFIER)
        self.processor = BasicProcessor(self.cookie_jar, [], [])

    def test_evaluate_rules_with_cookie_when_no_rules(self):
        halt = self.processor.evaluate_rules_with_cookie(self.cookie)

        self.assertFalse(halt)

    def test_evaluate_rules_with_cookie_when_no_matched_rules(self):
        self.processor.rules = self.rules
        halt = self.processor.evaluate_rules_with_cookie(self.cookie)

        for rule in self.rules:
            rule._action.assert_not_called()
        self.assertFalse(halt)

    def test_evaluate_rules_with_cookie_when_matched_rules_and_no_termination(self):
        extra_rules = [
            Rule(lambda *args: True, MagicMock(return_value=False), RULE_IDENTIFIER),
            Rule(lambda *args: True, MagicMock(return_value=False), RULE_IDENTIFIER)
        ]
        self.processor.rules = self.rules + extra_rules
        halt = self.processor.evaluate_rules_with_cookie(self.cookie)

        for rule in self.rules:
            rule._action.assert_not_called()
        for rule in extra_rules:
            self.assertEqual(rule._action.call_count, 1)
            self.assertEqual(rule._action.call_args[0][0].identifier, self.cookie.identifier)
        self.assertFalse(halt)

    def test_evaluate_rules_with_cookie_when_matched_rules_and_termination(self):
        production_to_be_called = MagicMock(return_value=True)
        extra_rules = [
            Rule(lambda *args: True, lambda *args: False, Priority.MIN_PRIORITY),
            Rule(lambda *args: True, production_to_be_called,
                 Priority.get_lower_priority_value(Priority.MAX_PRIORITY)),
            Rule(lambda *args: False, lambda *args: False, Priority.MAX_PRIORITY)
        ]
        self.processor.rules = self.rules + extra_rules
        halt = self.processor.evaluate_rules_with_cookie(self.cookie)

        total_call_count = 0
        for rule in self.rules:
            total_call_count += rule._action.call_count
        self.assertEqual(total_call_count, 0)
        self.assertEqual(production_to_be_called.call_count, 1)
        self.assertTrue(halt)

    def test_evaluate_rules_with_cookie_does_not_allow_rule_to_change_cookie_for_subsequent_rules(self):
        source = "my_enrichment"
        change_detected_in_next_rule = False

        def cookie_changer(cookie: Cookie) -> bool:
            enrichment = Enrichment(source, datetime(year=2000, month=1, day=1), Metadata())
            cookie.enrich(enrichment)
            return False

        def record_fail_if_changed(cookie: Cookie) -> bool:
            nonlocal change_detected_in_next_rule
            if source in cookie.get_enrichment_sources():
                change_detected_in_next_rule = True

        self.processor.rules = [
            Rule(cookie_changer, self.rules[0]._action, RULE_IDENTIFIER, priority=Priority.MAX_PRIORITY),
            Rule(record_fail_if_changed, self.rules[0]._action, RULE_IDENTIFIER, priority=Priority.MIN_PRIORITY)
        ]
        self.processor.process_cookie(self.cookie)

        self.assertFalse(change_detected_in_next_rule)

    def test_evaluate_rules_with_cookie_when_no_matched__does_not_enrich_with_rule_application_log(self):
        self.processor.rules = self.rules
        _ = self.processor.evaluate_rules_with_cookie(self.cookie)

        cookie = self.cookie_jar.fetch_cookie(self.cookie.identifier)
        self.assertEqual(len(cookie.enrichments), 1)

    def test_evaluate_rules_with_cookie_when_matched_enriches_with_rule_application_log(self):
        matched_rule_identifier = "matched_rule"
        terminates = False
        self.processor.rules = self.rules \
                               + [Rule(lambda *args: True, MagicMock(return_value=terminates), matched_rule_identifier)]
        _ = self.processor.evaluate_rules_with_cookie(self.cookie)

        cookie = self.cookie_jar.fetch_cookie(self.cookie.identifier)
        self.assertGreater(len(cookie.enrichments), 1)
        last_enrichment = cookie.enrichments[-1]
        self.assertEqual(last_enrichment.source, RULE_APPLICATION)
        rule_application_log = RuleApplicationLogJSONDecoder().decode_parsed(last_enrichment.metadata)  # type: RuleApplicationLog
        self.assertEqual(rule_application_log.rule_id, matched_rule_identifier)
        self.assertEqual(rule_application_log.terminated_processing, terminates)

    def test_handle_cookie_enrichment_when_matching_enrichments(self):
        self.processor.notification_receivers = [MagicMock()]
        self.processor.enrichment_loaders = [EnrichmentLoader(
            lambda *args: True, lambda *args: SAMPLE_ENRICHMENT, RULE_IDENTIFIER)]

        self.processor.handle_cookie_enrichment(self.cookie)

        self.processor.notification_receivers[0].receive.assert_not_called()
        cookie = self.cookie_jar.get_next_for_processing()
        self.assertIn(SAMPLE_ENRICHMENT, cookie.enrichments)
class TestBasicProcessor(unittest.TestCase):
    """
    Tests for `BasicProcessor`.
    """
    def setUp(self):
        self.cookie_jar = InMemoryCookieJar()
        self.rules = [Rule(lambda *args: False, MagicMock()) for _ in range(10)]
        self.cookie = Cookie(COOKIE_IDENTIFIER)
        self.processor = BasicProcessor(self.cookie_jar, [], [])

    def test_evaluate_rules_with_cookie_when_no_rules(self):
        halt = self.processor.evaluate_rules_with_cookie(self.cookie)

        self.assertFalse(halt)

    def test_evaluate_rules_with_cookie_when_no_matched_rules(self):
        self.processor.rules = self.rules
        halt = self.processor.evaluate_rules_with_cookie(self.cookie)

        for rule in self.rules:
            rule._action.assert_not_called()
        self.assertFalse(halt)

    def test_evaluate_rules_with_cookie_when_matched_rules_and_no_termination(self):
        extra_rules = [
            Rule(lambda *args: True, MagicMock(return_value=False)),
            Rule(lambda *args: True, MagicMock(return_value=False))
        ]
        self.processor.rules = self.rules + extra_rules
        halt = self.processor.evaluate_rules_with_cookie(self.cookie)

        for rule in self.rules:
            rule._action.assert_not_called()
        self.assertFalse(halt)

    def test_evaluate_rules_with_cookie_when_matched_rules_and_termination(self):
        production_to_be_called = MagicMock(return_value=True)
        extra_rules = [
            Rule(lambda *args: True, lambda *args: False, Priority.MIN_PRIORITY),
            Rule(lambda *args: True, production_to_be_called,
                 Priority.get_lower_priority_value(Priority.MAX_PRIORITY)),
            Rule(lambda *args: False, lambda *args: False, Priority.MAX_PRIORITY)
        ]
        self.processor.rules = self.rules + extra_rules
        halt = self.processor.evaluate_rules_with_cookie(self.cookie)

        total_call_count = 0
        for rule in self.rules:
            total_call_count += rule._action.call_count
        self.assertEqual(total_call_count, 0)
        self.assertEqual(production_to_be_called.call_count, 1)
        self.assertTrue(halt)

    def test_evaluate_rules_with_cookie_does_not_allow_rule_to_change_cookie_for_subsequent_rules(self):
        source = "my_enrichment"
        change_detected_in_next_rule = False

        def cookie_changer(cookie: Cookie) -> bool:
            enrichment = Enrichment(source, datetime(year=2000, month=1, day=1), Metadata())
            cookie.enrich(enrichment)
            return False

        def record_fail_if_changed(cookie: Cookie) -> bool:
            nonlocal change_detected_in_next_rule
            if source in cookie.get_enrichment_sources():
                change_detected_in_next_rule = True

        self.processor.rules = [
            Rule(cookie_changer, self.rules[0]._action, priority=Priority.MAX_PRIORITY),
            Rule(record_fail_if_changed, self.rules[0]._action, priority=Priority.MIN_PRIORITY)
        ]
        self.processor.process_cookie(self.cookie)

        self.assertFalse(change_detected_in_next_rule)

    def test_handle_cookie_enrichment_when_matching_enrichments(self):
        self.processor.notification_receivers = [MagicMock()]
        self.processor.enrichment_loaders = [EnrichmentLoader(lambda *args: True, lambda *args: SAMPLE_ENRICHMENT)]

        self.processor.handle_cookie_enrichment(self.cookie)

        self.processor.notification_receivers[0].receive.assert_not_called()
        cookie = self.cookie_jar.get_next_for_processing()
        self.assertIn(SAMPLE_ENRICHMENT, cookie.enrichments)