chore: bump actions/checkout from 4 to 6 #49
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: E2E Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| env: | |
| E2E_DIR: tests/e2e | |
| jobs: | |
| e2e-test: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Eneru | |
| run: pip install ".[notifications]" | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y nut-client openssh-client | |
| - name: Generate SSH keys for E2E tests | |
| run: | | |
| ssh-keygen -t ed25519 -f /tmp/e2e-ssh-key -N "" -C "eneru-e2e-test" | |
| mkdir -p ${{ env.E2E_DIR }}/ssh-target | |
| cp /tmp/e2e-ssh-key.pub ${{ env.E2E_DIR }}/ssh-target/authorized_keys | |
| - name: Build and start E2E environment | |
| run: | | |
| cd ${{ env.E2E_DIR }} | |
| # Build and start services (authorized_keys is bind-mounted from ssh-target/) | |
| docker compose up -d --build | |
| docker compose ps | |
| - name: Wait for services to be ready | |
| run: | | |
| echo "Waiting for NUT server..." | |
| for i in {1..30}; do | |
| if upsc TestUPS@localhost:3493 2>/dev/null | grep -q "ups.status"; then | |
| echo "NUT server ready!" | |
| break | |
| fi | |
| echo " Attempt $i/30..." | |
| sleep 2 | |
| done | |
| echo "" | |
| echo "Waiting for SSH server..." | |
| for i in {1..30}; do | |
| if nc -z localhost 2222 2>/dev/null; then | |
| echo "SSH server ready!" | |
| break | |
| fi | |
| echo " Attempt $i/30..." | |
| sleep 2 | |
| done | |
| echo "" | |
| echo "=== E2E Environment Ready ===" | |
| upsc TestUPS@localhost:3493 2>/dev/null | head -15 | |
| - name: Create tmpfs mount for unmount testing | |
| run: | | |
| sudo mkdir -p /tmp/eneru-e2e-tmpfs | |
| sudo mount -t tmpfs -o size=1M tmpfs /tmp/eneru-e2e-tmpfs | |
| echo "test data" | sudo tee /tmp/eneru-e2e-tmpfs/testfile | |
| mount | grep eneru-e2e-tmpfs | |
| - name: Test SSH connectivity | |
| run: | | |
| ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
| -i /tmp/e2e-ssh-key -p 2222 testuser@localhost \ | |
| "echo 'SSH connection successful!'" | |
| # ======================================== | |
| # TEST 1: Validate config against real NUT | |
| # ======================================== | |
| - name: "Test 1: Validate E2E config" | |
| run: | | |
| echo "=== Test 1: Config Validation ===" | |
| eneru validate --config ${{ env.E2E_DIR }}/config-e2e.yaml | |
| # ======================================== | |
| # TEST 2: Monitor normal (online) state | |
| # ======================================== | |
| - name: "Test 2: Monitor normal state (no shutdown triggered)" | |
| run: | | |
| echo "=== Test 2: Normal State Monitoring ===" | |
| # Ensure UPS is in online state | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply.dev | |
| sleep 3 | |
| # Run Eneru for 5 seconds - should NOT trigger shutdown | |
| timeout 5 eneru run --config ${{ env.E2E_DIR }}/config-e2e-dry-run.yaml 2>&1 | tee /tmp/test2.log || true | |
| # Verify no shutdown was triggered | |
| if grep -q "SHUTDOWN SEQUENCE" /tmp/test2.log; then | |
| echo "FAIL: Shutdown was triggered during normal operation!" | |
| exit 1 | |
| fi | |
| echo "PASS: No shutdown triggered during normal operation" | |
| # ======================================== | |
| # TEST 3: Detect power failure (dry-run) | |
| # ======================================== | |
| - name: "Test 3: Detect power failure (dry-run)" | |
| run: | | |
| echo "=== Test 3: Power Failure Detection ===" | |
| # Clean up any previous shutdown flags | |
| rm -f /tmp/eneru-e2e-shutdown-flag | |
| # Switch to low battery scenario | |
| cp ${{ env.E2E_DIR }}/scenarios/low-battery.dev ${{ env.E2E_DIR }}/scenarios/apply.dev | |
| sleep 3 | |
| # Run Eneru in dry-run mode | |
| eneru run --config ${{ env.E2E_DIR }}/config-e2e-dry-run.yaml --exit-after-shutdown 2>&1 | tee /tmp/test3.log || true | |
| # Verify shutdown was triggered (in dry-run) | |
| if ! grep -q "SHUTDOWN SEQUENCE" /tmp/test3.log; then | |
| echo "FAIL: Shutdown was NOT triggered for low battery!" | |
| cat /tmp/test3.log | |
| exit 1 | |
| fi | |
| if ! grep -q "DRY-RUN" /tmp/test3.log; then | |
| echo "FAIL: Dry-run mode not indicated!" | |
| exit 1 | |
| fi | |
| echo "PASS: Low battery correctly triggered shutdown (dry-run)" | |
| # ======================================== | |
| # TEST 4: SSH remote shutdown (real execution) | |
| # ======================================== | |
| # NOTE: Container shutdown is tested in dry-run (Test 3). Real container shutdown | |
| # with shutdown_all_remaining_containers=true would stop the NUT server container, | |
| # causing connection loss and repeated shutdown loops. So Test 4 focuses on SSH. | |
| - name: "Test 4: SSH remote shutdown" | |
| run: | | |
| echo "=== Test 4: SSH Remote Shutdown ===" | |
| cd ${{ env.E2E_DIR }} | |
| # Reset SSH target state | |
| docker compose exec -T ssh-target sh -c "rm -f /var/run/shutdown-triggered && touch /var/run/server-alive" | |
| # Clean up shutdown flag | |
| rm -f /tmp/eneru-e2e-shutdown-flag | |
| # Switch to low battery scenario | |
| cp scenarios/low-battery.dev scenarios/apply.dev | |
| sleep 3 | |
| # Run Eneru briefly - will trigger shutdown and send SSH command | |
| eneru run --config config-e2e.yaml --exit-after-shutdown 2>&1 | tee /tmp/test4.log || true | |
| echo "" | |
| echo "=== Verifying SSH shutdown ===" | |
| # Verify SSH shutdown command was sent | |
| if docker compose exec -T ssh-target test -f /var/run/shutdown-triggered; then | |
| echo "PASS: SSH shutdown command was received" | |
| else | |
| echo "FAIL: SSH shutdown command was NOT received" | |
| docker compose logs ssh-target | |
| exit 1 | |
| fi | |
| # Check the shutdown log | |
| echo "SSH target shutdown log:" | |
| docker compose exec -T ssh-target cat /var/log/shutdown.log || true | |
| echo "" | |
| echo "=== Test 4 PASSED: SSH remote shutdown executed successfully ===" | |
| # ======================================== | |
| # TEST 5: FSD (Forced Shutdown) trigger | |
| # ======================================== | |
| - name: "Test 5: FSD flag triggers immediate shutdown" | |
| run: | | |
| echo "=== Test 5: FSD Trigger ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag | |
| # Switch to FSD scenario | |
| cp ${{ env.E2E_DIR }}/scenarios/fsd.dev ${{ env.E2E_DIR }}/scenarios/apply.dev | |
| sleep 3 | |
| # Run Eneru in dry-run mode | |
| eneru run --config ${{ env.E2E_DIR }}/config-e2e-dry-run.yaml --exit-after-shutdown 2>&1 | tee /tmp/test5.log || true | |
| # Verify FSD triggered shutdown | |
| if ! grep -q "FSD" /tmp/test5.log; then | |
| echo "FAIL: FSD was not detected!" | |
| cat /tmp/test5.log | |
| exit 1 | |
| fi | |
| echo "PASS: FSD correctly triggered shutdown" | |
| # ======================================== | |
| # TEST 6: Voltage events (brownout, AVR) | |
| # ======================================== | |
| - name: "Test 6: Voltage event detection" | |
| run: | | |
| echo "=== Test 6: Voltage Events ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag | |
| # Start with normal state | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply.dev | |
| sleep 2 | |
| # Switch to brownout scenario | |
| cp ${{ env.E2E_DIR }}/scenarios/brownout.dev ${{ env.E2E_DIR }}/scenarios/apply.dev | |
| sleep 2 | |
| # Run briefly to detect brownout | |
| timeout 8 eneru run --config ${{ env.E2E_DIR }}/config-e2e-dry-run.yaml 2>&1 | tee /tmp/test6.log || true | |
| # Verify brownout was detected (should log a voltage warning) | |
| if grep -q -i "voltage\|brownout" /tmp/test6.log; then | |
| echo "PASS: Voltage event detected" | |
| else | |
| echo "Note: Voltage event may not have been logged in short window" | |
| fi | |
| # ======================================== | |
| # TEST 7: Notification test (if secret available) | |
| # ======================================== | |
| - name: "Test 7: Notification delivery" | |
| env: | |
| E2E_NOTIFICATION_URL: ${{ secrets.E2E_NOTIFICATION_URL }} | |
| run: | | |
| echo "=== Test 7: Notification Delivery ===" | |
| if [ -z "$E2E_NOTIFICATION_URL" ]; then | |
| echo "SKIP: E2E_NOTIFICATION_URL secret not configured" | |
| exit 0 | |
| fi | |
| # Create config with notification URL substituted | |
| sed "s|\${E2E_NOTIFICATION_URL}|${E2E_NOTIFICATION_URL}|g" \ | |
| ${{ env.E2E_DIR }}/config-e2e-notifications.yaml > /tmp/config-notif.yaml | |
| # Test notification delivery | |
| eneru test-notifications --config /tmp/config-notif.yaml | |
| echo "PASS: Notification sent successfully" | |
| # ======================================== | |
| # TEST 8: Multi-UPS config validation | |
| # ======================================== | |
| - name: "Test 8: Multi-UPS config validation" | |
| run: | | |
| echo "=== Test 8: Multi-UPS Config Validation ===" | |
| # Verify both UPSes are reachable | |
| upsc UPS1@localhost:3493 2>/dev/null | grep -q "ups.status" || { echo "FAIL: UPS1 not reachable"; exit 1; } | |
| upsc UPS2@localhost:3493 2>/dev/null | grep -q "ups.status" || { echo "FAIL: UPS2 not reachable"; exit 1; } | |
| echo "Both UPS1 and UPS2 reachable on NUT server" | |
| # Validate multi-UPS config | |
| eneru validate --config ${{ env.E2E_DIR }}/config-e2e-multi-ups.yaml | |
| echo "PASS: Multi-UPS config validates against real NUT server" | |
| # ======================================== | |
| # TEST 9: Multi-UPS isolation - UPS1 fails, UPS2 unaffected | |
| # ======================================== | |
| - name: "Test 9: Multi-UPS isolation (UPS1 fails, UPS2 normal)" | |
| run: | | |
| echo "=== Test 9: Multi-UPS Isolation ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag* | |
| # Ensure both UPSes start online | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS1.dev | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS2.dev | |
| sleep 3 | |
| # Now fail UPS1 (low battery) while UPS2 stays online | |
| cp ${{ env.E2E_DIR }}/scenarios/low-battery.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS1.dev | |
| sleep 3 | |
| # Verify UPS1 is on battery / low | |
| UPS1_STATUS=$(upsc UPS1@localhost:3493 ups.status 2>/dev/null) | |
| UPS2_STATUS=$(upsc UPS2@localhost:3493 ups.status 2>/dev/null) | |
| echo "UPS1 status: $UPS1_STATUS" | |
| echo "UPS2 status: $UPS2_STATUS" | |
| if echo "$UPS1_STATUS" | grep -q "OB"; then | |
| echo "PASS: UPS1 correctly shows on battery" | |
| else | |
| echo "FAIL: UPS1 should be on battery, got: $UPS1_STATUS" | |
| exit 1 | |
| fi | |
| if echo "$UPS2_STATUS" | grep -q "OL"; then | |
| echo "PASS: UPS2 correctly shows online (unaffected)" | |
| else | |
| echo "FAIL: UPS2 should still be online, got: $UPS2_STATUS" | |
| exit 1 | |
| fi | |
| # Run multi-UPS Eneru briefly -- UPS1 should trigger shutdown for its group | |
| eneru run --config ${{ env.E2E_DIR }}/config-e2e-multi-ups.yaml --exit-after-shutdown 2>&1 | tee /tmp/test9.log || true | |
| # Verify UPS1 triggered shutdown | |
| if grep -q "SHUTDOWN SEQUENCE\|SHUTDOWN INITIATED\|Triggering immediate shutdown" /tmp/test9.log; then | |
| echo "PASS: UPS1 low battery triggered shutdown" | |
| else | |
| echo "FAIL: No shutdown triggered for UPS1" | |
| cat /tmp/test9.log | |
| exit 1 | |
| fi | |
| # Verify the log shows UPS1 context (prefixed with display name or UPS name) | |
| if grep -q "E2E UPS1\|UPS1@localhost" /tmp/test9.log; then | |
| echo "PASS: Shutdown log correctly identifies UPS1" | |
| else | |
| echo "Note: UPS identification in logs not verified" | |
| fi | |
| echo "PASS: Multi-UPS isolation working correctly" | |
| # ======================================== | |
| # TEST 10: Multi-UPS both online (no false triggers) | |
| # ======================================== | |
| - name: "Test 10: Multi-UPS both online (no false triggers)" | |
| run: | | |
| echo "=== Test 10: Multi-UPS Normal Operation ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag* | |
| # Both UPSes online | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS1.dev | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS2.dev | |
| sleep 3 | |
| # Run briefly -- should NOT trigger any shutdown | |
| timeout 5 eneru run --config ${{ env.E2E_DIR }}/config-e2e-multi-ups.yaml 2>&1 | tee /tmp/test10.log || true | |
| if grep -q "SHUTDOWN SEQUENCE\|SHUTDOWN INITIATED" /tmp/test10.log; then | |
| echo "FAIL: Shutdown triggered during normal multi-UPS operation!" | |
| exit 1 | |
| fi | |
| echo "PASS: No false shutdown triggers with both UPSes online" | |
| # ======================================== | |
| # TEST 11: Ownership validation (non-local with containers) | |
| # ======================================== | |
| - name: "Test 11: Ownership validation rejects non-local containers" | |
| run: | | |
| echo "=== Test 11: Ownership Validation ===" | |
| cat > /tmp/config-bad-ownership.yaml <<YAML | |
| ups: | |
| - name: "UPS1@localhost:3493" | |
| is_local: true | |
| - name: "UPS2@localhost:3493" | |
| containers: | |
| enabled: true | |
| YAML | |
| # validate should report ERROR for non-local group with containers | |
| OUTPUT=$(eneru validate --config /tmp/config-bad-ownership.yaml 2>&1) || true | |
| if echo "$OUTPUT" | grep -q "ERROR.*containers"; then | |
| echo "PASS: Ownership violation correctly detected" | |
| else | |
| echo "FAIL: Ownership violation not detected" | |
| echo "$OUTPUT" | |
| exit 1 | |
| fi | |
| # ======================================== | |
| # TEST 12: CLI safety (bare eneru shows help) | |
| # ======================================== | |
| - name: "Test 12: CLI safety - bare eneru shows help" | |
| run: | | |
| echo "=== Test 12: CLI Safety ===" | |
| OUTPUT=$(eneru 2>&1) || true | |
| EXIT_CODE=$? | |
| if echo "$OUTPUT" | grep -q "run\|validate\|monitor"; then | |
| echo "PASS: Bare 'eneru' shows help with subcommands" | |
| else | |
| echo "FAIL: Bare 'eneru' did not show help" | |
| echo "$OUTPUT" | |
| exit 1 | |
| fi | |
| # ======================================== | |
| # TEST 13: TUI --once snapshot | |
| # ======================================== | |
| - name: "Test 13: TUI --once snapshot" | |
| run: | | |
| echo "=== Test 13: TUI --once ===" | |
| OUTPUT=$(eneru monitor --config ${{ env.E2E_DIR }}/config-e2e-dry-run.yaml --once 2>&1) | |
| if echo "$OUTPUT" | grep -q "TestUPS@localhost\|Eneru v"; then | |
| echo "PASS: TUI --once outputs UPS status" | |
| else | |
| echo "FAIL: TUI --once did not produce expected output" | |
| echo "$OUTPUT" | |
| exit 1 | |
| fi | |
| # ======================================== | |
| # TEST 14: Multi-UPS concurrent failure | |
| # ======================================== | |
| - name: "Test 14: Multi-UPS concurrent failure (both UPSes fail)" | |
| run: | | |
| echo "=== Test 14: Concurrent UPS Failure ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag* | |
| # Both UPSes go low-battery simultaneously | |
| cp ${{ env.E2E_DIR }}/scenarios/low-battery.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS1.dev | |
| cp ${{ env.E2E_DIR }}/scenarios/low-battery.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS2.dev | |
| sleep 3 | |
| # Run multi-UPS Eneru -- both groups should trigger shutdown | |
| eneru run --config ${{ env.E2E_DIR }}/config-e2e-multi-ups.yaml --exit-after-shutdown 2>&1 | tee /tmp/test14.log || true | |
| # Verify shutdown was triggered | |
| if ! grep -q "SHUTDOWN SEQUENCE\|SHUTDOWN INITIATED\|Triggering immediate shutdown" /tmp/test14.log; then | |
| echo "FAIL: No shutdown triggered during concurrent failure" | |
| cat /tmp/test14.log | |
| exit 1 | |
| fi | |
| # Verify both UPSes are referenced in the log | |
| if grep -q "E2E UPS1\|UPS1@localhost" /tmp/test14.log; then | |
| echo "PASS: UPS1 shutdown logged" | |
| else | |
| echo "Note: UPS1 identification not verified in logs" | |
| fi | |
| if grep -q "E2E UPS2\|UPS2@localhost" /tmp/test14.log; then | |
| echo "PASS: UPS2 shutdown logged" | |
| else | |
| echo "Note: UPS2 identification not verified in logs" | |
| fi | |
| echo "PASS: Concurrent failure handled correctly" | |
| # ======================================== | |
| # TEST 15: Non-local failure (UPS2 fails, UPS1 unaffected) | |
| # ======================================== | |
| - name: "Test 15: Non-local failure (UPS2 fails, UPS1 unaffected)" | |
| run: | | |
| echo "=== Test 15: Non-Local UPS Failure ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag* | |
| # UPS1 stays online, UPS2 goes low-battery | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS1.dev | |
| cp ${{ env.E2E_DIR }}/scenarios/low-battery.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS2.dev | |
| sleep 3 | |
| # Verify UPS states | |
| UPS1_STATUS=$(upsc UPS1@localhost:3493 ups.status 2>/dev/null) | |
| UPS2_STATUS=$(upsc UPS2@localhost:3493 ups.status 2>/dev/null) | |
| echo "UPS1 status: $UPS1_STATUS (should be OL)" | |
| echo "UPS2 status: $UPS2_STATUS (should be OB)" | |
| # Run multi-UPS Eneru -- UPS2 (non-local) should trigger, UPS1 unaffected | |
| eneru run --config ${{ env.E2E_DIR }}/config-e2e-multi-ups.yaml --exit-after-shutdown 2>&1 | tee /tmp/test15.log || true | |
| # Verify UPS2 triggered shutdown | |
| if ! grep -q "SHUTDOWN SEQUENCE\|SHUTDOWN INITIATED\|Triggering immediate shutdown" /tmp/test15.log; then | |
| echo "FAIL: No shutdown triggered for UPS2" | |
| cat /tmp/test15.log | |
| exit 1 | |
| fi | |
| # Verify UPS2 is identified in shutdown context | |
| if grep -q "E2E UPS2\|UPS2@localhost" /tmp/test15.log; then | |
| echo "PASS: UPS2 correctly triggered shutdown" | |
| else | |
| echo "Note: UPS2 identification not verified in logs" | |
| fi | |
| echo "PASS: Non-local failure correctly handled" | |
| # ======================================== | |
| # TEST 16: Local drain (drain_on_local_shutdown=true) | |
| # ======================================== | |
| - name: "Test 16: Local drain (drain_on_local_shutdown=true)" | |
| run: | | |
| echo "=== Test 16: Local Drain ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag* | |
| # UPS1 (is_local) goes low-battery, UPS2 stays online | |
| cp ${{ env.E2E_DIR }}/scenarios/low-battery.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS1.dev | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS2.dev | |
| sleep 3 | |
| # Run with drain config | |
| eneru run --config ${{ env.E2E_DIR }}/config-e2e-multi-ups-drain.yaml --exit-after-shutdown 2>&1 | tee /tmp/test16.log || true | |
| # Verify shutdown was triggered | |
| if ! grep -q "SHUTDOWN SEQUENCE\|SHUTDOWN INITIATED\|Triggering immediate shutdown" /tmp/test16.log; then | |
| echo "FAIL: No shutdown triggered" | |
| cat /tmp/test16.log | |
| exit 1 | |
| fi | |
| # Verify drain message appears | |
| if grep -qi "drain" /tmp/test16.log; then | |
| echo "PASS: Drain message logged" | |
| else | |
| echo "FAIL: Drain message not found in logs" | |
| cat /tmp/test16.log | |
| exit 1 | |
| fi | |
| echo "PASS: Local drain correctly executed" | |
| # ======================================== | |
| # TEST 17: Local no-drain (drain_on_local_shutdown=false) | |
| # ======================================== | |
| - name: "Test 17: Local no-drain (drain_on_local_shutdown=false)" | |
| run: | | |
| echo "=== Test 17: Local No-Drain ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag* | |
| # UPS1 (is_local) goes low-battery, UPS2 stays online | |
| cp ${{ env.E2E_DIR }}/scenarios/low-battery.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS1.dev | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply-UPS2.dev | |
| sleep 3 | |
| # Run with default multi-UPS config (drain=false) | |
| eneru run --config ${{ env.E2E_DIR }}/config-e2e-multi-ups.yaml --exit-after-shutdown 2>&1 | tee /tmp/test17.log || true | |
| # Verify UPS1 shutdown triggered | |
| if ! grep -q "SHUTDOWN SEQUENCE\|SHUTDOWN INITIATED\|Triggering immediate shutdown" /tmp/test17.log; then | |
| echo "FAIL: No shutdown triggered for UPS1" | |
| cat /tmp/test17.log | |
| exit 1 | |
| fi | |
| # Verify NO drain message | |
| if grep -qi "drain" /tmp/test17.log; then | |
| echo "FAIL: Drain should NOT occur with drain_on_local_shutdown=false" | |
| exit 1 | |
| fi | |
| echo "PASS: No-drain correctly skipped drain step" | |
| # ======================================== | |
| # TEST 18: Recovery (OB then power restored) | |
| # ======================================== | |
| - name: "Test 18: Recovery - power restored before shutdown" | |
| run: | | |
| echo "=== Test 18: Power Recovery ===" | |
| # Clean up | |
| rm -f /tmp/eneru-e2e-shutdown-flag* | |
| # Start with on-battery (above thresholds -- no shutdown trigger) | |
| cp ${{ env.E2E_DIR }}/scenarios/on-battery.dev ${{ env.E2E_DIR }}/scenarios/apply.dev | |
| sleep 3 | |
| # Run Eneru in background | |
| timeout 12 eneru run --config ${{ env.E2E_DIR }}/config-e2e-dry-run.yaml 2>&1 | tee /tmp/test18.log & | |
| ENERU_PID=$! | |
| # Wait for Eneru to detect on-battery state | |
| sleep 4 | |
| # Restore power | |
| cp ${{ env.E2E_DIR }}/scenarios/online-charging.dev ${{ env.E2E_DIR }}/scenarios/apply.dev | |
| sleep 3 | |
| # Wait for Eneru to detect recovery | |
| wait $ENERU_PID 2>/dev/null || true | |
| # Verify POWER_RESTORED was logged | |
| if grep -qi "POWER_RESTORED\|power.*restored\|Power restored" /tmp/test18.log; then | |
| echo "PASS: Power restoration detected" | |
| else | |
| echo "FAIL: POWER_RESTORED not logged" | |
| cat /tmp/test18.log | |
| exit 1 | |
| fi | |
| # Verify NO shutdown was triggered | |
| if grep -q "SHUTDOWN SEQUENCE" /tmp/test18.log; then | |
| echo "FAIL: Shutdown should NOT have been triggered during recovery" | |
| exit 1 | |
| fi | |
| echo "PASS: Recovery correctly handled - no shutdown, power restored logged" | |
| - name: Collect logs on failure | |
| if: failure() | |
| run: | | |
| echo "=== Docker Compose Logs ===" | |
| cd ${{ env.E2E_DIR }} | |
| docker compose logs | |
| echo "" | |
| echo "=== Eneru Test Logs ===" | |
| cat /tmp/test*.log 2>/dev/null || echo "No test logs found" | |
| - name: Cleanup | |
| if: always() | |
| run: | | |
| cd ${{ env.E2E_DIR }} | |
| docker compose down -v --remove-orphans | |
| sudo umount /tmp/eneru-e2e-tmpfs 2>/dev/null || true |