diff --git a/catalyst-gateway/Earthfile b/catalyst-gateway/Earthfile index bde5adb213f..e2b4735ac9e 100644 --- a/catalyst-gateway/Earthfile +++ b/catalyst-gateway/Earthfile @@ -123,20 +123,4 @@ check-builder-src-cache: RUN diff ../src_fingerprint.txt ../src_fingerprint_uncached.txt \ || (echo "ERROR: Source fingerprints do not match. Caching Error Detected!!" && exit 1) \ - && echo "Source fingerprints match. Caching OK." - -test: - FROM +builder-src - - COPY docker-compose.yml . - - ENV EVENT_DB_URL "postgres://catalyst-event-dev:CHANGE_ME@localhost/CatalystEventDev" - - WITH DOCKER \ - --compose "./docker-compose.yml" \ - --load ./event-db+build \ - --pull alpine:3.20.3 \ - --service event-db-is-running - RUN --mount=$EARTHLY_RUST_CARGO_HOME_CACHE --mount=$EARTHLY_RUST_TARGET_CACHE \ - cargo nextest run --release --run-ignored=only signed_docs - END \ No newline at end of file + && echo "Source fingerprints match. Caching OK." \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/event/legacy/queries/event/mod.rs b/catalyst-gateway/bin/src/db/event/legacy/queries/event/mod.rs index 1acf7ddcb1c..0272089622f 100644 --- a/catalyst-gateway/bin/src/db/event/legacy/queries/event/mod.rs +++ b/catalyst-gateway/bin/src/db/event/legacy/queries/event/mod.rs @@ -52,7 +52,7 @@ impl EventDB { let ends = row .try_get::<&'static str, Option>("end_time")? .map(|val| val.and_local_timezone(Utc).unwrap()); - let is_final = ends.map_or(false, |ends| Utc::now() > ends); + let is_final = ends.is_some_and(|ends| Utc::now() > ends); events.push(EventSummary { id: EventId(row.try_get("row_id")?), name: row.try_get("name")?, @@ -79,7 +79,7 @@ impl EventDB { let ends = row .try_get::<&'static str, Option>("end_time")? .map(|val| val.and_local_timezone(Utc).unwrap()); - let is_final = ends.map_or(false, |ends| Utc::now() > ends); + let is_final = ends.is_some_and(|ends| Utc::now() > ends); let voting_power = VotingPowerSettings { alg: VotingPowerAlgorithm::ThresholdStakedADA, diff --git a/catalyst-gateway/bin/src/db/event/legacy/queries/search.rs b/catalyst-gateway/bin/src/db/event/legacy/queries/search.rs index b26c96961e1..16e9d5e7970 100644 --- a/catalyst-gateway/bin/src/db/event/legacy/queries/search.rs +++ b/catalyst-gateway/bin/src/db/event/legacy/queries/search.rs @@ -138,7 +138,7 @@ impl EventDB { let ends = row .try_get::<&'static str, Option>("end_time")? .map(|val| val.and_local_timezone(Utc).unwrap()); - let is_final = ends.map_or(false, |ends| Utc::now() > ends); + let is_final = ends.is_some_and(|ends| Utc::now() > ends); events.push(EventSummary { id: EventId(row.try_get("row_id")?), name: row.try_get("name")?, diff --git a/catalyst-gateway/bin/src/db/index/mod.rs b/catalyst-gateway/bin/src/db/index/mod.rs index f4157be8550..d7ab2bb602b 100644 --- a/catalyst-gateway/bin/src/db/index/mod.rs +++ b/catalyst-gateway/bin/src/db/index/mod.rs @@ -4,3 +4,5 @@ pub(crate) mod block; pub(crate) mod queries; pub(crate) mod schema; pub(crate) mod session; +#[cfg(test)] +mod tests; diff --git a/catalyst-gateway/bin/src/db/index/queries/sync_status/update.rs b/catalyst-gateway/bin/src/db/index/queries/sync_status/update.rs index 9780318348a..938962f9e2e 100644 --- a/catalyst-gateway/bin/src/db/index/queries/sync_status/update.rs +++ b/catalyst-gateway/bin/src/db/index/queries/sync_status/update.rs @@ -20,7 +20,7 @@ use crate::{ const INSERT_SYNC_STATUS_QUERY: &str = include_str!("../cql/insert_sync_status.cql"); /// Sync Status Row Record Module -pub(super) mod row { +pub(crate) mod row { use scylla::{frame::value::CqlTimestamp, DeserializeRow, SerializeRow}; /// Sync Status Record Row (used for both Insert and Query response) diff --git a/catalyst-gateway/bin/src/db/index/tests/mod.rs b/catalyst-gateway/bin/src/db/index/tests/mod.rs new file mode 100644 index 00000000000..4775780bc2c --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/tests/mod.rs @@ -0,0 +1,42 @@ +//! Integration tests of the `IndexDB` queries +//! This module contains utility functions used with different testing modules. + +use std::sync::Arc; + +use tokio::sync::OnceCell; + +use super::session::CassandraSession; + +mod scylla_queries; +mod scylla_session; + +static SHARED_SESSION: OnceCell> = OnceCell::const_new(); + +async fn setup_test_database() -> Result<(), String> { + CassandraSession::init(); + + CassandraSession::wait_is_ready(core::time::Duration::from_secs(1)).await; + + if !CassandraSession::is_ready() { + return Err(String::from("Cassandra session is not ready")); + } + + Ok(()) +} + +fn get_session() -> Result<(Arc, Arc), String> { + let Some(persistent) = CassandraSession::get(true) else { + return Err(String::from("Failed to acquire db session")); + }; + let Some(volatile) = CassandraSession::get(false) else { + return Err(String::from("Failed to acquire db session")); + }; + + Ok((persistent, volatile)) +} + +async fn get_shared_session() -> Result<(Arc, Arc), String> { + SHARED_SESSION.get_or_init(setup_test_database).await; + + get_session() +} diff --git a/catalyst-gateway/bin/src/db/index/tests/scylla_queries.rs b/catalyst-gateway/bin/src/db/index/tests/scylla_queries.rs new file mode 100644 index 00000000000..4c83e087e85 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/tests/scylla_queries.rs @@ -0,0 +1,223 @@ +//! Integration tests of the `IndexDB` queries testing on its session. +//! This is mainly to test whether the defined queries work with the database or not. + +use futures::StreamExt; + +use super::*; +use crate::db::index::queries::{ + rbac::{get_chain_root::*, get_registrations::*, get_role0_chain_root::*}, + registrations::{ + get_from_stake_addr::*, get_from_stake_hash::*, get_from_vote_key::*, get_invalid::*, + }, + staked_ada::{ + get_assets_by_stake_address::*, get_txi_by_txn_hash::*, get_txo_by_stake_address::*, + update_txo_spent::*, + }, + sync_status::update::*, +}; + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_assets_by_stake_addr() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = GetAssetsByStakeAddressQuery::execute( + &session, + GetAssetsByStakeAddressParams::new(vec![], num_bigint::BigInt::from(i64::MAX)), + ) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_chain_root() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = GetChainRootQuery::execute(&session, GetChainRootQueryParams { + stake_address: vec![], + }) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_invalid_registration_w_stake_addr() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = GetInvalidRegistrationQuery::execute( + &session, + GetInvalidRegistrationParams::new(vec![], num_bigint::BigInt::from(i64::MAX)), + ) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_registrations_by_chain_root() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = GetRegistrationsByChainRootQuery::execute( + &session, + GetRegistrationsByChainRootQueryParams { chain_root: vec![] }, + ) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_registrations_w_stake_addr() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = GetRegistrationQuery::execute(&session, GetRegistrationParams { + stake_address: vec![], + }) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_role0_key_chain_root() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = GetRole0ChainRootQuery::execute(&session, GetRole0ChainRootQueryParams { + role0_key: vec![], + }) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_stake_addr_w_stake_key_hash() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = + GetStakeAddrQuery::execute(&session, GetStakeAddrParams { stake_hash: vec![] }) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_stake_addr_w_vote_key() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = + GetStakeAddrFromVoteKeyQuery::execute(&session, GetStakeAddrFromVoteKeyParams { + vote_key: vec![], + }) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +// Note: `get_sync_status` query is not available. +// #[ignore = "An integration test which requires a running Scylla node instance, disabled +// from `testunit` CI run"] #[tokio::test] +// async fn test_get_sync_status() { +// let (session, _) = +// get_shared_session().await.unwrap(); + +// Ok(()) +// } + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_txi_by_txn_hashes() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = + GetTxiByTxnHashesQuery::execute(&session, GetTxiByTxnHashesQueryParams::new(vec![])) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_get_txo_by_stake_address() { + let (session, _) = get_shared_session().await.unwrap(); + + let mut row_stream = GetTxoByStakeAddressQuery::execute( + &session, + GetTxoByStakeAddressQueryParams::new(vec![], num_bigint::BigInt::from(i64::MAX)), + ) + .await + .unwrap(); + + while let Some(row_res) = row_stream.next().await { + let row = row_res.unwrap(); + drop(row); + } +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_insert_sync_status() { + let (session, _) = get_shared_session().await.unwrap(); + + SyncStatusInsertQuery::execute( + &session, + row::SyncStatusQueryParams::new(u64::MAX, u64::MAX), + ) + .await + .unwrap(); +} + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_update_txo_spent() { + let (session, _) = get_shared_session().await.unwrap(); + + UpdateTxoSpentQuery::execute(&session, vec![]) + .await + .unwrap(); +} diff --git a/catalyst-gateway/bin/src/db/index/tests/scylla_session.rs b/catalyst-gateway/bin/src/db/index/tests/scylla_session.rs new file mode 100644 index 00000000000..3eeec951086 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/tests/scylla_session.rs @@ -0,0 +1,9 @@ +//! Integration tests of the `IndexDB` queries testing on its session + +use super::*; + +#[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] +#[tokio::test] +async fn test_session() { + get_shared_session().await.unwrap(); +} diff --git a/catalyst-gateway/blueprint.cue b/catalyst-gateway/blueprint.cue index 9e36433f3f4..c81c19d56c6 100644 --- a/catalyst-gateway/blueprint.cue +++ b/catalyst-gateway/blueprint.cue @@ -12,9 +12,4 @@ project: { } } } - ci: { - targets: { - test: privileged: true - } - } } diff --git a/catalyst-gateway/docker-compose.yml b/catalyst-gateway/docker-compose.yml index 00b2a348764..0dfb7f3914c 100644 --- a/catalyst-gateway/docker-compose.yml +++ b/catalyst-gateway/docker-compose.yml @@ -21,14 +21,6 @@ services: timeout: 5s retries: 10 -# it is a helper service to wait until the event-db will be ready -# mainly its a trick for Earthly how to wait until service will be fully functional - event-db-is-running: - image: alpine:3.20.3 - depends_on: - event-db: - condition: service_healthy - cat-gateway: image: cat-gateway:latest environment: diff --git a/catalyst-gateway/tests/Earthfile b/catalyst-gateway/tests/Earthfile index 85383a20403..421c6934771 100644 --- a/catalyst-gateway/tests/Earthfile +++ b/catalyst-gateway/tests/Earthfile @@ -1,5 +1,7 @@ VERSION 0.8 + IMPORT github.com/input-output-hk/catalyst-ci/earthly/spectral:v3.2.24 AS spectral-ci +IMPORT .. AS gateway # cspell: words oapi # test-lint-openapi - OpenAPI linting from an artifact @@ -12,3 +14,49 @@ test-lint-openapi: COPY --dir ./openapi-v3.0-lints/* . # Scan the doc directory where type of file is JSON. DO spectral-ci+LINT --dir=./doc + +test-postgres: + FROM gateway+builder-src + + COPY ./docker/docker-compose.postgres.yml docker-compose.yml + + ENV EVENT_DB_URL "postgres://catalyst-event-dev:CHANGE_ME@localhost/CatalystEventDev" + + WITH DOCKER \ + --compose "./docker-compose.yml" \ + --load ../event-db+build \ + --pull alpine:3.20.3 \ + --service event-db-is-running + RUN --mount=$EARTHLY_RUST_CARGO_HOME_CACHE --mount=$EARTHLY_RUST_TARGET_CACHE \ + cargo nextest run --release --run-ignored=only signed_docs + END + +# test-scylla - Runs the integration test for scylla. +test-scylla: + FROM gateway+builder-src + + DO +INSTALL_SCYLLA --VERSION=6.2 + + # limit the nextest duration to prevent freezing in case of failure. + RUN sed -i '/\[profile.default\]/a\slow-timeout = { period = "30s", terminate-after = 2 }' .config/nextest.toml + + RUN --mount=$EARTHLY_RUST_CARGO_HOME_CACHE --mount=$EARTHLY_RUST_TARGET_CACHE \ + scylla --options-file /etc/scylla/scylla.yaml --smp=2 --memory=4G --overprovisioned --developer-mode=1 & \ + cargo nextest run --release --run-ignored=only scylla_session scylla_queries + +# INSTALL_SCYLLA - Installs scylla for bookworm-slim/debian +INSTALL_SCYLLA: + FUNCTION + + ARG --required VERSION + + RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + wget gnupg ca-certificates && \ + mkdir -p /etc/apt/keyrings && \ + gpg --homedir /tmp --no-default-keyring --keyring /etc/apt/keyrings/scylladb.gpg \ + --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys a43e06657bac99e3 && \ + wget -O /etc/apt/sources.list.d/scylla.list http://downloads.scylladb.com/deb/debian/scylla-$VERSION.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + scylla \ No newline at end of file diff --git a/catalyst-gateway/tests/blueprint.cue b/catalyst-gateway/tests/blueprint.cue index 17b84893858..19f46d145c9 100644 --- a/catalyst-gateway/tests/blueprint.cue +++ b/catalyst-gateway/tests/blueprint.cue @@ -1,2 +1,9 @@ version: "1.0.0" -project: name: "catalyst-gateway-tests" +project: { + name: "catalyst-gateway-tests" + ci: { + targets: { + "test-postgres": privileged: true + } + } +} diff --git a/catalyst-gateway/tests/docker/docker-compose.postgres.yml b/catalyst-gateway/tests/docker/docker-compose.postgres.yml new file mode 100644 index 00000000000..4c9801de859 --- /dev/null +++ b/catalyst-gateway/tests/docker/docker-compose.postgres.yml @@ -0,0 +1,30 @@ +services: + event-db: + image: event-db:latest + environment: + - DB_HOST=localhost + - DB_PORT=5432 + - DB_NAME=CatalystEventDev + - DB_DESCRIPTION="Catalyst Event DB" + - DB_SUPERUSER=postgres + - DB_SUPERUSER_PASSWORD=postgres + - DB_USER=catalyst-event-dev + - DB_USER_PASSWORD=CHANGE_ME + + - INIT_AND_DROP_DB=true + - WITH_MIGRATIONS=true + ports: + - 5432:5432 + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${DB_SUPERUSER} -d $${DB_SUPERUSER_PASSWORD}"] + interval: 10s + timeout: 5s + retries: 10 + +# it is a helper service to wait until the event-db will be ready +# mainly its a trick for Earthly how to wait until service will be fully functional + event-db-is-running: + image: alpine:3.20.3 + depends_on: + event-db: + condition: service_healthy