487 lines
16 KiB
Rust
487 lines
16 KiB
Rust
use std::path::Path;
|
|
|
|
// This is a test/example file demonstrating how to use the new repository functions
|
|
// Note: This requires the database to be set up and proper imports
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use chrono::Utc;
|
|
use sqlx::SqlitePool;
|
|
|
|
use crate::models::{
|
|
exercise::Exercise,
|
|
node::Node,
|
|
path::{Metadata, Path},
|
|
};
|
|
use crate::repositories::{
|
|
path_json_utils::PathJsonUtils,
|
|
repository_manager::RepositoryManager,
|
|
};
|
|
|
|
async fn setup_test_database() -> SqlitePool {
|
|
// This would normally connect to a test database
|
|
// For demonstration purposes only
|
|
todo!("Setup test database connection")
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_save_and_retrieve_path() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Create a test path
|
|
let test_path = create_sample_path();
|
|
|
|
// Save the path
|
|
let saved_path_id = repo_manager
|
|
.paths()
|
|
.save_path(test_path.clone())
|
|
.await
|
|
.expect("Failed to save path");
|
|
|
|
println!("Saved path with ID: {}", saved_path_id);
|
|
|
|
// Retrieve the path
|
|
let path_id_int = saved_path_id.parse::<i32>().expect("Invalid path ID");
|
|
let retrieved_path = repo_manager
|
|
.paths()
|
|
.get_path_by_id(path_id_int)
|
|
.await
|
|
.expect("Failed to retrieve path");
|
|
|
|
// Verify the data
|
|
assert_eq!(retrieved_path.id, test_path.id);
|
|
assert_eq!(retrieved_path.title, test_path.title);
|
|
assert_eq!(retrieved_path.nodes.len(), test_path.nodes.len());
|
|
|
|
println!("✅ Successfully saved and retrieved path!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_update_path() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Create and save initial path
|
|
let mut test_path = create_sample_path();
|
|
let path_id = repo_manager
|
|
.paths()
|
|
.save_path(test_path.clone())
|
|
.await
|
|
.expect("Failed to save path");
|
|
|
|
// Modify the path
|
|
test_path.title = "Updated Path Title".to_string();
|
|
test_path.description = "Updated description with new content".to_string();
|
|
|
|
// Add a new node
|
|
let new_node = Node {
|
|
id: 999, // This will be auto-assigned
|
|
title: "New Node".to_string(),
|
|
description: "A newly added node".to_string(),
|
|
path_id: test_path.id.clone(),
|
|
exercises: vec![Exercise {
|
|
id: 999,
|
|
ex_type: "vocabulary".to_string(),
|
|
content: r#"{"word": "neu", "translation": "new", "example": "Das ist neu."}"#
|
|
.to_string(),
|
|
node_id: 999,
|
|
}],
|
|
};
|
|
test_path.nodes.push(new_node);
|
|
|
|
// Update the path
|
|
repo_manager
|
|
.paths()
|
|
.update_path(test_path.clone())
|
|
.await
|
|
.expect("Failed to update path");
|
|
|
|
// Retrieve and verify
|
|
let path_id_int = path_id.parse::<i32>().expect("Invalid path ID");
|
|
let updated_path = repo_manager
|
|
.paths()
|
|
.get_path_by_id(path_id_int)
|
|
.await
|
|
.expect("Failed to retrieve updated path");
|
|
|
|
assert_eq!(updated_path.title, "Updated Path Title");
|
|
assert_eq!(updated_path.nodes.len(), 3); // Original 2 + 1 new
|
|
|
|
println!("✅ Successfully updated path!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_clone_path() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Create and save original path
|
|
let original_path = create_sample_path();
|
|
let original_path_id = repo_manager
|
|
.paths()
|
|
.save_path(original_path.clone())
|
|
.await
|
|
.expect("Failed to save original path");
|
|
|
|
// Clone the path
|
|
let original_id_int = original_path_id.parse::<i32>().expect("Invalid path ID");
|
|
let cloned_path_id = repo_manager
|
|
.clone_path_complete(
|
|
original_id_int,
|
|
"cloned_path_001",
|
|
"Cloned German Basics",
|
|
)
|
|
.await
|
|
.expect("Failed to clone path");
|
|
|
|
// Retrieve the cloned path
|
|
let cloned_id_int = cloned_path_id.parse::<i32>().unwrap_or(0);
|
|
let cloned_path = repo_manager
|
|
.paths()
|
|
.get_path_by_id(cloned_id_int)
|
|
.await
|
|
.expect("Failed to retrieve cloned path");
|
|
|
|
// Verify clone
|
|
assert_eq!(cloned_path.id, "cloned_path_001");
|
|
assert_eq!(cloned_path.title, "Cloned German Basics");
|
|
assert_eq!(cloned_path.nodes.len(), original_path.nodes.len());
|
|
|
|
println!("✅ Successfully cloned path!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_json_import_export() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Create sample JSON
|
|
let json_content = r#"
|
|
{
|
|
"id": "test_json_path",
|
|
"title": "JSON Test Path",
|
|
"description": "Testing JSON import/export functionality",
|
|
"metadata": [
|
|
{
|
|
"path_id": "test_json_path",
|
|
"version": "1.0.0",
|
|
"created_at": "2024-01-01T12:00:00Z",
|
|
"updated_at": "2024-01-01T12:00:00Z"
|
|
}
|
|
],
|
|
"nodes": [
|
|
{
|
|
"id": 1,
|
|
"title": "JSON Test Node",
|
|
"description": "Testing node from JSON",
|
|
"path_id": "test_json_path",
|
|
"exercises": [
|
|
{
|
|
"id": 1,
|
|
"ex_type": "vocabulary",
|
|
"content": "{\"word\": \"Test\", \"translation\": \"Test\", \"example\": \"This is a test.\"}",
|
|
"node_id": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
"#;
|
|
|
|
// Import from JSON
|
|
let imported_path_id = repo_manager
|
|
.import_path_from_json(json_content)
|
|
.await
|
|
.expect("Failed to import path from JSON");
|
|
|
|
println!("Imported path ID: {}", imported_path_id);
|
|
|
|
// Export back to JSON
|
|
let path_id_int = imported_path_id.parse::<i32>().expect("Invalid path ID");
|
|
let exported_json = repo_manager
|
|
.export_path_to_json(path_id_int)
|
|
.await
|
|
.expect("Failed to export path to JSON");
|
|
|
|
println!("Exported JSON length: {} characters", exported_json.len());
|
|
|
|
// Verify the exported JSON contains expected content
|
|
assert!(exported_json.contains("JSON Test Path"));
|
|
assert!(exported_json.contains("test_json_path"));
|
|
|
|
println!("✅ Successfully imported and exported JSON!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_path_statistics() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Create and save path
|
|
let test_path = create_sample_path();
|
|
let path_id = repo_manager
|
|
.paths()
|
|
.save_path(test_path)
|
|
.await
|
|
.expect("Failed to save path");
|
|
|
|
// Get statistics
|
|
let path_id_int = path_id.parse::<i32>().expect("Invalid path ID");
|
|
let stats = repo_manager
|
|
.get_path_statistics(path_id_int)
|
|
.await
|
|
.expect("Failed to get path statistics");
|
|
|
|
// Print statistics
|
|
stats.print_detailed_summary();
|
|
|
|
// Verify statistics
|
|
assert_eq!(stats.node_count, 2);
|
|
assert_eq!(stats.total_exercises, 3);
|
|
assert!(stats.exercise_types.contains_key("vocabulary"));
|
|
assert!(stats.exercise_types.contains_key("multiple_choice"));
|
|
|
|
println!("✅ Successfully generated path statistics!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_path_validation() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Create and save path
|
|
let test_path = create_sample_path();
|
|
let path_id = repo_manager
|
|
.paths()
|
|
.save_path(test_path)
|
|
.await
|
|
.expect("Failed to save path");
|
|
|
|
// Validate path integrity
|
|
let path_id_int = path_id.parse::<i32>().expect("Invalid path ID");
|
|
let issues = repo_manager
|
|
.validate_path_integrity(path_id_int)
|
|
.await
|
|
.expect("Failed to validate path");
|
|
|
|
if issues.is_empty() {
|
|
println!("✅ Path validation passed - no issues found!");
|
|
} else {
|
|
println!("⚠️ Path validation found issues:");
|
|
for issue in &issues {
|
|
println!(" - {}", issue);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_search_functionality() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Create and save multiple paths for searching
|
|
let path1 = create_sample_path();
|
|
let mut path2 = create_sample_path();
|
|
path2.id = "search_test_002".to_string();
|
|
path2.title = "Advanced German Grammar".to_string();
|
|
path2.description = "Complex grammatical structures and advanced vocabulary".to_string();
|
|
|
|
repo_manager
|
|
.paths()
|
|
.save_path(path1)
|
|
.await
|
|
.expect("Failed to save path1");
|
|
repo_manager
|
|
.paths()
|
|
.save_path(path2)
|
|
.await
|
|
.expect("Failed to save path2");
|
|
|
|
// Search for paths
|
|
let search_results = repo_manager
|
|
.search_paths("German")
|
|
.await
|
|
.expect("Failed to search paths");
|
|
|
|
println!("Search results for 'German':");
|
|
for result in &search_results {
|
|
result.print_summary();
|
|
println!();
|
|
}
|
|
|
|
assert!(!search_results.is_empty());
|
|
println!("✅ Search functionality working correctly!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_delete_operations() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Create and save path
|
|
let test_path = create_sample_path();
|
|
let path_id = repo_manager
|
|
.paths()
|
|
.save_path(test_path)
|
|
.await
|
|
.expect("Failed to save path");
|
|
|
|
// Verify path exists
|
|
let path_id_int = path_id.parse::<i32>().expect("Invalid path ID");
|
|
let path_exists = repo_manager
|
|
.paths()
|
|
.path_exists(path_id_int)
|
|
.await
|
|
.expect("Failed to check path existence");
|
|
assert!(path_exists);
|
|
|
|
// Delete the path
|
|
repo_manager
|
|
.paths()
|
|
.delete_path(path_id_int)
|
|
.await
|
|
.expect("Failed to delete path");
|
|
|
|
// Verify path no longer exists
|
|
let path_still_exists = repo_manager
|
|
.paths()
|
|
.path_exists(path_id_int)
|
|
.await
|
|
.expect("Failed to check path existence after deletion");
|
|
assert!(!path_still_exists);
|
|
|
|
println!("✅ Successfully deleted path and verified removal!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_database_statistics() {
|
|
let pool = setup_test_database().await;
|
|
let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Get database statistics
|
|
let stats = repo_manager
|
|
.get_stats()
|
|
.await
|
|
.expect("Failed to get database statistics");
|
|
|
|
println!("=== Database Statistics ===");
|
|
println!("Total paths: {}", stats.path_count);
|
|
println!("Total nodes: {}", stats.node_count);
|
|
println!("Total exercises: {}", stats.exercise_count);
|
|
println!("Total metadata records: {}", stats.metadata_count);
|
|
println!("Total records: {}", stats.total_records());
|
|
println!("Database empty: {}", stats.is_empty());
|
|
|
|
println!("✅ Successfully retrieved database statistics!");
|
|
}
|
|
|
|
// Helper function to create a sample path for testing
|
|
fn create_sample_path() -> Path {
|
|
let now = Utc::now();
|
|
|
|
let metadata = vec![Metadata {
|
|
path_id: "test_path_001".to_string(),
|
|
version: "1.0.0".to_string(),
|
|
created_at: now,
|
|
updated_at: now,
|
|
}];
|
|
|
|
let exercises1 = vec![
|
|
Exercise {
|
|
id: 1,
|
|
ex_type: "vocabulary".to_string(),
|
|
content: r#"{"word": "Hallo", "translation": "Hello", "audio": "/audio/hallo.mp3", "example": "Hallo, wie geht's?"}"#.to_string(),
|
|
node_id: 1,
|
|
},
|
|
Exercise {
|
|
id: 2,
|
|
ex_type: "multiple_choice".to_string(),
|
|
content: r#"{"question": "How do you say 'goodbye' in German?", "options": ["Tschüss", "Hallo", "Bitte", "Danke"], "correct": 0, "explanation": "Tschüss is the informal way to say goodbye."}"#.to_string(),
|
|
node_id: 1,
|
|
}
|
|
];
|
|
|
|
let exercises2 = vec![Exercise {
|
|
id: 3,
|
|
ex_type: "vocabulary".to_string(),
|
|
content: r#"{"word": "Danke", "translation": "Thank you", "audio": "/audio/danke.mp3", "example": "Danke schön!"}"#.to_string(),
|
|
node_id: 2,
|
|
}];
|
|
|
|
let nodes = vec![
|
|
Node {
|
|
id: 1,
|
|
title: "Basic Greetings".to_string(),
|
|
description: "Learn essential German greetings".to_string(),
|
|
path_id: "test_path_001".to_string(),
|
|
exercises: exercises1,
|
|
},
|
|
Node {
|
|
id: 2,
|
|
title: "Politeness".to_string(),
|
|
description: "Learn polite expressions".to_string(),
|
|
path_id: "test_path_001".to_string(),
|
|
exercises: exercises2,
|
|
},
|
|
];
|
|
|
|
Path {
|
|
id: "test_path_001".to_string(),
|
|
title: "German Basics Test".to_string(),
|
|
description: "A test path for demonstrating repository functionality".to_string(),
|
|
metadata,
|
|
nodes,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Example usage functions (not tests)
|
|
pub mod examples {
|
|
use super::*;
|
|
|
|
/// Example: How to use the repository manager in your application
|
|
pub async fn example_basic_usage() {
|
|
println!("=== Basic Repository Usage Example ===");
|
|
|
|
// This would normally use your actual database connection
|
|
// let pool = get_database_pool().await;
|
|
// let repo_manager = RepositoryManager::new(&pool);
|
|
|
|
// Example operations:
|
|
println!("1. Create repository manager");
|
|
println!("2. Save a new path");
|
|
println!("3. Retrieve and display path");
|
|
println!("4. Update path content");
|
|
println!("5. Search for paths");
|
|
println!("6. Generate statistics");
|
|
println!("7. Export to JSON");
|
|
println!("8. Cleanup/delete if needed");
|
|
}
|
|
|
|
/// Example: How to work with JSON imports
|
|
pub async fn example_json_workflow() {
|
|
println!("=== JSON Import/Export Workflow ===");
|
|
|
|
// Steps for JSON workflow:
|
|
println!("1. Validate JSON file structure");
|
|
println!("2. Import path from JSON");
|
|
println!("3. Verify import success");
|
|
println!("4. Make modifications if needed");
|
|
println!("5. Export updated version");
|
|
println!("6. Backup all paths to JSON files");
|
|
}
|
|
|
|
/// Example: How to perform bulk operations
|
|
pub async fn example_bulk_operations() {
|
|
println!("=== Bulk Operations Example ===");
|
|
|
|
// Bulk operation examples:
|
|
println!("1. Import multiple paths from directory");
|
|
println!("2. Validate all paths in database");
|
|
println!("3. Generate statistics for all paths");
|
|
println!("4. Search across all content");
|
|
println!("5. Export all paths to backup directory");
|
|
}
|
|
}
|