def test_new_hasher_third_hasher_config_reverted(self):
        """
        Hasher: request new hashers with config change followed by revert
        Repository must correctly save new Hashers if a Hasher
        configuration was requested. If a return to a previous
        configuration is requested thereafter, a new Hasher must be
        created with a new salt.

        """
        ts = datetime.utcnow().timestamp()
        meta = {'test': 'hasher_third_config_reverted', 'ts_start': ts}
        cfg = {'meta': meta}
        class_a = self.repo.supported_hashers['PBKDF2Hasher']
        class_b = self.repo.supported_hashers['ScryptHasher']
        time_res_s = self.repo.get_config()['time_res_s']
        muteacle.sleep_until_interval_end(time_res_s)

        req_a = self.repo.new_hasher(hasher_class=class_a, config=cfg)
        hasher_a = req_a['hasher']
        dt = req_a['datetime']
        req_b = self.repo.new_hasher(hasher_class=class_b, config=cfg)
        hasher_b = req_b['hasher']
        req_c = self.repo.new_hasher(hasher_class=class_a, config=cfg)
        hasher_c = req_c['hasher']
        hashers = self.repo.get_hashers(dt)

        self.assertIn(hasher_a, hashers)
        self.assertIn(hasher_b, hashers)
        self.assertIn(hasher_c, hashers)
        self.assertNotEqual(hasher_a.salt, hasher_c.salt)
    def test_set_config_change_pending_applied(self):
        """
        Repository: config change request /w change to config before applying
        Changes to pending configuration must be correctly applied
        once the waiting period is past

        """
        meta_a = {
            'dt': datetime.utcnow().timestamp(),
            'test': 'set_config_change_pending_applied',
            'action': 'set_config_a',
        }
        meta_b = {
            'dt': datetime.utcnow().timestamp(),
            'test': 'set_config_change_pending_applied',
            'action': 'set_config_b',
        }

        config_orig = self.repo.get_config()
        self.repo.set_config({'meta': meta_a})
        self.repo.set_config({'meta': meta_b})
        config_presched = self.repo.pending_repo_config()
        muteacle.sleep_until_interval_end(config_orig['time_res_s'])
        config_app = self.repo.get_config()
        config_postsched = self.repo.pending_repo_config()

        self.assertEqual(config_postsched, {})
        self.assertEqual(config_app, config_presched)
        # verify that unchanged config values are intact
        keys = [k for k in self.repo.set_config_keys if k != 'meta']
        for k in keys:
            self.assertEqual(config_app[k], config_orig[k])
 def tearDown(self):
     # set repository to end-of-test config to avoid confusing
     # subsequent runs if tests are run on persistent/storage memory
     meta = {
         'dt': datetime.utcnow().timestamp(),
         'test_suite_ended': 'RepositorySetupTests'
     }
     time_res_s = self.repo_kwargs['time_res_s']
     self.repo.set_config({'meta': meta, 'time_res_s': time_res_s})
     muteacle.sleep_until_interval_end(time_res_s)
    def test_set_config_change_nofx_applied(self):
        """
        Repository: ineffective config change
        Existing repository configuration must be correctly preserved
        if a configuration change effectively results in no changes

        """
        meta_x = {
            'dt': datetime.utcnow().timestamp(),
            'test': 'set_config_change_nofx_applied',
            'action': 'set_pending_config',
        }
        config_orig = self.repo.get_config()

        self.repo.set_config(config_orig)
        muteacle.sleep_until_interval_end(config_orig['time_res_s'])
        config_app = self.repo.get_config()

        self.assertEqual(config_app, config_orig)
    def test_get_hashers_interval_end(self):
        """
        Hasher: load hashers specifying datetime at end of interval

        """
        ts = datetime.utcnow().timestamp()
        meta = {'test': 'get_hashers_interval_end', 'ts': ts}
        time_res_s = self.repo._config['time_res_s']

        muteacle.sleep_until_interval_end(time_res_s)
        req = self.repo.new_hasher(config={'meta': meta})
        hasher = req['hasher']
        dt = req['datetime']

        n = muteacle.interval_number(dt, time_res_s)
        dt_end = muteacle.interval_end(dt.year, dt.month, dt.day, time_res_s,
                                       n)
        hashers_loaded = self.repo.get_hashers(dt_end)

        self.assertIn(hasher, hashers_loaded)
    def test_set_config_change_pending_cancelled(self):
        """
        Repository: config change request cancellation
        Existing repository configuration must be correctly preserved
        if a configuration change request is cancelled

        """
        meta_x = {
            'dt': datetime.utcnow().timestamp(),
            'test': 'set_config_change_pending_cancelled',
            'action': 'set_pending_config',
        }
        config_orig = self.repo.get_config()

        self.repo.set_config({'meta': meta_x})
        self.repo.set_config(config_orig)
        muteacle.sleep_until_interval_end(config_orig['time_res_s'])
        config_app = self.repo.get_config()

        self.assertEqual(config_app, config_orig)
    def test_new_hasher_interval_lapsed(self):
        """
        Hasher: request new hasher after interval lapses
        Repository must save a new Hasher if a new one is requested
        after the interval has lapsed, even if the configuration and
        class has not changed in the meantime. Hashers must not be
        retrievable using a datetime from a different interval.

        """
        ts = datetime.utcnow().timestamp()
        test_name = 'new_hasher_interval_passed'
        time_res_s = self.repo.get_config()['time_res_s']
        muteacle.sleep_until_interval_end(time_res_s)

        # first hasher
        meta_a = {'test': test_name, 'ts_start': ts, 'order': 1}
        req_a = self.repo.new_hasher(config={'meta': meta_a})
        hasher_a = req_a['hasher']
        dt_a = req_a['datetime']
        load_a = self.repo.get_hashers(dt_a)
        muteacle.sleep_until_interval_end(time_res_s)
        # second hasher
        meta_b = {'test': test_name, 'ts_start': ts, 'order': 2}
        req_b = self.repo.new_hasher(config={'meta': meta_b})
        hasher_b = req_b['hasher']
        dt_b = req_b['datetime']
        load_b = self.repo.get_hashers(dt_b)
        muteacle.sleep_until_interval_end(time_res_s)
        # third hasher
        meta_c = {'test': test_name, 'ts_start': ts, 'order': 3}
        req_c = self.repo.new_hasher(config={'meta': meta_c})
        hasher_c = req_c['hasher']
        dt_c = req_c['datetime']
        load_c = self.repo.get_hashers(dt_c)
        muteacle.sleep_until_interval_end(time_res_s)

        self.assertNotIn(hasher_a, load_b)
        self.assertNotIn(hasher_a, load_c)
        self.assertNotIn(hasher_b, load_a)
        self.assertNotIn(hasher_b, load_c)
        self.assertNotIn(hasher_c, load_a)
        self.assertNotIn(hasher_c, load_b)
        self.assertIn(hasher_a, load_a)
        self.assertIn(hasher_b, load_b)
        self.assertIn(hasher_c, load_c)
        self.assertEqual(len(load_a), 1)
        self.assertEqual(len(load_b), 1)
        self.assertEqual(len(load_c), 1)