Jesús Pérez dd68d190ef ci: Update pre-commit hooks configuration
- Exclude problematic markdown files from linting (existing legacy issues)
- Make clippy check less aggressive (warnings only, not -D warnings)
- Move cargo test to manual stage (too slow for pre-commit)
- Exclude SVG files from end-of-file-fixer and trailing-whitespace
- Add markdown linting exclusions for existing documentation

This allows pre-commit hooks to run successfully on new code without
blocking commits due to existing issues in legacy documentation files.
2026-01-11 21:32:56 +00:00

185 lines
5.8 KiB
Rust

use std::collections::HashMap;
use vapora_llm_router::{BudgetManager, RoleBudget};
fn create_test_budgets() -> HashMap<String, RoleBudget> {
let mut budgets = HashMap::new();
budgets.insert(
"architect".to_string(),
RoleBudget {
role: "architect".to_string(),
monthly_limit_cents: 50000, // $500
weekly_limit_cents: 12500, // $125
fallback_provider: "gemini".to_string(),
alert_threshold: 0.8,
},
);
budgets.insert(
"developer".to_string(),
RoleBudget {
role: "developer".to_string(),
monthly_limit_cents: 30000,
weekly_limit_cents: 7500,
fallback_provider: "ollama".to_string(),
alert_threshold: 0.8,
},
);
budgets
}
#[tokio::test]
async fn test_budget_initialization() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
let status = manager.check_budget("architect").await.unwrap();
assert_eq!(status.role, "architect");
assert_eq!(status.monthly_remaining_cents, 50000);
assert_eq!(status.monthly_utilization, 0.0);
assert!(!status.exceeded);
assert!(!status.near_threshold);
}
#[tokio::test]
async fn test_budget_spending() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
manager.record_spend("developer", 3000).await.unwrap();
let status = manager.check_budget("developer").await.unwrap();
assert_eq!(status.monthly_remaining_cents, 27000);
assert!((status.monthly_utilization - 0.1).abs() < 0.01);
}
#[tokio::test]
async fn test_multiple_spends_accumulate() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
manager.record_spend("developer", 5000).await.unwrap();
manager.record_spend("developer", 3000).await.unwrap();
manager.record_spend("developer", 2000).await.unwrap();
let status = manager.check_budget("developer").await.unwrap();
assert_eq!(status.monthly_remaining_cents, 20000); // 30000 - 10000
}
#[tokio::test]
async fn test_alert_threshold_near() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
// Spend 81% of weekly budget (12500 * 0.81 = 10125) to trigger near_threshold
// This keeps us under both monthly and weekly limits while triggering alert
let spend_amount = (12500.0 * 0.81) as u32; // 10125
manager
.record_spend("architect", spend_amount)
.await
.unwrap();
let status = manager.check_budget("architect").await.unwrap();
assert!(!status.exceeded);
assert!(status.near_threshold);
}
#[tokio::test]
async fn test_budget_exceeded() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
// Spend entire monthly budget
manager.record_spend("developer", 30000).await.unwrap();
let status = manager.check_budget("developer").await.unwrap();
assert!(status.exceeded);
assert_eq!(status.monthly_remaining_cents, 0);
}
#[tokio::test]
async fn test_budget_overspend() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
// Spend more than budget (overflow protection)
manager.record_spend("developer", 35000).await.unwrap();
let status = manager.check_budget("developer").await.unwrap();
assert!(status.exceeded);
assert_eq!(status.monthly_remaining_cents, 0); // Saturating subtract
}
#[tokio::test]
async fn test_weekly_budget_independent() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
// Spend 100% of weekly budget but only 25% of monthly
manager.record_spend("developer", 7500).await.unwrap();
let status = manager.check_budget("developer").await.unwrap();
assert_eq!(status.monthly_remaining_cents, 22500);
assert_eq!(status.weekly_remaining_cents, 0);
assert!(status.exceeded); // Both budgets checked
}
#[tokio::test]
async fn test_fallback_provider() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
let fallback_dev = manager.get_fallback_provider("developer").await.unwrap();
assert_eq!(fallback_dev, "ollama");
let fallback_arch = manager.get_fallback_provider("architect").await.unwrap();
assert_eq!(fallback_arch, "gemini");
}
#[tokio::test]
async fn test_unknown_role_error() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
let result = manager.check_budget("unknown").await;
assert!(result.is_err());
let result = manager.record_spend("unknown", 100).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_get_all_budgets() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
manager.record_spend("architect", 5000).await.unwrap();
manager.record_spend("developer", 3000).await.unwrap();
let all_statuses = manager.get_all_budgets().await;
assert_eq!(all_statuses.len(), 2);
let arch_status = all_statuses.iter().find(|s| s.role == "architect").unwrap();
assert_eq!(arch_status.monthly_remaining_cents, 45000);
let dev_status = all_statuses.iter().find(|s| s.role == "developer").unwrap();
assert_eq!(dev_status.monthly_remaining_cents, 27000);
}
#[tokio::test]
async fn test_budget_status_comprehensive() {
let budgets = create_test_budgets();
let manager = BudgetManager::new(budgets);
// Spend 6000 cents: keeps us at 12% of monthly and 48% of weekly (both safe)
manager.record_spend("architect", 6000).await.unwrap();
let status = manager.check_budget("architect").await.unwrap();
assert_eq!(status.monthly_remaining_cents, 44000);
assert!((status.monthly_utilization - 0.12).abs() < 0.01);
assert!(!status.exceeded);
assert!(!status.near_threshold); // 12% < 80%
assert_eq!(status.fallback_provider, "gemini");
}