예제 #1
0
 def test_tags_string_to_array_wrong_type(self):
     db = DatabaseSQLite(self.output_test_path,
                         self.TEST_DB_FILENAME,
                         None,
                         delete_all_data_from_db=True)
     res = db._tags_string_to_array(1)
     self.assertEqual(res, None)
     db.close()
예제 #2
0
    def test_import_not_existing_database(self):
        # clean test directory
        if os.path.exists(self.output_test_path + self.TEST_DB_FILENAME_OLD):
            os.remove(self.output_test_path + self.TEST_DB_FILENAME_OLD)

        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            old_db_relative_paths=None,
                            delete_all_data_from_db=True)
        result_import = db.import_external_database(self.output_test_path +
                                                    self.TEST_DB_FILENAME_OLD +
                                                    "")
        self.assertEqual(result_import, -1)
예제 #3
0
    def test_input_regex_attack(self):
        """
        check if a Regular expression Denial of Service (ReDoS) works
        the test is successful if the result is returned within 2 seconds

        update: because the regex in Ubuntu 16.04 behave differently and takes ~5 secs to execute this regex,
                we increased the wait value to 10. It is not an option solution but this corner case is acceptable.
        :return:
        """
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)

        element = "a" * 40
        attack = ["a", "a", "a", "a", "a", "a", "a", "a", "a", "b"]
        logging.debug("filter: " + str(attack))

        self.assertTrue(db.add_element(element, "", []))
        db.save_changes()

        tick = datetime.now()
        res = db.get_last_n_filtered_elements(generic_filters=attack)
        tock = datetime.now()
        diff = tock - tick
        self.assertEqual(len(res), 0)
        self.assertGreater(10, diff.seconds)
        db.close()
예제 #4
0
 def __init__(self,
              path_data_folder,
              name_db_file,
              mode=DATABASE_TYPE_SQLITE):
     self.last_search = None
     self.filtered_data = None
     if mode == self.DATABASE_TYPE_SQLITE:
         from fastHistory.database.databaseSQLite import DatabaseSQLite
         self.database = DatabaseSQLite(path_data_folder, name_db_file,
                                        DataManager._OLD_DB_RELATIVE_PATHS)
     else:
         logging.error("database type not supported")
     # set dummy as default
     self.search_filters = self.DUMMY_INPUT_DATA
     # define special chars based on the chosen database
     self.forbidden_chars = ['\n', '\r', self.database.CHAR_DIVIDER]
예제 #5
0
 def test_update_tags_field(self):
     db = DatabaseSQLite(self.output_test_path,
                         self.TEST_DB_FILENAME,
                         None,
                         delete_all_data_from_db=True)
     self.assertTrue(db.add_element("ls -ls", tags=["tag1"]))
     self.assertEqual(len(db.get_all_data()), 1)
     self.assertTrue(db.update_tags_field("ls -ls", tags=["tag2"]))
     self.assertTrue(db.update_tags_field("ls -ls",
                                          tags=["tag2"]))  # no change
     res = db.get_last_n_filtered_elements(tags_filters=["tag2"])
     self.assertEqual(len(res), 1)
     db.close()
예제 #6
0
 def test_remove_element(self):
     db = DatabaseSQLite(self.output_test_path,
                         self.TEST_DB_FILENAME,
                         None,
                         delete_all_data_from_db=True)
     self.assertTrue(db.add_element("ls -ls"))
     self.assertEqual(len(db.get_all_data()), 1)
     self.assertTrue(db.remove_element("ls -ls"))
     self.assertEqual(len(db.get_all_data()), 0)
     db.close()
예제 #7
0
 def test_remove_element_error(self):
     db = DatabaseSQLite(self.output_test_path,
                         self.TEST_DB_FILENAME,
                         None,
                         delete_all_data_from_db=True)
     self.assertFalse(db.remove_element(""))
     self.assertFalse(db.remove_element(None))
     self.assertFalse(db.remove_element("not existing command"))
     db.close()
예제 #8
0
    def test_update_position_element(self):
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)
        self.assertTrue(db.add_element("ls 1", description="desc1"))
        self.assertTrue(db.add_element("ls 2", tags=["tag1"]))
        self.assertTrue(db.add_element("ls 3"))
        res = db.get_all_data()
        self.assertEqual(len(res), 3)
        self.assertEqual(res[-1][DatabaseSQLite.COLUMN_INDEX_COMMAND],
                         "ls 3")  # data is shown reversed in UI
        self.assertEqual(res[2][DatabaseSQLite.COLUMN_INDEX_COUNTER], 0)

        self.assertTrue(db.update_position_selected_element("ls 1"))
        res = db.get_all_data()
        self.assertEqual(len(res), 3)
        self.assertEqual(res[-1][DatabaseSQLite.COLUMN_INDEX_COMMAND], "ls 1")
        self.assertEqual(res[2][DatabaseSQLite.COLUMN_INDEX_COUNTER], 1)
        db.close()
예제 #9
0
    def test_get_all_data(self):
        """
        this is used only for manual debug purposes

        :return:
        """

        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)
        db.add_element("ls", "test", ["sec"])
        db.add_element("ls", "test 2", ["sec"])

        arr = db.get_all_data()
        self.assertEqual(len(arr), 1)
        db.close()
예제 #10
0
 def test_command_update_with_error(self):
     db = DatabaseSQLite(self.output_test_path,
                         self.TEST_DB_FILENAME,
                         None,
                         delete_all_data_from_db=True)
     self.assertFalse(db.update_command_field("test", ""))
     self.assertFalse(
         db.update_command_field("same-command", "same-command"))
     self.assertFalse(
         db.update_command_field("not existing command", "new command"))
     db.close()
예제 #11
0
    def test_import_database_type_0(self):
        """
        test import database (type 0) to current database

        :return:
        """

        # clean test directory
        if os.path.exists(self.output_test_path + self.TEST_DB_FILENAME_OLD):
            os.remove(self.output_test_path + self.TEST_DB_FILENAME_OLD)

        # create old db with structure type 0
        self.conn = sqlite3.connect(self.output_test_path +
                                    self.TEST_DB_FILENAME_OLD)
        self.cursor = self.conn.cursor()
        self.cursor.execute("""CREATE TABLE history 
            (
                command  TEXT,
                counter BIGINT,
                description TEXT,
                tags TEXT
            )
            """)
        # add data with old structure (type 0)
        self.cursor.execute("INSERT INTO history values (?, ?, ?, ?)",
                            ("test1", 2, "description", "#tag1#tag2#tag3"))
        self.cursor.execute("INSERT INTO history values (?, ?, ?, ?)",
                            ("test2", 4, "only description", ""))
        self.cursor.execute("INSERT INTO history values (?, ?, ?, ?)",
                            ("test3", 4, "", "#only-tags"))
        self.conn.commit()
        self.conn.close()

        # initialize new db
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME, [],
                            delete_all_data_from_db=True)
        result_import = db.import_external_database(self.output_test_path +
                                                    self.TEST_DB_FILENAME_OLD)

        # check if the number of element matches
        self.assertEqual(result_import, 3)
        # test search for command
        res = db.get_last_n_filtered_elements(generic_filters=["test1"])
        self.assertEqual(len(res), 1)
        # test search for tag
        res = db.get_last_n_filtered_elements(generic_filters=["only-tags"])
        self.assertEqual(len(res), 1)
        # test search for description
        res = db.get_last_n_filtered_elements(
            generic_filters=["only description"])
        self.assertEqual(len(res), 1)
예제 #12
0
    def test_fill_db_with_100_entries(self):
        """
        fill db with 100 different entries and then check if db contain 100 entries
        :return:
        """
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)
        tot_line = 100
        for i in range(tot_line):
            self.assertTrue(
                db.add_element("ls " + str(i), "test " + str(i),
                               ["sec" + str(i)]))
        db.save_changes()
        # try to retrieve 200 entries
        res = db.get_last_n_filtered_elements(n=tot_line * 2)
        self.assertEqual(len(res), tot_line)

        db.close()
예제 #13
0
    def test_fill_db_with_wrong_entries(self):
        """
        store same command multiple times with different description and tags
        try then to retrieve it
        """
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)
        # illegal @ char
        self.assertFalse(db.add_element("test 1", "@test", ["test"]))
        self.assertFalse(db.add_element("test 2", "@test", ["@test"]))
        # illegal # char
        self.assertFalse(db.add_element("test 3", "#test", ["test"]))
        self.assertFalse(db.add_element("test 4", "test", ["#test"]))

        db.close()
예제 #14
0
    def test_get_first_20_filtered_elements(self):
        """
        fill db with 25 element (ls command) and retrieve the first 20 ls commands

        :return:
        """
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)
        for i in range(25):
            self.assertTrue(
                db.add_element("ls " + str(i), "test " + str(i),
                               ["sec" + str(i)]))

        res = db.get_last_n_filtered_elements(generic_filters=["ls"],
                                              description_filters=["test"],
                                              tags_filters=["sec"],
                                              n=20)
        self.assertEqual(len(res), 20)

        db.close()
예제 #15
0
    def test_wrong_matches(self):
        """
        test searches with special set of chars
        :return:
        """
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)
        self.assertTrue(db.add_element("1234", "1234", ["1234", "1234"]))

        # test if "34" + "12" matches something
        res = db.get_last_n_filtered_elements(generic_filters=["3412"], n=20)
        self.assertEqual(len(res), 0)
        res = db.get_last_n_filtered_elements(generic_filters=[],
                                              tags_filters=["3412"],
                                              n=20)
        self.assertEqual(len(res), 0)
        res = db.get_last_n_filtered_elements(generic_filters=[],
                                              description_filters=["3412"],
                                              n=20)
        self.assertEqual(len(res), 0)

        # test if "34" + "#" + "12"
        res = db.get_last_n_filtered_elements(generic_filters=["34#12"], n=20)
        self.assertEqual(len(res), 0)
        res = db.get_last_n_filtered_elements(generic_filters=[],
                                              tags_filters=["34#12"],
                                              n=20)
        self.assertEqual(len(res), 0)
        res = db.get_last_n_filtered_elements(generic_filters=[],
                                              description_filters=["34#12"],
                                              n=20)
        self.assertEqual(len(res), 0)

        # test if "34" + "@" + "12"
        res = db.get_last_n_filtered_elements(generic_filters=["34@12"], n=20)
        self.assertEqual(len(res), 0)
        res = db.get_last_n_filtered_elements(generic_filters=[],
                                              tags_filters=["34@12"],
                                              n=20)
        self.assertEqual(len(res), 0)
        res = db.get_last_n_filtered_elements(generic_filters=[],
                                              description_filters=["34@12"],
                                              n=20)
        self.assertEqual(len(res), 0)

        db.close()
예제 #16
0
    def test_search_by_tag_and_by_description(self):
        """
        store same command multiple times with different description and tags
        try then to retrieve it
        """
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)
        # insert case 1
        self.assertTrue(db.add_element("ls -la", "test1", ["security"]))
        self.assertTrue(db.add_element("ls -la", "test2", None))
        self.assertTrue(
            db.add_element("ls -la", "test3",
                           ["sec", "security", "supersecure"]))
        # insert case 2
        self.assertTrue(db.add_element("srm", "", tags=None))
        self.assertTrue(db.add_element("srm", "description command",
                                       ["tag-1"]))

        # test case 1
        res = db.get_last_n_filtered_elements(generic_filters=["supersecure"])
        # check number of matches
        self.assertEqual(len(res), 1)
        # check if tags are saved correctly
        self.assertEqual(res[0][2],
                         ["security", "sec", "supersecure"])  # note order
        # check if description is updated
        self.assertEqual(res[0][1], "test1. test2. test3")  # note order

        # test case 2 (search for description)
        res = db.get_last_n_filtered_elements(generic_filters=["description"])
        # check number of matches
        self.assertEqual(len(res), 1)
        # check if tags are saved correctly
        self.assertEqual(res[0][2], ["tag-1"])
        # check if description is updated
        self.assertEqual(res[0][1], "description command")

        # test case 3 (multi words generic search)
        # input: "la ls security"
        res = db.get_last_n_filtered_elements(
            generic_filters=["la", "ls", "security"])  # note the order
        self.assertEqual(len(res), 1)
        self.assertTrue(res[0][0], "ls -la")

        # test case 4 (multi words specific search)
        # input "la ls security #supersecure #sec @test3 test2"
        res = db.get_last_n_filtered_elements(
            generic_filters=["la", "ls", "security"],
            description_filters=["test3", "test2"],
            tags_filters=["supersecure", "sec"])
        self.assertEqual(len(res), 1)
        self.assertTrue(res[0][0], "ls -la")

        db.close()
예제 #17
0
class DataManager(object):
    """
	Class use to manage data and interact with the database
	"""
    MIN_LENGTH_SEARCH_FOR_DESC = 3

    class OPTION:
        INDEX_CMD = 0
        INDEX_DESC = 1
        INDEX_TAGS = 2

    DATABASE_TYPE_SQLITE = 0
    DATABASE_TYPE_MYSQL = 1

    _OLD_DB_RELATIVE_PATHS = [
        "../../../fastHistory/data/fh_v1.db",
        "../../../fastHistory-0.2-beta/data/fh_v1.db",
        "../../../fastHistory-0.1-beta/data/fh_v1.db"
    ]

    DUMMY_INPUT_DATA = InputData(False, "", [])

    def __init__(self,
                 path_data_folder,
                 name_db_file,
                 mode=DATABASE_TYPE_SQLITE):
        self.last_search = None
        self.filtered_data = None
        if mode == self.DATABASE_TYPE_SQLITE:
            from fastHistory.database.databaseSQLite import DatabaseSQLite
            self.database = DatabaseSQLite(path_data_folder, name_db_file,
                                           DataManager._OLD_DB_RELATIVE_PATHS)
        else:
            logging.error("database type not supported")
        # set dummy as default
        self.search_filters = self.DUMMY_INPUT_DATA
        # define special chars based on the chosen database
        self.forbidden_chars = ['\n', '\r', self.database.CHAR_DIVIDER]

    def get_search_filters(self):
        """
		return parsed filters calculated in the last "filter" call

		:return: [cmd_filter, description_filter, array_tag_filter]
		"""
        return self.search_filters

    def get_forbidden_chars(self):
        """
		the database uses a special chars and these cannot be use as input to avoid ambiguity
		:return:	array of forbidden chars
		"""
        return self.forbidden_chars

    def filter(self, search, n=100):
        """
		get filtered commands array
		:param n: 		max number of returned rows
		:param search:	filter text
		:return:		array with [cmd, description, tags array, bool advanced]
		"""
        # put all to lower case
        search = search.lower()

        # parse input search text
        input_data = InputParser.parse_input(search, is_search_mode=True)

        if input_data:
            self.search_filters = input_data

            if not input_data.is_advanced():
                filtered_data = self.database.get_last_n_filtered_elements(
                    generic_filters=input_data.get_main_words(), n=n)
            else:
                filtered_data = self.database.get_last_n_filtered_elements(
                    generic_filters=input_data.get_main_words(),
                    description_filters=input_data.get_description_words(
                        strict=True),
                    tags_filters=input_data.get_tags(strict=True),
                    n=n)
            if filtered_data:
                return filtered_data
            else:
                return []
        else:
            # the string inserted does not match the regex and a dummy response is returned
            self.search_filters = self.DUMMY_INPUT_DATA
            return []

    def add_new_element(self, cmd, description, tags):
        """
		add a new command to db
		:param cmd:			bash command
		:param description:	description (or none)
		:param tags:		list of tags (or none)
		:return:			true if value has been stored correctly
		"""
        return self.database.add_element(cmd, description, tags)

    def update_command(self, cmd, new_cmd):
        """
		update command string of a command
		:param cmd:
		:param new_cmd:
		:return:
		"""
        return self.database.update_command_field(cmd, new_cmd)

    def update_tags(self, cmd, tags):
        """
		update tag list of a command
		:param cmd:		command to update
		:param tags:	new tag array
		:return:		True is the database was successfully changed, False otherwise
		"""
        return self.database.update_tags_field(cmd, tags)

    def update_description(self, cmd, description):
        """
		update description of a command
		:param cmd:			command to update
		:param description: new description
		:return:			True is the database was successfully changed, False otherwise
		"""
        return self.database.update_description_field(cmd, description)

    def update_selected_element_order(self, cmd):
        """
		after a command was selected update the order
		:param cmd:		command to update
		:return:		True is the database was successfully changed, False otherwise
		"""
        return self.database.update_position_selected_element(cmd)

    def delete_element(self, cmd):
        """
		delete a command from db
		:param cmd:		cmd to delete
		:return:		True is the database was successfully changed, False otherwise
		"""
        return self.database.remove_element(cmd)

    def get_data_from_db(self):
        """
		this is a SLOW method to call as less as possible
		:param self:
		:return:
		"""
        return self.database.get_all_data()

    def import_data_to_db(self, db_abs_path):
        """
		import data from old or backed up database file
		:param db_abs_path:	database absolute path
		:return:
		"""
        return self.database.import_external_database(db_abs_path)
예제 #18
0
    def test_command_update(self):
        """
        test command edit feature with merging conflicts
        :return:
        """
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME,
                            None,
                            delete_all_data_from_db=True)
        self.assertTrue(db.add_element("t1", "test1",
                                       ["t1", "f1", "common"]))  # id 1
        self.assertTrue(db.add_element("t2", "test2",
                                       ["t2", "f2", "common"]))  # id 2
        self.assertTrue(db.add_element("t3", "test3",
                                       ["t3", "f3", "common"]))  # id 3
        self.assertTrue(db.add_element("t4", "test4",
                                       ["t4", "f4", "common"]))  # id 4

        # case 1 - simple renaming
        self.assertTrue(db.update_command_field("t1", "t1_new"))
        res = db.get_last_n_filtered_elements(generic_filters=["t1_new"],
                                              tags_filters=["t1"],
                                              description_filters=["test1"],
                                              n=20)
        self.assertEqual(len(res), 1)

        # case 2 - renaming with conflict ( id(t2) < id(t3) )
        self.assertTrue(db.update_command_field("t2", "t3"))
        res = db.get_last_n_filtered_elements(generic_filters=["t3"], n=20)
        self.assertEqual(len(res), 1)
        self.assertEqual(res[0][1], "test2. test3")
        self.assertEqual(res[0][2],
                         ["t2", "f2", "common", "t3", "f3"])  # note order!

        # case 3 - renaming with conflict ( id(t4) > id(t3) )
        self.assertTrue(db.update_command_field("t4", "t3"))
        res = db.get_last_n_filtered_elements(generic_filters=["t3"], n=20)
        self.assertEqual(len(res), 1)
        self.assertEqual(res[0][1], "test4. test2. test3")
        self.assertEqual(
            res[0][2],
            ["t4", "f4", "common", "t2", "f2", "t3", "f3"])  # note order!

        # only 2 items are left
        res = db.get_last_n_filtered_elements(generic_filters=[""], n=20)
        self.assertEqual(len(res), 2)

        db.close()
예제 #19
0
    def test_import_database_type_1(self):
        """
        test import database (type 1) to current database

        :return:
        """

        # clean test directory
        if os.path.exists(self.output_test_path + self.TEST_DB_FILENAME_OLD):
            os.remove(self.output_test_path + self.TEST_DB_FILENAME_OLD)

        # create old db with structure type 0
        self.conn = sqlite3.connect(self.output_test_path +
                                    self.TEST_DB_FILENAME_OLD)
        self.cursor = self.conn.cursor()
        self.cursor.execute("""CREATE TABLE history 
            (
                command  TEXT,
                description TEXT,
                tags TEXT,
                counter INTEGER,
                date INTEGER,
                synced TINYINT
            )
            """)
        # add data with old structure (type 0)
        self.cursor.execute(
            "INSERT INTO history values (?, ?, ?, ?, ?, ?)",
            ("test1", "description", "ǁtag1ǁtag2ǁtag3", 2, 1551202801, 0))
        self.cursor.execute(
            "INSERT INTO history values (?, ?, ?, ?, ?, ?)",
            ("test2", "only description", "", 4, int(time.time()), 0))
        self.cursor.execute(
            "INSERT INTO history values (?, ?, ?, ?, ?, ?)",
            ("test3", "", "ǁonly-tags", 4, int(time.time()), 0))
        self.cursor.execute(
            "INSERT INTO history values (?, ?, ?, ?, ?, ?)",
            (
                "test4-existing-item",
                "test4-new",
                "ǁtag4-new",
                4,
                1551202801,  # note: this is older than the item in the local database
                0))
        self.conn.commit()
        self.conn.close()

        # initialize new db
        db = DatabaseSQLite(self.output_test_path,
                            self.TEST_DB_FILENAME, [],
                            delete_all_data_from_db=True)
        # add element to create a merge conflict
        db.add_element("test4-existing-item",
                       "test4-old", ["tag4-old"],
                       7,
                       date=1551202920,
                       synced=0)
        res = db.get_column_field("test4-existing-item", "rowid")
        item_local_rowid = int(res)

        # import the database twice (this should give the same result as importing it once)
        db.import_external_database(self.output_test_path +
                                    self.TEST_DB_FILENAME_OLD)
        result_import = db.import_external_database(self.output_test_path +
                                                    self.TEST_DB_FILENAME_OLD)

        # check if the number of element matches
        self.assertEqual(result_import, 4)
        # test search for command
        res = db.get_last_n_filtered_elements(generic_filters=["test1"])
        self.assertEqual(len(res), 1)
        # test search for tag
        res = db.get_last_n_filtered_elements(generic_filters=["only-tags"])
        self.assertEqual(len(res), 1)
        # test search for description
        res = db.get_last_n_filtered_elements(
            generic_filters=["only description"])
        self.assertEqual(len(res), 1)
        res = db.get_last_n_filtered_elements(
            tags_filters=["tag4-old", "tag4-new"])
        self.assertEqual(len(res), 1)

        # check counter value
        res = db.get_column_field("test1", "counter")
        self.assertEqual(int(res), 2)

        res = db.get_column_field("test4-existing-item", "counter")
        self.assertEqual(int(res), 7)  # note: the kept value is the local one

        # check date value
        res = db.get_column_field("test1", "date")
        self.assertEqual(int(res), 1551202801)

        res = db.get_column_field("test4-existing-item", "date")
        self.assertEqual(int(res),
                         1551202920)  # note: the kept value is the newest one

        # check if row id value is kept the same
        res = db.get_column_field("test4-existing-item", "rowid")
        self.assertEqual(int(res), item_local_rowid)