From f6860510ea09bab769ed025ca5f7512e37f5a21c Mon Sep 17 00:00:00 2001 From: "Christopher K. Hoadley" Date: Mon, 21 Jan 2019 19:26:07 -0600 Subject: [PATCH] Add basic test infrastructure using unittest. Add tests to verify the 3 mechanisms of determining that a username is *not* supported on a given site. Created SherlockBaseTest() class that provides infrastructure for the tests. TODO: It seems that devRant is not detecting users that certainly exist. TODO: While running tests, there is a ResourceWarning from an unclosed SSLSocket. This needs work. --- README.md | 22 +++++++++- tests/__init__.py | 4 ++ tests/all.py | 94 ++++++++++++++++++++++++++++++++++++++++ tests/base.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 tests/__init__.py create mode 100644 tests/all.py create mode 100644 tests/base.py diff --git a/README.md b/README.md index 48496518..1abf5f85 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Sherlock -> Find usernames across [social networks](https://github.com/theyahya/sherlock/blob/master/sites.md) +> Find usernames across [social networks](https://github.com/theyahya/sherlock/blob/master/sites.md)

@@ -77,6 +77,26 @@ Or you can simply use "Docker Hub" to run `sherlock`: docker run theyahya/sherlock user123 ``` +## Tests +If you are contributing to Sherlock, then Thank You! + +Before creating a pull request with new development, please run the tests +to ensure that all is well. It would also be a good idea to run the tests +before starting development to distinguish problems between your +environment and the Sherlock software. + +The following is an example of the command line to run all the tests for +Sherlock. This invocation hides the progress text that Sherlock normally +outputs, and instead shows the verbose output of the tests. + +``` +% python3 -m unittest tests.all --buffer --verbose +``` + +Note that the tests are very much a work in progress. Significant work is +required to get full test coverage. But, the current tests are working +properly, and will be expanded as time goes by. + ## License MIT © [Yahya SayadArbabi](https://theyahya.com) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..944e27ce --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,4 @@ +"""Sherlock Tests + +This package contains various submodules used to run tests. +""" diff --git a/tests/all.py b/tests/all.py new file mode 100644 index 00000000..87d3b9b8 --- /dev/null +++ b/tests/all.py @@ -0,0 +1,94 @@ +"""Sherlock Tests + +This module contains various tests. +""" +from tests.base import SherlockBaseTest +import unittest + + +class SherlockDetectTests(SherlockBaseTest): + def test_detect_true(self): + """Test Username Existence Detection. + + This test ensures that the mechanism of ensuring that a Username + exists works properly. + + Keyword Arguments: + self -- This object. + + Return Value: + N/A. + Will trigger an assert if Usernames which are known to exist are + not detected. + """ + + self.username_check(['jack'], ['Twitter'], exist_check=True) + #self.username_check(['dfox'], ['devRant'], exist_check=True) + self.username_check(['blue'], ['Pinterest'], exist_check=True) + self.username_check(['kevin'], ['Instagram'], exist_check=True) + self.username_check(['zuck'], ['Facebook'], exist_check=True) + + return + + def test_detect_false_via_message(self): + """Test Username Does Not Exist (Via Message). + + This test ensures that the "message" detection mechanism of + ensuring that a Username does *not* exist works properly. + + Keyword Arguments: + self -- This object. + + Return Value: + N/A. + Will trigger an assert if detection mechanism did not work as expected. + """ + + self.username_check(['jackkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'], + ['Instagram'], + exist_check=False + ) + + return + + def test_detect_false_via_status_code(self): + """Test Username Does Not Exist (Via Status Code). + + This test ensures that the "status code" detection mechanism of + ensuring that a Username does *not* exist works properly. + + Keyword Arguments: + self -- This object. + + Return Value: + N/A. + Will trigger an assert if detection mechanism did not work as expected. + """ + + self.username_check(['jackkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'], + ['Facebook'], + exist_check=False + ) + + return + + def test_detect_false_via_response_url(self): + """Test Username Does Not Exist (Via Response URL). + + This test ensures that the "response URL" detection mechanism of + ensuring that a Username does *not* exist works properly. + + Keyword Arguments: + self -- This object. + + Return Value: + N/A. + Will trigger an assert if detection mechanism did not work as expected. + """ + + self.username_check(['jackkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'], + ['Pinterest'], + exist_check=False + ) + + return diff --git a/tests/base.py b/tests/base.py new file mode 100644 index 00000000..0f992ba8 --- /dev/null +++ b/tests/base.py @@ -0,0 +1,107 @@ +"""Sherlock Base Tests + +This module contains various utilities for running tests. +""" +import json +import os +import os.path +import unittest +import sherlock +import warnings + + +class SherlockBaseTest(unittest.TestCase): + def setUp(self): + """Sherlock Base Test Setup. + + Does common setup tasks for base Sherlock tests. + + Keyword Arguments: + self -- This object. + + Return Value: + N/A. + """ + + #This ignores the ResourceWarning from an unclosed SSLSocket. + #TODO: Figure out how to fix the code so this is not needed. + warnings.simplefilter("ignore", ResourceWarning) + + # Load the data file with all site information. + data_file_path = os.path.join(os.path.dirname(os.path.realpath(sherlock.__file__)), "data.json") + with open(data_file_path, "r", encoding="utf-8") as raw: + self.site_data_all = json.load(raw) + + self.verbose=False + self.tor=False + self.unique_tor=False + + return + + def site_data_filter(self, site_list): + """Filter Site Data. + + Keyword Arguments: + self -- This object. + site_list -- List of strings corresponding to sites which + should be filtered. + + Return Value: + Dictionary containing sub-set of site data specified by 'site_list'. + """ + + # Create new dictionary that has filtered site data based on input. + # Note that any site specified which is not understood will generate + # an error. + site_data = {} + for site in site_list: + with self.subTest(f"Checking test vector Site '{site}' " + f"exists in total site data." + ): + site_data[site] = self.site_data_all[site] + + return site_data + + def username_check(self, username_list, site_list, exist_check=True): + """Username Exist Check. + + Keyword Arguments: + self -- This object. + username_list -- List of strings corresponding to usernames + which should exist on *all* of the sites. + site_list -- List of strings corresponding to sites which + should be filtered. + exist_check -- Boolean which indicates if this should be + a check for Username existence, + or non-existence. + + Return Value: + N/A. + Will trigger an assert if Username does not have the expected + existence state. + """ + + #Filter all site data down to just what is needed for this test. + site_data = self.site_data_filter(site_list) + + if exist_check: + check_type_text = "exists" + exist_result_desired = "yes" + else: + check_type_text = "does not exist" + exist_result_desired = "no" + + for username in username_list: + results = sherlock.sherlock(username, + site_data, + verbose=self.verbose, + tor=self.tor, + unique_tor=self.unique_tor + ) + for site, result in results.items(): + with self.subTest(f"Checking Username '{username}' " + f"{check_type_text} on Site '{site}'" + ): + self.assertEqual(result['exists'], exist_result_desired) + + return