puerto 0.13.0

Puerto — Rust full-stack DDD framework CLI. Scaffolds Clean Architecture projects with auto-wired DI, SQLx support, and zero manual wiring. Formerly puerto-framework.
{% import "application/macros.tera" as macros %}{% set required = fields | filter(attribute="required_string", value=true) %}{% set string_vos = fields | filter(attribute="string_vo", value=true) %}use std::sync::Arc;

use async_trait::async_trait;

use crate::domain::{{ snake }}::{
    errors::{{ pascal }}Error,
    model::{{ pascal }},
    repository::{{ pascal }}RepositoryTrait,
    use_cases::list_{{ plural }}::{List{{ plural_pascal }}Params, List{{ plural_pascal }}UseCaseTrait},
};
use crate::domain::shared::pagination::Page;

pub struct List{{ plural_pascal }}UseCaseImpl {
    pub repository: Arc<dyn {{ pascal }}RepositoryTrait>,
}

#[async_trait]
impl List{{ plural_pascal }}UseCaseTrait for List{{ plural_pascal }}UseCaseImpl {
    async fn execute(&self, params: List{{ plural_pascal }}Params) -> Result<Page<{{ pascal }}>, {{ pascal }}Error> {
        tracing::info!(limit = params.page.limit(), offset = params.page.offset(), "list_{{ plural }}: start");
        self.repository
            .find_all(params.page)
            .await
            .inspect_err(|e| tracing::error!(error = %e, "list_{{ plural }}: repository failed"))
            .map_err({{ pascal }}Error::from)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::domain::shared::pagination::PageRequest;
    use crate::domain::{{ snake }}::{
        model::{{ "{" }}{{ pascal }}, {{ pascal }}Props},
        repository::mocks::Mock{{ pascal }}Repository,
    };
{% for import in test_imports %}    use {{ import }};
{% endfor %}

    #[tokio::test]
    async fn should_return_the_page_of_{{ plural }}() {
        let entities = vec![
            {{ pascal }}::new({{ pascal }}Props {
{{ macros::props(fields=fields, word="first") }}        }).unwrap(),
            {{ pascal }}::new({{ pascal }}Props {
{{ macros::props(fields=fields, word="second") }}        }).unwrap(),
        ];
        let mut mock_repo = Mock{{ pascal }}Repository::new();
        mock_repo
            .expect_find_all()
            .returning(move |page| Ok(Page::new(entities.clone(), 2, page)));
        let use_case = List{{ plural_pascal }}UseCaseImpl {
            repository: Arc::new(mock_repo),
        };

        let result = use_case
            .execute(List{{ plural_pascal }}Params { page: PageRequest::default() })
            .await;

        let page = result.unwrap();
        assert_eq!(page.items.len(), 2);
        assert_eq!(page.total, 2);
    }

    #[tokio::test]
    async fn should_return_an_empty_page_when_no_{{ plural }}_exist() {
        let mut mock_repo = Mock{{ pascal }}Repository::new();
        mock_repo
            .expect_find_all()
            .returning(|page| Ok(Page::new(vec![], 0, page)));
        let use_case = List{{ plural_pascal }}UseCaseImpl {
            repository: Arc::new(mock_repo),
        };

        let result = use_case
            .execute(List{{ plural_pascal }}Params { page: PageRequest::default() })
            .await;

        let page = result.unwrap();
        assert!(page.items.is_empty());
        assert_eq!(page.total, 0);
    }

    #[tokio::test]
    async fn should_pass_the_requested_page_to_the_repository() {
        let mut mock_repo = Mock{{ pascal }}Repository::new();
        mock_repo
            .expect_find_all()
            .withf(|page| page.limit() == 5 && page.offset() == 10)
            .returning(|page| Ok(Page::new(vec![], 0, page)));
        let use_case = List{{ plural_pascal }}UseCaseImpl {
            repository: Arc::new(mock_repo),
        };

        let result = use_case
            .execute(List{{ plural_pascal }}Params { page: PageRequest::new(Some(5), Some(10)) })
            .await;

        assert_eq!(result.unwrap().offset, 10);
    }
}