def test_uniqueness(self): """ Seeds should be different from one another. """ seeds = [ rank.seed_for(models.Participant(pk=33), '22'), rank.seed_for(models.Participant(pk=22), '33'), rank.seed_for(models.Participant(pk=22), '22'), ] self.assertEqual(len(seeds), len(set(seeds)))
def test_sort_key_randomness(self): """ We break ties with a random value. """ tweedle_dee = models.Participant(pk=5, affiliation='NG') tweedle_dum = models.Participant(pk=6, affiliation='NG') # All other ranking factors are equal self.ranker.number_trips_led.return_value = 0 solid_record = rank.TripCounts(attended=3, flaked=0, total=3) self.ranker.number_ws_trips.return_value = solid_record # Despite their equality, some randomness distinguishes keys dee_key = self.ranker.priority_key(tweedle_dee) dum_key = self.ranker.priority_key(tweedle_dum) self.assertNotEqual(dee_key, dum_key) self.assertEqual(dee_key[:-1], dum_key[:-1]) # (last item is random)
def test_deterministic(self): """ Affiliation-weighted random numbers are deterministic with the same seed. This enables us to have repeatable lottery results. """ participant = models.Participant(pk=12, affiliation='MU') self.assertEqual( rank.affiliation_weighted_rand(participant, 'trip-542'), rank.affiliation_weighted_rand(participant, 'trip-542'))
def test_lotteryinfo(self): """ Drivers are based off car status from that week. """ par = models.Participant() par.lotteryinfo = models.LotteryInfo(car_status=None) self.assertFalse(handle.par_is_driver(par)) par.lotteryinfo = models.LotteryInfo(car_status='own') self.assertTrue(handle.par_is_driver(par)) par.lotteryinfo = models.LotteryInfo(car_status='rent') self.assertTrue(handle.par_is_driver(par))
def test_weight_subtraction(self): """ Test the definition of affiliation-weighted randomness. Specifically, we take a random float from a particular seed, and subtract a known offset according to the participant's affiliation. """ # MIT undergraduates get an advantage: their number is more likely to be lower mit_undergrad = models.Participant(pk=12, affiliation='MU') seed = rank.seed_for(mit_undergrad, 'trip-142') random.seed(seed) self.assertEqual( random.random() - 0.3, rank.affiliation_weighted_rand(mit_undergrad, 'trip-142')) # Non-affiliates are just a random number non_affiliate = models.Participant(pk=24, affiliation='NA') seed = rank.seed_for(non_affiliate, 'trip-142') random.seed(seed) self.assertEqual( random.random(), rank.affiliation_weighted_rand(non_affiliate, 'trip-142'))
def test_leader_bump(self): """ All else held equal, the most active leaders get priority. """ # Both participants are MIT undergraduates, equally likely to flake novice = models.Participant(pk=1024, affiliation='MU', name='New Leader') veteran = models.Participant(pk=256, affiliation='MU', name='Veteran Leader') def attended_all(num): return rank.TripCounts(attended=num, flaked=0, total=num) # Key difference: the veteran leader has a greater balance of led trips mocked_counts = { veteran: { 'number_trips_led': 4, 'number_ws_trips': attended_all(1), }, # Net 3 novice: { 'number_trips_led': 2, 'number_ws_trips': attended_all(3), }, # Net -1 } def by_participant(attribute): """ Quick closure for looking up the count. """ return lambda par: mocked_counts[par][attribute] self.ranker.get_rank_override.return_value = 0 for attr in ['number_ws_trips', 'number_trips_led']: getattr(self.ranker, attr).side_effect = by_participant(attr) # Sanity check that our net trips led balance works properly self.assertEqual(self.ranker.trips_led_balance(veteran), 3) self.assertEqual(self.ranker.trips_led_balance(novice), 0) # Veteran is given higher ranking self.expect_ranking(veteran, novice)
def test_flaking(self): """ Those who flake on trips always come last. """ # Flaking participant is an MIT undergrad (would normally get priority) serial_flaker = models.Participant(pk=1, affiliation='MU', name='Serial Flaker') flaked_once = models.Participant(pk=2, affiliation='MG', name='One-time Flaker') reliable = models.Participant(pk=3, affiliation='NA', name='Reliable') mocked_counts = { flaked_once: { 'number_trips_led': 8, 'number_ws_trips': rank.TripCounts(attended=0, flaked=1, total=1), }, serial_flaker: { 'number_trips_led': 4, 'number_ws_trips': rank.TripCounts(attended=0, flaked=3, total=3), }, reliable: { 'number_trips_led': 0, 'number_ws_trips': rank.TripCounts(attended=4, flaked=0, total=4), }, } self.ranker.get_rank_override.return_value = 0 self.ranker.number_trips_led.side_effect = lambda par: mocked_counts[ par]['number_trips_led'] self.ranker.number_ws_trips.side_effect = lambda par: mocked_counts[ par]['number_ws_trips'] self.expect_ranking(reliable, flaked_once, serial_flaker)
def test_current_year(self, ws_year, ws_lectures_complete, lecture_attendance): """ Check attendance in current year, after lectures complete. We're in a year where attendance is recorded, and we're asking about the current year. Did the participant attend? """ participant = models.Participant() # We're asking about the current WS season, when lectures have occurred ws_year.return_value = current_year = 2020 ws_lectures_complete.return_value = True attendance_exists = lecture_attendance.return_value.filter.return_value.exists # When participant has no attendance recorded, they've missed lectures attendance_exists.return_value = False self.assertTrue(utils.missed_lectures(participant, current_year)) # When the participant attended, they did not miss lectures attendance_exists.return_value = True self.assertFalse(utils.missed_lectures(participant, current_year))
def test_no_lotteryinfo(self): """ Don't regard anybody as a driver if they didn't submit prefs. """ par = models.Participant() self.assertFalse(handle.par_is_driver(par))
def test_seed_contains_secret(self): """ The seed should contain the secret that participant's don't know. """ seed = rank.seed_for(models.Participant(pk=33), 'some extra seed') self.assertIn(settings.PRNG_SEED_SECRET, seed)
def test_participant_must_be_saved_to_db(self): """ We can't come up with a fair seed for a participant that lacks a pk. """ with self.assertRaises(ValueError): rank.seed_for(models.Participant(name='Not Saved'), lottery_key='hi')