Files
osirose-new/tests/character-service/character_service_tests.rs
raven a8755bd3de Add comprehensive documentation and unit tests
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
2025-04-09 13:29:53 -04:00

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"));
}