Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
//! 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());
|
||||
}
|
||||
Reference in New Issue
Block a user