def __init__( self, subject: Optional[str] = None, *, do_syntax_check_first: Optional[bool] = None, db_session: Optional[Session] = None, use_collection: Optional[bool] = None, ) -> None: self.collection_query_tool = CollectionQueryTool() if self.params is None: self.params = CheckerParamsBase() if self.status is None: self.status = CheckerStatusBase() if subject is not None: self.subject = subject if do_syntax_check_first is not None: self.do_syntax_check_first = do_syntax_check_first else: self.do_syntax_check_first = self.STD_DO_SYNTAX_CHECK_FIRST if use_collection is not None: self.use_collection = use_collection else: self.guess_and_set_use_collection() self.db_session = db_session
def __post_init__(self) -> None: self.stdout_printer = StdoutPrinter() self.file_printer = FilePrinter() self.whois_dataset = get_whois_dataset_object( db_session=self.db_session) self.inactive_dataset = get_inactive_dataset_object( db_session=self.db_session) self.continue_dataset = get_continue_databaset_object( db_session=self.db_session) self.status_file_generator = StatusFileGenerator().guess_all_settings() self.counter = FilesystemCounter() self.collection_query_tool = CollectionQueryTool() self.header_already_printed = False return super().__post_init__()
def test_setpreferred_status_origin_through_init(self) -> None: """ Tests the overwritting of the preferred status origin through the class constructor. """ given = "frequent" expected = given query_tool = CollectionQueryTool(preferred_status_origin=given) actual = query_tool.preferred_status_origin self.assertEqual(expected, actual)
def test_set_url_base_through_init(self) -> None: """ Tests the overwritting of the URL to work from through the class constructor. """ given = "https://example.net" expected = given query_tool = CollectionQueryTool(url_base=given) actual = query_tool.url_base self.assertEqual(expected, actual)
def test_set_token_through_init(self) -> None: """ Tests the overwritting of the token to work through the class constructor. """ given = secrets.token_urlsafe(6) expected = given query_tool = CollectionQueryTool(token=given) actual = query_tool.token self.assertEqual(expected, actual)
def test_set_preferred_status_origin_through_init_none_given(self) -> None: """ Tests the overwritting of the preferred status origin through the class constructor. In this test, we test the case that nothing is given. """ given = None expected = "frequent" query_tool = CollectionQueryTool(preferred_status_origin=given) actual = query_tool.preferred_status_origin self.assertEqual(expected, actual)
def test_set_url_base_through_init_none_given(self) -> None: """ Tests the overwritting of the URL to work from through the class constructor. In this test, we test the case that the URL base is not given. """ given = None expected = "http://localhost:8001" query_tool = CollectionQueryTool(url_base=given) actual = query_tool.url_base self.assertEqual(expected, actual)
def test_set_token_through_init_environment_variable_given(self) -> None: """ Tests the overwritting of the token to work through the class constructor. In this test we test the case that the environment variable is given. """ given = secrets.token_urlsafe(6) expected = given os.environ["PYFUNCEBLE_COLLECTION_API_TOKEN"] = given query_tool = CollectionQueryTool(token=None) actual = query_tool.token self.assertEqual(expected, actual)
def test_set_token_through_init_environment_variable_not_given( self) -> None: """ Tests the overwritting of the token to work through the class constructor. In this test we test the case that nothing is given or declared. """ if "PYFUNCEBLE_COLLECTION_API_TOKEN" in os.environ: del os.environ["PYFUNCEBLE_COLLECTION_API_TOKEN"] expected = "" query_tool = CollectionQueryTool(token=None) actual = query_tool.token self.assertEqual(expected, actual)
def setUp(self) -> None: """ Sets everything needed by the tests. """ self.query_tool = CollectionQueryTool() self.response_dataset = { "subject": "example.net", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "status": { "syntax": { "latest": { "status": "VALID", "status_source": "SYNTAX", "tested_at": "2021-09-28T19:32:07.167Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, "frequent": "VALID", }, "availability": { "latest": { "status": "ACTIVE", "status_source": "WHOIS", "tested_at": "2021-09-28T19:32:07.167Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, "frequent": "ACTIVE", }, "reputation": { "latest": { "status": "SANE", "status_source": "REPUTATION", "tested_at": "2021-09-28T19:32:07.167Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, "frequent": "SANE", }, "whois": { "expiration_date": "2021-09-28T19:32:07.167Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "subject_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, }, } self.status_dataset = { "status": "ACTIVE", "status_source": "WHOIS", "tested_at": "2021-09-28T20:55:41.730Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "subject_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", } self.availability_status_dataset = { "checker_type": "AVAILABILITY", "dns_lookup": { "NS": ["a.iana-servers.net.", "b.iana-servers.net."] }, "dns_lookup_record": { "dns_name": "example.com.", "follow_nameserver_order": True, "nameserver": "9.9.9.9", "port": 53, "preferred_protocol": "UDP", "query_record_type": "NS", "query_timeout": 5.0, "response": ["a.iana-servers.net.", "b.iana-servers.net."], "subject": "example.com", "used_protocol": "UDP", }, "domain_syntax": True, "expiration_date": None, "http_status_code": None, "idna_subject": "example.com", "ip_syntax": False, "ipv4_range_syntax": False, "ipv4_syntax": False, "ipv6_range_syntax": False, "ipv6_syntax": False, "netinfo": None, "params": { "do_syntax_check_first": False, "use_dns_lookup": True, "use_extra_rules": True, "use_http_code_lookup": True, "use_netinfo_lookup": True, "use_reputation_lookup": False, "use_whois_db": True, "use_whois_lookup": False, }, "second_level_domain_syntax": True, "status": "ACTIVE", "status_after_extra_rules": None, "status_before_extra_rules": None, "status_source": "DNSLOOKUP", "status_source_after_extra_rules": None, "status_source_before_extra_rules": None, "subdomain_syntax": False, "subject": "example.com", "tested_at": datetime.fromisoformat("2021-03-09T17:42:15.771647"), "url_syntax": False, "whois_lookup_record": { "expiration_date": None, "port": 43, "query_timeout": 5.0, "record": None, "server": None, "subject": "example.com", }, "whois_record": None, } return super().setUp()
class TestCollectionQueryTool(unittest.TestCase): """ Tests the Collection query tool. """ def setUp(self) -> None: """ Sets everything needed by the tests. """ self.query_tool = CollectionQueryTool() self.response_dataset = { "subject": "example.net", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "status": { "syntax": { "latest": { "status": "VALID", "status_source": "SYNTAX", "tested_at": "2021-09-28T19:32:07.167Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, "frequent": "VALID", }, "availability": { "latest": { "status": "ACTIVE", "status_source": "WHOIS", "tested_at": "2021-09-28T19:32:07.167Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, "frequent": "ACTIVE", }, "reputation": { "latest": { "status": "SANE", "status_source": "REPUTATION", "tested_at": "2021-09-28T19:32:07.167Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, "frequent": "SANE", }, "whois": { "expiration_date": "2021-09-28T19:32:07.167Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "subject_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, }, } self.status_dataset = { "status": "ACTIVE", "status_source": "WHOIS", "tested_at": "2021-09-28T20:55:41.730Z", "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "subject_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", } self.availability_status_dataset = { "checker_type": "AVAILABILITY", "dns_lookup": { "NS": ["a.iana-servers.net.", "b.iana-servers.net."] }, "dns_lookup_record": { "dns_name": "example.com.", "follow_nameserver_order": True, "nameserver": "9.9.9.9", "port": 53, "preferred_protocol": "UDP", "query_record_type": "NS", "query_timeout": 5.0, "response": ["a.iana-servers.net.", "b.iana-servers.net."], "subject": "example.com", "used_protocol": "UDP", }, "domain_syntax": True, "expiration_date": None, "http_status_code": None, "idna_subject": "example.com", "ip_syntax": False, "ipv4_range_syntax": False, "ipv4_syntax": False, "ipv6_range_syntax": False, "ipv6_syntax": False, "netinfo": None, "params": { "do_syntax_check_first": False, "use_dns_lookup": True, "use_extra_rules": True, "use_http_code_lookup": True, "use_netinfo_lookup": True, "use_reputation_lookup": False, "use_whois_db": True, "use_whois_lookup": False, }, "second_level_domain_syntax": True, "status": "ACTIVE", "status_after_extra_rules": None, "status_before_extra_rules": None, "status_source": "DNSLOOKUP", "status_source_after_extra_rules": None, "status_source_before_extra_rules": None, "subdomain_syntax": False, "subject": "example.com", "tested_at": datetime.fromisoformat("2021-03-09T17:42:15.771647"), "url_syntax": False, "whois_lookup_record": { "expiration_date": None, "port": 43, "query_timeout": 5.0, "record": None, "server": None, "subject": "example.com", }, "whois_record": None, } return super().setUp() def tearDown(self) -> None: """ Destroys everything needed by the tests. """ del self.query_tool del self.response_dataset del self.availability_status_dataset def test_set_token_return(self) -> None: """ Tests the response from the method which let us set the token to work with. """ given = secrets.token_urlsafe(6) actual = self.query_tool.set_token(given) self.assertIsInstance(actual, CollectionQueryTool) def test_set_token_method(self) -> None: """ Tests the method which let us set the token to work with. """ given = secrets.token_urlsafe(6) expected = given self.query_tool.set_token(given) actual = self.query_tool.token self.assertEqual(expected, actual) def test_set_token_attribute(self) -> None: """ Tests the overwritting of the token attribute. """ given = secrets.token_urlsafe(6) expected = given self.query_tool.token = given actual = self.query_tool.token self.assertEqual(expected, actual) def test_set_token_through_init(self) -> None: """ Tests the overwritting of the token to work through the class constructor. """ given = secrets.token_urlsafe(6) expected = given query_tool = CollectionQueryTool(token=given) actual = query_tool.token self.assertEqual(expected, actual) def test_set_token_through_init_environment_variable_not_given( self) -> None: """ Tests the overwritting of the token to work through the class constructor. In this test we test the case that nothing is given or declared. """ if "PYFUNCEBLE_COLLECTION_API_TOKEN" in os.environ: del os.environ["PYFUNCEBLE_COLLECTION_API_TOKEN"] expected = "" query_tool = CollectionQueryTool(token=None) actual = query_tool.token self.assertEqual(expected, actual) def test_set_token_through_init_environment_variable_given(self) -> None: """ Tests the overwritting of the token to work through the class constructor. In this test we test the case that the environment variable is given. """ given = secrets.token_urlsafe(6) expected = given os.environ["PYFUNCEBLE_COLLECTION_API_TOKEN"] = given query_tool = CollectionQueryTool(token=None) actual = query_tool.token self.assertEqual(expected, actual) def test_set_token_not_str(self) -> None: """ Tests the method which let us set the token to work with for the case that the given token is not a :py:class:`str`. """ given = ["Hello", "World!"] self.assertRaises(TypeError, lambda: self.query_tool.set_token(given)) def test_set_url_base_return(self) -> None: """ Tests the response from the method which let us set the URL to work from. """ given = "https://example.org" actual = self.query_tool.set_url_base(given) self.assertIsInstance(actual, CollectionQueryTool) def test_set_url_base_method(self) -> None: """ Tests the method which let us set the URL to work from. """ given = "https://example.org" expected = given self.query_tool.set_url_base(given) actual = self.query_tool.url_base self.assertEqual(expected, actual) def test_set_url_base_attribute(self) -> None: """ Tests the overwritting of the url_base attribute. """ given = "https://example.org" expected = given self.query_tool.url_base = given actual = self.query_tool.url_base self.assertEqual(expected, actual) def test_set_url_base_through_init(self) -> None: """ Tests the overwritting of the URL to work from through the class constructor. """ given = "https://example.net" expected = given query_tool = CollectionQueryTool(url_base=given) actual = query_tool.url_base self.assertEqual(expected, actual) def test_set_url_base_through_init_none_given(self) -> None: """ Tests the overwritting of the URL to work from through the class constructor. In this test, we test the case that the URL base is not given. """ given = None expected = "http://*****:*****@unittest.mock.patch.object(requests.Session, "post") def test_collection_contain(self, request_mock) -> None: """ Tests the method which let us pull the subject from the collection. """ response_dict = self.response_dataset response_dict["subject"] = "example.com" def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = json.dumps(response_dict) response = requests.models.Response() response.url = "https://example.org/v1/search" response.status_code = 200 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" request_mock.side_effect = mocking expected = True actual = "example.com" in self.query_tool self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_collection_not_contain(self, request_mock) -> None: """ Tests the method which let us pull the subject from the collection. """ response_dict = {"detail": "Invalid subject."} def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = json.dumps(response_dict) response = requests.models.Response() response.url = "https://example.org/v1/search" response.status_code = 404 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" request_mock.side_effect = mocking expected = False actual = "example.com" in self.query_tool self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_getitem(self, request_mock) -> None: """ Tests the method which let us pull the subject from the collection. """ response_dict = self.response_dataset response_dict["subject"] = "example.org" def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = json.dumps(response_dict) response = requests.models.Response() response.url = "https://example.org/v1/search" response.status_code = 200 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" request_mock.side_effect = mocking expected = response_dict actual = self.query_tool["example.org"] self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_getitem_not_found(self, request_mock) -> None: """ Tests the method which let us pull the subject from the collection. """ response_dict = {"detail": "Invalid subject."} def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = json.dumps(response_dict) response = requests.models.Response() response.url = "https://example.org/v1/search" response.status_code = 404 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" request_mock.side_effect = mocking expected = None actual = self.query_tool["example.de"] self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_pull(self, request_mock) -> None: """ Tests the method which let us pull the subject from the collection. """ response_dict = self.response_dataset response_dict["subject"] = "example.net" def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = json.dumps(response_dict) response = requests.models.Response() response.url = "https://example.org/v1/search" response.status_code = 200 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" request_mock.side_effect = mocking expected = response_dict actual = self.query_tool.pull("example.net") self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_pull_subject_not_found(self, request_mock) -> None: """ Tests the method which let us pull the subject from the collection. In this test case we check what happens when a subject is not found. """ response_dict = {"detail": "Invalid subject."} def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = json.dumps(response_dict) response = requests.models.Response() response.url = "https://example.org/v1/search" response.status_code = 404 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" request_mock.side_effect = mocking expected = None actual = self.query_tool.pull("example.net") self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_pull_subject_no_json_response(self, request_mock) -> None: """ Tests the method which let us pull the subject from the collection. In this test case we check what happens when no JSON response is given. """ def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = "I'm a teapot." response = requests.models.Response() response.url = "https://example.org/v1/search" response.status_code = 418 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" request_mock.side_effect = mocking expected = None actual = self.query_tool.pull("example.net") self.assertEqual(expected, actual) def test_pull_subject_not_str(self) -> None: """ Tests the method which let us pull the subject from the collection. In this test we test the case that the given subject is not a :py:class:`str`. """ self.query_tool.url_base = "https://example.org" self.assertRaises(TypeError, lambda: self.query_tool.pull(284)) @unittest.mock.patch.object(requests.Session, "post") def test_push(self, request_mock) -> None: """ Tests the method which let us push some dataset into the collection. """ response_dict = self.response_dataset response_dict["subject"] = "example.net" def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = json.dumps(response_dict) response = requests.models.Response() response.url = "https://example.org/v1/status/availability" response.status_code = 200 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" self.query_tool.token = secrets.token_urlsafe(6) request_mock.side_effect = mocking expected = response_dict actual = self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)) self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_push_no_json_response(self, request_mock) -> None: """ Tests the method which let us push some dataset into the collection. In this test case, we test the case that the response is not JSON encoded. """ response_dict = self.response_dataset response_dict["subject"] = "example.net" def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = "I'm a teapot." response = requests.models.Response() response.url = "https://example.org/v1/status/availability" response.status_code = 418 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" request_mock.side_effect = mocking expected = None actual = self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)) self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_push_with_whois(self, request_mock) -> None: """ Tests the method which let us push some dataset into the collection. """ response_dict = self.response_dataset response_dict["subject"] = "example.net" self.availability_status_dataset["expiration_date"] = "23-nov-2090" def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = json.dumps(response_dict) response = requests.models.Response() response.url = "https://example.org/v1/status/availability" response.status_code = 200 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" self.query_tool.token = secrets.token_urlsafe(6) request_mock.side_effect = mocking expected = response_dict actual = self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)) self.assertEqual(expected, actual) @unittest.mock.patch.object(requests.Session, "post") def test_push_with_whois_no_json_response(self, request_mock) -> None: """ Tests the method which let us push some dataset into the collection. """ response_dict = self.response_dataset response_dict["subject"] = "example.net" self.availability_status_dataset["expiration_date"] = "23-nov-2090" def mocking(*args, **kwargs): # pylint: disable=unused-argument response_content = "I'm a teapot." response = requests.models.Response() response.url = "https://example.org/v1/status/availability" response.status_code = 418 # pylint: disable=protected-access response._content = str.encode(response_content) response.history = [response] return response self.query_tool.url_base = "https://example.org" self.query_tool.token = secrets.token_urlsafe(6) request_mock.side_effect = mocking expected = None actual = self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)) self.assertEqual(expected, actual) def test_push_with_whois_token_not_given(self) -> None: """ Tests the method which let us push some dataset into the collection. In this test, we test the case that no token is given. """ response_dict = self.response_dataset response_dict["subject"] = "example.net" self.availability_status_dataset["expiration_date"] = "23-nov-2090" if "PYFUNCEBLE_COLLECTION_API_TOKEN" in os.environ: del os.environ["PYFUNCEBLE_COLLECTION_API_TOKEN"] self.query_tool.token = "" expected = None actual = self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)) self.assertEqual(expected, actual) def test_push_subject_not_str(self) -> None: """ Tests the method which let us push some dataset into the collection. In this test, we test the case that the given subject is not a string. """ self.availability_status_dataset["subject"] = 293 self.assertRaises( TypeError, lambda: self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)), ) def test_push_checker_status_not_correct(self) -> None: """ Tests the method which let us push some dataset into the collection. In this test, we test the case that the given checker status is not correct. """ self.availability_status_dataset["subject"] = "foo.example.org" self.assertRaises( TypeError, lambda: self.query_tool.push(self.availability_status_dataset), ) def test_push_subject_empty_str(self) -> None: """ Tests the method which let us push some dataset into the collection. In this test, we test the case that the given subject is an empty string. """ self.availability_status_dataset["subject"] = "" self.assertRaises( ValueError, lambda: self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)), ) def test_push_checker_type_not_str(self) -> None: """ Tests the method which let us push some dataset into the collection. In this test, we test the case that the given subject is not a string. """ self.availability_status_dataset["checker_type"] = 987 self.assertRaises( TypeError, lambda: self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)), ) def test_push_checker_type_not_supported(self) -> None: """ Tests the method which let us push some dataset into the collection. In this test, we test the case that the given subject is not a string. """ self.availability_status_dataset["checker_type"] = "GIT" self.query_tool.token = secrets.token_urlsafe(6) self.assertRaises( ValueError, lambda: self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)), ) def test_push_token_not_given(self) -> None: """ Tests the method which let us push some dataset into the collection. In this test, we test the case that no token is given. """ if "PYFUNCEBLE_COLLECTION_API_TOKEN" in os.environ: del os.environ["PYFUNCEBLE_COLLECTION_API_TOKEN"] self.query_tool.token = "" expected = None actual = self.query_tool.push( AvailabilityCheckerStatus(**self.availability_status_dataset)) self.assertEqual(expected, actual)
class ProducerWorker(WorkerBase): """ Provides our producer worker. The objective of this worker is to provides a single worker (or process if you prefer) which will be used to handle the production of output to stdout or files. """ STD_NAME: str = "pyfunceble_producer_worker" stdout_printer: Optional[StdoutPrinter] = None file_printer: Optional[FilePrinter] = None whois_dataset: Optional[WhoisDatasetBase] = None inactive_dataset: Optional[InactiveDatasetBase] = None continue_dataset: Optional[ContinueDatasetBase] = None status_file_generator: Optional[StatusFileGenerator] = None counter: Optional[FilesystemCounter] = None collection_query_tool: Optional[CollectionQueryTool] = None header_already_printed: Optional[bool] = None INACTIVE_STATUSES: Tuple[str, ...] = ( PyFunceble.storage.STATUS.down, PyFunceble.storage.STATUS.invalid, ) def __post_init__(self) -> None: self.stdout_printer = StdoutPrinter() self.file_printer = FilePrinter() self.whois_dataset = get_whois_dataset_object( db_session=self.db_session) self.inactive_dataset = get_inactive_dataset_object( db_session=self.db_session) self.continue_dataset = get_continue_databaset_object( db_session=self.db_session) self.status_file_generator = StatusFileGenerator().guess_all_settings() self.counter = FilesystemCounter() self.collection_query_tool = CollectionQueryTool() self.header_already_printed = False return super().__post_init__() @staticmethod def should_we_ignore(test_result: CheckerStatusBase) -> bool: """ Checks if we should ignore the given datasets. :param test_result: The test result to check. """ return isinstance(test_result, str) and test_result.startswith("ignored_") @staticmethod def should_we_print_status_to_stdout(status: str) -> bool: """ Checks if we are allows to print the given status (to stdout). :param status: The status to check. """ if isinstance( PyFunceble.storage.CONFIGURATION.cli_testing.display_mode. status, list): to_keep = PyFunceble.storage.CONFIGURATION.cli_testing.display_mode.status else: to_keep = [ PyFunceble.storage.CONFIGURATION.cli_testing.display_mode. status ] to_keep = [x.upper() for x in to_keep] status = status.upper() return "ALL" in to_keep or status in to_keep def should_we_block_status_file_printer( self, test_dataset: dict, test_result: CheckerStatusBase) -> bool: """ Checks if we should block the file printer. The reason behindn this is that we don't want to generate an output when a subject was already into the inactive database. """ return ("from_inactive" in test_dataset and test_result.status in self.INACTIVE_STATUSES) def run_stdout_printer(self, test_result: CheckerStatusBase) -> None: """ Runs the stdout printer (if necessary). :param test_result: The rest result dataset. """ if not PyFunceble.storage.CONFIGURATION.cli_testing.display_mode.quiet: # pylint: disable=line-too-long if self.should_we_print_status_to_stdout(test_result.status): self.stdout_printer.template_to_use = get_template_to_use() if not self.header_already_printed: self.stdout_printer.print_header() self.header_already_printed = True self.stdout_printer.set_dataset( test_result.to_dict()).print_interpolated_line() else: print_single_line() else: print_single_line() def run_whois_backup(self, test_result: CheckerStatusBase) -> None: """ Runs the backup or update of the WHOIS record in our dataset storage. :param test_result: The test result. """ if (hasattr(test_result, "expiration_date") and test_result.expiration_date and test_result.whois_record): # Note: The whois record is always given if the status does not come # from the database. self.whois_dataset.update({ "subject": test_result.subject, "idna_subject": test_result.idna_subject, "expiration_date": test_result.expiration_date, "epoch": str( datetime.datetime.strptime(test_result.expiration_date, "%d-%b-%Y").timestamp()), }) def run_inactive_backup(self, test_dataset: dict, test_result: CheckerStatusBase) -> None: """ Runs the backup or update of the Inactive dataset storage. The idea is that if the status is OK (active), we just remove it from the dataset storage. Otherwise, we just keep it in there :-) """ if test_dataset["type"] != "single": dataset = test_result.to_dict() dataset.update(test_dataset) PyFunceble.facility.Logger.debug("Test Dataset: %r", test_dataset) PyFunceble.facility.Logger.debug("Test Result: %r", test_result) if "from_inactive" in dataset: if test_result.status in self.INACTIVE_STATUSES: if dataset["destination"]: # Note: This handles the case that someone try to test a # single subject. # In fact, we should not generate any file if the output # directory is not given. self.inactive_dataset.update(dataset) else: self.inactive_dataset.remove(dataset) elif (test_result.status in self.INACTIVE_STATUSES and dataset["destination"]): self.inactive_dataset.update(dataset) def run_continue_backup(self, test_dataset: dict, test_result: CheckerStatusBase) -> None: """ Runs the backup or update of the auto-continue dataset storage. """ if test_dataset["type"] != "single": dataset = test_result.to_dict() dataset.update(test_dataset) if (isinstance(self.continue_dataset, CSVContinueDataset) and dataset["output_dir"]): # Note: This handles the case that someone try to test a single subject. # In fact, we should not generate any file if the output directory # is not given. # # The exception handler is just there to catch the case that the # method is not implemented because we are more likely using an # alternative backup (for example mariadb). self.continue_dataset.set_base_directory(dataset["output_dir"]) self.continue_dataset.update(dataset) def run_ignored_file_printer(self, test_dataset: dict, test_result: str) -> None: """ Runs the analytic behind the file printer. .. warning:: Thie method assume that the givne dataset is ignored from the normal file printer. """ if (test_result == "ignored_inactive" and test_dataset["destination"] and not PyFunceble.storage.CONFIGURATION.cli_testing. file_generation.no_file): if "idna_subject" not in test_dataset: # The printer always prints the idna subject. test_dataset["idna_subject"] = domain2idna( test_dataset["subject"]) # We use this object just to get the output directory :-) self.status_file_generator.set_parent_dirname( test_dataset["destination"]) # We now generate the file which let the end-user know that # we ignored the test of the current test dataset because there # is no need to retest it yet. # pylint: disable=line-too-long self.file_printer.destination = os.path.join( self.status_file_generator.get_output_basedir(), PyFunceble.cli.storage.OUTPUTS.logs.directories.parent, PyFunceble.cli.storage.OUTPUTS.logs.filenames. inactive_not_retested, ) self.file_printer.template_to_use = "plain" self.file_printer.dataset = test_dataset self.file_printer.print_interpolated_line() def run_status_file_printer(self, test_dataset: dict, test_result: CheckerStatusBase) -> None: """ Runs the status file printer. """ if (test_dataset["destination"] and not PyFunceble.storage. CONFIGURATION.cli_testing.file_generation.no_file): previous_allow_hosts_file = self.status_file_generator.allow_hosts_files previous_allow_plain_file = self.status_file_generator.allow_plain_files self.status_file_generator.parent_dirname = test_dataset[ "destination"] self.status_file_generator.status = test_result self.status_file_generator.test_dataset = test_dataset if test_dataset["subject_type"] == "url": self.status_file_generator.allow_hosts_files = False self.status_file_generator.allow_plain_files = True self.status_file_generator.start() self.status_file_generator.allow_hosts_files = previous_allow_hosts_file self.status_file_generator.allow_plain_files = previous_allow_plain_file def run_counter(self, test_dataset: dict, test_result: CheckerStatusBase) -> None: """ Runs the counter of the current file. """ if (test_dataset["destination"] and not PyFunceble.storage. CONFIGURATION.cli_testing.file_generation.no_file): # Note: We don't want hiden data to be counted. self.counter.set_parent_dirname( test_dataset["destination"]).count(test_result) def target(self, consumed: Any) -> Optional[Tuple[Any, ...]]: if not isinstance(consumed, tuple): PyFunceble.facility.Logger.info( "Skipping latest dataset because consumed data was not a tuple." ) return None # Just for human brain. test_dataset, test_result = consumed if not isinstance(test_dataset, dict): PyFunceble.facility.Logger.info( "Skipping because test dataset is not a dictionnary.") return None if self.should_we_ignore(test_result): PyFunceble.facility.Logger.info( "Ignored test dataset. Reason: No output wanted.") self.run_ignored_file_printer(test_dataset, test_result) return None if not isinstance(test_result, CheckerStatusBase): PyFunceble.facility.Logger.info( "Skipping latest dataset because consumed status is not " "a status object..") return None if (PyFunceble.storage.CONFIGURATION.collection.push and test_result.status_source != "COLLECTION"): self.collection_query_tool.push(test_result) self.run_whois_backup(test_result) self.run_inactive_backup(test_dataset, test_result) self.run_continue_backup(test_dataset, test_result) if not self.should_we_block_status_file_printer( test_dataset, test_result): self.run_status_file_printer(test_dataset, test_result) self.run_counter(test_dataset, test_result) self.run_stdout_printer(test_result) test_dataset["from_producer"] = True return test_dataset, test_result