# Styrene TUI Testing Skill

Automated testing capabilities for Styrene TUI, focusing on chat functionality, LXMF messaging, and node discovery.

## Purpose

Provides automated testing tools that can verify end-to-end functionality without manual TUI interaction. Enables programmatic testing of:

- LXMF message sending/receiving
- Node discovery and announce handling
- Database state validation
- Chat protocol functionality
- Hub connectivity and routing

## Infrastructure Context

### Reticulum Hub

The Styrene ecosystem uses a centralized Reticulum hub for fleet coordination:

**Location:** `/Users/cwilson/workspace/vanderlyn/styrene-lab/reticulum`
**Deployment:** Kubernetes (brutus cluster) via Kustomize
**Service:** MetalLB LoadBalancer at `192.168.0.102:4242`
**Hub Destination:** `6fc8bf22aa293588c9bf8d7a8102e95`

The hub provides:
- Centralized announce propagation across the mesh
- Routing between nodes that can't reach each other directly
- Fleet-wide device discovery
- LXMF message relay

**Hub connectivity is REQUIRED for inter-node communication in most configurations.**

### Test Hardware

| Device | Hostname | IP | Role | Announce Interval |
|--------|----------|-----|------|------------------|
| ASUS Q502L | styrene-node.vanderlyn.local | DHCP | Test daemon | 30s |
| MacBook Pro | MacBookPro.vanderlyn.local | DHCP | Development TUI | 300s (default) |

Both devices connect to the hub via TCPClientInterface at `192.168.0.102:4242`.

### Network Flow

```
Q502 Daemon                  Reticulum Hub (K8s)           MacBook TUI
   |                              |                             |
   |-- Announce (30s) ----------->|                             |
   |                              |-- Propagate announce ------>|
   |                              |                             |
   |                              |<-- Announce (300s) ---------|
   |<-- Propagate announce -------|                             |
   |                              |                             |
   |-- LXMF message ------------->|                             |
   |                              |-- Route message ----------->|
```

**Key Insight:** Nodes discover each other via hub-propagated announces. If announces don't include LXMF destinations, messaging will fail even if discovery succeeds.

## Tools

### `test_chat_e2e.py`

End-to-end chat message test that:
1. Initializes Styrene lifecycle
2. Waits for target node discovery
3. Verifies LXMF capability
4. Sends test message
5. Monitors for auto-reply
6. Reports results

**Usage:**
```bash
cd /Users/cwilson/workspace/vanderlyn/styrene-lab/styrene-tui
.venv/bin/python .claude/skills/styrene-tui-testing/test_chat_e2e.py [destination_hash]
```

**Arguments:**
- `destination_hash`: Optional. Target node's operator destination hash. If omitted, discovers Q502 automatically.

**Exit Codes:**
- 0: Test passed
- 1: Test failed (check stderr for details)

**Output:**
```
=== Styrene Chat E2E Test ===
✓ Styrene services initialized
✓ LXMF initialized (delivery: 4f17a08ffd4119d4...)
✓ Found Q502 node (9e21f49afdaae888...)
✓ Q502 has LXMF capability (f3e11839bf683643...)
✓ Message sent successfully
✓ Waiting for auto-reply (timeout: 30s)...
✓ Auto-reply received from Q502!

Test PASSED
```

### `verify_discovery.py`

Validates node discovery and database state:
1. Checks NodeStore for expected nodes
2. Verifies LXMF destinations are populated
3. Reports missing capabilities

**Usage:**
```bash
.venv/bin/python .claude/skills/styrene-tui-testing/verify_discovery.py
```

### `monitor_health.py`

Monitors system health during operation:
1. Checks NodeStore connection count
2. Verifies no file descriptor leaks
3. Reports service status

**Usage:**
```bash
.venv/bin/python .claude/skills/styrene-tui-testing/monitor_health.py
```

## Integration with Make

Add to Makefile:
```makefile
.PHONY: test-e2e
test-e2e:
	@echo "Running end-to-end chat test..."
	.venv/bin/python .claude/skills/styrene-tui-testing/test_chat_e2e.py
```

## Troubleshooting

### Test times out - No node discovery

**Symptom:** Node not discovered within timeout period

**Checks:**
1. **Hub connectivity** - Verify hub is reachable:
   ```bash
   nc -zv 192.168.0.102 4242
   ```

2. **Hub connection in logs** - Check if node connected to hub:
   ```bash
   # Q502
   ssh -i ~/.ssh/styrene-admin styrene@styrene-node.vanderlyn.local \
     'grep "Connected to hub" /tmp/styrene-daemon.log'

   # Local TUI
   grep "Connected to hub" /tmp/styrene-tui.log
   ```

3. **Announce frequency** - Q502 announces every 30s, test timeout should be > 60s to allow for propagation delay

4. **Announce content** - Verify LXMF destination is included:
   ```bash
   ssh -i ~/.ssh/styrene-admin styrene@styrene-node.vanderlyn.local \
     'grep "Including LXMF dest" /tmp/styrene-daemon.log'
   ```

5. **Hub pod status** - Verify hub is running:
   ```bash
   kubectl -n reticulum get pods
   kubectl -n reticulum logs -l app.kubernetes.io/name=reticulum --tail=50
   ```

### Test fails with "No path to destination"

LXMF requires a network path to the destination. Either:
1. Wait longer for path discovery (hub routing can take 10-30s)
2. Ensure target node is announcing with LXMF destination
3. Check hub is routing announces correctly
4. Verify both nodes connected to hub

### Test fails with "identity not known to RNS"

The target node's announce hasn't been received yet. Solutions:
1. Check target node is running and announcing
2. Wait longer for announce propagation through hub
3. Verify hub connectivity (both nodes must connect to hub)
4. Check announce includes identity hash

### Test fails with "node does not have LXMF capability"

The node was discovered but its announce didn't include LXMF destination:
1. Check if periodic re-announces include LXMF destination
2. Verify LXMF service initialized before first announce
3. Check daemon.py and app_lifecycle.py use consistent announce format
4. Delete stale database entry and wait for fresh announce

### Test hangs waiting for auto-reply

Auto-reply handler may not be running or has cooldown active:
1. Check target daemon logs for "Auto-reply handler started"
2. Verify cooldown period hasn't blocked the reply
3. Check LXMF router is processing messages
4. Verify message actually sent (check sender logs)

## Technical Notes

### Direct Service Access

Test scripts initialize services directly without launching the TUI:
```python
from styrene.services.app_lifecycle import initialize_styrene
lifecycle = initialize_styrene()
# Services available via get_*_service() functions
```

This avoids the complexity of TUI automation while testing core functionality.

### Message Callback Registration

Tests can register custom callbacks to intercept messages:
```python
from styrene.services.lxmf_service import get_lxmf_service

def handle_message(source_hash: str, payload: dict):
    print(f"Received: {payload}")

lxmf = get_lxmf_service()
lxmf.register_callback(handle_message)
```

### Timeout Handling

All tests use configurable timeouts:
- Node discovery: 30s default
- Message send: 10s default
- Auto-reply wait: 30s default

Adjust via environment variables:
```bash
DISCOVERY_TIMEOUT=60 MESSAGE_TIMEOUT=20 .venv/bin/python test_chat_e2e.py
```

## Future Enhancements

- [ ] Add tmux-based TUI automation for visual regression testing
- [ ] Support multi-node test scenarios
- [ ] Add performance benchmarking
- [ ] Generate test reports in JSON/HTML
- [ ] Integration with pytest for unit test harness
