Documentation: - Add detailed README files for all services (auth, character, database, launcher, packet, utils, world) - Create API documentation for the database service with detailed endpoint specifications - Document database schema and relationships - Add service architecture overviews and configuration instructions Unit Tests: - Implement comprehensive test suite for database repositories (user, character, session) - Add gRPC service tests for database interactions - Create tests for packet service components (bufferpool, connection, packets) - Add utility service tests (health check, logging, load balancer, redis cache, service discovery) - Implement auth service user tests - Add character service tests Code Structure: - Reorganize test files into a more consistent structure - Create a dedicated tests crate for integration testing - Add test helpers and mock implementations for easier testing
252 lines
9.3 KiB
Rust
252 lines
9.3 KiB
Rust
use character_service::character_service::character::character_service_server::CharacterService;
|
|
use character_service::character_service::character::{
|
|
CreateCharacterRequest, DeleteCharacterRequest, GetCharacterListRequest, GetCharacterRequest,
|
|
};
|
|
use character_service::character_service::MyCharacterService;
|
|
use mockall::predicate::*;
|
|
use mockall::predicate::eq;
|
|
use mockall::mock;
|
|
|
|
// Wrapper for the mock to implement the trait
|
|
struct MockWrapper(MockCharacterDbClientTrait);
|
|
|
|
impl character_service::character_db_client::CharacterDbClient for MockWrapper {
|
|
async fn get_character_list(&mut self, user_id: &str) -> Result<character_service::database::CharacterListResponse, Box<dyn std::error::Error>> {
|
|
self.0.get_character_list(user_id).await
|
|
}
|
|
|
|
async fn create_character(&mut self, user_id: &str, name: &str, race: i32, face: i32, hair: i32, stone: i32) -> Result<character_service::database::CreateCharacterResponse, Box<dyn std::error::Error>> {
|
|
self.0.create_character(user_id, name, race, face, hair, stone).await
|
|
}
|
|
|
|
async fn delete_character(&mut self, user_id: &str, char_id: &str, delete_type: i32) -> Result<character_service::database::DeleteCharacterResponse, Box<dyn std::error::Error>> {
|
|
self.0.delete_character(user_id, char_id, delete_type).await
|
|
}
|
|
|
|
async fn get_character(&mut self, user_id: &str, char_id: &str) -> Result<character_service::database::Character, Box<dyn std::error::Error>> {
|
|
self.0.get_character(user_id, char_id).await
|
|
}
|
|
}
|
|
use std::sync::Arc;
|
|
use tonic::{Request, Status};
|
|
|
|
// Define a trait for the CharacterDbClient to make it mockable
|
|
#[mockall::automock]
|
|
trait CharacterDbClientTrait {
|
|
async fn get_character_list(&mut self, user_id: &str) -> Result<character_service::database::CharacterListResponse, Box<dyn std::error::Error>>;
|
|
async fn create_character(&mut self, user_id: &str, name: &str, race: i32, face: i32, hair: i32, stone: i32) -> Result<character_service::database::CreateCharacterResponse, Box<dyn std::error::Error>>;
|
|
async fn delete_character(&mut self, user_id: &str, char_id: &str, delete_type: i32) -> Result<character_service::database::DeleteCharacterResponse, Box<dyn std::error::Error>>;
|
|
async fn get_character(&mut self, user_id: &str, char_id: &str) -> Result<character_service::database::Character, Box<dyn std::error::Error>>;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_character_list() {
|
|
// Create a mock CharacterDbClient
|
|
let mut mock_client = MockCharacterDbClientTrait::new();
|
|
|
|
// Set up expectations
|
|
mock_client
|
|
.expect_get_character_list()
|
|
.with(eq("test_user"))
|
|
.times(1)
|
|
.returning(|_| {
|
|
Ok(character_service::database::CharacterListResponse {
|
|
characters: vec![
|
|
character_service::database::Character {
|
|
id: 1,
|
|
user_id: "test_user".to_string(),
|
|
name: "Character1".to_string(),
|
|
money: 1000,
|
|
inventory: "{\"items\":[]}".to_string(),
|
|
stats: "{\"level\":1}".to_string(),
|
|
skills: "{\"skills\":[]}".to_string(),
|
|
looks: "{\"race\":0}".to_string(),
|
|
position: "{\"x\":0,\"y\":0,\"z\":0}".to_string(),
|
|
deleted_at: "".to_string(),
|
|
},
|
|
character_service::database::Character {
|
|
id: 2,
|
|
user_id: "test_user".to_string(),
|
|
name: "Character2".to_string(),
|
|
money: 2000,
|
|
inventory: "{\"items\":[]}".to_string(),
|
|
stats: "{\"level\":5}".to_string(),
|
|
skills: "{\"skills\":[]}".to_string(),
|
|
looks: "{\"race\":1}".to_string(),
|
|
position: "{\"x\":10,\"y\":10,\"z\":0}".to_string(),
|
|
deleted_at: "".to_string(),
|
|
},
|
|
],
|
|
})
|
|
});
|
|
|
|
// Create a wrapper struct that implements CharacterDbClient
|
|
struct MockWrapper(MockCharacterDbClientTrait);
|
|
impl std::ops::Deref for MockWrapper {
|
|
type Target = MockCharacterDbClientTrait;
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
impl std::ops::DerefMut for MockWrapper {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
impl Clone for MockWrapper {
|
|
fn clone(&self) -> Self {
|
|
panic!("Mock should not be cloned")
|
|
}
|
|
}
|
|
|
|
// Create the service with the mock client
|
|
let service = MyCharacterService {
|
|
character_db_client: Arc::new(MockWrapper(mock_client)),
|
|
};
|
|
|
|
// Create a request
|
|
let request = Request::new(GetCharacterListRequest {
|
|
user_id: "test_user".to_string(),
|
|
});
|
|
|
|
// Call the service method
|
|
let response = service.get_character_list(request).await.unwrap();
|
|
let response = response.into_inner();
|
|
|
|
// Verify the response
|
|
assert_eq!(response.characters.len(), 2);
|
|
assert_eq!(response.characters[0].name, "Character1");
|
|
assert_eq!(response.characters[1].name, "Character2");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_create_character() {
|
|
// Create a mock CharacterDbClient
|
|
let mut mock_client = MockCharacterDbClientTrait::new();
|
|
|
|
// Set up expectations
|
|
mock_client
|
|
.expect_create_character()
|
|
.with(eq("test_user"), eq("NewCharacter"), eq(0), eq(1), eq(2), eq(3))
|
|
.times(1)
|
|
.returning(|_, _, _, _, _, _| {
|
|
Ok(character_service::database::CreateCharacterResponse {
|
|
result: 0,
|
|
character_id: 3,
|
|
})
|
|
});
|
|
|
|
// Create the service with the mock client
|
|
let service = MyCharacterService {
|
|
character_db_client: Arc::new(MockWrapper(mock_client)),
|
|
};
|
|
|
|
// Create a request
|
|
let request = Request::new(CreateCharacterRequest {
|
|
user_id: "test_user".to_string(),
|
|
name: "NewCharacter".to_string(),
|
|
race: 0,
|
|
face: 1,
|
|
hair: 2,
|
|
stone: 3,
|
|
});
|
|
|
|
// Call the service method
|
|
let response = service.create_character(request).await.unwrap();
|
|
let response = response.into_inner();
|
|
|
|
// Verify the response
|
|
assert_eq!(response.result, 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_delete_character() {
|
|
// Create a mock CharacterDbClient
|
|
let mut mock_client = MockCharacterDbClientTrait::new();
|
|
|
|
// Set up expectations
|
|
mock_client
|
|
.expect_delete_character()
|
|
.with(eq("test_user"), eq("3"), eq(1))
|
|
.times(1)
|
|
.returning(|_, _, _| {
|
|
Ok(character_service::database::DeleteCharacterResponse {
|
|
remaining_time: 86400, // 24 hours in seconds
|
|
name: "DeletedCharacter".to_string(),
|
|
})
|
|
});
|
|
|
|
// Create the service with the mock client
|
|
let service = MyCharacterService {
|
|
character_db_client: Arc::new(MockWrapper(mock_client)),
|
|
};
|
|
|
|
// Create a request
|
|
let request = Request::new(DeleteCharacterRequest {
|
|
user_id: "test_user".to_string(),
|
|
char_id: "3".to_string(),
|
|
delete_type: 1,
|
|
});
|
|
|
|
// Call the service method
|
|
let response = service.delete_character(request).await.unwrap();
|
|
let response = response.into_inner();
|
|
|
|
// Verify the response
|
|
assert_eq!(response.remaining_time, 86400);
|
|
assert_eq!(response.name, "DeletedCharacter");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_character() {
|
|
// Create a mock CharacterDbClient
|
|
let mut mock_client = MockCharacterDbClientTrait::new();
|
|
|
|
// Set up expectations
|
|
mock_client
|
|
.expect_get_character()
|
|
.with(eq("test_user"), eq("1"))
|
|
.times(1)
|
|
.returning(|_, _| {
|
|
Ok(character_service::database::Character {
|
|
id: 1,
|
|
user_id: "test_user".to_string(),
|
|
name: "Character1".to_string(),
|
|
money: 1000,
|
|
inventory: "{\"items\":[{\"id\":1,\"count\":10}]}".to_string(),
|
|
stats: "{\"level\":10,\"hp\":100,\"mp\":50}".to_string(),
|
|
skills: "{\"skills\":[{\"id\":1,\"level\":5}]}".to_string(),
|
|
looks: "{\"race\":0,\"face\":1,\"hair\":2}".to_string(),
|
|
position: "{\"x\":100,\"y\":200,\"z\":0,\"map\":1}".to_string(),
|
|
deleted_at: "".to_string(),
|
|
})
|
|
});
|
|
|
|
// Create the service with the mock client
|
|
let service = MyCharacterService {
|
|
character_db_client: Arc::new(MockWrapper(mock_client)),
|
|
};
|
|
|
|
// Create a request
|
|
let request = Request::new(GetCharacterRequest {
|
|
user_id: "test_user".to_string(),
|
|
char_id: "1".to_string(),
|
|
});
|
|
|
|
// Call the service method
|
|
let response = service.get_character(request).await.unwrap();
|
|
let response = response.into_inner();
|
|
|
|
// Verify the response
|
|
let character = response.character.unwrap();
|
|
assert_eq!(character.name, "Character1");
|
|
assert_eq!(character.money, 1000);
|
|
|
|
// Verify JSON fields were parsed correctly
|
|
assert!(character.inventory.contains("items"));
|
|
assert!(character.stats.contains("level"));
|
|
assert!(character.skills.contains("skills"));
|
|
assert!(character.looks.contains("race"));
|
|
assert!(character.position.contains("map"));
|
|
}
|