|
| 1 | +#!/bin/sh |
| 2 | +# automated smoke test for the fauxmo container |
| 3 | +# builds the image, starts it with a single CommandLinePlugin device and asserts |
| 4 | +# fauxmo actually comes up and serves the WeMo setup.xml for that device. |
| 5 | +# |
| 6 | +# this test is the guard that lets us keep 'FROM alpine' (unpinned/latest) in the |
| 7 | +# Dockerfile: if a future alpine/python/fauxmo bump breaks startup or the HTTP |
| 8 | +# emulation, this fails instead of silently publishing a broken image. |
| 9 | +set -eu |
| 10 | + |
| 11 | +IMAGE=fauxmo-test |
| 12 | +NAME=fauxmo-test-run |
| 13 | +DEVICE_PORT=49915 |
| 14 | +DEVICE_NAME="test device" |
| 15 | + |
| 16 | +FAILED=0 |
| 17 | +fail() { |
| 18 | + echo "FAIL: $*" >&2 |
| 19 | + FAILED=1 |
| 20 | +} |
| 21 | + |
| 22 | +cleanup() { |
| 23 | + echo ">> cleanup: removing container $NAME" |
| 24 | + docker rm -f "$NAME" >/dev/null 2>&1 || true |
| 25 | +} |
| 26 | +trap cleanup EXIT INT TERM |
| 27 | + |
| 28 | +echo ">> building image $IMAGE" |
| 29 | +docker build -t "$IMAGE" . |
| 30 | + |
| 31 | +echo ">> (re)starting container $NAME" |
| 32 | +docker rm -f "$NAME" >/dev/null 2>&1 || true |
| 33 | +# minimal one-device config via env: a CommandLinePlugin device on DEVICE_PORT. |
| 34 | +# the on/off/state commands are no-ops (true) - we only care that fauxmo starts |
| 35 | +# and serves the WeMo HTTP emulation for the device. |
| 36 | +docker run -d --name "$NAME" \ |
| 37 | + -e FAUXMO_PLUGIN_COMMANDLINE_DEVICE_1_test="{ \"name\": \"$DEVICE_NAME\", \"port\": $DEVICE_PORT, \"on_cmd\": \"true\", \"off_cmd\": \"true\", \"state_cmd\": \"true\" }" \ |
| 38 | + "$IMAGE" |
| 39 | + |
| 40 | +echo ">> waiting for fauxmo to start (up to ~40s)" |
| 41 | +READY=0 |
| 42 | +i=0 |
| 43 | +while [ "$i" -lt 20 ]; do |
| 44 | + if ! docker ps --format '{{.Names}}' | grep -q "^${NAME}$"; then |
| 45 | + echo "!! container is not running anymore, dumping logs:" >&2 |
| 46 | + docker logs "$NAME" >&2 2>&1 || true |
| 47 | + fail "container exited during startup" |
| 48 | + break |
| 49 | + fi |
| 50 | + # fauxmo up AND the device port is listening |
| 51 | + if docker exec "$NAME" sh -c 'ps aux | grep -q "[f]auxmo"' 2>/dev/null \ |
| 52 | + && docker exec "$NAME" sh -c "netstat -ltn 2>/dev/null | grep -q ':$DEVICE_PORT '" 2>/dev/null; then |
| 53 | + READY=1 |
| 54 | + break |
| 55 | + fi |
| 56 | + i=$((i + 1)) |
| 57 | + sleep 2 |
| 58 | +done |
| 59 | + |
| 60 | +if [ "$READY" -ne 1 ] && [ "$FAILED" -eq 0 ]; then |
| 61 | + echo "!! fauxmo did not come up in time, dumping logs:" >&2 |
| 62 | + docker logs "$NAME" >&2 2>&1 || true |
| 63 | + fail "timed out waiting for fauxmo / device port" |
| 64 | +fi |
| 65 | + |
| 66 | +# only run the deeper assertions if the container is still up |
| 67 | +if docker ps --format '{{.Names}}' | grep -q "^${NAME}$"; then |
| 68 | + |
| 69 | + echo ">> assert: container is running" |
| 70 | + docker ps --format '{{.Names}}' | grep -q "^${NAME}$" \ |
| 71 | + && echo "ok - container running" || fail "container not running" |
| 72 | + |
| 73 | + echo ">> assert: config.json was generated with our device" |
| 74 | + if docker exec "$NAME" grep -q "$DEVICE_NAME" /etc/fauxmo/config.json; then |
| 75 | + echo "ok - /etc/fauxmo/config.json contains the device" |
| 76 | + else |
| 77 | + fail "generated config.json is missing the device" |
| 78 | + fi |
| 79 | + |
| 80 | + echo ">> assert: fauxmo python process present" |
| 81 | + if docker exec "$NAME" sh -c 'ps aux | grep -q "[f]auxmo"'; then |
| 82 | + echo "ok - fauxmo process running" |
| 83 | + else |
| 84 | + fail "fauxmo process not found" |
| 85 | + fi |
| 86 | + |
| 87 | + echo ">> assert: device HTTP port $DEVICE_PORT is listening" |
| 88 | + if docker exec "$NAME" sh -c "netstat -ltn 2>/dev/null | grep -q ':$DEVICE_PORT '"; then |
| 89 | + echo "ok - port $DEVICE_PORT listening" |
| 90 | + else |
| 91 | + fail "device port $DEVICE_PORT not listening" |
| 92 | + fi |
| 93 | + |
| 94 | + # fauxmo binds to the container's resolved interface IP ("ip_address": "auto"), |
| 95 | + # not 127.0.0.1, so probe the actual bound address from inside the container. |
| 96 | + echo ">> assert: device serves WeMo setup.xml over HTTP" |
| 97 | + SETUP=$(docker exec "$NAME" sh -c "curl -s -i \"http://\$(hostname -i):$DEVICE_PORT/setup.xml\"" 2>/dev/null || true) |
| 98 | + if echo "$SETUP" | grep -q '200 OK' \ |
| 99 | + && echo "$SETUP" | grep -q 'urn:Belkin:device:controllee' \ |
| 100 | + && echo "$SETUP" | grep -q "$DEVICE_NAME"; then |
| 101 | + echo "ok - setup.xml served (WeMo/Belkin emulation responding for '$DEVICE_NAME')" |
| 102 | + else |
| 103 | + echo "!! unexpected setup.xml response:" >&2 |
| 104 | + echo "$SETUP" >&2 |
| 105 | + fail "device did not serve a valid WeMo setup.xml" |
| 106 | + fi |
| 107 | + |
| 108 | + echo ">> assert: fauxmo identifies itself as Fauxmo in HTTP headers" |
| 109 | + if echo "$SETUP" | grep -qi 'X-User-Agent: Fauxmo'; then |
| 110 | + echo "ok - X-User-Agent: Fauxmo header present" |
| 111 | + else |
| 112 | + fail "missing 'X-User-Agent: Fauxmo' header" |
| 113 | + fi |
| 114 | + |
| 115 | +fi |
| 116 | + |
| 117 | +echo |
| 118 | +if [ "$FAILED" -eq 0 ]; then |
| 119 | + echo "ALL TESTS PASSED" |
| 120 | + exit 0 |
| 121 | +else |
| 122 | + echo "SOME TESTS FAILED" |
| 123 | + exit 1 |
| 124 | +fi |
0 commit comments