234 lines
9.2 KiB
Python
234 lines
9.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Unit tests for the pairing algorithm using pytest
|
|
"""
|
|
|
|
import pytest
|
|
import random
|
|
from app import create_pairs
|
|
|
|
|
|
class TestPairingAlgorithm:
|
|
"""Test class for the pairing algorithm"""
|
|
|
|
def test_minimum_names_requirement(self):
|
|
"""Test that at least 3 names are required"""
|
|
with pytest.raises(ValueError, match="Need at least 3 names"):
|
|
create_pairs([])
|
|
|
|
with pytest.raises(ValueError, match="Need at least 3 names"):
|
|
create_pairs(["Alice"])
|
|
|
|
with pytest.raises(ValueError, match="Need at least 3 names"):
|
|
create_pairs(["Alice", "Bob"])
|
|
|
|
def test_basic_pairing_4_people(self):
|
|
"""Test pairing with 4 people"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana"]
|
|
pairs = create_pairs(names)
|
|
|
|
# Should have exactly 4 pairs (4 people * 2 pairs each / 2 = 4 total pairs)
|
|
assert len(pairs) == 4
|
|
|
|
# Each person should have exactly 2 pairs
|
|
person_pair_counts = self._count_pairs_per_person(pairs)
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
# No duplicate pairs
|
|
self._assert_no_duplicate_pairs(pairs)
|
|
|
|
def test_pairing_5_people(self):
|
|
"""Test pairing with 5 people"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana", "Eve"]
|
|
pairs = create_pairs(names)
|
|
|
|
# Should have exactly 5 pairs
|
|
assert len(pairs) == 5
|
|
|
|
# Each person should have exactly 2 pairs
|
|
person_pair_counts = self._count_pairs_per_person(pairs)
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
# No duplicate pairs
|
|
self._assert_no_duplicate_pairs(pairs)
|
|
|
|
def test_pairing_6_people(self):
|
|
"""Test pairing with 6 people"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
|
|
pairs = create_pairs(names)
|
|
|
|
# Should have exactly 6 pairs
|
|
assert len(pairs) == 6
|
|
|
|
# Each person should have exactly 2 pairs
|
|
person_pair_counts = self._count_pairs_per_person(pairs)
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
# No duplicate pairs
|
|
self._assert_no_duplicate_pairs(pairs)
|
|
|
|
def test_pairing_7_people(self):
|
|
"""Test pairing with 7 people"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace"]
|
|
pairs = create_pairs(names)
|
|
|
|
# Should have exactly 7 pairs
|
|
assert len(pairs) == 7
|
|
|
|
# Each person should have exactly 2 pairs
|
|
person_pair_counts = self._count_pairs_per_person(pairs)
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
# No duplicate pairs
|
|
self._assert_no_duplicate_pairs(pairs)
|
|
|
|
def test_no_self_pairing(self):
|
|
"""Test that no person is paired with themselves"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana"]
|
|
pairs = create_pairs(names)
|
|
|
|
for pair in pairs:
|
|
assert pair["person1"] != pair["person2"], f"Self-pairing found: {pair}"
|
|
|
|
def test_pair_structure(self):
|
|
"""Test that pairs have the correct structure"""
|
|
names = ["Alice", "Bob", "Charlie"]
|
|
pairs = create_pairs(names)
|
|
|
|
for pair in pairs:
|
|
assert "person1" in pair, "Pair missing person1 key"
|
|
assert "person2" in pair, "Pair missing person2 key"
|
|
assert isinstance(pair["person1"], str), "person1 should be a string"
|
|
assert isinstance(pair["person2"], str), "person2 should be a string"
|
|
|
|
def test_randomness(self):
|
|
"""Test that the algorithm produces different results on multiple runs"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana", "Eve"]
|
|
|
|
# Run multiple times and collect results
|
|
results = []
|
|
for _ in range(10):
|
|
pairs = create_pairs(names)
|
|
# Convert to set of tuples for comparison
|
|
pair_set = {(p["person1"], p["person2"]) for p in pairs}
|
|
results.append(pair_set)
|
|
|
|
# Check that we get at least 2 different results (indicating randomness)
|
|
unique_results = set()
|
|
for result in results:
|
|
unique_results.add(tuple(sorted(result)))
|
|
|
|
assert len(unique_results) >= 2, "Algorithm doesn't seem to be random"
|
|
|
|
def test_edge_case_odd_number(self):
|
|
"""Test with odd number of people"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace"]
|
|
pairs = create_pairs(names)
|
|
|
|
# Should have exactly 7 pairs
|
|
assert len(pairs) == 7
|
|
|
|
# Each person should have exactly 2 pairs
|
|
person_pair_counts = self._count_pairs_per_person(pairs)
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
def test_edge_case_even_number(self):
|
|
"""Test with even number of people"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
|
|
pairs = create_pairs(names)
|
|
|
|
# Should have exactly 6 pairs
|
|
assert len(pairs) == 6
|
|
|
|
# Each person should have exactly 2 pairs
|
|
person_pair_counts = self._count_pairs_per_person(pairs)
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
def test_large_group(self):
|
|
"""Test with a larger group"""
|
|
names = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Henry", "Ivy", "Jack"]
|
|
pairs = create_pairs(names)
|
|
|
|
# Should have exactly 10 pairs
|
|
assert len(pairs) == 10
|
|
|
|
# Each person should have exactly 2 pairs
|
|
person_pair_counts = self._count_pairs_per_person(pairs)
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
# No duplicate pairs
|
|
self._assert_no_duplicate_pairs(pairs)
|
|
|
|
def test_special_characters_in_names(self):
|
|
"""Test with names containing special characters"""
|
|
names = ["José", "Mary-Jane", "O'Connor", "李小明"]
|
|
pairs = create_pairs(names)
|
|
|
|
# Should have exactly 4 pairs
|
|
assert len(pairs) == 4
|
|
|
|
# Each person should have exactly 2 pairs
|
|
person_pair_counts = self._count_pairs_per_person(pairs)
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
def _count_pairs_per_person(self, pairs):
|
|
"""Helper method to count how many pairs each person has"""
|
|
person_pair_counts = {}
|
|
for pair in pairs:
|
|
person1, person2 = pair["person1"], pair["person2"]
|
|
person_pair_counts[person1] = person_pair_counts.get(person1, 0) + 1
|
|
person_pair_counts[person2] = person_pair_counts.get(person2, 0) + 1
|
|
return person_pair_counts
|
|
|
|
def _assert_no_duplicate_pairs(self, pairs):
|
|
"""Helper method to assert no duplicate pairs exist"""
|
|
pair_strings = set()
|
|
for pair in pairs:
|
|
pair_str = f"{pair['person1']}-{pair['person2']}"
|
|
reverse_str = f"{pair['person2']}-{pair['person1']}"
|
|
|
|
assert pair_str not in pair_strings, f"Duplicate pair found: {pair}"
|
|
assert reverse_str not in pair_strings, f"Duplicate pair found (reverse): {pair}"
|
|
|
|
pair_strings.add(pair_str)
|
|
|
|
|
|
class TestPairingProperties:
|
|
"""Test class for mathematical properties of pairing"""
|
|
|
|
def test_total_pairs_formula(self):
|
|
"""Test that total pairs = number of people"""
|
|
for n in range(3, 11): # Test with 3 to 10 people
|
|
names = [f"Person{i}" for i in range(n)]
|
|
pairs = create_pairs(names)
|
|
|
|
# Total pairs should equal number of people
|
|
assert len(pairs) == n, f"Expected {n} pairs for {n} people, got {len(pairs)}"
|
|
|
|
def test_each_person_has_exactly_two_pairs(self):
|
|
"""Test that each person has exactly 2 pairs"""
|
|
for n in range(3, 11): # Test with 3 to 10 people
|
|
names = [f"Person{i}" for i in range(n)]
|
|
pairs = create_pairs(names)
|
|
|
|
person_pair_counts = {}
|
|
for pair in pairs:
|
|
person1, person2 = pair["person1"], pair["person2"]
|
|
person_pair_counts[person1] = person_pair_counts.get(person1, 0) + 1
|
|
person_pair_counts[person2] = person_pair_counts.get(person2, 0) + 1
|
|
|
|
for person in names:
|
|
assert person_pair_counts[person] == 2, f"{person} has {person_pair_counts[person]} pairs, expected 2"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Run tests with pytest
|
|
pytest.main([__file__, "-v"]) |