mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-01 10:11:30 +08:00
test(companion): Add state transition tests
Add end-to-end chains over the per-unit tests: spawn to STARTING, promote to RUNNING, unexpected exit to BACKOFF, retry back to STARTING; the stop path ending in manual STOPPED; and the restart path that respawns immediately when the old child exits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e780484d24
commit
e15dd583b9
@ -696,7 +696,7 @@ No per-companion logic in Arbiter.
|
||||
- [x] Add parent-death cleanup.
|
||||
- [x] Add lifecycle logs.
|
||||
- [x] Add tests for config validation.
|
||||
- [ ] Add tests for state transitions.
|
||||
- [x] Add tests for state transitions.
|
||||
- [ ] Add tests for control commands.
|
||||
- [ ] Add tests for transactional reread.
|
||||
- [ ] Add tests that HTTP worker behavior is unchanged.
|
||||
|
||||
@ -579,3 +579,51 @@ def test_spawn_parent_records_pid_and_starting():
|
||||
assert proc.state == State.STARTING
|
||||
assert proc.started_at is not None
|
||||
assert proc.manual_stop is False
|
||||
|
||||
|
||||
def test_lifecycle_running_crash_backoff_retry():
|
||||
manager = make_manager("rq")
|
||||
process = manager.processes["rq"]
|
||||
assert process.state == State.STOPPED
|
||||
with mock.patch("os.fork", return_value=100):
|
||||
manager.spawn_process(process)
|
||||
assert process.state == State.STARTING
|
||||
manager.promote_running(now=process.started_at + process.config.startsecs)
|
||||
assert process.state == State.RUNNING
|
||||
manager.handle_exit(process, now=1000.0)
|
||||
assert process.state == State.BACKOFF
|
||||
assert process.next_retry_at == 1000.0 + process.restart_delay
|
||||
with mock.patch("os.fork", return_value=101):
|
||||
manager.retry_backoff(now=process.next_retry_at)
|
||||
assert process.state == State.STARTING
|
||||
|
||||
|
||||
def test_lifecycle_stop_to_stopped():
|
||||
manager = make_manager("rq")
|
||||
process = manager.processes["rq"]
|
||||
with mock.patch("os.fork", return_value=200):
|
||||
manager.spawn_process(process)
|
||||
manager.promote_running(now=process.started_at + process.config.startsecs)
|
||||
with mock.patch("os.kill") as kill:
|
||||
manager.stop_process("rq", now=500.0)
|
||||
assert process.state == State.STOPPING
|
||||
assert process.manual_stop is True
|
||||
kill.assert_called_once()
|
||||
manager.handle_exit(process, now=501.0)
|
||||
assert process.state == State.STOPPED
|
||||
|
||||
|
||||
def test_lifecycle_restart_respawns_after_exit():
|
||||
manager = make_manager("rq")
|
||||
process = manager.processes["rq"]
|
||||
with mock.patch("os.fork", return_value=300):
|
||||
manager.spawn_process(process)
|
||||
manager.promote_running(now=process.started_at + process.config.startsecs)
|
||||
with mock.patch("os.kill"):
|
||||
manager.restart_process("rq", now=600.0)
|
||||
assert process.state == State.STOPPING
|
||||
assert process.restart_pending is True
|
||||
with mock.patch("os.fork", return_value=301):
|
||||
manager.handle_exit(process, now=601.0)
|
||||
assert process.state == State.STARTING
|
||||
assert process.restart_pending is False
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user