test(saas): Phase 1 integration tests — billing + scheduled_task + knowledge (68 tests)
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
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 TIMESTAMPTZ decode errors: add ::TEXT cast to all SELECT queries
where Row structs use String for TIMESTAMPTZ columns (~22 locations)
- Fix Axum 0.7 route params: {id} → :id in billing/knowledge/scheduled_task routes
- Fix JSONB bind: scheduled_task INSERT uses ::jsonb cast for input_payload
- Add billing_test.rs (14 tests): plans, subscription, usage, payments, invoices
- Add scheduled_task_test.rs (12 tests): CRUD, validation, isolation
- Add knowledge_test.rs (20 tests): categories, items, versions, search, analytics, permissions
- Fix auth test regression: 6 tests were failing due to TIMESTAMPTZ type mismatch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ use super::types::*;
|
||||
pub async fn list_roles(db: &PgPool) -> SaasResult<Vec<RoleInfo>> {
|
||||
let rows: Vec<RoleRow> =
|
||||
sqlx::query_as(
|
||||
"SELECT id, name, description, permissions, is_system, created_at, updated_at
|
||||
"SELECT id, name, description, permissions, is_system, created_at::TEXT, updated_at::TEXT
|
||||
FROM roles ORDER BY
|
||||
CASE id
|
||||
WHEN 'super_admin' THEN 1
|
||||
@@ -31,7 +31,7 @@ pub async fn list_roles(db: &PgPool) -> SaasResult<Vec<RoleInfo>> {
|
||||
pub async fn get_role(db: &PgPool, role_id: &str) -> SaasResult<RoleInfo> {
|
||||
let row: Option<RoleRow> =
|
||||
sqlx::query_as(
|
||||
"SELECT id, name, description, permissions, is_system, created_at, updated_at
|
||||
"SELECT id, name, description, permissions, is_system, created_at::TEXT, updated_at::TEXT
|
||||
FROM roles WHERE id = $1"
|
||||
)
|
||||
.bind(role_id)
|
||||
@@ -56,7 +56,7 @@ pub async fn create_role(db: &PgPool, req: &CreateRoleRequest) -> SaasResult<Rol
|
||||
return Err(SaasError::AlreadyExists(format!("角色 {} 已存在", req.id)));
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
let now = chrono::Utc::now();
|
||||
let permissions = serde_json::to_string(&req.permissions)?;
|
||||
|
||||
sqlx::query(
|
||||
@@ -77,8 +77,8 @@ pub async fn create_role(db: &PgPool, req: &CreateRoleRequest) -> SaasResult<Rol
|
||||
description: req.description.clone(),
|
||||
permissions: req.permissions.clone(),
|
||||
is_system: false,
|
||||
created_at: now.clone(),
|
||||
updated_at: now,
|
||||
created_at: now.to_rfc3339(),
|
||||
updated_at: now.to_rfc3339(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ pub async fn update_role(db: &PgPool, role_id: &str, req: &UpdateRoleRequest) ->
|
||||
return Err(SaasError::Forbidden("系统角色不可修改".into()));
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
let now = chrono::Utc::now();
|
||||
let name = req.name.as_ref().unwrap_or(&existing.name);
|
||||
let description = req.description.as_ref().or(existing.description.as_ref());
|
||||
let permissions = req.permissions.as_ref().unwrap_or(&existing.permissions);
|
||||
@@ -113,7 +113,7 @@ pub async fn update_role(db: &PgPool, role_id: &str, req: &UpdateRoleRequest) ->
|
||||
permissions: permissions.clone(),
|
||||
is_system: false,
|
||||
created_at: existing.created_at,
|
||||
updated_at: now,
|
||||
updated_at: now.to_rfc3339(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ pub async fn delete_role(db: &PgPool, role_id: &str) -> SaasResult<()> {
|
||||
pub async fn list_templates(db: &PgPool) -> SaasResult<Vec<PermissionTemplate>> {
|
||||
let rows: Vec<PermissionTemplateRow> =
|
||||
sqlx::query_as(
|
||||
"SELECT id, name, description, permissions, created_at, updated_at
|
||||
"SELECT id, name, description, permissions, created_at::TEXT, updated_at::TEXT
|
||||
FROM permission_templates ORDER BY created_at DESC"
|
||||
)
|
||||
.fetch_all(db)
|
||||
@@ -156,7 +156,7 @@ pub async fn list_templates(db: &PgPool) -> SaasResult<Vec<PermissionTemplate>>
|
||||
pub async fn get_template(db: &PgPool, template_id: &str) -> SaasResult<PermissionTemplate> {
|
||||
let row: Option<PermissionTemplateRow> =
|
||||
sqlx::query_as(
|
||||
"SELECT id, name, description, permissions, created_at, updated_at
|
||||
"SELECT id, name, description, permissions, created_at::TEXT, updated_at::TEXT
|
||||
FROM permission_templates WHERE id = $1"
|
||||
)
|
||||
.bind(template_id)
|
||||
@@ -171,7 +171,7 @@ pub async fn get_template(db: &PgPool, template_id: &str) -> SaasResult<Permissi
|
||||
|
||||
pub async fn create_template(db: &PgPool, req: &CreateTemplateRequest) -> SaasResult<PermissionTemplate> {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
let now = chrono::Utc::now();
|
||||
let permissions = serde_json::to_string(&req.permissions)?;
|
||||
|
||||
sqlx::query(
|
||||
@@ -191,8 +191,8 @@ pub async fn create_template(db: &PgPool, req: &CreateTemplateRequest) -> SaasRe
|
||||
name: req.name.clone(),
|
||||
description: req.description.clone(),
|
||||
permissions: req.permissions.clone(),
|
||||
created_at: now.clone(),
|
||||
updated_at: now,
|
||||
created_at: now.to_rfc3339(),
|
||||
updated_at: now.to_rfc3339(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ pub async fn apply_template_to_accounts(
|
||||
account_ids: &[String],
|
||||
) -> SaasResult<usize> {
|
||||
let template = get_template(db, template_id).await?;
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
let now = chrono::Utc::now();
|
||||
|
||||
let mut success_count = 0;
|
||||
for account_id in account_ids {
|
||||
|
||||
Reference in New Issue
Block a user