#![allow(clippy::expect_used, clippy::unwrap_used, clippy::missing_panics_doc)]
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use serde_json::Value;
use uuid::Uuid;
use crate::api::TypesRegistryClient;
use crate::error::TypesRegistryError;
use crate::models::{
GtsInstance, GtsTypeId, GtsTypeSchema, InstanceQuery, RegisterResult, TypeSchemaQuery,
is_type_schema_id,
};
use gts::GtsInstanceId;
#[derive(Default)]
pub struct MockTypesRegistryClient {
type_schemas: Vec<GtsTypeSchema>,
instances: Vec<GtsInstance>,
list_error: Option<TypesRegistryError>,
received_type_schema_queries: Mutex<Vec<TypeSchemaQuery>>,
received_instance_queries: Mutex<Vec<InstanceQuery>>,
}
impl MockTypesRegistryClient {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_type_schemas(mut self, items: impl IntoIterator<Item = GtsTypeSchema>) -> Self {
self.type_schemas.extend(items);
self
}
#[must_use]
pub fn with_instances(mut self, items: impl IntoIterator<Item = GtsInstance>) -> Self {
self.instances.extend(items);
self
}
#[must_use]
pub fn with_list_error(mut self, err: TypesRegistryError) -> Self {
self.list_error = Some(err);
self
}
#[must_use]
pub fn list_type_schema_calls(&self) -> usize {
self.received_type_schema_queries
.lock()
.expect("MockTypesRegistryClient: type-schema query log poisoned")
.len()
}
#[must_use]
pub fn list_instance_calls(&self) -> usize {
self.received_instance_queries
.lock()
.expect("MockTypesRegistryClient: instance query log poisoned")
.len()
}
#[must_use]
pub fn received_type_schema_queries(&self) -> Vec<TypeSchemaQuery> {
self.received_type_schema_queries
.lock()
.expect("MockTypesRegistryClient: type-schema query log poisoned")
.clone()
}
#[must_use]
pub fn received_instance_queries(&self) -> Vec<InstanceQuery> {
self.received_instance_queries
.lock()
.expect("MockTypesRegistryClient: instance query log poisoned")
.clone()
}
}
#[async_trait]
impl TypesRegistryClient for MockTypesRegistryClient {
async fn register(
&self,
entities: Vec<Value>,
) -> Result<Vec<RegisterResult>, TypesRegistryError> {
assert!(
entities.is_empty(),
"MockTypesRegistryClient::register is not implemented; \
pre-populate via `MockTypesRegistryClient::new().with_type_schemas(...).with_instances(...)`",
);
Ok(vec![])
}
async fn register_type_schemas(
&self,
type_schemas: Vec<Value>,
) -> Result<Vec<RegisterResult>, TypesRegistryError> {
assert!(
type_schemas.is_empty(),
"MockTypesRegistryClient::register_type_schemas is not implemented; \
pre-populate via `MockTypesRegistryClient::new().with_type_schemas(...)`",
);
Ok(vec![])
}
async fn get_type_schema(&self, type_id: &str) -> Result<GtsTypeSchema, TypesRegistryError> {
if !is_type_schema_id(type_id) {
return Err(TypesRegistryError::invalid_gts_type_id(format!(
"{type_id} does not end with `~`",
)));
}
self.type_schemas
.iter()
.find(|s| s.type_id == type_id)
.cloned()
.ok_or_else(|| TypesRegistryError::gts_type_schema_not_found(type_id))
}
async fn get_type_schema_by_uuid(
&self,
type_uuid: Uuid,
) -> Result<GtsTypeSchema, TypesRegistryError> {
self.type_schemas
.iter()
.find(|s| s.type_uuid == type_uuid)
.cloned()
.ok_or_else(|| TypesRegistryError::gts_type_schema_not_found(type_uuid.to_string()))
}
async fn get_type_schemas(
&self,
type_ids: Vec<String>,
) -> HashMap<String, Result<GtsTypeSchema, TypesRegistryError>> {
let mut out = HashMap::with_capacity(type_ids.len());
for id in type_ids {
let res = self.get_type_schema(&id).await;
out.insert(id, res);
}
out
}
async fn get_type_schemas_by_uuid(
&self,
type_uuids: Vec<Uuid>,
) -> HashMap<Uuid, Result<GtsTypeSchema, TypesRegistryError>> {
let mut out = HashMap::with_capacity(type_uuids.len());
for uuid in type_uuids {
let res = self.get_type_schema_by_uuid(uuid).await;
out.insert(uuid, res);
}
out
}
async fn list_type_schemas(
&self,
query: TypeSchemaQuery,
) -> Result<Vec<GtsTypeSchema>, TypesRegistryError> {
self.received_type_schema_queries
.lock()
.expect("MockTypesRegistryClient: type-schema query log poisoned")
.push(query);
if let Some(ref err) = self.list_error {
return Err(err.clone());
}
Ok(self.type_schemas.clone())
}
async fn register_instances(
&self,
instances: Vec<Value>,
) -> Result<Vec<RegisterResult>, TypesRegistryError> {
assert!(
instances.is_empty(),
"MockTypesRegistryClient::register_instances is not implemented; \
pre-populate via `MockTypesRegistryClient::new().with_instances(...)`",
);
Ok(vec![])
}
async fn get_instance(&self, id: &str) -> Result<GtsInstance, TypesRegistryError> {
if is_type_schema_id(id) {
return Err(TypesRegistryError::invalid_gts_instance_id(format!(
"{id} ends with `~` (looks like a type-schema id)",
)));
}
self.instances
.iter()
.find(|e| e.id == id)
.cloned()
.ok_or_else(|| TypesRegistryError::gts_instance_not_found(id))
}
async fn get_instance_by_uuid(&self, uuid: Uuid) -> Result<GtsInstance, TypesRegistryError> {
self.instances
.iter()
.find(|e| e.uuid == uuid)
.cloned()
.ok_or_else(|| TypesRegistryError::gts_instance_not_found(uuid.to_string()))
}
async fn get_instances(
&self,
ids: Vec<String>,
) -> HashMap<String, Result<GtsInstance, TypesRegistryError>> {
let mut out = HashMap::with_capacity(ids.len());
for id in ids {
let res = self.get_instance(&id).await;
out.insert(id, res);
}
out
}
async fn get_instances_by_uuid(
&self,
uuids: Vec<Uuid>,
) -> HashMap<Uuid, Result<GtsInstance, TypesRegistryError>> {
let mut out = HashMap::with_capacity(uuids.len());
for uuid in uuids {
let res = self.get_instance_by_uuid(uuid).await;
out.insert(uuid, res);
}
out
}
async fn list_instances(
&self,
query: InstanceQuery,
) -> Result<Vec<GtsInstance>, TypesRegistryError> {
self.received_instance_queries
.lock()
.expect("MockTypesRegistryClient: instance query log poisoned")
.push(query);
if let Some(ref err) = self.list_error {
return Err(err.clone());
}
Ok(self.instances.clone())
}
}
#[must_use]
pub fn make_test_type_schema(type_id: &str) -> GtsTypeSchema {
let parent = GtsTypeSchema::derive_parent_type_id(type_id)
.map(|p| Arc::new(make_test_type_schema(p.as_ref())));
GtsTypeSchema::try_new(GtsTypeId::new(type_id), serde_json::json!({}), None, parent)
.expect("synthetic type-schema is valid")
}
#[must_use]
pub fn make_test_instance(gts_id: &str, content: Value) -> GtsInstance {
let type_id = GtsInstance::derive_type_id(gts_id)
.unwrap_or_else(|| panic!("synthetic gts_id {gts_id} has no chain prefix"));
let type_schema = Arc::new(make_test_type_schema(type_id.as_ref()));
let segment = >s_id[type_id.as_ref().len()..];
let id = GtsInstanceId::new(type_id.as_ref(), segment);
GtsInstance::try_new(id, content, None, type_schema).expect("synthetic instance is valid")
}