SuperCacheProbe prüft beim Prüfen, ob wp_cache_post_edit die Cache-Datei einer Testseite entfernt; während der Probe ist nur der Einzelseiten-Purge erlaubt, der Pfad kommt aus home und es werden nur feste Dateinamen gelöscht. ProbeStep bündelt die Schrittlogik von Pods und WP Super Cache; ThrowawayPost hält Marker und Titel zentral und räumt auch private Probe-Seiten auf (WP Super Cache wie Pods beim Laden des Reiters). super-cache-smoke.sh prüft gegen das echte Plugin; lib.sh sperrt parallele Smoke-Läufe per flock, wp-config.php wird atomar wiederhergestellt. Doku: Host-Abhängigkeit des Einzel-Purges, WP-Cron für den Nachlauf, Komplett-Purge durch Divi beim Prüfen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
155 lines
11 KiB
Bash
155 lines
11 KiB
Bash
#!/usr/bin/env bash
|
|
# End-to-end checks of the Pods profile of the compatibility layer (plan 2026-11-06-kompatibilitaetsschicht, 1.25):
|
|
# status, check route /pods-check, tab "Compatibility", release (refused for a tested version, simulated for an
|
|
# untested one) and the lock by a red stored report. Runs INSIDE the DDEV web container:
|
|
# cd /home/janniclas/Projekte/WordPress && timeout 600 ddev exec bash wp-content/plugins/WP-LIT-MCP/tests/Integration/pods-compat-smoke.sh
|
|
# Temporarily activates the installed Pods plugin (restored afterwards); removes the lit_mcp_pods_* options, the
|
|
# fixture pod and its entry again. The minimum version of the profile is not changeable from outside, so the lock is
|
|
# triggered the way production does it: a red check report for the current state (lit_mcp_pods_check_report).
|
|
set -euo pipefail
|
|
cd /var/www/html
|
|
# shellcheck source=lib.sh
|
|
source wp-content/plugins/WP-LIT-MCP/tests/Integration/lib.sh
|
|
|
|
ADMIN_LOGIN=$(wp user list --role=administrator --field=user_login | head -1)
|
|
PODS_WAS_ACTIVE=no
|
|
wp plugin is-active pods && PODS_WAS_ACTIVE=yes
|
|
CREATED_POSTS=()
|
|
BASE=http://localhost
|
|
OPTIONS=(lit_mcp_pods_check_report lit_mcp_pods_releases lit_mcp_pods_check_lock)
|
|
|
|
restore() {
|
|
for id in "${CREATED_POSTS[@]}"; do
|
|
wp post delete "$id" --force >/dev/null 2>&1 || true
|
|
done
|
|
wp option delete "${OPTIONS[@]}" >/dev/null 2>&1 || true
|
|
wp eval-file "$PLUGIN/tests/Integration/pods-fixture.php" delete >/dev/null 2>&1 || true
|
|
[ "$PODS_WAS_ACTIVE" = "no" ] && wp plugin deactivate pods >/dev/null 2>&1 || true
|
|
cleanup_app_passwords_for "$ADMIN_LOGIN"
|
|
}
|
|
trap 'rc=$?; restore; guard_exit "$rc" || exit $?' EXIT
|
|
|
|
pods_component() { tool lit-site-status '{}' | jq -c '.components[] | select(.id == "pods")'; }
|
|
stored_option() { wp option get "$1" --format=json 2>/dev/null || true; }
|
|
# Runs the admin-post handler of the tab (pods-compat-release.php); prints the notice of its redirect.
|
|
handler() { wp eval-file "$PLUGIN/tests/Integration/pods-compat-release.php" "$1" "$2" 2>/dev/null | tail -1; }
|
|
# Stores a red report for the current Pods state (what a failed admin check leaves behind).
|
|
write_report() { # $1 = red|green
|
|
RESULT="$1" wp eval '
|
|
$profile = LIT\MCP\Integrations\Pods\PodsCompatibility::profile();
|
|
$problems = getenv("RESULT") === "red" ? ["Smoke: Pods-Aufruf fehlt"] : [];
|
|
LIT\MCP\Compatibility\CheckReportStore::forProfile($profile)->save(new LIT\MCP\Compatibility\CheckReport(
|
|
"smoke-report", gmdate("c"), 1, (string) PODS_VERSION, LIT\MCP\Plugin::VERSION, "",
|
|
[new LIT\MCP\Compatibility\CheckStep("manifest", $problems, false, "Pods-Funktionen")]
|
|
));' >/dev/null || fail "could not write the $1 report"
|
|
}
|
|
|
|
wp plugin activate pods >/dev/null
|
|
wp eval-file "$PLUGIN/tests/Integration/pods-fixture.php" create >/dev/null
|
|
connect "$ADMIN_LOGIN"
|
|
|
|
# 1. Pods active: version, no problems
|
|
pods_component | jq -e '.active == true and .version == "3.3.9.2" and .problems == []' >/dev/null \
|
|
|| fail "site status must show Pods 3.3.9.2 active without problems: $(pods_component)"
|
|
pass "lit-site-status: Pods 3.3.9.2 active, no problems"
|
|
|
|
# 2. Check route (application password instead of the cookie nonce, as in divi-release-smoke.sh)
|
|
APP_PASSWORD=$(wp user application-password create "$ADMIN_LOGIN" lit-mcp-integration-test --porcelain)
|
|
HEADERS=$(curl -s -o /dev/null -D - -X POST -H "Host: $HOST" -H 'X-Forwarded-Proto: https' -u "$ADMIN_LOGIN:$APP_PASSWORD" "$BASE/wp-json/lit-mcp/v1/pods-check")
|
|
echo "$HEADERS" | grep -qi '^HTTP/[0-9.]* 303' || fail "check route must answer 303: $HEADERS"
|
|
echo "$HEADERS" | grep -qi '^location:.*tab=compatibility.*lit_mcp_notice=compat_checked' || fail "redirect to the tab: $HEADERS"
|
|
REPORT=$(stored_option lit_mcp_pods_check_report)
|
|
echo "$REPORT" | jq -e '(.steps | length) >= 1 and all(.steps[]; .problems == [] and .skipped == false)
|
|
and .version == "3.3.9.2" and (has("divi_version") | not)' >/dev/null || fail "the stored report must be green with key version: $REPORT"
|
|
[ -z "$(stored_option lit_mcp_pods_check_lock)" ] || fail "the check lock must be released"
|
|
# The behaviour probe is part of the report (green, not skipped, its label is the real probe, not "not probed") ...
|
|
echo "$REPORT" | jq -e '[.steps[] | select(.key == "probe")] | length == 1 and (.[0] | .problems == [] and .skipped == false
|
|
and (.label | contains("nicht geprüft") | not))' >/dev/null || fail "the report must contain the green probe step: $REPORT"
|
|
# ... and leaves no entry behind (it ran on lit_person; any status, any marked entry).
|
|
LEFT=$(wp post list --post_type=any --post_status=any --meta_key=_lit_mcp_check_entry --format=count)
|
|
[ "$LEFT" = "0" ] || fail "the probe left $LEFT entries behind"
|
|
[ "$(wp post list --post_type=lit_person --post_status=any --format=count)" = "0" ] || fail "the probe left a lit_person entry"
|
|
UNAUTH=$(curl -s -o /dev/null -w '%{http_code}' -X POST -H "Host: $HOST" -H 'X-Forwarded-Proto: https' "$BASE/wp-json/lit-mcp/v1/pods-check")
|
|
[ "$UNAUTH" = "401" ] || [ "$UNAUTH" = "403" ] || fail "anonymous check must be refused, got $UNAUTH"
|
|
pass "/pods-check: 303 to the tab, green report with key version and probe step, no probe entry left, lock released, anonymous refused"
|
|
|
|
# 3. The tab renders the Pods section and, as 3.3.9.2 is tested, offers no release
|
|
TAB=$(wp --user="$ADMIN_LOGIN" eval '
|
|
require_once ABSPATH . "wp-admin/includes/template.php";
|
|
$_GET["tab"] = "compatibility";
|
|
LIT\MCP\Plugin::settingsPage()->render();')
|
|
echo "$TAB" | grep -q 'id="lit-mcp-compat-pods"' || fail "tab Compatibility must have a Pods section"
|
|
echo "$TAB" | grep -q '3\.3\.9\.2' || fail "the Pods section must show the version"
|
|
echo "$TAB" | grep -q 'freigegeben' || fail "the Pods section must show the state"
|
|
echo "$TAB" | grep -q 'lit-mcp-compat-check-pods' || fail "the Pods section must offer the check"
|
|
echo "$TAB" | grep -q 'lit-mcp-compat-release-pods' && fail "no release button for a tested version"
|
|
pass "tab Compatibility: Pods section with version and state, check button, no release button"
|
|
|
|
# 3b. Orphan cleanup: an old marked probe entry (a dead check) is deleted on the tab's profile-load hook, a young one and
|
|
# an unmarked entry with the same title are not.
|
|
ORPHANS=$(wp --user="$ADMIN_LOGIN" eval '
|
|
$make = static fn (string $date, bool $marked): int => (int) wp_insert_post([
|
|
"post_type" => "lit_person", "post_status" => "draft", "post_title" => LIT\MCP\Compatibility\ThrowawayPost::TITLE . " smoke",
|
|
"post_date" => get_date_from_gmt($date), "post_date_gmt" => $date,
|
|
"meta_input" => $marked ? [LIT\MCP\Compatibility\ThrowawayPost::MARKER => "1"] : [],
|
|
]);
|
|
$old = $make(gmdate("Y-m-d H:i:s", time() - 3600), true);
|
|
$young = $make(gmdate("Y-m-d H:i:s"), true);
|
|
$unmarked = $make(gmdate("Y-m-d H:i:s", time() - 3600), false);
|
|
do_action(LIT\MCP\Admin\CompatibilityTab::LOAD_ACTION, "pods");
|
|
echo json_encode([$old, $young, $unmarked, get_post_status($old), get_post_status($young), get_post_status($unmarked)]);')
|
|
OLD_ID=$(echo "$ORPHANS" | jq -r '.[0]'); YOUNG_ID=$(echo "$ORPHANS" | jq -r '.[1]'); UNMARKED_ID=$(echo "$ORPHANS" | jq -r '.[2]')
|
|
CREATED_POSTS+=("$OLD_ID" "$YOUNG_ID" "$UNMARKED_ID")
|
|
echo "$ORPHANS" | jq -e '.[3] == false and .[4] == "draft" and .[5] == "draft"' >/dev/null \
|
|
|| fail "orphan cleanup must delete only the old marked entry: $ORPHANS"
|
|
pass "orphan cleanup: old marked probe entry deleted, young and unmarked ones kept"
|
|
|
|
# 4. Release: refused for the tested version; for an untested one (simulated profile) it is only documented
|
|
[ "$(handler release real)" = "compat_release_refused" ] || fail "releasing a tested version must be refused"
|
|
[ -z "$(stored_option lit_mcp_pods_releases)" ] || fail "a refused release must write nothing"
|
|
[ "$(handler release untested)" = "compat_released" ] || fail "releasing an untested version after a green report must work"
|
|
stored_option lit_mcp_pods_releases | jq -e 'length == 1 and .[0].version == "3.3.9.2" and (.[0] | has("divi_version") | not)' >/dev/null \
|
|
|| fail "the release must be stored with key version: $(stored_option lit_mcp_pods_releases)"
|
|
pods_component | jq -e '.problems == []' >/dev/null || fail "a release must not change the Pods status"
|
|
tool lit-content-types '{"post_type":"lit_person"}' | jq -e '.post_types[0].fields[] | select(.key == "role") | .status == "writable"' >/dev/null \
|
|
|| fail "a release must not lock or free anything"
|
|
[ "$(handler withdraw untested)" = "compat_withdrawn" ] || fail "withdrawing the release must work"
|
|
stored_option lit_mcp_pods_releases | jq -e 'length == 0' >/dev/null || fail "the withdrawn release must be gone"
|
|
pass "release: refused for tested 3.3.9.2; simulated untested one stored and withdrawn, no lock effect"
|
|
|
|
# 5. Lock: a red report for the current state locks Pods (all of its capabilities); a green one frees it again
|
|
PERSON=$(tool lit-content-create '{"post_type":"lit_person","title":"Smoke Sperre","fields":{"role":"Leitung"}}')
|
|
PERSON_ID=$(echo "$PERSON" | jq -r '.changed[0].id // empty')
|
|
[ -n "$PERSON_ID" ] || fail "fixture entry: $PERSON"
|
|
CREATED_POSTS+=("$PERSON_ID")
|
|
REV=$(tool lit-content-get "{\"post_id\":$PERSON_ID}" | jq -r '.rev')
|
|
|
|
write_report red
|
|
pods_component | jq -e '(.problems | length) >= 1 and (.problems | any(contains("Prüfung")))' >/dev/null \
|
|
|| fail "a red report must show as a problem: $(pods_component)"
|
|
tool lit-content-types '{"post_type":"lit_person"}' | jq -e '[.post_types[0].fields[] | select(.key == "role")] | all(.status == "readonly")' >/dev/null \
|
|
|| fail "locked: a Pods field must be read-only: $(tool lit-content-types '{"post_type":"lit_person"}')"
|
|
BLOCKED=$(tool lit-content-update "{\"post_id\":$PERSON_ID,\"rev\":\"$REV\",\"fields\":{\"role\":\"Gesperrt\"}}")
|
|
echo "$BLOCKED" | jq -e 'has("__error")' >/dev/null || fail "locked: a Pods field must not be writable: $BLOCKED"
|
|
[ "$(wp post meta get "$PERSON_ID" role)" = "Leitung" ] || fail "locked: the value must be unchanged"
|
|
pass "red report: status shows the problem, Pods field read-only, update refused ($(error_code "$BLOCKED"))"
|
|
|
|
wp option delete lit_mcp_pods_check_report >/dev/null
|
|
pods_component | jq -e '.problems == []' >/dev/null || fail "without the report Pods must be free: $(pods_component)"
|
|
tool lit-content-types '{"post_type":"lit_person"}' | jq -e '[.post_types[0].fields[] | select(.key == "role")] | all(.status == "writable")' >/dev/null \
|
|
|| fail "free again: the Pods field must be writable"
|
|
FREE=$(tool lit-content-update "{\"post_id\":$PERSON_ID,\"rev\":\"$REV\",\"fields\":{\"role\":\"Frei\"}}")
|
|
echo "$FREE" | jq -e '.ok == true' >/dev/null || fail "free again: update must work: $FREE"
|
|
pass "report removed: status clean, field writable, update works"
|
|
|
|
# 6. Nothing is left behind
|
|
restore
|
|
for option in "${OPTIONS[@]}"; do
|
|
[ -z "$(stored_option "$option")" ] || fail "option $option is left behind"
|
|
done
|
|
[ "$(wp plugin is-active pods && echo yes || echo no)" = "$PODS_WAS_ACTIVE" ] || fail "Pods state not restored"
|
|
[ "$(wp post list --post_type=lit_person --post_status=any --format=count 2>/dev/null || echo 0)" = "0" ] || fail "fixture entry left"
|
|
pass "cleanup: options, entry, fixture pod gone; Pods state restored"
|
|
|
|
finish
|