Python SDK for shellbound Django applications. Provides ShellApp, ShardContext, ShellboundMiddleware. Emits Chronicle events to stdout in dev mode. Includes fix for IndexError in apps.py when DJANGO_SETTINGS_MODULE has no dots (e.g. instance_settings). Shard name now falls back safely without eager default argument parsing. Implements SHELLBOUND-APP-0001 §4 (dev mode). Wired into entropyopposition as of 2026-03-18.
73 lines
3.2 KiB
Python
73 lines
3.2 KiB
Python
"""Tests for ShellApp registration and lifecycle."""
|
|
|
|
import json
|
|
import os
|
|
from unittest.mock import patch
|
|
|
|
from substrate_sdk.app import ShellApp
|
|
|
|
|
|
class TestShellAppDevMode:
|
|
|
|
def test_register_no_socket(self, capsys):
|
|
with patch.dict(os.environ, {"SUBSTRATE_SHELL_SOCKET": "/tmp/nonexistent.sock"}):
|
|
app = ShellApp(shard_name="test-app", version="1.0.0")
|
|
ctx = app.register()
|
|
assert ctx.is_governed is False
|
|
assert ctx.shard_id # UUID assigned
|
|
|
|
def test_emits_shard_started(self, capsys):
|
|
with patch.dict(os.environ, {"SUBSTRATE_SHELL_SOCKET": "/tmp/nonexistent.sock"}):
|
|
app = ShellApp(shard_name="test-app")
|
|
app.register()
|
|
lines = capsys.readouterr().out.strip().split("\n")
|
|
events = [json.loads(l) for l in lines if l.strip()]
|
|
started = [e for e in events if e.get("kind") == "APP_SHARD_STARTED"]
|
|
assert len(started) == 1
|
|
assert started[0]["payload"]["governed"] is False
|
|
|
|
def test_shutdown_emits_shard_stopped(self, capsys):
|
|
with patch.dict(os.environ, {"SUBSTRATE_SHELL_SOCKET": "/tmp/nonexistent.sock"}):
|
|
app = ShellApp(shard_name="test-app")
|
|
app.register()
|
|
app.shutdown("normal")
|
|
lines = capsys.readouterr().out.strip().split("\n")
|
|
events = [json.loads(l) for l in lines if l.strip()]
|
|
stopped = [e for e in events if e.get("kind") == "APP_SHARD_STOPPED"]
|
|
assert len(stopped) == 1
|
|
|
|
def test_register_timeout_fallback(self, tmp_path):
|
|
sock_path = str(tmp_path / "shard.sock")
|
|
# Create file so os.path.exists returns True, but nothing listens
|
|
open(sock_path, "w").close()
|
|
with patch.dict(os.environ, {"SUBSTRATE_SHELL_SOCKET": sock_path, "SUBSTRATE_REGISTER_TIMEOUT": "0.1"}):
|
|
app = ShellApp(shard_name="timeout-test")
|
|
ctx = app.register()
|
|
assert ctx.is_governed is False
|
|
|
|
def test_shard_name_in_context(self):
|
|
with patch.dict(os.environ, {"SUBSTRATE_SHELL_SOCKET": "/tmp/nonexistent.sock"}):
|
|
app = ShellApp(shard_name="my-shard")
|
|
ctx = app.register()
|
|
assert ctx.shard_name == "my-shard"
|
|
|
|
def test_emitter_available(self):
|
|
with patch.dict(os.environ, {"SUBSTRATE_SHELL_SOCKET": "/tmp/nonexistent.sock"}):
|
|
app = ShellApp(shard_name="test")
|
|
app.register()
|
|
assert app.emitter is not None
|
|
|
|
def test_capabilities_in_started(self, capsys):
|
|
with patch.dict(os.environ, {"SUBSTRATE_SHELL_SOCKET": "/tmp/nonexistent.sock"}):
|
|
app = ShellApp(shard_name="cap-test", capabilities={"network": True})
|
|
app.register()
|
|
lines = capsys.readouterr().out.strip().split("\n")
|
|
started = json.loads(lines[0])
|
|
assert started["payload"]["capabilities"]["network"] is True
|
|
|
|
def test_version_in_started(self, capsys):
|
|
with patch.dict(os.environ, {"SUBSTRATE_SHELL_SOCKET": "/tmp/nonexistent.sock"}):
|
|
app = ShellApp(shard_name="ver-test", version="1.2.3")
|
|
app.register()
|
|
started = json.loads(capsys.readouterr().out.strip().split("\n")[0])
|
|
assert started["payload"]["version"] == "1.2.3"
|