import random import types import importlib # Import app module once to avoid re-declaring ORM tables across tests APP_MOD = importlib.import_module('policy_survey_biases.__init__') def _make_stub_players(n): class StubParticipant: def __init__(self, pid): self.id_in_session = pid self.vars = {} class StubPlayer: def __init__(self, pid): self.participant = StubParticipant(pid) self.order_a = None self.order_b = None self.order_c = None def field_maybe_none(self, name): return getattr(self, name, None) return [StubPlayer(i + 1) for i in range(n)] class StubSubsession: def __init__(self, n): self._players = _make_stub_players(n) def get_players(self): return self._players def _assign_once(n): subsession = StubSubsession(n) # Call the app's method directly with our stub — Python only needs duck-typing APP_MOD.Subsession.creating_session(subsession) players = subsession.get_players() a = [p.order_a for p in players] b = [p.order_b for p in players] c = [p.order_c for p in players] return a, b, c def test_balanced_assignment_single_session(): for n in (2, 3, 6, 7): a, b, c = _assign_once(n) # Each binary assignment should be as even as possible (difference <= 1) for arr in (a, b, c): counts = {v: arr.count(v) for v in set(arr)} if len(counts) == 2: diff = abs(list(counts.values())[0] - list(counts.values())[1]) assert diff <= 1, (n, arr, counts) else: # Degenerate case n==1 or all same — not expected here assert n <= 1, (n, arr, counts) def test_variation_across_sessions(): random.seed() # ensure non-deterministic shuffles across runs seen_a = set() seen_b = set() seen_c = set() n = 6 # Run multiple times to detect shuffling variation for _ in range(10): a, b, c = _assign_once(n) seen_a.update(a) seen_b.update(b) seen_c.update(c) assert seen_a == {'info_before', 'info_after'} assert seen_b == {'support_first', 'eval_first'} assert seen_c == {'recall_first', 'opinion_first'}