447 lines
26 KiB
Bash
Executable File
447 lines
26 KiB
Bash
Executable File
#!/bin/bash
|
||
# ══════════════════════════════════════════════════════════════════════
|
||
# Smoke-тесты Morozovka Portal — без GUI, через curl + psql
|
||
# Запуск: sudo bash tests/run_smoke.sh
|
||
# Вывод: цветной PASS/FAIL + tests/last_run.log
|
||
# ══════════════════════════════════════════════════════════════════════
|
||
|
||
set -uo pipefail
|
||
|
||
# ── Настройки ─────────────────────────────────────────────────────────
|
||
BASE="http://127.0.0.1:8000"
|
||
PG_HOST="127.0.0.1"
|
||
PG_USER="morozovka"
|
||
PG_PASS="${PG_PASS:-rf-slf-1945}"
|
||
PG_DB="morozovka_portal"
|
||
LOG_FILE="$(dirname "$0")/last_run.log"
|
||
COOKIE="/tmp/smoke_cookies_$$.txt"
|
||
TMP="/tmp/smoke_$$"
|
||
TS="$(date +%s)"
|
||
TEST_USER="smoke_test_${TS}"
|
||
TEST_PASS="SmokePass_${TS}!"
|
||
TEST_EMAIL="${TEST_USER}@test.local"
|
||
|
||
mkdir -p "$TMP"
|
||
: > "$LOG_FILE"
|
||
|
||
PASS=0
|
||
FAIL=0
|
||
TOTAL=0
|
||
START_TIME=$(date +%s)
|
||
|
||
# ── Цвета ─────────────────────────────────────────────────────────────
|
||
if [ -t 1 ]; then
|
||
C_GREEN='\033[0;32m'; C_RED='\033[0;31m'; C_YELLOW='\033[1;33m'
|
||
C_CYAN='\033[0;36m'; C_BOLD='\033[1m'; C_RESET='\033[0m'
|
||
else
|
||
C_GREEN=''; C_RED=''; C_YELLOW=''; C_CYAN=''; C_BOLD=''; C_RESET=''
|
||
fi
|
||
|
||
# ── Хелперы печати ────────────────────────────────────────────────────
|
||
section() {
|
||
local msg="$1"
|
||
echo
|
||
echo -e "${C_BOLD}═══ $msg ═══${C_RESET}"
|
||
echo "--- $msg ---" >> "$LOG_FILE"
|
||
}
|
||
info() {
|
||
echo -e "${C_CYAN} → $1${C_RESET}"
|
||
echo " INFO: $1" >> "$LOG_FILE"
|
||
}
|
||
pass() {
|
||
PASS=$((PASS+1)); TOTAL=$((TOTAL+1))
|
||
echo -e " ${C_GREEN}✓${C_RESET} $1"
|
||
echo " PASS: $1" >> "$LOG_FILE"
|
||
}
|
||
fail() {
|
||
FAIL=$((FAIL+1)); TOTAL=$((TOTAL+1))
|
||
echo -e " ${C_RED}✗${C_RESET} $1"
|
||
echo " ожидалось: $2"
|
||
echo " получено: $3"
|
||
echo " FAIL: $1" >> "$LOG_FILE"
|
||
echo " expected: $2" >> "$LOG_FILE"
|
||
echo " actual: $3" >> "$LOG_FILE"
|
||
}
|
||
|
||
# ── Хелперы проверок ──────────────────────────────────────────────────
|
||
check_status() {
|
||
local name="$1" expected="$2" actual="$3"
|
||
if [ "$actual" = "$expected" ]; then pass "$name"; else fail "$name" "$expected" "$actual"; fi
|
||
}
|
||
check_contains() {
|
||
local name="$1" pattern="$2" file="$3"
|
||
if grep -qF "$pattern" "$file" 2>/dev/null; then pass "$name"; else fail "$name" "contains '$pattern'" "не найдено в $file"; fi
|
||
}
|
||
check_not_empty() {
|
||
local name="$1" val="$2"
|
||
if [ -n "$val" ] && [ "$val" != "0" ]; then pass "$name"; else fail "$name" "not empty" "пусто"; fi
|
||
}
|
||
check_equal() {
|
||
local name="$1" expected="$2" actual="$3"
|
||
if [ "$actual" = "$expected" ]; then pass "$name"; else fail "$name" "$expected" "$actual"; fi
|
||
}
|
||
|
||
# ── psql ──────────────────────────────────────────────────────────────
|
||
psql_run() {
|
||
PGPASSWORD="$PG_PASS" psql -h "$PG_HOST" -U "$PG_USER" -d "$PG_DB" -tAc "$1" 2>/dev/null
|
||
}
|
||
psql_val() {
|
||
psql_run "$1" | tr -d ' \n'
|
||
}
|
||
|
||
# ── curl обёртки ──────────────────────────────────────────────────────
|
||
http_code_get() {
|
||
curl -sS -o "$2" -w "%{http_code}" -b "$COOKIE" "$1" 2>/dev/null
|
||
}
|
||
http_code_post_form() {
|
||
curl -sS -o "$2" -w "%{http_code}" -b "$COOKIE" -c "$COOKIE" -X POST "$1" -d "$3" 2>/dev/null
|
||
}
|
||
http_code_post_json() {
|
||
curl -sS -o "$2" -w "%{http_code}" -b "$COOKIE" -c "$COOKIE" -X POST "$1" \
|
||
-H "Content-Type: application/json" -d "$3" 2>/dev/null
|
||
}
|
||
|
||
# ── Cleanup ───────────────────────────────────────────────────────────
|
||
cleanup() {
|
||
info "Cleanup тестовых данных…"
|
||
|
||
local USER_ID="${TEST_USER_ID:-}"
|
||
if [ -n "$USER_ID" ]; then
|
||
# Удаляем строго по ID, ничего чужого не заденем
|
||
psql_run "DELETE FROM portalfile WHERE uploaded_by = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM fileversion WHERE uploaded_by = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM filefolderacl WHERE user_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM filespaceacl WHERE user_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM filefolder WHERE created_by = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM filespace WHERE owner_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM project WHERE owner_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM workshop WHERE lead_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM projecttask WHERE created_by = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM projectpost WHERE author_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM workshoppost WHERE author_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM useronboarding WHERE user_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM eventsubscriber WHERE user_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM usersession WHERE user_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM notification WHERE user_id = $USER_ID" >/dev/null
|
||
psql_run "DELETE FROM \"user\" WHERE id = $USER_ID" >/dev/null
|
||
fi
|
||
|
||
rm -f "$COOKIE"
|
||
rm -rf "$TMP"
|
||
info "Cleanup завершён"
|
||
}
|
||
trap cleanup EXIT
|
||
|
||
# ══════════════════════════════════════════════════════════════════════
|
||
# НАЧАЛО
|
||
# ══════════════════════════════════════════════════════════════════════
|
||
echo -e "${C_BOLD}╔══════════════════════════════════════════════════════╗${C_RESET}"
|
||
echo -e "${C_BOLD}║ Morozovka Portal — Smoke Tests (no GUI) ║${C_RESET}"
|
||
echo -e "${C_BOLD}╚══════════════════════════════════════════════════════╝${C_RESET}"
|
||
echo "Start: $(date -Is)"
|
||
echo "Base: $BASE"
|
||
echo "User: $TEST_USER"
|
||
echo "Log: $LOG_FILE"
|
||
echo
|
||
echo "Start: $(date -Is)" >> "$LOG_FILE"
|
||
echo "Base: $BASE" >> "$LOG_FILE"
|
||
echo "User: $TEST_USER" >> "$LOG_FILE"
|
||
|
||
# ── 0. Проверка, что сервис жив ───────────────────────────────────────
|
||
section "0. Health"
|
||
CODE=$(http_code_get "$BASE/healthz" "$TMP/healthz.json")
|
||
check_status "GET /healthz → 200" "200" "$CODE"
|
||
check_contains "healthz → status:ok" '"status":"ok"' "$TMP/healthz.json"
|
||
check_contains "healthz → db:ok" '"db":"ok"' "$TMP/healthz.json"
|
||
|
||
# ── 1. Публичные роуты ────────────────────────────────────────────────
|
||
section "1. Публичные роуты"
|
||
check_status "GET / → 200" "200" "$(http_code_get "$BASE/" "$TMP/index.html")"
|
||
check_status "GET /schedule → 200" "200" "$(http_code_get "$BASE/schedule" "$TMP/schedule.html")"
|
||
check_status "GET /feed → 200" "200" "$(http_code_get "$BASE/feed" "$TMP/feed.html")"
|
||
check_status "GET /projects → 200" "200" "$(http_code_get "$BASE/projects" "$TMP/projects.html")"
|
||
check_status "GET /workshops → 200" "200" "$(http_code_get "$BASE/workshops" "$TMP/workshops.html")"
|
||
check_status "GET /login → 200" "200" "$(http_code_get "$BASE/login" "$TMP/login.html")"
|
||
check_status "GET /register → 200" "200" "$(http_code_get "$BASE/register" "$TMP/register.html")"
|
||
check_status "GET /forgot_password → 200" "200" "$(http_code_get "$BASE/forgot_password" "$TMP/forgot.html")"
|
||
|
||
# Защищённые без cookie → 302 на login
|
||
rm -f "$COOKIE"
|
||
check_status "GET /files без cookie → 302" "302" "$(http_code_get "$BASE/files" "$TMP/files_noauth.html")"
|
||
check_status "GET /dashboard без cookie → 302" "302" "$(http_code_get "$BASE/dashboard" "$TMP/dash_noauth.html")"
|
||
check_status "GET /onboarding без cookie → 302" "302" "$(http_code_get "$BASE/onboarding" "$TMP/onb_noauth.html")"
|
||
|
||
# ── 2. Регистрация тестового пользователя ────────────────────────────
|
||
section "2. Регистрация пользователя"
|
||
CODE=$(http_code_post_form "$BASE/register" "$TMP/reg_resp.html" \
|
||
"username=${TEST_USER}&email=${TEST_EMAIL}&password=${TEST_PASS}&full_name=Smoke+Test&organization=%D0%9D%D0%9D%D0%93%D0%A3")
|
||
check_status "POST /register → 200" "200" "$CODE"
|
||
|
||
TEST_USER_ID=$(psql_val "SELECT id FROM \"user\" WHERE username = '$TEST_USER'")
|
||
check_not_empty "user создан в БД" "$TEST_USER_ID"
|
||
|
||
if [ -z "$TEST_USER_ID" ] || [ "$TEST_USER_ID" = "0" ]; then
|
||
echo -e "${C_RED}КРИТИЧНО: тестовый юзер не создан. Останавливаюсь.${C_RESET}"
|
||
exit 1
|
||
fi
|
||
info "user_id = $TEST_USER_ID"
|
||
|
||
# Промоут до admin (иначе require_role заблокирует создание проектов/МК)
|
||
psql_run "UPDATE \"user\" SET role = 'admin', is_verified = true WHERE id = $TEST_USER_ID" >/dev/null
|
||
check_equal "роль admin установлена" "admin" "$(psql_val "SELECT role FROM \"user\" WHERE id = $TEST_USER_ID")"
|
||
|
||
# ── 3. Логин ──────────────────────────────────────────────────────────
|
||
section "3. Логин"
|
||
rm -f "$COOKIE"
|
||
CODE=$(curl -sS -o "$TMP/login_resp.html" -w "%{http_code}" -c "$COOKIE" -X POST "$BASE/login" \
|
||
-d "username=${TEST_USER}&password=${TEST_PASS}" 2>/dev/null)
|
||
check_status "POST /login → 302" "302" "$CODE"
|
||
check_status "GET /dashboard после логина → 200" "200" "$(http_code_get "$BASE/dashboard" "$TMP/dashboard.html")"
|
||
check_contains "dashboard содержит 'Ближайшие события'" "Ближайшие события" "$TMP/dashboard.html"
|
||
|
||
# ── 4. Онбординг ──────────────────────────────────────────────────────
|
||
section "4. Онбординг"
|
||
check_status "GET /onboarding → 200" "200" "$(http_code_get "$BASE/onboarding" "$TMP/onb.html")"
|
||
CODE=$(http_code_post_json "$BASE/onboarding/mark" "$TMP/onb_mark.json" \
|
||
'{"key":"visited_schedule","value":true}')
|
||
check_status "POST /onboarding/mark → 200" "200" "$CODE"
|
||
CODE=$(http_code_post_form "$BASE/onboarding/complete" "$TMP/onb_complete.html" "")
|
||
check_status "POST /onboarding/complete → 302" "302" "$CODE"
|
||
|
||
# ── 5. Уведомления ────────────────────────────────────────────────────
|
||
section "5. Уведомления"
|
||
check_status "GET /api/notifications → 200" "200" "$(http_code_get "$BASE/api/notifications" "$TMP/notif.json")"
|
||
check_contains "notifications JSON содержит 'items'" '"items"' "$TMP/notif.json"
|
||
CODE=$(http_code_post_json "$BASE/api/notifications/read-all" "$TMP/notif_read.json" "{}")
|
||
check_status "POST /api/notifications/read-all → 200" "200" "$CODE"
|
||
|
||
# ── 6. Расписание + ассистент ────────────────────────────────────────
|
||
section "6. Расписание + ассистент"
|
||
check_status "GET /schedule → 200" "200" "$(http_code_get "$BASE/schedule" "$TMP/sched.html")"
|
||
check_contains "/schedule содержит 'cal-body'" 'cal-body' "$TMP/sched.html"
|
||
check_contains "/schedule содержит 'chat-toggle'" 'chat-toggle' "$TMP/sched.html"
|
||
|
||
# Ассистент — 5 интентов
|
||
for M in "Где сейчас?" "Когда следующая пара?" "Что сегодня?" "Он опаздывает?" "Как связаться?"; do
|
||
CODE=$(http_code_post_json "$BASE/api/assistant" "$TMP/assist.json" "{\"message\":\"$M\"}")
|
||
check_status "assistant '$M' → 200" "200" "$CODE"
|
||
check_contains " ответ содержит 'reply'" '"reply"' "$TMP/assist.json"
|
||
done
|
||
|
||
# ICS export
|
||
CODE=$(http_code_get "$BASE/schedule/export.ics" "$TMP/sched.ics")
|
||
check_status "GET /schedule/export.ics → 200" "200" "$CODE"
|
||
check_contains "ICS содержит 'BEGIN:VCALENDAR'" "BEGIN:VCALENDAR" "$TMP/sched.ics"
|
||
|
||
# ── 7. Проекты ────────────────────────────────────────────────────────
|
||
section "7. Проекты"
|
||
PROJ_NAME="SmokeTest Project $TS"
|
||
CODE=$(http_code_post_form "$BASE/projects/new" "$TMP/proj_new.html" \
|
||
"title=$(printf '%s' "$PROJ_NAME" | sed 's/ /+/g')&description=auto&status=in_progress&visibility=internal")
|
||
check_status "POST /projects/new → 302" "302" "$CODE"
|
||
|
||
PROJ_ID=$(psql_val "SELECT id FROM project WHERE title = '$PROJ_NAME' ORDER BY id DESC LIMIT 1")
|
||
check_not_empty "проект создан" "$PROJ_ID"
|
||
info "project_id = $PROJ_ID"
|
||
|
||
check_status "GET /projects/<id> → 200" "200" "$(http_code_get "$BASE/projects/$PROJ_ID" "$TMP/proj.html")"
|
||
check_status "GET /projects/<id>/board → 200" "200" "$(http_code_get "$BASE/projects/$PROJ_ID/board" "$TMP/board.html")"
|
||
|
||
# Задача
|
||
CODE=$(http_code_post_form "$BASE/projects/$PROJ_ID/tasks/new" "$TMP/task.html" \
|
||
"title=Smoke+Task&priority=medium&status=backlog")
|
||
check_status "POST /tasks/new → 302" "302" "$CODE"
|
||
TASK_ID=$(psql_val "SELECT id FROM projecttask WHERE project_id = $PROJ_ID ORDER BY id DESC LIMIT 1")
|
||
check_not_empty "задача создана" "$TASK_ID"
|
||
|
||
# ── 8. Мастер-классы ─────────────────────────────────────────────────
|
||
section "8. Мастер-классы"
|
||
WS_NAME="SmokeTest WS $TS"
|
||
CODE=$(http_code_post_form "$BASE/workshops/new" "$TMP/ws_new.html" \
|
||
"title=$(printf '%s' "$WS_NAME" | sed 's/ /+/g')&description=auto&category=other&difficulty=beginner&status=published&visibility=public&lead_id=$TEST_USER_ID")
|
||
check_status "POST /workshops/new → 302" "302" "$CODE"
|
||
|
||
WS_ID=$(psql_val "SELECT id FROM workshop WHERE title = '$WS_NAME' ORDER BY id DESC LIMIT 1")
|
||
check_not_empty "МК создан" "$WS_ID"
|
||
info "workshop_id = $WS_ID"
|
||
|
||
check_status "GET /workshops/<id> → 200" "200" "$(http_code_get "$BASE/workshops/$WS_ID" "$TMP/ws.html")"
|
||
|
||
# Регистрация на МК
|
||
CODE=$(http_code_post_form "$BASE/workshops/$WS_ID/register" "$TMP/ws_reg.html" "")
|
||
check_status "POST /workshops/<id>/register → 302" "302" "$CODE"
|
||
REG_COUNT=$(psql_val "SELECT COUNT(*) FROM workshopregistration WHERE workshop_id = $WS_ID AND user_id = $TEST_USER_ID")
|
||
check_equal "регистрация в БД" "1" "$REG_COUNT"
|
||
|
||
# ── 9. Файлы v2.0 ────────────────────────────────────────────────────
|
||
section "9. Файлы v2.0 — пространство/папка/файл/версии/ACL/move/delete"
|
||
|
||
SPACE_NAME="SmokeTest Space $TS"
|
||
CODE=$(http_code_post_form "$BASE/files/space/new" "$TMP/sp_new.html" \
|
||
"name=$(printf '%s' "$SPACE_NAME" | sed 's/ /+/g')&description=auto&visibility=internal")
|
||
check_status "POST /files/space/new → 302" "302" "$CODE"
|
||
|
||
SPACE_ID=$(psql_val "SELECT id FROM filespace WHERE name = '$SPACE_NAME' ORDER BY id DESC LIMIT 1")
|
||
check_not_empty "пространство создано" "$SPACE_ID"
|
||
info "space_id = $SPACE_ID"
|
||
|
||
check_status "GET /files/space/<id> → 200" "200" "$(http_code_get "$BASE/files/space/$SPACE_ID" "$TMP/space.html")"
|
||
|
||
# Папка
|
||
FOLDER_NAME="TestFolder_$TS"
|
||
CODE=$(http_code_post_form "$BASE/files/folder/new" "$TMP/folder_new.html" \
|
||
"space_id=$SPACE_ID&parent_id=&name=$FOLDER_NAME&description=auto")
|
||
check_status "POST /files/folder/new → 302" "302" "$CODE"
|
||
|
||
FOLDER_ID=$(psql_val "SELECT id FROM filefolder WHERE name = '$FOLDER_NAME' AND space_id = $SPACE_ID")
|
||
check_not_empty "папка создана" "$FOLDER_ID"
|
||
info "folder_id = $FOLDER_ID"
|
||
|
||
check_status "GET /files/folder/<id> → 200" "200" "$(http_code_get "$BASE/files/folder/$FOLDER_ID" "$TMP/folder.html")"
|
||
|
||
# Файл
|
||
echo "test content v1 — $(date -Is)" > "$TMP/upload.txt"
|
||
CODE=$(curl -sS -o "$TMP/up_resp.html" -w "%{http_code}" -b "$COOKIE" -c "$COOKIE" \
|
||
-X POST "$BASE/files/upload" \
|
||
-F "file=@$TMP/upload.txt;filename=smoke_test_$TS.txt" \
|
||
-F "space_id=$SPACE_ID" -F "folder_id=$FOLDER_ID" 2>/dev/null)
|
||
check_status "POST /files/upload → 302" "302" "$CODE"
|
||
|
||
FILE_ID=$(psql_val "SELECT id FROM portalfile WHERE space_id = $SPACE_ID AND folder_id = $FOLDER_ID ORDER BY id DESC LIMIT 1")
|
||
check_not_empty "файл создан" "$FILE_ID"
|
||
info "file_id = $FILE_ID"
|
||
|
||
# Проверка FileVersion(1)
|
||
FILE_VERSION=$(psql_val "SELECT version FROM portalfile WHERE id = $FILE_ID")
|
||
check_equal "file.version = 1" "1" "$FILE_VERSION"
|
||
CUR_VER_ID=$(psql_val "SELECT current_version_id FROM portalfile WHERE id = $FILE_ID")
|
||
check_not_empty "current_version_id задан" "$CUR_VER_ID"
|
||
VER_COUNT=$(psql_val "SELECT COUNT(*) FROM fileversion WHERE file_id = $FILE_ID")
|
||
check_equal "fileversion count = 1" "1" "$VER_COUNT"
|
||
|
||
# Скачать текущую
|
||
check_status "GET /files/download/<id> → 200" "200" "$(http_code_get "$BASE/files/download/$FILE_ID" "$TMP/dl1.txt")"
|
||
check_contains "скачано v1" "test content v1" "$TMP/dl1.txt"
|
||
|
||
# История
|
||
check_status "GET /files/<id>/history → 200" "200" "$(http_code_get "$BASE/files/$FILE_ID/history" "$TMP/history.html")"
|
||
check_contains "история содержит 'v1'" "v1" "$TMP/history.html"
|
||
|
||
# Новая версия
|
||
echo "test content v2 — $(date -Is)" > "$TMP/upload_v2.txt"
|
||
CODE=$(curl -sS -o "$TMP/newv.html" -w "%{http_code}" -b "$COOKIE" -c "$COOKIE" \
|
||
-X POST "$BASE/files/$FILE_ID/new_version" \
|
||
-F "file=@$TMP/upload_v2.txt;filename=smoke_test_$TS.txt" \
|
||
-F "comment=Auto v2" 2>/dev/null)
|
||
check_status "POST /files/<id>/new_version → 302" "302" "$CODE"
|
||
check_equal "version = 2" "2" "$(psql_val "SELECT version FROM portalfile WHERE id = $FILE_ID")"
|
||
check_equal "fileversion count = 2" "2" "$(psql_val "SELECT COUNT(*) FROM fileversion WHERE file_id = $FILE_ID")"
|
||
|
||
# Скачать v1
|
||
V1_ID=$(psql_val "SELECT id FROM fileversion WHERE file_id = $FILE_ID AND version_num = 1")
|
||
check_status "GET /files/<id>/version/<v1>/download → 200" "200" \
|
||
"$(http_code_get "$BASE/files/$FILE_ID/version/$V1_ID/download" "$TMP/dl_v1.txt")"
|
||
check_contains "скачано содержимое v1" "test content v1" "$TMP/dl_v1.txt"
|
||
|
||
# Скачать текущую (v2)
|
||
check_status "GET /files/download/<id> (v2) → 200" "200" "$(http_code_get "$BASE/files/download/$FILE_ID" "$TMP/dl_v2.txt")"
|
||
check_contains "скачано v2" "test content v2" "$TMP/dl_v2.txt"
|
||
|
||
# ACL пространства
|
||
CODE=$(http_code_post_form "$BASE/files/space/$SPACE_ID/acl/add" "$TMP/acl.html" \
|
||
"username=$TEST_USER&role=editor")
|
||
check_status "POST /files/space/<id>/acl/add → 302" "302" "$CODE"
|
||
# (нельзя добавить себя-владельца, роут вернёт redirect без изменений — это OK, проверяем что не 500)
|
||
ACL_PAGE_CODE=$(http_code_get "$BASE/files/space/$SPACE_ID/acl" "$TMP/acl_page.html")
|
||
check_status "GET /files/space/<id>/acl → 200" "200" "$ACL_PAGE_CODE"
|
||
|
||
# Move: файл из пространства в NULL
|
||
CODE=$(http_code_post_form "$BASE/files/$FILE_ID/move" "$TMP/move1.html" \
|
||
"space_id=&folder_id=")
|
||
check_status "POST /files/<id>/move → NULL → 302" "302" "$CODE"
|
||
check_equal "space_id обнулён" "" "$(psql_val "SELECT space_id FROM portalfile WHERE id = $FILE_ID AND space_id IS NOT NULL")"
|
||
check_equal "version сброшен в 1" "1" "$(psql_val "SELECT version FROM portalfile WHERE id = $FILE_ID")"
|
||
check_equal "current_version_id = NULL" "" "$(psql_val "SELECT current_version_id FROM portalfile WHERE id = $FILE_ID AND current_version_id IS NOT NULL")"
|
||
check_equal "fileversion удалены" "0" "$(psql_val "SELECT COUNT(*) FROM fileversion WHERE file_id = $FILE_ID")"
|
||
|
||
# Move обратно в пространство
|
||
CODE=$(http_code_post_form "$BASE/files/$FILE_ID/move" "$TMP/move2.html" \
|
||
"space_id=$SPACE_ID&folder_id=$FOLDER_ID")
|
||
check_status "POST /files/<id>/move → обратно в space → 302" "302" "$CODE"
|
||
check_equal "space_id восстановлен" "$SPACE_ID" "$(psql_val "SELECT space_id FROM portalfile WHERE id = $FILE_ID")"
|
||
check_equal "fileversion count = 1 (миграция)" "1" "$(psql_val "SELECT COUNT(*) FROM fileversion WHERE file_id = $FILE_ID")"
|
||
|
||
# Удаление файла
|
||
CODE=$(http_code_post_form "$BASE/files/$FILE_ID/delete" "$TMP/fdel.html" "next=/files/space/$SPACE_ID")
|
||
check_status "POST /files/<id>/delete → 302" "302" "$CODE"
|
||
FILE_EXISTS=$(psql_val "SELECT COUNT(*) FROM portalfile WHERE id = $FILE_ID")
|
||
check_equal "файл удалён из БД" "0" "$FILE_EXISTS"
|
||
|
||
# Удаление ПАПКИ (главная проверка!)
|
||
CODE=$(http_code_post_form "$BASE/files/folder/$FOLDER_ID/delete" "$TMP/folder_del.html" "")
|
||
check_status "POST /files/folder/<id>/delete → 302" "302" "$CODE"
|
||
FOLDER_EXISTS=$(psql_val "SELECT COUNT(*) FROM filefolder WHERE id = $FOLDER_ID")
|
||
check_equal "папка удалена из БД" "0" "$FOLDER_EXISTS"
|
||
|
||
# Удаление ПРОСТРАНСТВА
|
||
CODE=$(http_code_post_form "$BASE/files/space/$SPACE_ID/delete" "$TMP/space_del.html" "")
|
||
check_status "POST /files/space/<id>/delete → 302" "302" "$CODE"
|
||
SPACE_EXISTS=$(psql_val "SELECT COUNT(*) FROM filespace WHERE id = $SPACE_ID")
|
||
check_equal "пространство удалено из БД" "0" "$SPACE_EXISTS"
|
||
|
||
# ── 10. Вебхук ───────────────────────────────────────────────────────
|
||
section "10. Вебхук Gitea"
|
||
CODE=$(curl -sS -o "$TMP/wh_bad.json" -w "%{http_code}" -X POST "$BASE/webhooks/gitea" \
|
||
-H "X-Gitea-Event: push" -H "X-Gitea-Signature: DEADBEEF" \
|
||
-H "Content-Type: application/json" -d '{}' 2>/dev/null)
|
||
check_status "POST /webhooks/gitea (плохая подпись) → 401" "401" "$CODE"
|
||
|
||
# Валидная подпись
|
||
SECRET=$(grep '^GITEA_WEBHOOK_SECRET=' /opt/morozovka-portal/.env 2>/dev/null | cut -d= -f2- | tr -d '"' | tr -d "'")
|
||
if [ -n "$SECRET" ]; then
|
||
BODY='{"repository":{"full_name":"test/nonexistent"},"pusher":{"username":"smoke"},"ref":"refs/heads/main","commits":[]}'
|
||
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" 2>/dev/null | awk '{print $NF}')
|
||
CODE=$(curl -sS -o "$TMP/wh_ok.json" -w "%{http_code}" -X POST "$BASE/webhooks/gitea" \
|
||
-H "X-Gitea-Event: push" -H "X-Gitea-Signature: $SIG" \
|
||
-H "Content-Type: application/json" -d "$BODY" 2>/dev/null)
|
||
check_status "POST /webhooks/gitea (валидная подпись) → 200" "200" "$CODE"
|
||
else
|
||
info "GITEA_WEBHOOK_SECRET не найден в .env — пропускаю валидную проверку"
|
||
fi
|
||
|
||
# ── 11. Логаут ────────────────────────────────────────────────────────
|
||
section "11. Логаут"
|
||
check_status "GET /logout → 302" "302" "$(http_code_get "$BASE/logout" "$TMP/logout.html")"
|
||
rm -f "$COOKIE"
|
||
check_status "GET /dashboard после logout → 302" "302" "$(http_code_get "$BASE/dashboard" "$TMP/dash_after.html")"
|
||
|
||
# ══════════════════════════════════════════════════════════════════════
|
||
# ИТОГ
|
||
# ══════════════════════════════════════════════════════════════════════
|
||
END_TIME=$(date +%s)
|
||
DURATION=$((END_TIME - START_TIME))
|
||
|
||
echo
|
||
echo -e "${C_BOLD}╔══════════════════════════════════════════════════════╗${C_RESET}"
|
||
if [ "$FAIL" -eq 0 ]; then
|
||
echo -e "${C_BOLD}║${C_RESET} ${C_GREEN}${C_BOLD}ВСЕ ТЕСТЫ ПРОШЛИ${C_RESET} ${C_BOLD}║${C_RESET}"
|
||
else
|
||
echo -e "${C_BOLD}║${C_RESET} ${C_RED}${C_BOLD}ЕСТЬ ПРОВАЛЫ: $FAIL${C_RESET} ${C_BOLD}║${C_RESET}"
|
||
fi
|
||
echo -e "${C_BOLD}╚══════════════════════════════════════════════════════╝${C_RESET}"
|
||
echo -e " ${C_GREEN}PASS:${C_RESET} $PASS"
|
||
echo -e " ${C_RED}FAIL:${C_RESET} $FAIL"
|
||
echo -e " Всего: $TOTAL"
|
||
echo -e " Длительность: ${DURATION}s"
|
||
echo -e " Лог: ${C_CYAN}$LOG_FILE${C_RESET}"
|
||
|
||
echo "" >> "$LOG_FILE"
|
||
echo "=== ИТОГ ===" >> "$LOG_FILE"
|
||
echo "PASS: $PASS" >> "$LOG_FILE"
|
||
echo "FAIL: $FAIL" >> "$LOG_FILE"
|
||
echo "TOTAL: $TOTAL" >> "$LOG_FILE"
|
||
echo "DURATION: ${DURATION}s" >> "$LOG_FILE"
|
||
echo "End: $(date -Is)" >> "$LOG_FILE"
|
||
|
||
if [ "$FAIL" -eq 0 ]; then exit 0; else exit 1; fi
|