# Mutations the gate tests must catch. One row each: file | exact text | replacement | why
# `python3 .github/scripts/run_tests.py --mutants --only gate` applies each row to a scratch copy
# of gate/ and fails when the tests still pass. The text must occur exactly once in the file.
gwt_gate.py | if already: | if False: | the notify command runs once per parking, never again for a second report of the same captcha
gwt_gate.py | if parked: | if False: | while the gate is parked for a person, wait refuses every lane, with no timer
gwt_gate.py | s['human'] = None | pass | clear ends a parking for a person too
gwt_gate.py | if reminded: | if False: | wait-clear sends one reminder after the timeout, then gives up with the gate still parked
gwt_gate.py | argv = [w.format(**fields) for w in shlex.split(self.notify_cmd)] | argv = shlex.split(self.notify_cmd.format(**fields)) | each notify word is filled in alone, so text from the site never splits into extra words or commands
gwt_gate.py | 'mtime': _mtime(path), 'why': what} | 'mtime': _mtime(path), 'why': what}; s['gap'] = min(self.ceil, s['gap'] * 3) | an expired session never slows the pace: auth-expired leaves the gap alone
gwt_gate.py | auth = s.get('auth', {}).get(account) if account else None | auth = next(iter(s.get('auth', {}).values()), None) | auth-expired parks only the lanes of that account
gwt_gate.py | if _mtime(auth['session']) != auth['mtime']: | if False: | a parked account resumes on its own once its session file changes
gwt_gate.py | if self.per_hour and len(recent) >= self.per_hour: | if False: | a per-hour cap refuses the next request in the hour
gwt_gate.py | if self.burst and len(recent) >= self.burst and | if False and | a burst limit (a Crawl-delay) spaces calls by its cooldown
gwt_gate.py | if cap - now > self.max_wait: | if False: | a cap with long to run raises Paused at once instead of sleeping the hour away
gwt_gate.py | if vendor else 1 | if True else 1 | gwt-gate challenge exits 1 when the answer holds no known wall
gwt_gate.py | self.clear(f'probe passed: {probe}', human_only=True) | self.clear(f'probe passed: {probe}') | a passing probe ends the parking only, never a block or pause that is running
gwt_gate.py | first = max(s['pause_until'], s['last'] + s['gap'], cap, now) - now | first = max(s['pause_until'], s['last'] + s['gap'], now) - now | eta counts a per-hour cap that is spent
gwt_gate.py | if any(v is not None and v < 1 for v in (per_hour, burst)) or cooldown < 0: | if False: | a flag of 0 or below exits 2 instead of a traceback or a silent no limit
gwt_gate.py | if any(v is not None and v < 1 for v in (rows[r['host'].lower()]['per_hour'], | if False and any(v is not None and v < 1 for v in (rows[r['host'].lower()]['per_hour'], | a profiles row with a per_hour or burst below 1 is refused
gwt_gate.py | path = os.path.abspath(os.path.expanduser(session)) | path = os.path.expanduser(session) | a relative session path is stored whole, so a lane in another directory does not unpark the account
gwt_gate.py | s['auth'] = {} | pass | clear releases an expired account too, since a sign-in may never rewrite the watched file
gwt_gate.py | ('cloudflare', ('cf-chl', 'cf_chl_opt', '<title>just a moment...</title>')), | ('cloudflare', ('cf-chl', 'cf_chl_opt', '/cdn-cgi/challenge-platform', '<title>just a moment...</title>')), | an ordinary page that loads Cloudflare's script is not called a wall
gwt_gate.py | except OSError as exc:  # exit 1 means | except ZeroDivisionError as exc:  # exit 1 means | challenge on a file it cannot read exits 2, never 1 (no wall)
gwt_gate.py | elif gate.notice != 0: | elif False: | a notice that fails is said on stderr with exit 1, so a parking nobody heard of is never silent
gwt_gate.py | [w.format(**fields) for w in shlex.split(a.notify)] | pass | a --notify template that cannot be filled exits 2 before the gate parks
gwt_gate.py | raise  # no retry can fix a probe that does not exist | rc = None | a probe that cannot run stops wait-clear at once instead of retrying for two timeouts
gwt_gate.py | if self.per_hour else self.cooldown  # a burst needs | if True else self.cooldown  # a burst needs | a burst alone keeps only its cooldown of call times, not an hour of them
gwt_gate.py | urllib.parse.urlsplit(a.profile if '://' in a.profile else '//' + a.profile).hostname or '' | a.profile.split('://', 1)[-1].split('/', 1)[0].lower() | a --profile URL with a port or capitals finds its host's row
gwt_gate.py | if len(cells) != len(header): | if False: | a profiles row with too few or too many cells is refused with its line number, never a KeyError
gwt_gate.py | except subprocess.SubprocessError: | except ZeroDivisionError: | a probe that hangs past its timeout counts as a failed probe, so wait-clear keeps polling instead of crashing
gwt_gate.py | path = os.path.expanduser(path or default_profiles())  # every error below names the file to edit | pass | an error in the default profiles file names that file, never "None"
