test(gateway): cover null start_time scoped-lock self-reacquire
Regression for Discord 503 reconnect false-positive discord-bot-token lock against the live gateway PID (#81468).
This commit is contained in:
parent
2e18e29723
commit
e54ba2ade2
|
|
@ -418,6 +418,62 @@ class TestScopedLocks:
|
|||
assert payload["pid"] == os.getpid()
|
||||
assert payload["metadata"]["platform"] == "telegram"
|
||||
|
||||
def test_acquire_scoped_lock_self_reacquires_when_disk_start_time_null(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
"""#81468: same PID with on-disk start_time=null must self-reacquire.
|
||||
|
||||
After Discord 503 reconnect, the scoped lock still names this process
|
||||
but may carry ``start_time: null`` (psutil miss at first write). The
|
||||
freshly built record has a real fingerprint. Requiring equality made
|
||||
the gateway report its own PID as a foreign token squatter.
|
||||
"""
|
||||
monkeypatch.setenv("HERMES_GATEWAY_LOCK_DIR", str(tmp_path / "locks"))
|
||||
lock_path = tmp_path / "locks" / "discord-bot-token-2bb80d537b1da3e3.lock"
|
||||
lock_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
lock_path.write_text(json.dumps({
|
||||
"pid": os.getpid(),
|
||||
"start_time": None,
|
||||
"kind": "hermes-gateway",
|
||||
"argv": ["hermes_cli/main.py", "--profile", "milena", "gateway", "run", "--replace"],
|
||||
"scope": "discord-bot-token",
|
||||
}))
|
||||
|
||||
# Live process can resolve start_time; disk cannot — the mismatch
|
||||
# that previously failed the self-reacquire short-circuit.
|
||||
monkeypatch.setattr(status, "_get_process_start_time", lambda pid: 987654321)
|
||||
# If we wrongly fall through to staleness, a gateway-looking self PID
|
||||
# would be treated as a live foreign holder (return False).
|
||||
monkeypatch.setattr(status, "_pid_exists", lambda pid: True)
|
||||
monkeypatch.setattr(status, "_looks_like_gateway_process", lambda pid: True)
|
||||
|
||||
acquired, existing = status.acquire_scoped_lock(
|
||||
"discord-bot-token", "secret", metadata={"platform": "discord"}
|
||||
)
|
||||
|
||||
assert acquired is True
|
||||
assert existing["pid"] == os.getpid()
|
||||
payload = json.loads(lock_path.read_text())
|
||||
assert payload["pid"] == os.getpid()
|
||||
assert payload["start_time"] == 987654321
|
||||
assert payload["metadata"]["platform"] == "discord"
|
||||
|
||||
def test_release_scoped_lock_allows_null_disk_start_time(self, tmp_path, monkeypatch):
|
||||
"""#81468: release must not no-op when disk start_time is null."""
|
||||
monkeypatch.setenv("HERMES_GATEWAY_LOCK_DIR", str(tmp_path / "locks"))
|
||||
lock_path = tmp_path / "locks" / "discord-bot-token-2bb80d537b1da3e3.lock"
|
||||
lock_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
lock_path.write_text(json.dumps({
|
||||
"pid": os.getpid(),
|
||||
"start_time": None,
|
||||
"kind": "hermes-gateway",
|
||||
}))
|
||||
monkeypatch.setattr(status, "_get_process_start_time", lambda pid: 987654321)
|
||||
|
||||
status.release_scoped_lock("discord-bot-token", "secret")
|
||||
|
||||
assert not lock_path.exists()
|
||||
|
||||
|
||||
def test_acquire_scoped_lock_race_second_acquirer_loses(self, tmp_path, monkeypatch):
|
||||
"""Two racing starters both observe the same stale lock. The loser's
|
||||
|
|
|
|||
Loading…
Reference in New Issue