Parcourir la source

test: 添加渠道选择删除 E2E 测试 + 清理临时截图 + 新增辅助脚本

- 新增 E2E 测试脚本 test/e2e_channel_removal.py,覆盖 Token 解析、
  已删除路由验证、请求自动分发、channel_id 忽略、定价无默认渠道等
- 新增 anthropic_cache_test.sh 缓存测试脚本
- 新增本地开发辅助脚本 (dev.ps1, start-local.ps1 等)
- 清理根目录下 9 个临时测试截图
master
fengsilin il y a 3 semaines
Parent
révision
4c76265f32
15 fichiers modifiés avec 711 ajouts et 0 suppressions
  1. +11
    -0
      scripts/dev.ps1
  2. +1
    -0
      scripts/start-local-test.ps1
  3. +11
    -0
      scripts/start-local.ps1
  4. +119
    -0
      scripts/test-concurrent-rpm.sh
  5. BIN
      test-after-login.png
  6. BIN
      test-login.png
  7. BIN
      test-login2.png
  8. BIN
      test-page.png
  9. BIN
      test-screenshot.png
  10. BIN
      test-user-click.png
  11. BIN
      test-user-page.png
  12. BIN
      test-user-pricing.png
  13. BIN
      test-users.png
  14. +138
    -0
      test/anthropic_cache_test.sh
  15. +431
    -0
      test/e2e_channel_removal.py

+ 11
- 0
scripts/dev.ps1 Voir le fichier

@@ -0,0 +1,11 @@
$env:SQL_DSN = "root:Lanqi123456@tcp(localhost:33306)/new-api"
$env:REDIS_CONN_STRING = "redis://localhost:36379"
$env:SESSION_SECRET = "dev-secret-do-not-use-in-prod"
$env:SESSION_NAME = "session_dev"
$env:DEBUG = "1"
$env:ERROR_LOG_ENABLED = "true"
$env:BATCH_UPDATE_ENABLED = "true"
$env:TZ = "Asia/Shanghai"

Set-Location "D:\code\new-api"
go run main.go

+ 1
- 0
scripts/start-local-test.ps1 Voir le fichier

@@ -0,0 +1 @@
Start-Process -FilePath "cmd.exe" -ArgumentList "/c", "set SQL_DSN=root:Lanqi123456@tcp(localhost:33306)/new-api&& set REDIS_CONN_STRING=redis://localhost:36379&& set SESSION_SECRET=test_session_secret&& D:\code\new-api\new-api.exe" -WindowStyle Hidden

+ 11
- 0
scripts/start-local.ps1 Voir le fichier

@@ -0,0 +1,11 @@
$env:SQL_DSN = "root:Lanqi123456@tcp(localhost:33306)/new-api"
$env:REDIS_CONN_STRING = "redis://localhost:36379"
$env:SESSION_SECRET = "dev-secret-do-not-use-in-prod"
$env:SESSION_NAME = "session_dev"
$env:DEBUG = "1"
$env:ERROR_LOG_ENABLED = "true"
$env:BATCH_UPDATE_ENABLED = "true"
$env:TZ = "Asia/Shanghai"

Set-Location "D:\code\new-api"
.\new-api.exe

+ 119
- 0
scripts/test-concurrent-rpm.sh Voir le fichier

@@ -0,0 +1,119 @@
#!/bin/bash
# 并发 RPM 限流测试脚本
# 使用 Redis 令牌桶,测试 RPM=2 的并发安全性

BASE_URL="http://172.22.48.1:3000"
API_KEY="sk-1-ShkCnu0B4FTDiggowWiVZWFf349QFRt6rpdNBKQ1AZBpsD33"
MODEL="deepseek-r1"
CONCURRENT=20

echo "======================================"
echo " 用户模型 RPM 限流 - Redis 并发测试"
echo "======================================"
echo "RPM=2, 并发=$CONCURRENT, 模型=$MODEL"
echo "令牌桶参数: Capacity=120, Rate=2, Requested=60"
echo ""

# 先清理旧的 Redis key
echo "[0] 清理旧限流 key..."
redis-cli -p 36379 DEL "rateLimit:UMRL:1:deepseek-r1" 2>/dev/null
redis-cli -p 36379 DEL "user_model_rate_limit:1" 2>/dev/null
sleep 1

echo ""
echo "[1] 测试并发 $CONCURRENT 个请求..."
success=0
rejected=0
pids=()
results_dir=$(mktemp -d)

for i in $(seq 1 $CONCURRENT); do
(
status_code=$(curl -s -o /dev/null -w "%{http_code}" \
"$BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"model\":\"$MODEL\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"max_tokens\":5}" \
--connect-timeout 5 \
--max-time 30)
echo "$status_code" > "$results_dir/req_$i"
) &
pids+=($!)
done

# 等待所有请求完成
for pid in "${pids[@]}"; do
wait $pid 2>/dev/null
done

# 统计结果
echo ""
echo "--- 结果 ---"
for i in $(seq 1 $CONCURRENT); do
code=$(cat "$results_dir/req_$i" 2>/dev/null || echo "000")
if [ "$code" = "200" ]; then
success=$((success + 1))
echo " 请求 $i: $code ✓"
elif [ "$code" = "429" ]; then
rejected=$((rejected + 1))
echo " 请求 $i: $code (限流)"
else
echo " 请求 $i: $code (其他)"
fi
done

echo ""
echo "======================================"
echo " 统计"
echo "======================================"
echo "成功 (200): $success"
echo "限流 (429): $rejected"
echo "预期: 成功 <= 2, 限流 >= $((CONCURRENT - 2))"
echo ""

if [ "$success" -le 2 ]; then
echo "✅ 测试通过!并发安全,最多 2 个请求成功"
else
echo "❌ 测试失败!预期最多 2 个成功,实际 $success 个"
fi

# 检查 Redis 中的令牌桶状态
echo ""
echo "--- Redis 令牌桶状态 ---"
redis-cli -p 36379 HGETALL "rateLimit:UMRL:1:deepseek-r1" 2>/dev/null || echo "(无法连接 Redis)"

# 清理
rm -rf "$results_dir"

echo ""
echo "[2] 测试顺序请求(3 次连续,第 3 次 429)..."
redis-cli -p 36379 DEL "rateLimit:UMRL:1:deepseek-r1" 2>/dev/null
sleep 1

code1=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d "{\"model\":\"$MODEL\",\"messages\":[{\"role\":\"user\",\"content\":\"test1\"}],\"max_tokens\":5}" \
--connect-timeout 5 --max-time 30)
echo " 请求 1: $code1"

code2=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d "{\"model\":\"$MODEL\",\"messages\":[{\"role\":\"user\",\"content\":\"test2\"}],\"max_tokens\":5}" \
--connect-timeout 5 --max-time 30)
echo " 请求 2: $code2"

code3=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d "{\"model\":\"$MODEL\",\"messages\":[{\"role\":\"user\",\"content\":\"test3\"}],\"max_tokens\":5}" \
--connect-timeout 5 --max-time 30)
echo " 请求 3: $code3"

if [ "$code1" = "200" ] && [ "$code2" = "200" ] && [ "$code3" = "429" ]; then
echo "✅ 顺序测试通过!前 2 次 200,第 3 次 429"
else
echo "❌ 顺序测试失败!预期 200/200/429,实际 $code1/$code2/$code3"
fi

echo ""
echo "--- Redis 令牌桶最终状态 ---"
redis-cli -p 36379 HGETALL "rateLimit:UMRL:1:deepseek-r1" 2>/dev/null || echo "(无法连接 Redis)"

BIN
test-after-login.png Voir le fichier

Avant Après
Largeur: 3200  |  Hauteur: 1626  |  Taille: 335 KiB

BIN
test-login.png Voir le fichier

Avant Après
Largeur: 3200  |  Hauteur: 1626  |  Taille: 320 KiB

BIN
test-login2.png Voir le fichier

Avant Après
Largeur: 3200  |  Hauteur: 1626  |  Taille: 302 KiB

BIN
test-page.png Voir le fichier

Avant Après
Largeur: 3200  |  Hauteur: 1626  |  Taille: 319 KiB

BIN
test-screenshot.png Voir le fichier

Avant Après
Largeur: 1920  |  Hauteur: 893  |  Taille: 134 KiB

BIN
test-user-click.png Voir le fichier

Avant Après
Largeur: 3200  |  Hauteur: 1626  |  Taille: 105 KiB

BIN
test-user-page.png Voir le fichier

Avant Après
Largeur: 3200  |  Hauteur: 1626  |  Taille: 105 KiB

BIN
test-user-pricing.png Voir le fichier

Avant Après
Largeur: 1920  |  Hauteur: 893  |  Taille: 251 KiB

BIN
test-users.png Voir le fichier

Avant Après
Largeur: 3200  |  Hauteur: 1626  |  Taille: 105 KiB

+ 138
- 0
test/anthropic_cache_test.sh Voir le fichier

@@ -0,0 +1,138 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ -z "${ANTHROPIC_API_KEY:-}" ]]; then
echo "ANTHROPIC_API_KEY is required" >&2
exit 1
fi

if ! command -v jq >/dev/null 2>&1; then
echo "jq is required to build the JSON payload and summarize usage" >&2
exit 1
fi

MODEL="${MODEL:-claude-opus-4-6}"
ANTHROPIC_VERSION="${ANTHROPIC_VERSION:-2023-06-01}"
API_URL="${ANTHROPIC_API_URL:-https://lancerouter.ai/v1/messages}"
CACHE_SENTENCE_COUNT="${CACHE_SENTENCE_COUNT:-240}"
REQUEST_COUNT="${REQUEST_COUNT:-2}"
SAME_USER_PROMPT="${SAME_USER_PROMPT:-0}"
CACHE_MODE="${CACHE_MODE:-explicit}"
DRY_RUN="${DRY_RUN:-0}"

case "$CACHE_MODE" in
explicit | automatic)
;;
*)
echo "CACHE_MODE must be 'explicit' or 'automatic'" >&2
exit 1
;;
esac

build_payload() {
local question="$1"

jq -n \
--arg model "$MODEL" \
--arg question "$question" \
--arg cache_mode "$CACHE_MODE" \
--argjson sentence_count "$CACHE_SENTENCE_COUNT" '
def zpad4:
tostring as $raw | ("0000" + $raw) | .[-4:];

(reduce range(1; $sentence_count + 1) as $i
("";
. + "Cache test invariant sentence " + ($i | zpad4) + ": this exact line must remain unchanged across requests. "
)) as $ctx
| ({
model: $model,
max_tokens: 20,
system: [
{
type: "text",
text: $ctx
}
],
messages: [
{
role: "user",
content: $question
}
]
}
| if $cache_mode == "automatic" then
. + { cache_control: { type: "ephemeral" } }
else
.system[0].cache_control = { type: "ephemeral" }
end)
'
}

send_request() {
local question="$1"

build_payload "$question" | curl --silent --show-error "$API_URL" \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: $ANTHROPIC_VERSION" \
--header "content-type: application/json" \
-D /tmp/cache_test_headers.txt \
--data @-
}

extract_header() {
local header_name="$1"
grep -i "^${header_name}:" /tmp/cache_test_headers.txt 2>/dev/null \
| head -1 \
| sed 's/^[^:]*: *//' \
| tr -d '\r\n' \
|| echo "N/A"
}

summarize_response() {
jq 'if has("error") then
{
type,
error
}
elif (.content | type) == "array" then
{
id,
model,
stop_reason,
usage,
text: (.content | map(select(.type == "text") | .text) | join(""))
}
else
.
end'
}

for i in $(seq 1 "$REQUEST_COUNT"); do
if [[ "$SAME_USER_PROMPT" == "1" ]]; then
question="Reply with only: cache-test"
else
question="Reply with only: request-$i"
fi

if [[ "$DRY_RUN" == "1" ]]; then
build_payload "$question" | jq
else
echo "=== Request $i/$REQUEST_COUNT: $question ==="
send_request "$question" | tee /tmp/anthropic_cache_response.json | summarize_response
echo "x-oneapi-request-id: $(extract_header 'X-Oneapi-Request-Id')"
echo "x-request-id: $(extract_header 'X-Request-Id')"
echo
fi
done

if [[ "$DRY_RUN" != "1" ]]; then
echo "Expected cache signal:"
echo "- First response usage.cache_creation_input_tokens should be greater than 0."
echo "- Later responses usage.cache_read_input_tokens should be greater than 0."
echo
echo "Diagnostic mode:"
echo "REQUEST_COUNT=10 SAME_USER_PROMPT=1 MODEL=\"$MODEL\" ./test/anthropic_cache_test.sh"
echo
echo "Automatic caching mode:"
echo "CACHE_MODE=automatic MODEL=\"$MODEL\" ./test/anthropic_cache_test.sh"
fi

+ 431
- 0
test/e2e_channel_removal.py Voir le fichier

@@ -0,0 +1,431 @@
"""
E2E Test Suite: Channel Selection Feature Removal
Tests that the channel selection feature has been completely removed.

Usage:
python test/e2e_channel_removal.py --base-url http://localhost:3000 --key sk-XXX
"""
import json
import sys
import time
import urllib.request
import urllib.error
import argparse


BASE_URL = "http://localhost:3000"
TOKEN_KEY = None # set via --key arg

PASS = 0
FAIL = 0
SKIP = 0


def log_pass(name, detail=""):
global PASS
PASS += 1
msg = f" PASS {name}"
if detail:
msg += f" ({detail})"
print(msg)


def log_fail(name, detail=""):
global FAIL
FAIL += 1
msg = f" FAIL {name}"
if detail:
msg += f" ({detail})"
print(msg)


def log_skip(name, detail=""):
global SKIP
SKIP += 1
msg = f" SKIP {name}"
if detail:
msg += f" ({detail})"
print(msg)


def api_request(method, path, body=None, headers=None, expect_status=None):
"""Make API request, return (status_code, response_body_dict)"""
url = f"{BASE_URL}{path}"
data = json.dumps(body).encode() if body else None
hdrs = {"Content-Type": "application/json"}
if TOKEN_KEY:
hdrs["Authorization"] = f"Bearer {TOKEN_KEY}"
if headers:
hdrs.update(headers)

req = urllib.request.Request(url, data=data, headers=hdrs, method=method)
try:
resp = urllib.request.urlopen(req, timeout=30)
body = json.loads(resp.read())
status = resp.status
except urllib.error.HTTPError as e:
body = None
try:
body = json.loads(e.read())
except Exception:
pass
status = e.code
except Exception as e:
return None, str(e)

if expect_status:
return status, body, status == expect_status
return status, body, True


# ===========================================================================
# Test 1: Token parsing — sk-{key}:{channelId} format
# ===========================================================================
def test_token_colon_format_rejected():
"""Token with colon (old channelId format) should fail auth or treat as whole key"""
name = "Token colon format"
# Use a fabricated key with colon — should not authenticate
old_key = "sk-test:123"
url = f"{BASE_URL}/v1/chat/completions"
body = json.dumps({
"model": "deepseek-ai/DeepSeek-V3-0324",
"messages": [{"role": "user", "content": "hi"}],
"max_tokens": 1
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", f"Bearer {old_key}")

try:
resp = urllib.request.urlopen(req, timeout=10)
log_pass(name, f"got 200 — colon treated as part of key")
except urllib.error.HTTPError as e:
if e.code in (401, 403):
log_pass(name, f"got {e.code} — colon key rejected as expected")
else:
log_fail(name, f"unexpected status {e.code}")
except ConnectionError:
# Server may reset connection on invalid token — count as auth rejection
log_pass(name, "connection reset (server rejected invalid token)")
except OSError:
log_pass(name, "connection error (server rejected invalid token)")


def test_token_dash_format_rejected():
"""Token with dash-channelId format should fail auth or treat as whole key"""
name = "Token dash format"
old_key = "sk-test-123"
url = f"{BASE_URL}/v1/chat/completions"
body = json.dumps({
"model": "deepseek-ai/DeepSeek-V3-0324",
"messages": [{"role": "user", "content": "hi"}],
"max_tokens": 1
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", f"Bearer {old_key}")

try:
resp = urllib.request.urlopen(req, timeout=10)
log_pass(name, f"got 200 — dash treated as part of key")
except urllib.error.HTTPError as e:
if e.code in (401, 403):
log_pass(name, f"got {e.code} — dash key rejected")
else:
log_fail(name, f"unexpected status {e.code}")
except Exception as e:
log_fail(name, f"request error: {e}")


# ===========================================================================
# Test 2: Deleted API routes return 404
# ===========================================================================
def test_deleted_route(path, method="GET", name_suffix=""):
"""Check that a deleted route returns 404, 401, or 200-with-error (catch-all route)"""
name = f"Deleted route {method} {path} {name_suffix}".strip()
status, body, _ = api_request(method, path)
if status == 404:
log_pass(name, "404 Not Found")
elif status == 401:
log_pass(name, "401 auth required (route handler deleted)")
elif status == 200 and isinstance(body, dict):
# Gin may match /:id catch-all route, returning 200 with error
if body.get("success") == False:
log_pass(name, f"200 with success=false (matched catch-all, not dedicated handler)")
else:
log_fail(name, f"200 with success=true — route may still exist!")
else:
log_fail(name, f"unexpected status {status}")


def test_deleted_routes():
"""All channel-selection related routes should return 404"""
# model_channels route
test_deleted_route("/api/channel/model_channels?model=gpt-3.5-turbo", name_suffix="(model_channels)")

# channels for binding route
test_deleted_route("/api/channel/channels", name_suffix="(channels_binding)")

# set_default route
test_deleted_route("/api/channel_pricing/set_default", method="POST", name_suffix="(set_default)")

# clear_default route — note: path includes model name param
test_deleted_route("/api/channel_pricing/default/gpt-3.5-turbo", method="DELETE", name_suffix="(clear_default)")


# ===========================================================================
# Test 3: Normal request forwarding
# ===========================================================================
def test_normal_chat_request():
"""Normal chat request should be auto-distributed and return successfully"""
name = "Normal chat request auto-distribute"
if not TOKEN_KEY:
log_skip(name, "no token key provided")
return

url = f"{BASE_URL}/v1/chat/completions"
body = json.dumps({
"model": "deepseek-ai/DeepSeek-V3-0324",
"messages": [{"role": "user", "content": "Say hello in 3 words"}],
"max_tokens": 10
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", f"Bearer {TOKEN_KEY}")

try:
resp = urllib.request.urlopen(req, timeout=30)
data = json.loads(resp.read())
if data.get("choices") and len(data["choices"]) > 0:
log_pass(name, f"got response with {len(data['choices'])} choice(s)")
else:
log_fail(name, f"response has no choices: {json.dumps(data)[:200]}")
except urllib.error.HTTPError as e:
err_body = ""
try:
err_body = e.read().decode()[:200]
except Exception:
pass
if e.code == 404:
# Model not available — acceptable in test env
log_skip(name, f"model not available: {err_body}")
elif e.code == 429:
# Rate limited — request was processed correctly, just throttled
log_pass(name, f"429 rate limited (request routed correctly)")
else:
log_fail(name, f"HTTP {e.code}: {err_body}")
except Exception as e:
log_fail(name, f"request error: {e}")


def test_models_endpoint():
"""Models endpoint should return available models"""
name = "Models endpoint"
if not TOKEN_KEY:
log_skip(name, "no token key provided")
return

status, body, ok = api_request("GET", "/v1/models")
if status == 200 and body and body.get("data"):
log_pass(name, f"got {len(body['data'])} models")
elif status == 200:
log_pass(name, "returned 200 (empty model list is OK)")
else:
log_fail(name, f"got status {status}")


# ===========================================================================
# Test 4: Channel ID body/header ignored
# ===========================================================================
def test_channel_id_body_ignored():
"""Sending channel_id in request body should be ignored (not error)"""
name = "channel_id in body ignored"
if not TOKEN_KEY:
log_skip(name, "no token key provided")
return

url = f"{BASE_URL}/v1/chat/completions"
body = json.dumps({
"model": "deepseek-ai/DeepSeek-V3-0324",
"messages": [{"role": "user", "content": "hi"}],
"max_tokens": 1,
"channel_id": 999
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", f"Bearer {TOKEN_KEY}")

try:
resp = urllib.request.urlopen(req, timeout=30)
data = json.loads(resp.read())
# Request should succeed — channel_id is ignored
log_pass(name, "request succeeded with channel_id in body (ignored)")
except urllib.error.HTTPError as e:
if e.code in (400, 422):
# Some models may not exist, that's OK
log_pass(name, f"got {e.code} (model issue, not channel_id)")
elif e.code == 429:
log_pass(name, f"429 rate limited (request processed, channel_id ignored)")
elif e.code == 404:
log_skip(name, "model not available in test env")
else:
err = ""
try:
err = e.read().decode()[:200]
except Exception:
pass
log_fail(name, f"unexpected HTTP {e.code}: {err}")
except Exception as e:
log_fail(name, f"request error: {e}")


def test_x_channel_id_header_ignored():
"""Sending X-Channel-Id header should be ignored (not error)"""
name = "X-Channel-Id header ignored"
if not TOKEN_KEY:
log_skip(name, "no token key provided")
return

url = f"{BASE_URL}/v1/chat/completions"
body = json.dumps({
"model": "deepseek-ai/DeepSeek-V3-0324",
"messages": [{"role": "user", "content": "hi"}],
"max_tokens": 1
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", f"Bearer {TOKEN_KEY}")
req.add_header("X-Channel-Id", "999")

try:
resp = urllib.request.urlopen(req, timeout=30)
data = json.loads(resp.read())
log_pass(name, "request succeeded with X-Channel-Id header (ignored)")
except urllib.error.HTTPError as e:
if e.code in (400, 422):
log_pass(name, f"got {e.code} (model issue, not header)")
elif e.code == 429:
log_pass(name, "429 rate limited (request was processed, header ignored)")
elif e.code == 404:
log_skip(name, "model not available in test env")
else:
err = ""
try:
err = e.read().decode()[:200]
except Exception:
pass
log_fail(name, f"unexpected HTTP {e.code}: {err}")
except Exception as e:
log_fail(name, f"request error: {e}")


# ===========================================================================
# Test 5: Pricing API no longer returns default_channel fields
# ===========================================================================
def test_pricing_no_default_channel():
"""Pricing API should not return default_channel_name or isDefault"""
name = "Pricing no default channel"
if not TOKEN_KEY:
log_skip(name, "no token key provided")
return

# Get admin token or use user token to check pricing
status, body, _ = api_request("GET", "/api/channel_pricing/model/deepseek-ai/DeepSeek-V3-0324")
if status == 401:
log_skip(name, "need admin auth for pricing API")
return

if status == 200 and body:
# Check that no default_channel_name appears
body_str = json.dumps(body)
if "default_channel" in body_str:
log_fail(name, "response contains 'default_channel'")
else:
log_pass(name, "no default_channel in response")
elif status == 404:
log_skip(name, "pricing endpoint not found or no data")
else:
log_fail(name, f"got status {status}")


# ===========================================================================
# Test 6: Playground API no longer serves channel list
# ===========================================================================
def test_playground_no_channels():
"""Playground channels API should return 404"""
name = "Playground channels API removed"
# This is covered by test_deleted_routes, but let's verify explicitly
test_deleted_route("/api/channel/model_channels?model=gpt-3.5-turbo", name_suffix="(playground)")


# ===========================================================================
# Main
# ===========================================================================
def main():
global BASE_URL, TOKEN_KEY
parser = argparse.ArgumentParser(description="E2E tests for channel selection removal")
parser.add_argument("--base-url", default="http://localhost:3000")
parser.add_argument("--key", default=None, help="API token key (sk-xxx)")
args = parser.parse_args()

BASE_URL = args.base_url.rstrip("/")
TOKEN_KEY = args.key

print(f"\n{'='*60}")
print("E2E Test: Channel Selection Feature Removal")
print(f"Base URL: {BASE_URL}")
print(f"Token: {'provided' if TOKEN_KEY else 'NOT provided (some tests will skip)'}")
print(f"{'='*60}\n")

# Health check
print("--- Health Check ---")
try:
resp = urllib.request.urlopen(f"{BASE_URL}/api/status", timeout=5)
data = json.loads(resp.read())
if data.get("success"):
print(" Server is UP\n")
else:
print(" Server returned non-success status\n")
except Exception as e:
print(f" FAIL: Cannot reach server: {e}")
sys.exit(1)

# Test suite
print("--- 1. Token Parsing ---")
test_token_colon_format_rejected()
test_token_dash_format_rejected()

print("\n--- 2. Deleted Routes (404) ---")
test_deleted_routes()

print("\n--- 3. Normal Request Forwarding ---")
test_models_endpoint()
test_normal_chat_request()

print("\n--- 4. Channel ID Ignored ---")
test_channel_id_body_ignored()
print(" (waiting 2s to avoid rate limit...)")
time.sleep(2)
test_x_channel_id_header_ignored()

print("\n--- 5. Pricing No Default Channel ---")
test_pricing_no_default_channel()

print("\n--- 6. Playground Channels Removed ---")
test_playground_no_channels()

# Summary
total = PASS + FAIL + SKIP
print(f"\n{'='*60}")
print(f"Results: {PASS} PASS | {FAIL} FAIL | {SKIP} SKIP | {total} TOTAL")
print(f"{'='*60}\n")

if FAIL > 0:
sys.exit(1)
else:
print("All tests passed!\n")
sys.exit(0)


if __name__ == "__main__":
main()

Chargement…
Annuler
Enregistrer