test(frontend): add provider admin acceptance coverage

Add a dedicated acceptance script for providers.html, cover it in the local real-host script regression suite, and document the current frontend review baseline, closure audit, providers action matrix, and remediation task board.

This keeps the frontend acceptance boundary explicit: providers.html now has a repeatable verification entry point for its page-level actions, while non-UI provider operations remain documented as backend-only capabilities.
This commit is contained in:
phamnazage-jpg
2026-06-01 09:58:20 +08:00
parent c588a95c7d
commit 5fbac6ef0b
8 changed files with 2047 additions and 0 deletions

View File

@@ -20,6 +20,7 @@
- 例如:
- `real_host_acceptance.sh`
- `import_remote43_provider.sh`
- `verify_provider_admin_actions.sh`
- `check_deepseek_completion_split.sh`
- `scripts/test/`
- 脚本自身的回归与资产检查
@@ -42,6 +43,7 @@ bash ./scripts/test/test_tksea_portal_assets.sh
bash ./scripts/test/verify_quality_gates.sh
scripts/deploy/build_local_image.sh
bash ./scripts/acceptance/real_host_acceptance.sh
bash ./scripts/acceptance/verify_provider_admin_actions.sh
```
## 统一质量门禁

View File

@@ -0,0 +1,378 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# shellcheck disable=SC1091
source "$ROOT_DIR/scripts/acceptance/route_acceptance_lib.sh"
PROVIDER_ADMIN_ROOT="${PROVIDER_ADMIN_ROOT:-$ROOT_DIR/artifacts/provider-admin-matrix}"
PROVIDER_ADMIN_PAGE_URL="${PROVIDER_ADMIN_PAGE_URL:-https://sub.tksea.top/portal/admin/providers.html}"
TS="${TS:-$(timestamp_token)}"
ARTIFACT_DIR="${ARTIFACT_DIR:-$PROVIDER_ADMIN_ROOT/${TS}_provider_admin_actions}"
PACK_ID="${PACK_ID:-}"
HOST_ID="${HOST_ID:-}"
PACK_PATH="${PACK_PATH:-}"
PROVIDER_ID="${PROVIDER_ID:-}"
MODE="${MODE:-partial}"
ACCESS_MODE="${ACCESS_MODE:-self_service}"
ACCESS_API_KEY="${ACCESS_API_KEY:-}"
PROVIDER_KEYS="${PROVIDER_KEYS:-}"
SUBSCRIPTION_USERS="${SUBSCRIPTION_USERS:-}"
SUBSCRIPTION_DAYS="${SUBSCRIPTION_DAYS:-30}"
VERIFY_PUBLISH="${VERIFY_PUBLISH:-0}"
crud_provider_id="${CRUD_PROVIDER_ID:-provider-admin-draft-$TS}"
crud_display_name="${CRUD_DISPLAY_NAME:-Provider Admin Draft $TS}"
crud_platform="${CRUD_PLATFORM:-openai}"
crud_base_url="${CRUD_BASE_URL:-https://draft-$TS.example.com/v1}"
crud_smoke_model="${CRUD_SMOKE_MODEL:-provider-admin-draft-$TS}"
crud_supported_models="${CRUD_SUPPORTED_MODELS:-provider-admin-draft-$TS,provider-admin-draft-mini-$TS}"
crud_notes="${CRUD_NOTES:-provider-admin draft acceptance $TS}"
crud_updated_display_name="${CRUD_UPDATED_DISPLAY_NAME:-Provider Admin Draft Updated $TS}"
crud_updated_base_url="${CRUD_UPDATED_BASE_URL:-https://draft-updated-$TS.example.com/v1}"
crud_updated_smoke_model="${CRUD_UPDATED_SMOKE_MODEL:-provider-admin-draft-updated-$TS}"
crud_updated_supported_models="${CRUD_UPDATED_SUPPORTED_MODELS:-provider-admin-draft-updated-$TS}"
crud_updated_notes="${CRUD_UPDATED_NOTES:-provider-admin draft acceptance updated $TS}"
publish_provider_id="${PUBLISH_PROVIDER_ID:-provider-admin-publish-$TS}"
publish_display_name="${PUBLISH_DISPLAY_NAME:-Provider Admin Publish $TS}"
publish_platform="${PUBLISH_PLATFORM:-openai}"
publish_base_url="${PUBLISH_BASE_URL:-https://publish-$TS.example.com/v1}"
publish_smoke_model="${PUBLISH_SMOKE_MODEL:-provider-admin-publish-$TS}"
publish_supported_models="${PUBLISH_SUPPORTED_MODELS:-provider-admin-publish-$TS}"
publish_notes="${PUBLISH_NOTES:-provider-admin publish acceptance $TS}"
publish_commit_message="${PUBLISH_COMMIT_MESSAGE:-feat(pack): publish provider admin draft $publish_provider_id}"
require_var CRM_BASE
require_var ACCESS_API_KEY
require_var PROVIDER_KEYS
crm_auth_init
ensure_artifact_dir
curl_status_to_file "$PROVIDER_ADMIN_PAGE_URL" "$ARTIFACT_DIR/00-provider-admin.html"
json_field_from_file() {
local file="$1"
local path="$2"
python3 - "$file" "$path" <<'PY'
import json
import sys
file_path, path = sys.argv[1:3]
value = json.load(open(file_path, "r", encoding="utf-8"))
for part in path.split("."):
if isinstance(value, dict):
value = value.get(part)
else:
value = None
break
if value is None:
raise SystemExit(2)
if isinstance(value, (dict, list)):
print(json.dumps(value, ensure_ascii=False))
else:
print(value)
PY
}
first_collection_field_from_file() {
local file="$1"
local collection="$2"
local field="$3"
python3 - "$file" "$collection" "$field" <<'PY'
import json
import sys
file_path, collection, field = sys.argv[1:4]
payload = json.load(open(file_path, "r", encoding="utf-8"))
items = payload.get(collection) or []
if not items:
raise SystemExit(2)
value = items[0].get(field)
if value in ("", None):
raise SystemExit(2)
print(value)
PY
}
normalize_csv_json() {
local raw="$1"
python3 - "$raw" <<'PY'
import json
import sys
raw = sys.argv[1]
values = []
for line in raw.replace("\r", "\n").split("\n"):
for item in line.split(","):
item = item.strip()
if item:
values.append(item)
print(json.dumps(values, ensure_ascii=False))
PY
}
build_preview_payload() {
local host_id="$1"
local pack_path="$2"
local provider_id="$3"
local mode="$4"
local keys_json="$5"
python3 - "$host_id" "$pack_path" "$provider_id" "$mode" "$keys_json" <<'PY'
import json
import sys
host_id, pack_path, provider_id, mode, keys_json = sys.argv[1:6]
print(json.dumps({
"host_id": host_id,
"pack_path": pack_path,
"provider_id": provider_id,
"keys": json.loads(keys_json),
"mode": mode,
}, ensure_ascii=False))
PY
}
build_import_payload() {
local host_id="$1"
local pack_path="$2"
local provider_id="$3"
local mode="$4"
local access_mode="$5"
local access_api_key="$6"
local keys_json="$7"
local subscription_users_json="$8"
local subscription_days="$9"
python3 - "$host_id" "$pack_path" "$provider_id" "$mode" "$access_mode" "$access_api_key" "$keys_json" "$subscription_users_json" "$subscription_days" <<'PY'
import json
import sys
host_id, pack_path, provider_id, mode, access_mode, access_api_key, keys_json, subscription_users_json, subscription_days = sys.argv[1:10]
payload = {
"host_id": host_id,
"pack_path": pack_path,
"provider_id": provider_id,
"keys": json.loads(keys_json),
"mode": mode,
"access_mode": access_mode,
"access_api_key": access_api_key,
}
if access_mode == "subscription":
payload["subscription_users"] = json.loads(subscription_users_json)
payload["subscription_days"] = int(subscription_days)
print(json.dumps(payload, ensure_ascii=False))
PY
}
build_draft_payload() {
local pack_id="$1"
local provider_id="$2"
local display_name="$3"
local platform="$4"
local base_url="$5"
local smoke_model="$6"
local supported_models_json="$7"
local source_host_id="$8"
local notes="$9"
python3 - "$pack_id" "$provider_id" "$display_name" "$platform" "$base_url" "$smoke_model" "$supported_models_json" "$source_host_id" "$notes" <<'PY'
import json
import sys
pack_id, provider_id, display_name, platform, base_url, smoke_model, supported_models_json, source_host_id, notes = sys.argv[1:10]
supported_models = json.loads(supported_models_json)
manifest = {
"provider_id": provider_id,
"display_name": display_name,
"platform": platform,
"base_url": base_url,
"smoke_test_model": smoke_model,
"supported_models": supported_models,
}
print(json.dumps({
"pack_id": pack_id,
"provider_id": provider_id,
"display_name": display_name,
"platform": platform,
"base_url": base_url,
"smoke_test_model": smoke_model,
"supported_models": supported_models,
"source_host_id": source_host_id,
"notes": notes,
"manifest": manifest,
}, ensure_ascii=False))
PY
}
crm_curl_status() {
local method="$1"
local path="$2"
local payload="${3:-}"
local -a curl_args
curl_args=(-fsS -o /dev/null -w '%{http_code}' -X "$method")
if [[ -n "${crm_token:-}" ]]; then
curl_args+=(-H "Authorization: Bearer $crm_token")
elif [[ -n "${crm_cookie_jar:-}" ]]; then
curl_args+=(-b "$crm_cookie_jar" -c "$crm_cookie_jar")
else
echo "missing CRM auth: set CRM_ADMIN_TOKEN or CRM_ADMIN_USERNAME/CRM_ADMIN_PASSWORD" >&2
exit 2
fi
if [[ -n "$payload" ]]; then
curl_args+=(-H 'Content-Type: application/json' "${CRM_BASE%/}${path}" -d "$payload")
else
curl_args+=("${CRM_BASE%/}${path}")
fi
curl "${curl_args[@]}"
}
provider_keys_json="$(normalize_csv_json "$PROVIDER_KEYS")"
subscription_users_json="$(normalize_csv_json "$SUBSCRIPTION_USERS")"
crud_supported_models_json="$(normalize_csv_json "$crud_supported_models")"
crud_updated_supported_models_json="$(normalize_csv_json "$crud_updated_supported_models")"
publish_supported_models_json="$(normalize_csv_json "$publish_supported_models")"
save_json 01-packs "$(crm_curl_json GET "/api/packs")"
if [[ -z "$PACK_ID" ]]; then
PACK_ID="$(first_collection_field_from_file "$ARTIFACT_DIR/01-packs.json" packs pack_id)"
fi
save_json 02-hosts "$(crm_curl_json GET "/api/hosts")"
if [[ -z "$HOST_ID" ]]; then
HOST_ID="$(first_collection_field_from_file "$ARTIFACT_DIR/02-hosts.json" hosts host_id)"
fi
if [[ -z "$PACK_PATH" ]]; then
PACK_PATH="/app/packs/$PACK_ID"
fi
save_json 03-pack-providers "$(crm_curl_json GET "/api/packs/$PACK_ID/providers")"
if [[ -z "$PROVIDER_ID" ]]; then
PROVIDER_ID="$(first_collection_field_from_file "$ARTIFACT_DIR/03-pack-providers.json" providers provider_id)"
fi
preview_payload="$(build_preview_payload "$HOST_ID" "$PACK_PATH" "$PROVIDER_ID" "$MODE" "$provider_keys_json")"
save_json 04-preview-import "$(crm_curl_json POST "/api/providers/$PROVIDER_ID/preview-import" "$preview_payload")"
import_payload="$(build_import_payload "$HOST_ID" "$PACK_PATH" "$PROVIDER_ID" "$MODE" "$ACCESS_MODE" "$ACCESS_API_KEY" "$provider_keys_json" "$subscription_users_json" "$SUBSCRIPTION_DAYS")"
save_json 05-import "$(crm_curl_json POST "/api/providers/$PROVIDER_ID/import" "$import_payload")"
crud_create_payload="$(build_draft_payload "$PACK_ID" "$crud_provider_id" "$crud_display_name" "$crud_platform" "$crud_base_url" "$crud_smoke_model" "$crud_supported_models_json" "$HOST_ID" "$crud_notes")"
save_json 06-create-draft "$(crm_curl_json POST "/api/provider-drafts" "$crud_create_payload")"
crud_draft_id="$(json_field_from_file "$ARTIFACT_DIR/06-create-draft.json" draft.draft_id)"
save_json 07-list-drafts-before-delete "$(crm_curl_json GET "/api/provider-drafts?pack_id=$PACK_ID")"
save_json 08-get-draft "$(crm_curl_json GET "/api/provider-drafts/$crud_draft_id")"
crud_update_payload="$(build_draft_payload "$PACK_ID" "$crud_provider_id" "$crud_updated_display_name" "$crud_platform" "$crud_updated_base_url" "$crud_updated_smoke_model" "$crud_updated_supported_models_json" "$HOST_ID" "$crud_updated_notes")"
save_json 09-update-draft "$(crm_curl_json PUT "/api/provider-drafts/$crud_draft_id" "$crud_update_payload")"
delete_status="$(crm_curl_status DELETE "/api/provider-drafts/$crud_draft_id")"
save_text 10-delete-draft.status "$delete_status"
save_json 11-list-drafts-after-delete "$(crm_curl_json GET "/api/provider-drafts?pack_id=$PACK_ID")"
publish_verified="false"
if [[ "$VERIFY_PUBLISH" == "1" ]]; then
publish_create_payload="$(build_draft_payload "$PACK_ID" "$publish_provider_id" "$publish_display_name" "$publish_platform" "$publish_base_url" "$publish_smoke_model" "$publish_supported_models_json" "$HOST_ID" "$publish_notes")"
save_json 12-create-publish-draft "$(crm_curl_json POST "/api/provider-drafts" "$publish_create_payload")"
publish_draft_id="$(json_field_from_file "$ARTIFACT_DIR/12-create-publish-draft.json" draft.draft_id)"
publish_payload="$(python3 - "$publish_commit_message" <<'PY'
import json
import sys
print(json.dumps({"commit_message": sys.argv[1]}, ensure_ascii=False))
PY
)"
save_json 13-publish-draft "$(crm_curl_json POST "/api/provider-drafts/$publish_draft_id/publish" "$publish_payload")"
publish_verified="true"
fi
python3 - \
"$ARTIFACT_DIR" \
"$PACK_ID" \
"$HOST_ID" \
"$PACK_PATH" \
"$PROVIDER_ID" \
"$crud_draft_id" \
"$crud_updated_display_name" \
"$publish_verified" \
"$publish_provider_id" \
>"$ARTIFACT_DIR/99-summary.json" <<'PY'
import json
import sys
from pathlib import Path
(
artifact_dir,
pack_id,
host_id,
pack_path,
provider_id,
crud_draft_id,
crud_updated_display_name,
publish_verified,
publish_provider_id,
) = sys.argv[1:10]
art = Path(artifact_dir)
page = (art / "00-provider-admin.html").read_text(encoding="utf-8")
packs = json.loads((art / "01-packs.json").read_text(encoding="utf-8"))
hosts = json.loads((art / "02-hosts.json").read_text(encoding="utf-8"))
providers = json.loads((art / "03-pack-providers.json").read_text(encoding="utf-8"))
preview = json.loads((art / "04-preview-import.json").read_text(encoding="utf-8"))
import_result = json.loads((art / "05-import.json").read_text(encoding="utf-8"))
create_draft = json.loads((art / "06-create-draft.json").read_text(encoding="utf-8"))["draft"]
list_before = json.loads((art / "07-list-drafts-before-delete.json").read_text(encoding="utf-8"))["provider_drafts"]
get_draft = json.loads((art / "08-get-draft.json").read_text(encoding="utf-8"))["draft"]
update_draft = json.loads((art / "09-update-draft.json").read_text(encoding="utf-8"))["draft"]
delete_status = (art / "10-delete-draft.status").read_text(encoding="utf-8").strip()
list_after = json.loads((art / "11-list-drafts-after-delete.json").read_text(encoding="utf-8"))["provider_drafts"]
assert "Provider Admin" in page
assert packs["packs"]
assert hosts["hosts"]
assert providers["providers"]
assert pack_id
assert host_id
assert pack_path
assert provider_id
assert int(preview["accepted_keys_count"]) >= 1
assert int(import_result["batch_id"]) > 0
assert import_result["batch_status"]
assert create_draft["draft_id"] == crud_draft_id
assert any(item["draft_id"] == crud_draft_id for item in list_before)
assert get_draft["draft_id"] == crud_draft_id
assert update_draft["display_name"] == crud_updated_display_name
assert delete_status == "204"
assert not any(item["draft_id"] == crud_draft_id for item in list_after)
summary = {
"page_title_seen": "Provider Admin" in page,
"pack_id": pack_id,
"host_id": host_id,
"pack_path": pack_path,
"provider_id": provider_id,
"preview_accepted_keys_count": int(preview["accepted_keys_count"]),
"import_batch_id": int(import_result["batch_id"]),
"import_batch_status": import_result["batch_status"],
"crud_draft_id": crud_draft_id,
"crud_updated_display_name": update_draft["display_name"],
"crud_delete_status": delete_status,
"publish_verified": publish_verified == "true",
}
if publish_verified == "true":
create_publish = json.loads((art / "12-create-publish-draft.json").read_text(encoding="utf-8"))["draft"]
publish_result = json.loads((art / "13-publish-draft.json").read_text(encoding="utf-8"))["publish"]
assert create_publish["provider_id"] == publish_provider_id
assert publish_result["provider_id"] == publish_provider_id
assert publish_result["commit_sha"]
summary["publish_draft_id"] = create_publish["draft_id"]
summary["publish_provider_id"] = publish_result["provider_id"]
summary["publish_commit_sha"] = publish_result["commit_sha"]
else:
summary["publish_skipped_reason"] = "VERIFY_PUBLISH=0"
print(json.dumps(summary, ensure_ascii=False, indent=2))
PY
cat "$ARTIFACT_DIR/99-summary.json"

View File

@@ -830,6 +830,131 @@ EOF
assert_contains "$payloads" '"subscription_user_id": "36"'
}
run_test_verify_provider_admin_actions_script() {
local tmpdir fakebin artifact_dir state_dir stdout_file
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' RETURN
fakebin="$tmpdir/bin"
artifact_dir="$tmpdir/artifacts"
state_dir="$tmpdir/state"
stdout_file="$tmpdir/verify_provider_admin_actions.stdout.txt"
mkdir -p "$fakebin" "$artifact_dir" "$state_dir"
cat > "$fakebin/curl" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
method="GET"
url=""
payload=""
output_file=""
write_out=""
prev=""
for arg in "$@"; do
case "$prev" in
-X) method="$arg"; prev=""; continue ;;
-d|--data) payload="$arg"; prev=""; continue ;;
-o) output_file="$arg"; prev=""; continue ;;
-w) write_out="$arg"; prev=""; continue ;;
esac
case "$arg" in
-X|-d|--data|-o|-w) prev="$arg"; continue ;;
http://*|https://*) url="$arg" ;;
esac
done
state_dir="${FAKE_STATE_DIR:?missing FAKE_STATE_DIR}"
write_body() {
local body="$1"
if [[ -n "$output_file" ]]; then
printf '%s\n' "$body" > "$output_file"
else
printf '%s\n' "$body"
fi
}
write_status() {
local status="$1"
if [[ -n "$write_out" ]]; then
printf '%s' "$status"
fi
}
case "$method $url" in
"GET http://portal.example.com/providers.html")
write_body '<html><title>Provider Admin</title><body>Provider Admin</body></html>'
;;
"GET http://crm.example.com/api/packs")
write_body '{"packs":[{"pack_id":"openai-cn-pack","version":"1.1.9"}]}'
;;
"GET http://crm.example.com/api/hosts")
write_body '{"hosts":[{"host_id":"remote43-current-host","base_url":"http://host.example.com","host_version":"0.1.129"}]}'
;;
"GET http://crm.example.com/api/packs/openai-cn-pack/providers")
write_body '{"providers":[{"provider_id":"deepseek","display_name":"DeepSeek","platform":"openai","host_overlays":1}]}'
;;
"POST http://crm.example.com/api/providers/deepseek/preview-import")
write_body '{"accepted_keys_count":1,"host_overlays":1,"names":["DeepSeek"],"decisions":[{"action":"accept"}]}'
;;
"POST http://crm.example.com/api/providers/deepseek/import")
write_body '{"batch_id":321,"batch_status":"succeeded","provider_status":"ready","access_status":"self_service_ready","accepted_keys_count":1,"accounts_count":1,"host_overlays":1,"group":{"id":"g1"},"channel":{"id":"c1"},"plan":{"id":"p1"},"gateway":{"id":"gw1"}}'
;;
"POST http://crm.example.com/api/provider-drafts")
if [[ "$payload" == *'provider-admin-publish-1700000003'* ]]; then
printf '%s\n' present > "$state_dir/publish-draft.present"
write_body '{"draft":{"draft_id":"draft_publish_001","pack_id":"openai-cn-pack","provider_id":"provider-admin-publish-1700000003","display_name":"Provider Admin Publish 1700000003","platform":"openai","base_url":"https://publish-1700000003.example.com/v1","smoke_test_model":"provider-admin-publish-1700000003","supported_models":["provider-admin-publish-1700000003"]}}'
else
printf '%s\n' present > "$state_dir/crud-draft.present"
write_body '{"draft":{"draft_id":"draft_crud_001","pack_id":"openai-cn-pack","provider_id":"provider-admin-draft-1700000003","display_name":"Provider Admin Draft 1700000003","platform":"openai","base_url":"https://draft-1700000003.example.com/v1","smoke_test_model":"provider-admin-draft-1700000003","supported_models":["provider-admin-draft-1700000003","provider-admin-draft-mini-1700000003"]}}'
fi
;;
"GET http://crm.example.com/api/provider-drafts?pack_id=openai-cn-pack")
if [[ -f "$state_dir/crud-draft.present" ]]; then
write_body '{"provider_drafts":[{"draft_id":"draft_crud_001","pack_id":"openai-cn-pack","provider_id":"provider-admin-draft-1700000003","display_name":"Provider Admin Draft 1700000003","platform":"openai"}]}'
else
write_body '{"provider_drafts":[]}'
fi
;;
"GET http://crm.example.com/api/provider-drafts/draft_crud_001")
write_body '{"draft":{"draft_id":"draft_crud_001","pack_id":"openai-cn-pack","provider_id":"provider-admin-draft-1700000003","display_name":"Provider Admin Draft 1700000003","platform":"openai","base_url":"https://draft-1700000003.example.com/v1","smoke_test_model":"provider-admin-draft-1700000003","supported_models":["provider-admin-draft-1700000003","provider-admin-draft-mini-1700000003"]}}'
;;
"PUT http://crm.example.com/api/provider-drafts/draft_crud_001")
write_body '{"draft":{"draft_id":"draft_crud_001","pack_id":"openai-cn-pack","provider_id":"provider-admin-draft-1700000003","display_name":"Provider Admin Draft Updated 1700000003","platform":"openai","base_url":"https://draft-updated-1700000003.example.com/v1","smoke_test_model":"provider-admin-draft-updated-1700000003","supported_models":["provider-admin-draft-updated-1700000003"]}}'
;;
"DELETE http://crm.example.com/api/provider-drafts/draft_crud_001")
rm -f "$state_dir/crud-draft.present"
write_status "204"
;;
"POST http://crm.example.com/api/provider-drafts/draft_publish_001/publish")
write_body '{"publish":{"draft_id":"draft_publish_001","pack_id":"openai-cn-pack","provider_id":"provider-admin-publish-1700000003","provider_path":"packs/openai-cn-pack/providers/provider-admin-publish-1700000003.json","pack_version_before":"1.1.9","pack_version_after":"1.1.10","publish_mode":"created","commit_message":"feat(pack): publish provider admin draft provider-admin-publish-1700000003","commit_sha":"abc1234","repo_root":"/srv/sub2api-cn-relay-manager"}}'
;;
*)
echo "unexpected curl request: $method $url payload=$payload" >&2
exit 1
;;
esac
EOF
chmod +x "$fakebin/curl"
PATH="$fakebin:$PATH" \
FAKE_STATE_DIR="$state_dir" \
CRM_BASE="http://crm.example.com" \
CRM_ADMIN_TOKEN="token" \
PROVIDER_ADMIN_PAGE_URL="http://portal.example.com/providers.html" \
TS="1700000003" \
ACCESS_API_KEY="sk-access" \
PROVIDER_KEYS="sk-provider-1" \
VERIFY_PUBLISH="1" \
ARTIFACT_DIR="$artifact_dir" \
bash "$ROOT_DIR/scripts/acceptance/verify_provider_admin_actions.sh" >"$stdout_file"
local summary
summary="$(cat "$artifact_dir/99-summary.json")"
assert_contains "$summary" '"page_title_seen": true'
assert_contains "$summary" '"provider_id": "deepseek"'
assert_contains "$summary" '"preview_accepted_keys_count": 1'
assert_contains "$summary" '"import_batch_id": 321'
assert_contains "$summary" '"crud_delete_status": "204"'
assert_contains "$summary" '"publish_verified": true'
assert_contains "$summary" '"publish_commit_sha": "abc1234"'
}
run_test_verify_route_health_ui_script() {
local tmpdir fakebin artifact_dir stdout_file
tmpdir="$(mktemp -d)"
@@ -1056,6 +1181,7 @@ run_test_import_remote43_provider_subscription_prep
run_test_migrate_historical_artifacts
run_test_verify_route_control_plane_script
run_test_verify_route_data_plane_script
run_test_verify_provider_admin_actions_script
run_test_verify_route_health_ui_script
run_test_remote43_patched_stack_renderers
run_test_setup_remote43_patched_stack_dry_run