f7a720204a
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
208 lines
6.2 KiB
Rust
208 lines
6.2 KiB
Rust
//! Black-box integration tests for `IOAuthTokenRepository`.
|
|
//!
|
|
//! Tests exercise the repository trait interface without knowledge of
|
|
//! the underlying SQLite implementation details.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use nomifun_db::{
|
|
DbError, IOAuthTokenRepository, SqliteOAuthTokenRepository, UpsertOAuthTokenParams, init_database_memory,
|
|
};
|
|
|
|
async fn repo() -> (Arc<dyn IOAuthTokenRepository>, nomifun_db::Database) {
|
|
let db = init_database_memory().await.unwrap();
|
|
let r = Arc::new(SqliteOAuthTokenRepository::new(db.pool().clone()));
|
|
(r as Arc<dyn IOAuthTokenRepository>, db)
|
|
}
|
|
|
|
fn sample_params() -> UpsertOAuthTokenParams<'static> {
|
|
UpsertOAuthTokenParams {
|
|
server_url: "https://mcp.example.com",
|
|
access_token: "enc_access_token_123",
|
|
refresh_token: Some("enc_refresh_token_456"),
|
|
token_type: "bearer",
|
|
expires_at: Some(1700000000000),
|
|
}
|
|
}
|
|
|
|
// -- OA-1: Unauthenticated server --
|
|
|
|
#[tokio::test]
|
|
async fn get_by_url_nonexistent_returns_none() {
|
|
let (r, _db) = repo().await;
|
|
assert!(r.get_by_url("https://nope.com").await.unwrap().is_none());
|
|
}
|
|
|
|
// -- OA-2: Insert and retrieve --
|
|
|
|
#[tokio::test]
|
|
async fn upsert_insert_then_get_returns_token() {
|
|
let (r, _db) = repo().await;
|
|
let inserted = r.upsert(sample_params()).await.unwrap();
|
|
|
|
assert_eq!(inserted.server_url, "https://mcp.example.com");
|
|
assert_eq!(inserted.access_token, "enc_access_token_123");
|
|
assert_eq!(inserted.refresh_token.as_deref(), Some("enc_refresh_token_456"));
|
|
assert_eq!(inserted.token_type, "bearer");
|
|
assert_eq!(inserted.expires_at, Some(1700000000000));
|
|
assert!(inserted.created_at > 0);
|
|
|
|
let found = r.get_by_url("https://mcp.example.com").await.unwrap().unwrap();
|
|
assert_eq!(found.access_token, "enc_access_token_123");
|
|
}
|
|
|
|
// -- Upsert updates existing --
|
|
|
|
#[tokio::test]
|
|
async fn upsert_updates_existing_token() {
|
|
let (r, _db) = repo().await;
|
|
let original = r.upsert(sample_params()).await.unwrap();
|
|
|
|
let updated = r
|
|
.upsert(UpsertOAuthTokenParams {
|
|
server_url: "https://mcp.example.com",
|
|
access_token: "new_access_token",
|
|
refresh_token: None,
|
|
token_type: "bearer",
|
|
expires_at: Some(1800000000000),
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(updated.server_url, original.server_url);
|
|
assert_eq!(updated.access_token, "new_access_token");
|
|
assert!(updated.refresh_token.is_none());
|
|
assert_eq!(updated.expires_at, Some(1800000000000));
|
|
// created_at preserved from original insert
|
|
assert_eq!(updated.created_at, original.created_at);
|
|
}
|
|
|
|
// -- Upsert without optional fields --
|
|
|
|
#[tokio::test]
|
|
async fn upsert_without_refresh_token_or_expires_at() {
|
|
let (r, _db) = repo().await;
|
|
let token = r
|
|
.upsert(UpsertOAuthTokenParams {
|
|
server_url: "https://simple.example.com",
|
|
access_token: "simple_token",
|
|
refresh_token: None,
|
|
token_type: "bearer",
|
|
expires_at: None,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(token.refresh_token.is_none());
|
|
assert!(token.expires_at.is_none());
|
|
}
|
|
|
|
// -- OA-6: Delete existing --
|
|
|
|
#[tokio::test]
|
|
async fn delete_existing_token() {
|
|
let (r, _db) = repo().await;
|
|
r.upsert(sample_params()).await.unwrap();
|
|
|
|
r.delete("https://mcp.example.com").await.unwrap();
|
|
assert!(r.get_by_url("https://mcp.example.com").await.unwrap().is_none());
|
|
}
|
|
|
|
// -- OA-7: Delete idempotency (returns NotFound for nonexistent) --
|
|
|
|
#[tokio::test]
|
|
async fn delete_nonexistent_returns_not_found() {
|
|
let (r, _db) = repo().await;
|
|
let err = r.delete("https://nope.com").await.unwrap_err();
|
|
assert!(matches!(err, DbError::NotFound(_)));
|
|
}
|
|
|
|
// -- OA-3: List authenticated URLs --
|
|
|
|
#[tokio::test]
|
|
async fn list_authenticated_urls_empty() {
|
|
let (r, _db) = repo().await;
|
|
let urls = r.list_authenticated_urls().await.unwrap();
|
|
assert!(urls.is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn list_authenticated_urls_returns_all() {
|
|
let (r, _db) = repo().await;
|
|
r.upsert(sample_params()).await.unwrap();
|
|
r.upsert(UpsertOAuthTokenParams {
|
|
server_url: "https://other.example.com",
|
|
access_token: "token2",
|
|
refresh_token: None,
|
|
token_type: "bearer",
|
|
expires_at: None,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
let urls = r.list_authenticated_urls().await.unwrap();
|
|
assert_eq!(urls.len(), 2);
|
|
assert!(urls.contains(&"https://mcp.example.com".to_string()));
|
|
assert!(urls.contains(&"https://other.example.com".to_string()));
|
|
}
|
|
|
|
// -- Delete does not affect other tokens --
|
|
|
|
#[tokio::test]
|
|
async fn delete_one_does_not_affect_others() {
|
|
let (r, _db) = repo().await;
|
|
r.upsert(sample_params()).await.unwrap();
|
|
r.upsert(UpsertOAuthTokenParams {
|
|
server_url: "https://other.example.com",
|
|
access_token: "token2",
|
|
refresh_token: None,
|
|
token_type: "bearer",
|
|
expires_at: None,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
r.delete("https://mcp.example.com").await.unwrap();
|
|
|
|
let urls = r.list_authenticated_urls().await.unwrap();
|
|
assert_eq!(urls.len(), 1);
|
|
assert_eq!(urls[0], "https://other.example.com");
|
|
}
|
|
|
|
// -- Full lifecycle --
|
|
|
|
#[tokio::test]
|
|
async fn full_oauth_lifecycle() {
|
|
let (r, _db) = repo().await;
|
|
|
|
// Initially no tokens
|
|
assert!(r.list_authenticated_urls().await.unwrap().is_empty());
|
|
assert!(r.get_by_url("https://mcp.example.com").await.unwrap().is_none());
|
|
|
|
// Store token
|
|
let token = r.upsert(sample_params()).await.unwrap();
|
|
assert_eq!(token.access_token, "enc_access_token_123");
|
|
|
|
// Verify stored
|
|
let urls = r.list_authenticated_urls().await.unwrap();
|
|
assert_eq!(urls.len(), 1);
|
|
|
|
// Update token (refresh)
|
|
let refreshed = r
|
|
.upsert(UpsertOAuthTokenParams {
|
|
server_url: "https://mcp.example.com",
|
|
access_token: "refreshed_token",
|
|
refresh_token: Some("new_refresh"),
|
|
token_type: "bearer",
|
|
expires_at: Some(1900000000000),
|
|
})
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(refreshed.access_token, "refreshed_token");
|
|
assert_eq!(refreshed.created_at, token.created_at);
|
|
|
|
// Logout (delete)
|
|
r.delete("https://mcp.example.com").await.unwrap();
|
|
assert!(r.list_authenticated_urls().await.unwrap().is_empty());
|
|
}
|