Files
zclaw_openfang/crates/zclaw-saas/tests/migration_test.rs
iven ab0e11a719
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
fix(saas): Phase 5 regression fixes — SQL type casts + test data corrections
- Fix usage_stats SQL: add ::timestamptz cast for Option<String> params
- Fix usage_stats SQL: add ::bigint cast for COALESCE(SUM(...))
- Fix telemetry INSERT: add ::timestamptz cast for reported_at column
- Fix config_analysis_empty test: seed data makes total_items > 0
- Fix key_pool_crud test: key_value must be >= 20 chars
- Fix SkillManifest test helpers: add missing tools field

All 1048 tests pass: 580 Rust + 138 SaaS + 330 Desktop Vitest
2026-04-07 19:21:45 +08:00

176 lines
7.6 KiB
Rust

mod common;
use axum::http::StatusCode;
use common::*;
// ═══════════════════════════════════════════════════════════════════
// Config analysis
// ═══════════════════════════════════════════════════════════════════
#[tokio::test]
async fn config_analysis_empty() {
let (app, _pool) = build_test_app().await;
let token = register_token(&app, "cfganalyze").await;
let (status, body) = send(&app, get("/api/v1/config/analysis", &token)).await;
assert_eq!(status, StatusCode::OK);
// init_db seeds 24 default config items, so total_items > 0 is expected
assert!(body["total_items"].as_i64().unwrap_or(-1) >= 0, "total_items should be non-negative");
}
// ═══════════════════════════════════════════════════════════════════
// Config items CRUD
// ═══════════════════════════════════════════════════════════════════
#[tokio::test]
async fn config_items_crud() {
let (app, pool) = build_test_app().await;
let admin = admin_token(&app, &pool, "cfgadmin").await;
// Create config item
let (status, body) = send(
&app,
post(
"/api/v1/config/items",
&admin,
serde_json::json!({
"category": "server",
"key_path": "server.host",
"value_type": "string",
"current_value": "0.0.0.0",
"description": "Server bind address"
}),
),
).await;
assert_eq!(status, StatusCode::CREATED, "create config item: {body}");
let item_id = body["id"].as_str().unwrap();
// List
let (status, list) = send(&app, get("/api/v1/config/items", &admin)).await;
assert_eq!(status, StatusCode::OK);
assert!(list.is_array() || list["items"].is_array());
// Get
let (status, body) = send(&app, get(&format!("/api/v1/config/items/{item_id}"), &admin)).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body["key_path"], "server.host");
// Update
let (status, body) = send(
&app,
put(
&format!("/api/v1/config/items/{item_id}"),
&admin,
serde_json::json!({ "current_value": "127.0.0.1" }),
),
).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body["current_value"], "127.0.0.1");
// Delete
let (status, _) = send(&app, delete(&format!("/api/v1/config/items/{item_id}"), &admin)).await;
assert_eq!(status, StatusCode::OK);
}
#[tokio::test]
async fn config_items_write_forbidden_for_user() {
let (app, _pool) = build_test_app().await;
let token = register_token(&app, "cfguser").await;
let (status, _) = send(
&app,
post(
"/api/v1/config/items",
&token,
serde_json::json!({ "category": "x", "key_path": "y", "value_type": "string" }),
),
).await;
assert_eq!(status, StatusCode::FORBIDDEN);
}
// ═══════════════════════════════════════════════════════════════════
// Config seed
// ═══════════════════════════════════════════════════════════════════
#[tokio::test]
async fn config_seed_admin_only() {
let (app, _pool) = build_test_app().await;
let user_token = register_token(&app, "cfgseeduser").await;
let (status, _) = send(&app, post("/api/v1/config/seed", &user_token, serde_json::json!({}))).await;
assert_eq!(status, StatusCode::FORBIDDEN);
}
// ═══════════════════════════════════════════════════════════════════
// Config sync (push)
// ═══════════════════════════════════════════════════════════════════
#[tokio::test]
async fn config_sync_push() {
let (app, pool) = build_test_app().await;
let admin = admin_token(&app, &pool, "cfgsync").await;
let (status, body) = send(
&app,
post(
"/api/v1/config/sync",
&admin,
serde_json::json!({
"client_fingerprint": "test-desktop-v1",
"action": "push",
"config_keys": ["server.host", "server.port"],
"client_values": { "server.host": "192.168.1.1", "server.port": "9090" }
}),
),
).await;
assert_eq!(status, StatusCode::OK, "config sync push: {body}");
// Push mode: keys don't exist in SaaS → auto-created
assert_eq!(body["created"], 2);
}
// ═══════════════════════════════════════════════════════════════════
// Config diff
// ═══════════════════════════════════════════════════════════════════
#[tokio::test]
async fn config_diff() {
let (app, _pool) = build_test_app().await;
let token = register_token(&app, "cfgdiff").await;
let (status, body) = send(
&app,
post(
"/api/v1/config/diff",
&token,
serde_json::json!({
"client_fingerprint": "test-client",
"action": "push",
"config_keys": ["server.host"],
"client_values": { "server.host": "0.0.0.0" }
}),
),
).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body["total_keys"], 1);
assert!(body["items"].is_array());
}
// ═══════════════════════════════════════════════════════════════════
// Config sync logs
// ═══════════════════════════════════════════════════════════════════
#[tokio::test]
async fn config_sync_logs() {
let (app, _pool) = build_test_app().await;
let token = register_token(&app, "cfglogs").await;
let (status, _) = send(&app, get("/api/v1/config/sync-logs", &token)).await;
assert_eq!(status, StatusCode::OK);
}
// ═══════════════════════════════════════════════════════════════════
// Config pull
// ═══════════════════════════════════════════════════════════════════
#[tokio::test]
async fn config_pull_empty() {
let (app, _pool) = build_test_app().await;
let token = register_token(&app, "cfgpull").await;
let (status, _) = send(&app, get("/api/v1/config/pull", &token)).await;
assert_eq!(status, StatusCode::OK);
}