create TESTING

This commit is contained in:
2026-06-13 13:46:53 +07:00
parent 1a8bddb037
commit 695a942a5d
15 changed files with 831 additions and 14 deletions

200
scripts/api-smoke.sh Executable file
View File

@@ -0,0 +1,200 @@
#!/usr/bin/env bash
# API smoke tests for lidar_manager_web — run against a live server instance.
set -euo pipefail
BASE="${1:-http://127.0.0.1:18080}"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MISSION_ID="${TEST_MISSION_ID:-testmission00001}"
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
PASS=0
FAIL=0
log_pass() { echo -e "${GREEN}PASS${NC} $*"; PASS=$((PASS + 1)); }
log_fail() { echo -e "${RED}FAIL${NC} $*"; FAIL=$((FAIL + 1)); }
json_field() {
python3 - "$1" "$2" <<'PY'
import json, sys
doc = json.loads(open(sys.argv[1]).read())
path = sys.argv[2].split(".")
cur = doc
for p in path:
cur = cur[p]
print(cur)
PY
}
http_code() {
curl -s -o "$1" -w '%{http_code}' "${@:2}"
}
assert_code() {
local name="$1" expect="$2" file="$3"
shift 3
local code
code="$(http_code "$file" "$@")"
if [[ "$code" == "$expect" ]]; then
log_pass "$name (HTTP $code)"
else
log_fail "$name — expected HTTP $expect, got $code"
[[ -f "$file" ]] && head -c 400 "$file" >&2 || true
echo >&2
fi
}
assert_json_true() {
local name="$1" file="$2" expr="$3"
if python3 - "$file" "$expr" <<'PY'
import json, sys
doc = json.loads(open(sys.argv[1]).read())
env = {"doc": doc, "any": any, "all": all, "len": len, "list": list, "isinstance": isinstance}
ok = eval(sys.argv[2], {"__builtins__": {}}, env)
sys.exit(0 if ok else 1)
PY
then
log_pass "$name"
else
log_fail "$name"
head -c 400 "$file" >&2 || true
echo >&2
fi
}
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
echo "API smoke tests → $BASE"
echo "Fixture mission id: $MISSION_ID"
echo
# --- Health & static ---
assert_code "GET /api/health" 200 "$TMP/health.json" -X GET "$BASE/api/health"
assert_json_true "health ok" "$TMP/health.json" 'doc.get("ok") is True'
assert_code "GET /" 200 "$TMP/index.html" -X GET "$BASE/"
assert_code "GET /missions.js" 200 "$TMP/missions.js" -X GET "$BASE/missions.js"
assert_code "GET /api/state" 200 "$TMP/state.json" -X GET "$BASE/api/state"
assert_code "GET /api/missions" 200 "$TMP/missions.json" -X GET "$BASE/api/missions"
assert_json_true "missions fixture present" "$TMP/missions.json" \
'any(m.get("id") == "'"$MISSION_ID"'" for m in doc.get("missions", []))'
# --- Queue pause/continue (chạy sớm, trước các test enqueue khác) ---
curl -s -X DELETE "$BASE/api/mission_queue" -o /dev/null || true
curl -s -X POST "$BASE/api/mission_queue" \
-H 'Content-Type: application/json' \
-d "{\"mission_id\":\"$MISSION_ID\"}" -o "$TMP/qpost.json"
for _ in $(seq 1 30); do
curl -s "$BASE/api/mission_queue" -o "$TMP/runner_poll.json"
RUNNER_STATE="$(python3 -c "import json; print(json.load(open('$TMP/runner_poll.json')).get('runner',{}).get('state',''))")"
if [[ "$RUNNER_STATE" == "running" ]]; then
break
fi
sleep 0.2
done
if [[ "$RUNNER_STATE" == "running" ]]; then
assert_code "POST /api/mission_queue/pause" 200 "$TMP/pause.json" \
-X POST "$BASE/api/mission_queue/pause"
assert_json_true "runner paused" "$TMP/pause.json" 'doc.get("state") == "paused"'
assert_code "POST /api/mission_queue/continue" 200 "$TMP/cont.json" \
-X POST "$BASE/api/mission_queue/continue"
assert_json_true "runner not paused" "$TMP/cont.json" 'doc.get("state") != "paused"'
else
log_fail "runner never reached running (pause test skipped)"
fi
# --- LiDAR CRUD ---
assert_code "POST /api/lidars" 201 "$TMP/lidar.json" \
-X POST "$BASE/api/lidars" \
-H 'Content-Type: application/json' \
-d '{"name":"smoke-lidar","ip":"192.168.99.1","port":2112}'
LIDAR_ID="$(json_field "$TMP/lidar.json" id 2>/dev/null || echo "")"
if [[ -n "$LIDAR_ID" ]]; then
log_pass "lidar created id=$LIDAR_ID"
PASS=$((PASS + 1))
assert_code "PUT /api/lidars" 200 "$TMP/lidar_put.json" \
-X PUT "$BASE/api/lidars/$LIDAR_ID" \
-H 'Content-Type: application/json' \
-d '{"name":"smoke-lidar","ip":"192.168.99.2","port":2112}'
assert_code "DELETE /api/lidars" 204 "$TMP/lidar_del.txt" \
-X DELETE "$BASE/api/lidars/$LIDAR_ID"
else
log_fail "lidar create — no id in response"
fi
# --- IMU CRUD ---
assert_code "POST /api/imus" 201 "$TMP/imu.json" \
-X POST "$BASE/api/imus" \
-H 'Content-Type: application/json' \
-d '{"name":"smoke-imu","frame_id":"imu_smoke","topic":"/imu/smoke","source":"external"}'
IMU_ID="$(json_field "$TMP/imu.json" id 2>/dev/null || echo "")"
if [[ -n "$IMU_ID" ]]; then
log_pass "imu created id=$IMU_ID"
PASS=$((PASS + 1))
assert_code "DELETE /api/imus" 204 "$TMP/imu_del.txt" -X DELETE "$BASE/api/imus/$IMU_ID"
else
log_fail "imu create — no id in response"
fi
# --- Clear queue ---
curl -s -X DELETE "$BASE/api/mission_queue" -o /dev/null || true
curl -s -X DELETE "$BASE/api/v2.0.0/mission_queue" -o /dev/null || true
# --- MiR v2 enqueue (Cách C REST) ---
assert_code "POST /api/v2.0.0/mission_queue" 201 "$TMP/v2q.json" \
-X POST "$BASE/api/v2.0.0/mission_queue" \
-H 'Content-Type: application/json' \
-d "{\"mission_id\":\"$MISSION_ID\",\"priority\":2,\"robot_id\":\"default\"}"
assert_json_true "v2 queue entry mission_id" "$TMP/v2q.json" \
'doc.get("mission_id") == "'"$MISSION_ID"'"'
assert_code "GET /api/v2.0.0/mission_queue" 200 "$TMP/v2list.json" -X GET "$BASE/api/v2.0.0/mission_queue"
assert_json_true "v2 queue non-empty" "$TMP/v2list.json" 'isinstance(doc, list) and len(doc) >= 1'
assert_code "GET /api/v2.0.0/status" 200 "$TMP/v2status.json" -X GET "$BASE/api/v2.0.0/status"
assert_json_true "v2 status has state_text" "$TMP/v2status.json" '"state_text" in doc'
# --- Modbus trigger (Cách C) ---
assert_code "POST /api/triggers" 201 "$TMP/trig.json" \
-X POST "$BASE/api/triggers" \
-H 'Content-Type: application/json' \
-d "{\"name\":\"smoke-trigger\",\"coil_id\":1001,\"mission_id\":\"$MISSION_ID\"}"
TRIG_ID="$(json_field "$TMP/trig.json" id 2>/dev/null || echo "")"
if [[ -n "$TRIG_ID" ]]; then
log_pass "trigger created id=$TRIG_ID"
PASS=$((PASS + 1))
assert_code "POST modbus fire coil" 200 "$TMP/fire.json" \
-X POST "$BASE/api/modbus/coils/1001/trigger"
assert_code "DELETE /api/triggers" 204 "$TMP/trig_del.txt" \
-X DELETE "$BASE/api/triggers/$TRIG_ID"
else
log_fail "trigger create — no id"
fi
# --- Fleet schedule ---
assert_code "POST /api/fleet/schedules" 201 "$TMP/sched.json" \
-X POST "$BASE/api/fleet/schedules" \
-H 'Content-Type: application/json' \
-d "{\"name\":\"smoke-schedule\",\"mission_id\":\"$MISSION_ID\",\"start_mode\":\"asap\",\"priority\":1}"
SCHED_ID="$(json_field "$TMP/sched.json" id 2>/dev/null || echo "")"
if [[ -n "$SCHED_ID" ]]; then
log_pass "schedule created id=$SCHED_ID"
PASS=$((PASS + 1))
assert_code "DELETE /api/fleet/schedules" 204 "$TMP/sched_del.txt" \
-X DELETE "$BASE/api/fleet/schedules/$SCHED_ID"
else
log_fail "schedule create — no id"
fi
assert_code "GET /api/fleet/robots" 200 "$TMP/robots.json" -X GET "$BASE/api/fleet/robots"
assert_json_true "robots list" "$TMP/robots.json" 'isinstance(doc, list) and len(doc) >= 1'
echo
echo "Results: $PASS passed, $FAIL failed"
if [[ "$FAIL" -gt 0 ]]; then
exit 1
fi

71
scripts/run-tests.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/usr/bin/env bash
# Build unit tests, start lidar_manager_web on a temp data dir, run API smoke + pytest.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
PORT="${TEST_PORT:-18080}"
BASE="http://127.0.0.1:${PORT}"
BIN="${ROOT}/build/lidar_manager_web"
DATA_DIR="$(mktemp -d)"
SERVER_PID=""
cleanup() {
if [[ -n "$SERVER_PID" ]] && kill -0 "$SERVER_PID" 2>/dev/null; then
kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
fi
rm -rf "$DATA_DIR"
}
trap cleanup EXIT
echo "==> Configure & build (BUILD_TESTING=ON)"
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
cmake --build build -j
echo "==> C++ unit tests (GTest)"
./build/lidar_manager_tests
echo "==> Prepare isolated data directory"
cp -a tests/fixtures/data/. "$DATA_DIR/"
mkdir -p "$DATA_DIR/models"
echo "==> Start server on port $PORT"
"$BIN" "$PORT" "$ROOT/www" "$DATA_DIR/state.json" >"$DATA_DIR/server.log" 2>&1 &
SERVER_PID=$!
for i in $(seq 1 30); do
if curl -sf "$BASE/api/health" >/dev/null 2>&1; then
break
fi
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo "Server exited early:" >&2
cat "$DATA_DIR/server.log" >&2 || true
exit 1
fi
sleep 0.2
done
if ! curl -sf "$BASE/api/health" >/dev/null 2>&1; then
echo "Server did not become ready on $BASE" >&2
cat "$DATA_DIR/server.log" >&2 || true
exit 1
fi
echo "==> API smoke tests"
chmod +x scripts/api-smoke.sh
./scripts/api-smoke.sh "$BASE"
if command -v python3 >/dev/null 2>&1; then
echo "==> Python integration tests (pytest)"
if ! python3 -c "import pytest" 2>/dev/null; then
python3 -m pip install --user -q -r tests/requirements.txt
fi
TEST_BASE_URL="$BASE" python3 -m pytest tests/test_api_integration.py -q
else
echo "==> Skipping pytest (python3 not found)"
fi
echo
echo "All tests passed."