Search code examples
rustsea-orm

How to mock COUNT query result in sea-orm MockDatabase for testing?


I'm trying to test a function that checks if a space exists in the database using sea-orm's MockDatabase. The function performs a COUNT query, but my test is failing.

Here's my repository code:

pub async fn space_check_exist(db: &DatabaseConnection, space_id: &str) -> Result<bool, DbErr> {
    let count = Space::find()
        .filter(space::Column::SpaceId.eq(space_id))
        .count(db)
        .await?;

    Ok(count > 0)
}

And here's my test code:

#[tokio::test]
async fn test_space_check_exist_when_space_exists() {
    let db = MockDatabase::new(DatabaseBackend::Postgres)
        .append_query_results([vec![space::Model {
            id: 1,
            space_id: "test-space-id".to_owned(),
            space_status_id: 1,
            space_config: Json::Null,
            created_at: chrono::Utc::now().naive_utc(),
            updated_at: chrono::Utc::now().naive_utc(),
            deleted_at: None,
        } as space::Model] as Vec<space::Model>])
        .into_connection();

    // Run test
    let result = SpaceRepository::space_check_exist(&db, "test-space-id").await;

    // Verify result
    // assert!(result.is_ok());
    assert!(result.unwrap());
}

The result.is_ok() test fails with:

thread 'modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists' panicked at src/modules/space/repository/space_repository.rs:41:9:
assertion failed: result.is_ok()
   Compiling myapp v0.1.0 (/opt/app/myapp/app)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 11.08s
     Running unittests src/lib.rs (target/debug/deps/myapp-23ce339f247c6933)

running 1 test
test modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists ... FAILED

successes:

successes:

failures:

---- modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists stdout ----
thread 'modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists' panicked at src/modules/space/repository/space_repository.rs:41:9:
assertion failed: result.is_ok()
stack backtrace:
   0: rust_begin_unwind
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/std/src/panicking.rs:665:5
   1: core::panicking::panic_fmt
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/panicking.rs:74:14
   2: core::panicking::panic
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/panicking.rs:148:5
   3: myapp::modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists::{{closure}}
             at ./src/modules/space/repository/space_repository.rs:41:9
   4: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/future.rs:123:9
   5: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/future.rs:123:9
   6: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:729:57
   7: tokio::runtime::coop::with_budget
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/coop.rs:107:5
   8: tokio::runtime::coop::budget
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/coop.rs:73:5
   9: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:729:25
  10: tokio::runtime::scheduler::current_thread::Context::enter
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:428:19
  11: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:728:36
  12: tokio::runtime::scheduler::current_thread::CoreGuard::enter::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:807:68
  13: tokio::runtime::context::scoped::Scoped<T>::set
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/context/scoped.rs:40:9
  14: tokio::runtime::context::set_scheduler::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/context.rs:180:26
  15: std::thread::local::LocalKey<T>::try_with
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:283:12
  16: std::thread::local::LocalKey<T>::with
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:260:9
  17: tokio::runtime::context::set_scheduler
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/context.rs:180:9
  18: tokio::runtime::scheduler::current_thread::CoreGuard::enter
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:807:27
  19: tokio::runtime::scheduler::current_thread::CoreGuard::block_on
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:716:19
  20: tokio::runtime::scheduler::current_thread::CurrentThread::block_on::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:196:28
  21: tokio::runtime::context::runtime::enter_runtime
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/context/runtime.rs:65:16
  22: tokio::runtime::scheduler::current_thread::CurrentThread::block_on
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:184:9
  23: tokio::runtime::runtime::Runtime::block_on_inner
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/runtime.rs:368:47
  24: tokio::runtime::runtime::Runtime::block_on
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/runtime.rs:342:13
  25: myapp::modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists
             at ./src/modules/space/repository/space_repository.rs:41:9
  26: myapp::modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists::{{closure}}
             at ./src/modules/space/repository/space_repository.rs:24:56
  27: core::ops::function::FnOnce::call_once
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
  28: core::ops::function::FnOnce::call_once
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.


failures:
    modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 87 filtered out; finished in 0.10s

error: test failed, to rerun pass `-p myapp --lib`

The assert!(result.unwrap()); test fails with:

   Compiling myapp v0.1.0 (/opt/app/myapp/app)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 9.65s
     Running unittests src/lib.rs (target/debug/deps/myapp-23ce339f247c6933)

running 1 test
test modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists ... FAILED

successes:

successes:

failures:

---- modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists stdout ----
thread 'modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists' panicked at src/modules/space/repository/space_repository.rs:42:24:
called `Result::unwrap()` on an `Err` value: Type("A null value was encountered while decoding \"num_items\"")
stack backtrace:
   0: rust_begin_unwind
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/std/src/panicking.rs:665:5
   1: core::panicking::panic_fmt
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/panicking.rs:74:14
   2: core::result::unwrap_failed
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/result.rs:1700:5
   3: core::result::Result<T,E>::unwrap
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:1104:23
   4: myapp::modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists::{{closure}}
             at ./src/modules/space/repository/space_repository.rs:42:17
   5: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/future.rs:123:9
   6: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/future.rs:123:9
   7: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:729:57
   8: tokio::runtime::coop::with_budget
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/coop.rs:107:5
   9: tokio::runtime::coop::budget
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/coop.rs:73:5
  10: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:729:25
  11: tokio::runtime::scheduler::current_thread::Context::enter
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:428:19
  12: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:728:36
  13: tokio::runtime::scheduler::current_thread::CoreGuard::enter::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:807:68
  14: tokio::runtime::context::scoped::Scoped<T>::set
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/context/scoped.rs:40:9
  15: tokio::runtime::context::set_scheduler::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/context.rs:180:26
  16: std::thread::local::LocalKey<T>::try_with
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:283:12
  17: std::thread::local::LocalKey<T>::with
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:260:9
  18: tokio::runtime::context::set_scheduler
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/context.rs:180:9
  19: tokio::runtime::scheduler::current_thread::CoreGuard::enter
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:807:27
  20: tokio::runtime::scheduler::current_thread::CoreGuard::block_on
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:716:19
  21: tokio::runtime::scheduler::current_thread::CurrentThread::block_on::{{closure}}
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:196:28
  22: tokio::runtime::context::runtime::enter_runtime
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/context/runtime.rs:65:16
  23: tokio::runtime::scheduler::current_thread::CurrentThread::block_on
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/scheduler/current_thread/mod.rs:184:9
  24: tokio::runtime::runtime::Runtime::block_on_inner
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/runtime.rs:368:47
  25: tokio::runtime::runtime::Runtime::block_on
             at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.42.0/src/runtime/runtime.rs:342:13
  26: myapp::modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists
             at ./src/modules/space/repository/space_repository.rs:42:9
  27: myapp::modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists::{{closure}}
             at ./src/modules/space/repository/space_repository.rs:24:56
  28: core::ops::function::FnOnce::call_once
             at /usr/local/rustup/toolchains/1.83.0-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
  29: core::ops::function::FnOnce::call_once
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.


failures:
    modules::space::repository::space_repository::tests::test_space_check_exist_when_space_exists

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 87 filtered out; finished in 0.09s

error: test failed, to rerun pass `-p myapp --lib`

I've tried various approaches to mock the COUNT query result:

Using a full space::Model Using MockRow with different configurations

But I can't get it to work. How can I correctly mock a COUNT query result in sea-orm? Environment:

Rust 1.83.0 sea-orm 1.1.4


Solution

  • I could not solve the problem by the method described in the question.

    Therefore, we used a different approach to solve the problem.

    The following method solved the problem.

    To mock the results of aggregate functions (COUNT) using Sea-ORM's MockDatabase, you can use the append_query_results method and BTreeMap.

    To mock the results of aggregate functions, follow these steps:

    1. Create a BTreeMap with column names as keys and corresponding values
    2. For aggregate functions specifically, set values of appropriate types in the query results
    3. Pass this BTreeMap to the append_query_results method
    pub async fn space_check_exist(db: &DatabaseConnection, space_id: &str) -> Result<bool, DbErr> {
        #[derive(Debug, FromQueryResult)]
        struct CountResult {
            space_count: i64,
        }
    
        let count = space::Entity::find()
            .select_only()
            .column_as(space::Column::Id.count(), "space_count")
            .filter(space::Column::SpaceName.eq(space_name))
            .into_model::<CountResult>()
            .one(db)
            .await?;
    
        let space_name_count = count.map_or(0, |c| c.space_count);
    
        Ok(space_name_count > 0)
    }
    
    #[tokio::test]
    async fn test_space_check_exist_when_space_exists() {
        let db = MockDatabase::new(DatabaseBackend::Postgres)
            .append_query_results(vec![vec![BTreeMap::from([(
                "space_count".to_string(),
                Value::BigInt(Some(1)),
            )])]])
            .into_connection();
    
        // test result
        let result = SpaceRepository::space_check_exist(&db, "test_space")
            .await
            .unwrap();
    
        assert!(result);
    }
    

    The key points are as follows:

    1. Column name matching: The column name used in the mock ("space_count") must match the alias name specified with column_as in the query.
    2. Appropriate value type: Since the result of the COUNT() aggregate function is typically a BigInt type, we use Value::BigInt(Some(1)).
    3. Nested vectors: append_query_results accepts data in the format of Vec<Vec<BTreeMap<...>>>. The outer vector represents the number of query executions, and the inner vector represents the result rows for each query.

    To verify that queries are executed as expected, you can check the transaction log as follows:

    // After test execution
    let queries = db.into_transaction_log();
    assert_eq!(queries.len(), 1);
    
    // Verify the content of the executed SQL query
    let executed_query = queries.first().unwrap();
    assert_eq!(
        executed_query,
        &Transaction::from_sql_and_values(
            DatabaseBackend::Postgres,
            r#"SELECT COUNT("space"."id") AS "space_count" FROM "space" WHERE "space"."space_name" = $1 LIMIT $2"#,
            vec!["test_space".into(), 1u64.into()]
        )
    );