From 3db8f1726906768e34ed9e1fa340901545327192 Mon Sep 17 00:00:00 2001 From: Oleksandr Prokhorenko Date: Tue, 16 Apr 2024 09:56:07 +0200 Subject: [PATCH] feat: merge m3 (#426) * docs: Add CatalystDataGatewayRepository docs (#388) * test: Fix tests after cat-gateway update. * docs: Add `CatalystDataGatewayRepository` usage examples. * docs: Move docs from README to code comment. * test: Update `CatalystDataGatewayRepository` tests. * test: Use Fake instead of Mock. * chore: Fix Markdown errors. * chore: Explicit use of `HttpStatus` codes. * adds one more worker and compression for catgateway logs (#400) * feat: Collect flutter code coverage (#404) * test: Fix tests after cat-gateway update. * docs: Add `CatalystDataGatewayRepository` usage examples. * docs: Move docs from README to code comment. * test: Update `CatalystDataGatewayRepository` tests. * test: Use Fake instead of Mock. * chore: Fix Markdown errors. * chore: Explicit use of `HttpStatus` codes. * chore(deps-dev): bump vite in /utilities/wallet-tester (#397) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.1.6 to 5.1.7. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.1.7/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.1.7/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Apisit Ritreungroj <38898766+apskhem@users.noreply.github.com> * feat: Get slot number by provided date-time endpoint (#387) * fix CardanoStakeAddress error handling * refactor, add sync_state_get endpoint * refactor types * refactor * add block_hash validation * wip * wip * wip * wip * add check_network fn * fix * fix schematisis test * try * wip * try * try * try * try * wip * try * try * fix * update Network * add test_utxo test * try * fix * try * fix * wip * fix * fix docket-compose.yml file * try * try * fix * try * try * try * try * wip * fix * wip * try * try * wip * try * try * revert * wip * wip * wip * fix * fix * fix * remove mithril_snapshot loader * wip * wip * wip * wip * wip * wip * wip * add stake addr bech32 encode utils function * wip * wip * update indexing of the utxo data * fix spelling * wip * wip * finish utxo test * fix deny * fix check * fix * fix * update earthly builder versions * wip * ignore test_utxo.py in CI * dont ignore tests * add date_time_to_slot_number_get endpoint * add sql queries * fix * update slot info, fix follower indexing block time issue * add previous slot info field * fix * refactor * fix sync_state_get * wip * fix check * try * fix * finish slot_info test, fix queries * fix * cleanup * wip * wip * wip * feat: RBAC Documentation Drafting (#332) * chore: wip * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * docs(docs): Use latest docs builders and fix concepts page * docs(cips): Start drafting the CIPS for milestone 2 * docs(cips): More text for RBAC metadata draft * docs(cips): WIP updates to draft cip for role registration * docs(cips): define draft specification for a ULID cbor tag * docs(cips): Further WIP edits to RBAC * docs(cips): fix ulid spec binary encoding reference * docs(cips): Add a tag to the epoch time. * docs(cips): Add CBOR tag cip for ED25519-BIP32 Keys, Derivation paths and Signatures * docs(cips): Properly define the field tags to use where known, and clean up Stake Address specification. * docs(cips): Fix nonce so its reliable without needing blockchain data * docs(cips): updates * docs(docs): Add CDDL definition for POC x509 envelope metadata * fix(vscode): update vscode extension recommendations * docs(cips): rbac x509 envelope fix * docs(cips): wip updates to high level docs * docs(cips): Add overview of cardano transaction processign and data * docs(cips): update cardano block to be complete for clarity * docs(cips): fix layout engine * docs(cips): wip cddl for envelope metadata * docs(cips): Add cddl specs and diagrams for x509 rbac registration work * docs(cips): Add full transaction/metadata relationship diagram * refactor(cips): reorganize documentation ready for drafting descriptive prose about the formats and uses * docs(cips): add cip draft for catalyst roles using the x509-rbac standard * docs(cips): Add c509 cddl with restrictions and enhancements for plutus usage * docs(cips): Metadata envelope specification draft complete * Update docs/src/catalyst-standards/draft-cips/c509-plutus-restricted-certificate/c509-cert-plutus-restricted.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/c509-plutus-restricted-certificate/c509-cert-plutus-restricted.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-role-registration-metadata/x509-roles.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-role-registration-metadata/x509-roles.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-envelope-metadata/x509-envelope.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-envelope-metadata/x509-envelope.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-envelope-metadata/x509-envelope.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/c509-plutus-restricted-certificate/c509-cert-plutus-restricted.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * docs(cips): Fix time and algorithm comments * build(frontend): Relax flutter/dart version requirements to last minor release * docs(cips): wip * fix(cips): rename draft x509 envelope CIP so its easier to identify * docs(cips): WIP updates to x509 roles * fix(cips): rename RBAC definition CIP draft so its easier to identify * docs(cips): x509 certificate registration format fully defined * docs(cips): Document the restricted plutus subset. * docs(cips): Add document detailing how CIP-30 is used to sign the transaction * fix(cips): remove trailing spaces * fix(cips): Fix line lengths * fix(cips): Correct spelling * fix(cips): spelling * fix(frontend): revert changes to flutter/dart versions * fix(frontend): more flutter/dart version corrections * fix(frontend): Revert flutter files to same as main branch * fix(frontend): revert more flutter .yml files to those in main * fix(cips): Fix links between files * docs(cips): Add catalyst specific role registration documentation * docs(spelling): fix spelling --------- Co-authored-by: minikin Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * ci: configure static analysis & code formatting check * style: formatting * fix: revert browser installation scripts * style: format code * ci: optimize directions include in repo-catalyst-voices-all artifact to include only needed ones * refactor: remove empty tests * ci: add melos script to generate test reports * ci: melos script to generate test report * style: revert previously generated files formatting * style: format files * ci: update melos to exclude generated code form code coverage * ci: cleanup build script * ci: generate multiple junit test reports and save them at /test_reports * ci: depend on melos analyze instead of custom command * docs: improve melos docs * ci: remove unused melos scripts * ci: format files in test & integration_test directories * ci: break code to make sure CI will report failure for demonstration purposes * style: fix lint issues * ci: change WORKDIR after creating the user to make sure it will be owned by that user * ci: restore root user * Revert "Merge branch 'main' into feat/collect-flutter-code-coverage" This reverts commit d0f66b2c1e7228141009ea153b35b488bf7922b1, reversing changes made to 39ce4017c3624a807b93116633ac1b249f2d86bd. * style: format code * ci: revert test-unit target name --------- Signed-off-by: dependabot[bot] Co-authored-by: Lucio Baglione Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Apisit Ritreungroj <38898766+apskhem@users.noreply.github.com> Co-authored-by: Alex Pozhylenkov Co-authored-by: Steven Johnson Co-authored-by: minikin Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * refactor: update schema_validation check (#414) * update schema_validation check * wip * fix --------- Signed-off-by: dependabot[bot] Co-authored-by: Lucio Baglione Co-authored-by: Stefano Cunego <93382903+kukkok3@users.noreply.github.com> Co-authored-by: Dominik Toton <166132265+dtscalac@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Apisit Ritreungroj <38898766+apskhem@users.noreply.github.com> Co-authored-by: Alex Pozhylenkov Co-authored-by: Steven Johnson Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> --- .github/workflows/generate-allure-report.yml | 2 +- Earthfile | 10 + catalyst-gateway/bin/src/cli.rs | 2 +- catalyst-gateway/bin/src/event_db/error.rs | 4 - catalyst-gateway/bin/src/event_db/mod.rs | 12 +- .../cardano/date_time_to_slot_number_get.rs | 22 +- .../service/api/cardano/registration_get.rs | 20 +- .../src/service/api/cardano/staked_ada_get.rs | 19 +- .../src/service/api/cardano/sync_state_get.rs | 19 +- .../bin/src/service/api/health/ready_get.rs | 11 +- catalyst-gateway/bin/src/service/mod.rs | 3 - .../utilities/middleware/schema_validation.rs | 7 +- catalyst-gateway/bin/src/state/mod.rs | 70 +---- catalyst-gateway/tests/Earthfile | 12 +- .../tests/api_tests/api_tests/__init__.py | 70 ++--- catalyst_voices/Earthfile | 30 ++- .../lib/pages/coming_soon/coming_soon.dart | 4 +- .../test/src/catalyst_voices_blocs_test.dart | 3 - .../catalyst_voices_localizations.dart | 40 +-- .../catalyst_voices_localizations_en.dart | 3 +- .../catalyst_voices_localizations_es.dart | 6 +- .../test/src/catalyst_voices_models_test.dart | 1 - .../catalyst_voices_repositories/README.md | 22 -- .../src/catalyst_data_gateway_repository.dart | 58 ++++- .../catalyst_voices_repositories/pubspec.yaml | 1 + ...catalyst_data_gateway_repository_test.dart | 241 +++++++++++------- ...st_data_gateway_repository_test.mocks.dart | 201 --------------- .../src/catalyst_voices_services_test.dart | 1 - .../lib/src/platform/io_platform.dart | 24 +- .../lib/src/platform/web_platform.dart | 26 +- .../platform_aware_builder.dart | 48 ++-- .../src/responsive/responsive_builder.dart | 45 ++-- .../lib/src/responsive/responsive_child.dart | 18 +- .../src/responsive/responsive_padding.dart | 19 +- .../test/src/platform_aware_builder_test.dart | 16 +- .../test/src/responsive_builder_test.dart | 119 +++++---- .../test/src/responsive_child_test.dart | 153 ++++++----- .../test/src/responsive_padding_test.dart | 63 +++-- .../src/catalyst_voices_view_models_test.dart | 5 - catalyst_voices/test/app/view/app_test.dart | 12 +- melos.yaml | 83 +++--- .../catalyst_voices_remote_widgets_test.dart | 1 - 42 files changed, 651 insertions(+), 875 deletions(-) delete mode 100644 catalyst_voices/packages/catalyst_voices_blocs/test/src/catalyst_voices_blocs_test.dart delete mode 100644 catalyst_voices/packages/catalyst_voices_models/test/src/catalyst_voices_models_test.dart delete mode 100644 catalyst_voices/packages/catalyst_voices_repositories/test/src/catalyst_data_gateway_repository/catalyst_data_gateway_repository_test.mocks.dart delete mode 100644 catalyst_voices/packages/catalyst_voices_services/test/src/catalyst_voices_services_test.dart delete mode 100644 catalyst_voices/packages/catalyst_voices_view_models/test/src/catalyst_voices_view_models_test.dart delete mode 100644 utilities/catalyst_voices_remote_widgets/test/src/catalyst_voices_remote_widgets_test.dart diff --git a/.github/workflows/generate-allure-report.yml b/.github/workflows/generate-allure-report.yml index 9bd73285f..58bf452e6 100644 --- a/.github/workflows/generate-allure-report.yml +++ b/.github/workflows/generate-allure-report.yml @@ -67,7 +67,7 @@ jobs: with: earthfile: ./catalyst_voices/ flags: - targets: test-unit + targets: test-report target_flags: runner_address: ${{ secrets.EARTHLY_SATELLITE_ADDRESS }} artifact: "false" diff --git a/Earthfile b/Earthfile index ee9593ff9..9fa6990e1 100644 --- a/Earthfile +++ b/Earthfile @@ -44,4 +44,14 @@ repo-catalyst-voices-packages: WORKDIR /repo COPY --dir catalyst_voices_packages . + SAVE ARTIFACT /repo repo + +# repo-catalyst-voices-all - Creates artifacts of all configuration files, +# packages and folders related to catalyst_voices frontend. +repo-catalyst-voices-all: + FROM scratch + + WORKDIR /repo + COPY --dir catalyst_voices catalyst_voices_packages utilities melos.yaml pubspec.yaml . + SAVE ARTIFACT /repo repo \ No newline at end of file diff --git a/catalyst-gateway/bin/src/cli.rs b/catalyst-gateway/bin/src/cli.rs index 2e9664ddd..b86f25061 100644 --- a/catalyst-gateway/bin/src/cli.rs +++ b/catalyst-gateway/bin/src/cli.rs @@ -58,7 +58,7 @@ impl Cli { let machine_id = settings.follower_settings.machine_uid; let state = Arc::new(State::new(Some(settings.database_url)).await?); - let event_db = state.event_db()?; + let event_db = state.event_db(); tokio::spawn(async move { match service::run(&settings.docs_settings, state.clone()).await { diff --git a/catalyst-gateway/bin/src/event_db/error.rs b/catalyst-gateway/bin/src/event_db/error.rs index a23ac1223..a17bcf1bc 100644 --- a/catalyst-gateway/bin/src/event_db/error.rs +++ b/catalyst-gateway/bin/src/event_db/error.rs @@ -1,5 +1,4 @@ //! Database Errors -use std::env::VarError; use bb8::RunError; @@ -26,9 +25,6 @@ pub(crate) enum Error { /// Unknown error #[error("error: {0}")] Unknown(String), - /// Variable error - #[error(transparent)] - VarErr(#[from] VarError), /// No config #[error("No config")] NoConfig, diff --git a/catalyst-gateway/bin/src/event_db/mod.rs b/catalyst-gateway/bin/src/event_db/mod.rs index a3992313b..5c1b23bb1 100644 --- a/catalyst-gateway/bin/src/event_db/mod.rs +++ b/catalyst-gateway/bin/src/event_db/mod.rs @@ -54,9 +54,7 @@ pub(crate) struct EventDB { /// /// The env var "`DATABASE_URL`" can be set directly as an anv var, or in a /// `.env` file. -pub(crate) async fn establish_connection( - url: Option, do_schema_check: bool, -) -> Result { +pub(crate) async fn establish_connection(url: Option) -> Result { // Support env vars in a `.env` file, doesn't need to exist. dotenv().ok(); @@ -72,11 +70,5 @@ pub(crate) async fn establish_connection( let pool = Pool::builder().build(pg_mgr).await?; - let db = EventDB { pool }; - - if do_schema_check { - db.schema_version_check().await?; - } - - Ok(db) + Ok(EventDB { pool }) } diff --git a/catalyst-gateway/bin/src/service/api/cardano/date_time_to_slot_number_get.rs b/catalyst-gateway/bin/src/service/api/cardano/date_time_to_slot_number_get.rs index 2927130f7..387c8a4a3 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/date_time_to_slot_number_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/date_time_to_slot_number_get.rs @@ -1,13 +1,9 @@ //! Implementation of the GET `/date_time_to_slot_number` endpoint -use poem_extensions::{ - response, - UniResponse::{T200, T503}, -}; +use poem_extensions::{response, UniResponse::T200}; use poem_openapi::payload::Json; use crate::{ - cli::Error, event_db::{ error::Error as DBError, follower::{DateTime, SlotInfoQueryType}, @@ -23,7 +19,7 @@ use crate::{ resp_5xx::{server_error_response, ServerError, ServiceUnavailable}, }, }, - state::{SchemaVersionStatus, State}, + state::State, }; /// # All Responses @@ -39,19 +35,7 @@ pub(crate) type AllResponses = response! { pub(crate) async fn endpoint( state: &State, date_time: Option, network: Option, ) -> AllResponses { - let event_db = match state.event_db() { - Ok(event_db) => event_db, - Err(Error::EventDb(DBError::MismatchedSchema { was, expected })) => { - tracing::error!( - expected = expected, - current = was, - "DB schema version status mismatch" - ); - state.set_schema_version_status(SchemaVersionStatus::Mismatch); - return T503(ServiceUnavailable); - }, - Err(err) => return server_error_response!("{err}"), - }; + let event_db = state.event_db(); let date_time = date_time.unwrap_or_else(chrono::Utc::now); let network = network.unwrap_or(Network::Mainnet); diff --git a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs index 6ba43be15..252144627 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs @@ -2,12 +2,11 @@ use poem_extensions::{ response, - UniResponse::{T200, T400, T404, T503}, + UniResponse::{T200, T400, T404}, }; use poem_openapi::payload::Json; use crate::{ - cli::Error, event_db::{error::Error as DBError, follower::SlotNumber}, service::{ common::{ @@ -22,7 +21,7 @@ use crate::{ }, utilities::check_network, }, - state::{SchemaVersionStatus, State}, + state::State, }; /// # All Responses @@ -40,19 +39,8 @@ pub(crate) async fn endpoint( state: &State, stake_address: StakeAddress, provided_network: Option, slot_num: Option, ) -> AllResponses { - let event_db = match state.event_db() { - Ok(event_db) => event_db, - Err(Error::EventDb(DBError::MismatchedSchema { was, expected })) => { - tracing::error!( - expected = expected, - current = was, - "DB schema version status mismatch" - ); - state.set_schema_version_status(SchemaVersionStatus::Mismatch); - return T503(ServiceUnavailable); - }, - Err(err) => return server_error_response!("{err}"), - }; + let event_db = state.event_db(); + let date_time = slot_num.unwrap_or(SlotNumber::MAX); let stake_credential = stake_address.payload().as_hash().to_vec(); let network = match check_network(stake_address.network(), provided_network) { diff --git a/catalyst-gateway/bin/src/service/api/cardano/staked_ada_get.rs b/catalyst-gateway/bin/src/service/api/cardano/staked_ada_get.rs index 4c6f33cc0..94c61ed07 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/staked_ada_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/staked_ada_get.rs @@ -2,12 +2,11 @@ use poem_extensions::{ response, - UniResponse::{T200, T400, T404, T503}, + UniResponse::{T200, T400, T404}, }; use poem_openapi::payload::Json; use crate::{ - cli::Error, event_db::{error::Error as DBError, follower::SlotNumber}, service::{ common::{ @@ -22,7 +21,7 @@ use crate::{ }, utilities::check_network, }, - state::{SchemaVersionStatus, State}, + state::State, }; /// # All Responses @@ -39,19 +38,7 @@ pub(crate) async fn endpoint( state: &State, stake_address: StakeAddress, provided_network: Option, slot_num: Option, ) -> AllResponses { - let event_db = match state.event_db() { - Ok(event_db) => event_db, - Err(Error::EventDb(DBError::MismatchedSchema { was, expected })) => { - tracing::error!( - expected = expected, - current = was, - "DB schema version status mismatch" - ); - state.set_schema_version_status(SchemaVersionStatus::Mismatch); - return T503(ServiceUnavailable); - }, - Err(err) => return server_error_response!("{err}"), - }; + let event_db = state.event_db(); let date_time = slot_num.unwrap_or(SlotNumber::MAX); let stake_credential = stake_address.payload().as_hash().to_vec(); diff --git a/catalyst-gateway/bin/src/service/api/cardano/sync_state_get.rs b/catalyst-gateway/bin/src/service/api/cardano/sync_state_get.rs index ab0801acd..0b677b2f4 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/sync_state_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/sync_state_get.rs @@ -2,12 +2,11 @@ use poem_extensions::{ response, - UniResponse::{T200, T404, T503}, + UniResponse::{T200, T404}, }; use poem_openapi::payload::Json; use crate::{ - cli::Error, event_db::error::Error as DBError, service::common::{ objects::cardano::{network::Network, sync_state::SyncState}, @@ -17,7 +16,7 @@ use crate::{ resp_5xx::{server_error_response, ServerError, ServiceUnavailable}, }, }, - state::{SchemaVersionStatus, State}, + state::State, }; /// # All Responses @@ -32,19 +31,7 @@ pub(crate) type AllResponses = response! { /// # GET `/sync_state` #[allow(clippy::unused_async)] pub(crate) async fn endpoint(state: &State, network: Option) -> AllResponses { - let event_db = match state.event_db() { - Ok(event_db) => event_db, - Err(Error::EventDb(DBError::MismatchedSchema { was, expected })) => { - tracing::error!( - expected = expected, - current = was, - "DB schema version status mismatch" - ); - state.set_schema_version_status(SchemaVersionStatus::Mismatch); - return T503(ServiceUnavailable); - }, - Err(err) => return server_error_response!("{err}"), - }; + let event_db = state.event_db(); let network = network.unwrap_or(Network::Mainnet); diff --git a/catalyst-gateway/bin/src/service/api/health/ready_get.rs b/catalyst-gateway/bin/src/service/api/health/ready_get.rs index 511872f5c..ad309288c 100644 --- a/catalyst-gateway/bin/src/service/api/health/ready_get.rs +++ b/catalyst-gateway/bin/src/service/api/health/ready_get.rs @@ -9,14 +9,13 @@ use poem_extensions::{ }; use crate::{ - cli::Error, event_db::error::Error as DBError, service::common::responses::{ resp_2xx::NoContent, resp_4xx::ApiValidationError, resp_5xx::{server_error_response, ServerError, ServiceUnavailable}, }, - state::{SchemaVersionStatus, State}, + state::State, }; /// All responses @@ -53,22 +52,20 @@ pub(crate) type AllResponses = response! { /// but unlikely) /// * 503 Service Unavailable - Service is not ready, do not send other requests. pub(crate) async fn endpoint(state: Data<&Arc>) -> AllResponses { - match state.schema_version_check().await { + match state.event_db().schema_version_check().await { Ok(_) => { tracing::debug!("DB schema version status ok"); - state.set_schema_version_status(SchemaVersionStatus::Ok); T204(NoContent) }, - Err(Error::EventDb(DBError::MismatchedSchema { was, expected })) => { + Err(DBError::MismatchedSchema { was, expected }) => { tracing::error!( expected = expected, current = was, "DB schema version status mismatch" ); - state.set_schema_version_status(SchemaVersionStatus::Mismatch); T503(ServiceUnavailable) }, - Err(Error::EventDb(DBError::TimedOut)) => T503(ServiceUnavailable), + Err(DBError::TimedOut) => T503(ServiceUnavailable), Err(err) => server_error_response!("{err}"), } } diff --git a/catalyst-gateway/bin/src/service/mod.rs b/catalyst-gateway/bin/src/service/mod.rs index 1d001a710..6ce731a3f 100644 --- a/catalyst-gateway/bin/src/service/mod.rs +++ b/catalyst-gateway/bin/src/service/mod.rs @@ -23,9 +23,6 @@ pub(crate) enum Error { /// An IO error has occurred #[error(transparent)] Io(#[from] std::io::Error), - /// A mismatch in the expected `EventDB` schema version - #[error("expected schema version mismatch")] - SchemaVersionMismatch, } /// # Run Catalyst Gateway Service. diff --git a/catalyst-gateway/bin/src/service/utilities/middleware/schema_validation.rs b/catalyst-gateway/bin/src/service/utilities/middleware/schema_validation.rs index 87fd858fa..c889dd9f6 100644 --- a/catalyst-gateway/bin/src/service/utilities/middleware/schema_validation.rs +++ b/catalyst-gateway/bin/src/service/utilities/middleware/schema_validation.rs @@ -11,10 +11,7 @@ use std::sync::Arc; use poem::{web::Data, Endpoint, EndpointExt, Middleware, Request, Result}; -use crate::{ - service::common::responses::resp_5xx::ServiceUnavailable, - state::{SchemaVersionStatus, State}, -}; +use crate::{service::common::responses::resp_5xx::ServiceUnavailable, state::State}; /// A middleware that raises an error with `ServiceUnavailable` and 503 status code /// if a DB schema version mismatch is found the existing `State`. @@ -44,7 +41,7 @@ impl Endpoint for SchemaVersionValidationImpl { // if so, return the `ServiceUnavailable` error, which implements // `ResponseError`, with status code `503`. // Otherwise, return the endpoint as usual. - if state.is_schema_version_status(&SchemaVersionStatus::Mismatch) { + if state.event_db().schema_version_check().await.is_err() { return Err(ServiceUnavailable.into()); } } diff --git a/catalyst-gateway/bin/src/state/mod.rs b/catalyst-gateway/bin/src/state/mod.rs index c0e073dc2..4457d1b26 100644 --- a/catalyst-gateway/bin/src/state/mod.rs +++ b/catalyst-gateway/bin/src/state/mod.rs @@ -1,22 +1,11 @@ //! Shared state used by all endpoints. -use std::sync::{Arc, Mutex, MutexGuard}; +use std::sync::Arc; use crate::{ cli::Error, event_db::{establish_connection, EventDB}, - service::Error as ServiceError, }; -/// The status of the expected DB schema version. -#[derive(Debug, PartialEq, Eq)] -pub(crate) enum SchemaVersionStatus { - /// The current DB schema version matches what is expected. - Ok, - /// There is a mismatch between the current DB schema version - /// and what is expected. - Mismatch, -} - /// Global State of the service pub(crate) struct State { /// This can be None, or a handle to the DB. @@ -27,22 +16,15 @@ pub(crate) struct State { // Private need to get it with a function. event_db: Arc, /* This needs to be obsoleted, we want the DB * to be able to be down. */ - /// Status of the last DB schema version check. - schema_version_status: Mutex, } impl State { /// Create a new global [`State`] pub(crate) async fn new(database_url: Option) -> Result { // Get a configured pool to the Database, runs schema version check internally. - let event_db = Arc::new(establish_connection(database_url, false).await?); + let event_db = Arc::new(establish_connection(database_url).await?); - let state = Self { - event_db, - // It is safe to assume that the schema version matches if `event_db` doesn't fail, - // due to the interior check ran by `establish_connection`. - schema_version_status: Mutex::new(SchemaVersionStatus::Ok), - }; + let state = Self { event_db }; // We don't care if this succeeds or not. // We just try our best to connect to the event DB. @@ -52,49 +34,7 @@ impl State { } /// Get the reference to the database connection pool for `EventDB`. - pub(crate) fn event_db(&self) -> Result, Error> { - let guard = self.schema_version_status_lock(); - match *guard { - SchemaVersionStatus::Ok => Ok(self.event_db.clone()), - SchemaVersionStatus::Mismatch => Err(ServiceError::SchemaVersionMismatch.into()), - } - } - - /// Check the DB schema version matches the one expected by the service. - pub(crate) async fn schema_version_check(&self) -> Result { - Ok(self.event_db.schema_version_check().await?) - } - - /// Compare the `State`'s inner value with a given `&SchemaVersionStatus`, returns - /// `bool`. - pub(crate) fn is_schema_version_status(&self, svs: &SchemaVersionStatus) -> bool { - let guard = self.schema_version_status_lock(); - &*guard == svs - } - - /// Set the state's `SchemaVersionStatus`. - pub(crate) fn set_schema_version_status(&self, svs: SchemaVersionStatus) { - let mut guard = self.schema_version_status_lock(); - tracing::debug!( - status = format!("{:?}", svs), - "db schema version status was set" - ); - *guard = svs; - } - - /// Get the `MutexGuard` from inner the variable. - /// - /// Handle poisoned mutex by recovering the guard, and tracing the error. - fn schema_version_status_lock(&self) -> MutexGuard { - match self.schema_version_status.lock() { - Ok(guard) => guard, - Err(poisoned) => { - tracing::error!( - error = format!("{:?}", poisoned), - "recovering DB schema version status fom poisoned mutex" - ); - poisoned.into_inner() - }, - } + pub(crate) fn event_db(&self) -> Arc { + self.event_db.clone() } } diff --git a/catalyst-gateway/tests/Earthfile b/catalyst-gateway/tests/Earthfile index 1dc998738..90a4760d8 100644 --- a/catalyst-gateway/tests/Earthfile +++ b/catalyst-gateway/tests/Earthfile @@ -7,7 +7,7 @@ package-schemathesis: ARG max_examples=1000 ARG max_response_time=300 ARG wait_for_schema=15 - ARG workers=1 + ARG workers=2 ARG schema_version=30 ARG openapi_spec @@ -31,7 +31,7 @@ package-schemathesis: fuzzer-api: FROM earthly/dind:alpine-3.19 RUN apk update && apk add iptables-legacy # workaround for https://github.com/earthly/earthly/issues/3784 - RUN apk add yq + RUN apk add yq zstd ARG DB_URL="postgres://catalyst-event-dev:CHANGE_ME@localhost/CatalystEventDev" ARG CAT_ADDRESS="127.0.0.1:3030" ARG OPENAPI_SPEC="http://127.0.0.1:3030/docs/cat-gateway.json" @@ -46,21 +46,19 @@ fuzzer-api: --service cat-gateway \ --allow-privileged RUN docker run --net=host --name=st schemathesis:latest || echo fail > fail && \ - docker logs cat-gateway > ./cat-gateway.log && \ + docker logs cat-gateway > ./cat-gateway.log && zstd -9 cat-gateway.log && \ docker cp st:/results/junit-report.xml junit-report.xml && \ docker cp st:/results/cassette.yaml cassette.yaml END WAIT SAVE ARTIFACT junit-report.xml AS LOCAL schemathesis.junit-report.xml - SAVE ARTIFACT cat-gateway.log AS LOCAL cat-gateway.log + SAVE ARTIFACT cat-gateway.log.zst AS LOCAL cat-gateway.log.zst SAVE ARTIFACT cassette.yaml AS LOCAL cassette.yaml END IF [ -f fail ] - RUN echo -e "\033[0;31mSchemathesis test run failed\n" && \ + RUN echo -e "\033[0;31mSchemathesis test run failed. Run the test locally to get Catalyst Gateway logs\n" && \ echo -e "\033[0;31mFailed tests:" && \ yq -PC '.http_interactions.[] | select(.status == "FAILURE")' cassette.yaml && \ - echo -e "\033[0;31mCatalyst Gateway logs:" && \ - jq --color-output . cat-gateway.log cat-gateway.log && \ exit 1 END diff --git a/catalyst-gateway/tests/api_tests/api_tests/__init__.py b/catalyst-gateway/tests/api_tests/api_tests/__init__.py index 47a40143c..14d243793 100644 --- a/catalyst-gateway/tests/api_tests/api_tests/__init__.py +++ b/catalyst-gateway/tests/api_tests/api_tests/__init__.py @@ -98,40 +98,44 @@ def sync_to(network: str, slot_num: int, timeout: int): while True: # Get current sync state sync_state = get_sync_state(network=network) - if last_slot_num == -1: - first_slot_num = sync_state["slot_number"] - # If we reached our target sync state, then continue the test - if sync_state != None and sync_state["slot_number"] >= slot_num: - logger.info(f"cat-gateway synced to target slot {slot_num}: {sync_state}") - break - - # If the sync state changed since last time, reset the timeout and log the new sync state - if last_slot_num != sync_state["slot_number"]: - slots_last_interval = (sync_state["slot_number"] - last_slot_num) + 1 - last_interval = time.time() - start_time - sps_last_interval = slots_last_interval / max(last_interval, 1.0) - - last_slot_num = sync_state["slot_number"] - start_time = time.time() - - total_time = start_time - true_start - total_slots = (last_slot_num - first_slot_num) + 1 - slots_per_second = total_slots / max(total_time, 0.001) - - residual_sync_slots = max(slot_num - last_slot_num, 0) - estimated_time_remaining = residual_sync_slots / slots_per_second - - # Total slots/second and in the last interval - sps = f"{slots_per_second:.2f}({sps_last_interval:.2f})" - - logger.info( - f"{last_slot_num : >16} : " - + f"{printable_time(total_time) : >16} : " - + f"{sps : >20} : " - + f"{residual_sync_slots : >16} : " - + f"{printable_time(estimated_time_remaining) : >16} :" - ) + if sync_state != None: + if last_slot_num == -1: + first_slot_num = sync_state["slot_number"] + + # If we reached our target sync state, then continue the test + if sync_state["slot_number"] >= slot_num: + logger.info( + f"cat-gateway synced to target slot {slot_num}: {sync_state}" + ) + break + + # If the sync state changed since last time, reset the timeout and log the new sync state + if last_slot_num != sync_state["slot_number"]: + slots_last_interval = (sync_state["slot_number"] - last_slot_num) + 1 + last_interval = time.time() - start_time + sps_last_interval = slots_last_interval / max(last_interval, 1.0) + + last_slot_num = sync_state["slot_number"] + start_time = time.time() + + total_time = start_time - true_start + total_slots = (last_slot_num - first_slot_num) + 1 + slots_per_second = total_slots / max(total_time, 0.001) + + residual_sync_slots = max(slot_num - last_slot_num, 0) + estimated_time_remaining = residual_sync_slots / slots_per_second + + # Total slots/second and in the last interval + sps = f"{slots_per_second:.2f}({sps_last_interval:.2f})" + + logger.info( + f"{last_slot_num : >16} : " + + f"{printable_time(total_time) : >16} : " + + f"{sps : >20} : " + + f"{residual_sync_slots : >16} : " + + f"{printable_time(estimated_time_remaining) : >16} :" + ) # If sync state does not update for timeout seconds, then fail the test if start_time + timeout <= time.time(): diff --git a/catalyst_voices/Earthfile b/catalyst_voices/Earthfile index 309bfa502..5ee37c74a 100644 --- a/catalyst_voices/Earthfile +++ b/catalyst_voices/Earthfile @@ -8,7 +8,7 @@ VERSION --try --global-cache --arg-scope-and-set 0.7 deps: FROM debian:bookworm-slim RUN apt-get update - RUN apt-get install -y git curl unzip bzip2 bash jq gpg + RUN apt-get install -y git curl unzip bzip2 bash jq gpg lcov COPY --dir test_driver/scripts . RUN chmod +x scripts/install-chrome-linux64.sh && ./scripts/install-chrome-linux64.sh RUN chmod +x scripts/install-edge-linux64.sh && ./scripts/install-edge-linux64.sh @@ -23,7 +23,9 @@ deps: RUN flutter --version RUN flutter doctor -v RUN flutter config --enable-web + RUN dart pub global activate melos RUN dart pub global activate junitreport + RUN dart pub global activate coverage src: FROM +deps @@ -60,6 +62,20 @@ check-flutter-code-generator: # Check diff between local code and earthly artifacts RUN diff /tmp/repo_generated lib/generated/catalyst_gateway +check-static-analysis: + FROM +deps + COPY ../+repo-catalyst-voices-all/repo repo/ + WORKDIR repo + RUN melos bootstrap + RUN melos analyze --fatal-infos --fatal-warnings + +check-code-formatting: + FROM +deps + COPY ../+repo-catalyst-voices-all/repo repo/ + WORKDIR repo + RUN melos bootstrap + RUN melos run format-check + # Build web version of Catalyst Voices build: FROM +src @@ -72,12 +88,12 @@ build: SAVE ARTIFACT web /web AS LOCAL web test-unit: - FROM +build - WORKDIR /frontend - TRY - RUN flutter test --reporter expanded . --machine | tojunit --output flutter.junit-report.xml - FINALLY - SAVE ARTIFACT flutter.junit-report.xml AS LOCAL flutter-unit-tests.junit-report.xml + FROM +deps + COPY ../+repo-catalyst-voices-all/repo repo/ + WORKDIR repo + RUN melos run test-report + WAIT + SAVE ARTIFACT test_reports AS LOCAL test_reports END package: diff --git a/catalyst_voices/lib/pages/coming_soon/coming_soon.dart b/catalyst_voices/lib/pages/coming_soon/coming_soon.dart index 79e7ad0b4..7c4b28710 100644 --- a/catalyst_voices/lib/pages/coming_soon/coming_soon.dart +++ b/catalyst_voices/lib/pages/coming_soon/coming_soon.dart @@ -19,8 +19,8 @@ final class ComingSoonPage extends StatelessWidget { decoration: BoxDecoration( image: DecorationImage( image: CatalystImage.asset( - VoicesAssets.images.comingSoonBkg.path, - ).image, + VoicesAssets.images.comingSoonBkg.path, + ).image, fit: BoxFit.cover, ), ), diff --git a/catalyst_voices/packages/catalyst_voices_blocs/test/src/catalyst_voices_blocs_test.dart b/catalyst_voices/packages/catalyst_voices_blocs/test/src/catalyst_voices_blocs_test.dart deleted file mode 100644 index d717b8cb4..000000000 --- a/catalyst_voices/packages/catalyst_voices_blocs/test/src/catalyst_voices_blocs_test.dart +++ /dev/null @@ -1,3 +0,0 @@ -// ignore_for_file: prefer_const_constructors - -void main() {} diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart index e91c8b2ff..d3b4a86e5 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart @@ -4,8 +4,10 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:intl/intl.dart' as intl; -import 'catalyst_voices_localizations_en.dart' deferred as catalyst_voices_localizations_en; -import 'catalyst_voices_localizations_es.dart' deferred as catalyst_voices_localizations_es; +import 'catalyst_voices_localizations_en.dart' + deferred as catalyst_voices_localizations_en; +import 'catalyst_voices_localizations_es.dart' + deferred as catalyst_voices_localizations_es; /// Callers can lookup localized strings with an instance of VoicesLocalizations /// returned by `VoicesLocalizations.of(context)`. @@ -59,7 +61,8 @@ import 'catalyst_voices_localizations_es.dart' deferred as catalyst_voices_local /// be consistent with the languages listed in the VoicesLocalizations.supportedLocales /// property. abstract class VoicesLocalizations { - VoicesLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + VoicesLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -67,7 +70,8 @@ abstract class VoicesLocalizations { return Localizations.of(context, VoicesLocalizations); } - static const LocalizationsDelegate delegate = _VoicesLocalizationsDelegate(); + static const LocalizationsDelegate delegate = + _VoicesLocalizationsDelegate(); /// A list of this localizations delegate along with the default localizations /// delegates. @@ -79,7 +83,8 @@ abstract class VoicesLocalizations { /// Additional delegates can be added by appending to this list in /// MaterialApp. This list does not have to be used at all if a custom list /// of delegates is preferred or required. - static const List> localizationsDelegates = >[ + static const List> localizationsDelegates = + >[ delegate, GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, @@ -177,7 +182,8 @@ abstract class VoicesLocalizations { String get comingSoonDescription; } -class _VoicesLocalizationsDelegate extends LocalizationsDelegate { +class _VoicesLocalizationsDelegate + extends LocalizationsDelegate { const _VoicesLocalizationsDelegate(); @override @@ -186,25 +192,27 @@ class _VoicesLocalizationsDelegate extends LocalizationsDelegate ['en', 'es'].contains(locale.languageCode); + bool isSupported(Locale locale) => + ['en', 'es'].contains(locale.languageCode); @override bool shouldReload(_VoicesLocalizationsDelegate old) => false; } Future lookupVoicesLocalizations(Locale locale) { - - // Lookup logic when only language code is specified. switch (locale.languageCode) { - case 'en': return catalyst_voices_localizations_en.loadLibrary().then((dynamic _) => catalyst_voices_localizations_en.VoicesLocalizationsEn()); - case 'es': return catalyst_voices_localizations_es.loadLibrary().then((dynamic _) => catalyst_voices_localizations_es.VoicesLocalizationsEs()); + case 'en': + return catalyst_voices_localizations_en.loadLibrary().then((dynamic _) => + catalyst_voices_localizations_en.VoicesLocalizationsEn()); + case 'es': + return catalyst_voices_localizations_es.loadLibrary().then((dynamic _) => + catalyst_voices_localizations_es.VoicesLocalizationsEs()); } throw FlutterError( - 'VoicesLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' - 'an issue with the localizations generation tool. Please file an issue ' - 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.' - ); + 'VoicesLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart index 7f270f05d..0208ba73b 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart @@ -44,5 +44,6 @@ class VoicesLocalizationsEn extends VoicesLocalizations { String get comingSoonTitle2 => 'soon'; @override - String get comingSoonDescription => 'Project Catalyst is the world\'s largest decentralized innovation engine for solving real-world challenges.'; + String get comingSoonDescription => + 'Project Catalyst is the world\'s largest decentralized innovation engine for solving real-world challenges.'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart index 67c9f0888..a8fd058cb 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart @@ -20,7 +20,8 @@ class VoicesLocalizationsEs extends VoicesLocalizations { String get passwordHintText => 'Mi1ContraseñaSecreta'; @override - String get passwordErrorText => 'La contraseña debe tener al menos 8 caracteres'; + String get passwordErrorText => + 'La contraseña debe tener al menos 8 caracteres'; @override String get loginTitleText => 'Acceso'; @@ -44,5 +45,6 @@ class VoicesLocalizationsEs extends VoicesLocalizations { String get comingSoonTitle2 => 'soon'; @override - String get comingSoonDescription => 'Project Catalyst is the world\'s largest decentralized innovation engine for solving real-world challenges.'; + String get comingSoonDescription => + 'Project Catalyst is the world\'s largest decentralized innovation engine for solving real-world challenges.'; } diff --git a/catalyst_voices/packages/catalyst_voices_models/test/src/catalyst_voices_models_test.dart b/catalyst_voices/packages/catalyst_voices_models/test/src/catalyst_voices_models_test.dart deleted file mode 100644 index ab73b3a23..000000000 --- a/catalyst_voices/packages/catalyst_voices_models/test/src/catalyst_voices_models_test.dart +++ /dev/null @@ -1 +0,0 @@ -void main() {} diff --git a/catalyst_voices/packages/catalyst_voices_repositories/README.md b/catalyst_voices/packages/catalyst_voices_repositories/README.md index 9ff8a8461..5316d7d48 100644 --- a/catalyst_voices/packages/catalyst_voices_repositories/README.md +++ b/catalyst_voices/packages/catalyst_voices_repositories/README.md @@ -1,23 +1 @@ # Catalyst Voices Repositories - -## Catalyst Data Gateway Repository - -### Tests - -When extending the `CatalystDataGatewayRepository` it is necessary to generate -proper mocks to have them available in tests. -To do that we need to run - -```sh -flutter pub run build_runner build --delete-conflicting-outputs -``` - - or - - ```sh -dart run build_runner build --delete-conflicting-outputs -``` - -in the Catalyst Voices Repositories package root. -The decorator `@GenerateNiceMocks` provided by mockito is used to indicate the -repository to generate the mocks for. diff --git a/catalyst_voices/packages/catalyst_voices_repositories/lib/src/catalyst_data_gateway_repository.dart b/catalyst_voices/packages/catalyst_voices_repositories/lib/src/catalyst_data_gateway_repository.dart index 7dafda845..394d79891 100644 --- a/catalyst_voices/packages/catalyst_voices_repositories/lib/src/catalyst_data_gateway_repository.dart +++ b/catalyst_voices/packages/catalyst_voices_repositories/lib/src/catalyst_data_gateway_repository.dart @@ -1,16 +1,58 @@ import 'dart:async'; import 'package:catalyst_voices_models/catalyst_voices_models.dart'; -import 'package:catalyst_voices_services/generated/catalyst_gateway/cat_gateway_api.enums.swagger.dart' as enums; +import 'package:catalyst_voices_services/generated/catalyst_gateway/cat_gateway_api.enums.swagger.dart' + as enums; import 'package:catalyst_voices_services/generated/catalyst_gateway/cat_gateway_api.swagger.dart'; import 'package:chopper/chopper.dart'; import 'package:result_type/result_type.dart'; -interface class CatalystDataGatewayRepository { +// The [CatalystDataGatewayRepository] provides a structured interface to +// interact with the `catalyst-gateway` backend API. +// Network communication and error handling is abstracted allowing the +// integration of API calls in an easy way. +// All methods return `Future` objects to allow async execution. +// +// The repository uses, under the hood, the [CatGatewayApi] directly generated +// from backend OpenAPI specification. +// +// To use the repository is necessary to initialize it by specifying the API +// base URL: +// +// ```dart +// final repository = CatalystDataGatewayRepository(Uri.parse('https://example.org/api')); +// ``` +// +// Once initialized it is possible, for example, to check the health status of +// the service: +// +// ```dart +// final health_status = await repository.getHealthLive(); +// ``` +// +// fetch staked ADA by stake address: +// +// ```dart +// final stake_info = await repository.getCardanoStakedAdaStakeAddress( +// // cspell: disable-next-line +// stakeAddress:'stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw', +// ); +// ``` +// +// or get the sync state: +// +// ```dart +// final sync_state = await repository.getCardanoSyncState(); +// ``` + +final class CatalystDataGatewayRepository { final CatGatewayApi _catGatewayApi; - CatalystDataGatewayRepository(Uri baseUrl) - : _catGatewayApi = CatGatewayApi.create(baseUrl: baseUrl); + CatalystDataGatewayRepository( + Uri baseUrl, { + CatGatewayApi? catGatewayApiInstance, + }) : _catGatewayApi = + catGatewayApiInstance ?? CatGatewayApi.create(baseUrl: baseUrl); Future> getHealthStarted() async { try { @@ -42,13 +84,13 @@ interface class CatalystDataGatewayRepository { Future> getCardanoStakedAdaStakeAddress({ required String stakeAddress, enums.Network network = enums.Network.mainnet, - DateTime? dateTime, + int? slotNumber, }) async { try { final stakeInfo = await _catGatewayApi.apiCardanoStakedAdaStakeAddressGet( stakeAddress: stakeAddress, network: network, - dateTime: dateTime, + slotNumber: slotNumber, ); return Success(stakeInfo.bodyOrThrow); } on ChopperHttpException catch (error) { @@ -78,10 +120,10 @@ interface class CatalystDataGatewayRepository { Result _emptyBodyOrThrow(Response response) { // `bodyOrThrow` from chopper can't be used when the body is empty (like in // case the endpoint replies with 204) because it would throw an exception - // as a false positive. + // as a false positive. if (response.isSuccessful) { return Success(null); } throw ChopperHttpException(response); - } + } } diff --git a/catalyst_voices/packages/catalyst_voices_repositories/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_repositories/pubspec.yaml index a2453a86a..83d79fee1 100644 --- a/catalyst_voices/packages/catalyst_voices_repositories/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_repositories/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: chopper: ^7.2.0 flutter: sdk: flutter + http: ^1.2.1 result_type: ^0.2.0 rxdart: ^0.27.7 diff --git a/catalyst_voices/packages/catalyst_voices_repositories/test/src/catalyst_data_gateway_repository/catalyst_data_gateway_repository_test.dart b/catalyst_voices/packages/catalyst_voices_repositories/test/src/catalyst_data_gateway_repository/catalyst_data_gateway_repository_test.dart index faa941857..746020be8 100644 --- a/catalyst_voices/packages/catalyst_voices_repositories/test/src/catalyst_data_gateway_repository/catalyst_data_gateway_repository_test.dart +++ b/catalyst_voices/packages/catalyst_voices_repositories/test/src/catalyst_data_gateway_repository/catalyst_data_gateway_repository_test.dart @@ -1,145 +1,198 @@ +import 'dart:io'; + import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_repositories/src/catalyst_data_gateway_repository.dart'; +import 'package:catalyst_voices_services/generated/catalyst_gateway/cat_gateway_api.enums.swagger.dart' + as enums; import 'package:catalyst_voices_services/generated/catalyst_gateway/cat_gateway_api.swagger.dart'; -import 'package:mockito/annotations.dart'; +import 'package:chopper/chopper.dart' as chopper; +import 'package:http/http.dart' as http; import 'package:mockito/mockito.dart'; -import 'package:result_type/result_type.dart'; import 'package:test/test.dart'; -import 'catalyst_data_gateway_repository_test.mocks.dart'; +// `@GenerateNiceMocks` from `mockito` can't be used here because +// `chopper.Response` use the `base` modifier which "disallows +// implementations outside of its own library". For this reason +// `@GenerateNiceMocks` doesn't work as intended and we opted to +// mock the `CatGatewayApi` using `Fake`. +class FakeCatGatewayApi extends Fake implements CatGatewayApi { + final chopper.Response response; + + FakeCatGatewayApi(this.response); + + @override + Future> apiHealthStartedGet() async => response; + @override + Future> apiHealthReadyGet() async => response; + + @override + Future> apiHealthLiveGet() async => response; + + @override + Future> apiCardanoStakedAdaStakeAddressGet({ + required String? stakeAddress, + enums.Network? network, + int? slotNumber, + }) async => + response as chopper.Response; + + @override + Future> apiCardanoSyncStateGet({ + enums.Network? network, + }) async => + response as chopper.Response; +} -@GenerateNiceMocks([MockSpec()]) void main() { + CatalystDataGatewayRepository setupRepository( + chopper.Response response, + ) { + final fakeCatGatewayApi = FakeCatGatewayApi(response); + return CatalystDataGatewayRepository( + Uri.parse('https://localhost/api'), + catGatewayApiInstance: fakeCatGatewayApi, + ); + } + group('CatalystDataGatewayRepository', () { - final mock = MockCatalystDataGatewayRepository(); + final repository = setupRepository( + chopper.Response(http.Response('', HttpStatus.noContent), null), + ); test('getHealthStarted success', () async { - when(mock.getHealthStarted()).thenAnswer((_) async => Success(null)); - final result = await mock.getHealthStarted(); + final result = await repository.getHealthStarted(); expect(result.isSuccess, true); }); test('getHealthStarted Internal Server Error', () async { - when(mock.getHealthStarted()).thenAnswer((_) async { - return Failure(NetworkErrors.internalServerError); - }); - - final result = await mock.getHealthStarted(); - + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.internalServerError), + null, + ), + ); + final result = await repository.getHealthStarted(); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.internalServerError)); }); test('getHealthStarted Service Unavailable', () async { - when(mock.getHealthStarted()).thenAnswer((_) async { - return Failure(NetworkErrors.serviceUnavailable); - }); - final result = await mock.getHealthStarted(); + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.serviceUnavailable), + null, + ), + ); + final result = await repository.getHealthStarted(); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.serviceUnavailable)); }); test('getHealthReady success', () async { - when(mock.getHealthReady()).thenAnswer((_) async => Success(null)); - final result = await mock.getHealthReady(); + final repository = setupRepository( + chopper.Response(http.Response('', HttpStatus.noContent), null), + ); + final result = await repository.getHealthReady(); expect(result.isSuccess, true); }); + test('getHealthReady Internal Server Error', () async { - when(mock.getHealthReady()).thenAnswer((_) async { - return Failure(NetworkErrors.internalServerError); - }); - final result = await mock.getHealthReady(); + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.internalServerError), + null, + ), + ); + final result = await repository.getHealthReady(); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.internalServerError)); }); test('getHealthReady Service Unavailable', () async { - when(mock.getHealthReady()).thenAnswer((_) async { - return Failure(NetworkErrors.serviceUnavailable); - }); - final result = await mock.getHealthReady(); + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.serviceUnavailable), + null, + ), + ); + final result = await repository.getHealthReady(); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.serviceUnavailable)); }); test('getHealthLive success', () async { - when(mock.getHealthLive()).thenAnswer((_) async => Success(null)); - final result = await mock.getHealthLive(); + final repository = setupRepository( + chopper.Response(http.Response('', HttpStatus.ok), null), + ); + final result = await repository.getHealthLive(); expect(result.isSuccess, true); }); test('getHealthLive Internal Server Error', () async { - when(mock.getHealthLive()).thenAnswer((_) async { - return Failure(NetworkErrors.internalServerError); - }); - final result = await mock.getHealthLive(); + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.internalServerError), + null, + ), + ); + final result = await repository.getHealthLive(); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.internalServerError)); }); test('getHealthLive Service Unavailable', () async { - when(mock.getHealthLive()).thenAnswer((_) async { - return Failure(NetworkErrors.serviceUnavailable); - }); - final result = await mock.getHealthLive(); + final repository = setupRepository( + chopper.Response(http.Response('', 503), null), + ); + final result = await repository.getHealthLive(); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.serviceUnavailable)); }); // cspell: disable - const validStakeAddress = - 'stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw'; + const validStakeAddress = + 'stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw'; // cspell: enable const notValidStakeAddress = 'stake1wrong1stake'; + test('getCardanoStakedAdaStakeAddress success', () async { - final stakeInfo = StakeInfo( + const stakeInfo = StakeInfo( amount: 1, slotNumber: 5, - blockTime: DateTime.utc(1970), ); - when( - mock.getCardanoStakedAdaStakeAddress( - stakeAddress: validStakeAddress, - ), - ).thenAnswer((_) async => Success(stakeInfo)); - final result = await mock.getCardanoStakedAdaStakeAddress( + final repository = setupRepository( + chopper.Response(http.Response('', HttpStatus.ok), stakeInfo), + ); + final result = await repository.getCardanoStakedAdaStakeAddress( stakeAddress: validStakeAddress, ); expect(result.isSuccess, true); expect(result.success, equals(stakeInfo)); }); test('getCardanoStakedAdaStakeAddress Bad request', () async { - when( - mock.getCardanoStakedAdaStakeAddress( - stakeAddress: notValidStakeAddress, - ), - ).thenAnswer((_) async { - return Failure(NetworkErrors.badRequest); - }); - final result = await mock.getCardanoStakedAdaStakeAddress( + final repository = setupRepository( + chopper.Response(http.Response('', HttpStatus.badRequest), null), + ); + final result = await repository.getCardanoStakedAdaStakeAddress( stakeAddress: notValidStakeAddress, ); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.badRequest)); }); + test('getCardanoStakedAdaStakeAddress Not found', () async { - when( - mock.getCardanoStakedAdaStakeAddress( - stakeAddress: validStakeAddress, - ), - ).thenAnswer((_) async { - return Failure(NetworkErrors.notFound); - }); - final result = await mock.getCardanoStakedAdaStakeAddress( - stakeAddress: validStakeAddress, + final repository = setupRepository( + chopper.Response(http.Response('', HttpStatus.notFound), null), + ); + final result = await repository.getCardanoStakedAdaStakeAddress( + stakeAddress: notValidStakeAddress, ); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.notFound)); }); test('getCardanoStakedAdaStakeAddress Server Error', () async { - when( - mock.getCardanoStakedAdaStakeAddress( - stakeAddress: validStakeAddress, + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.internalServerError), + null, ), - ).thenAnswer((_) async { - return Failure(NetworkErrors.internalServerError); - }); - final result = await mock.getCardanoStakedAdaStakeAddress( + ); + final result = await repository.getCardanoStakedAdaStakeAddress( stakeAddress: validStakeAddress, ); expect(result.isFailure, true); @@ -147,47 +200,57 @@ void main() { }); test('getCardanoStakedAdaStakeAddress Service Unavailable', () async { - when(mock.getCardanoStakedAdaStakeAddress( - stakeAddress: validStakeAddress, - ),).thenAnswer((_) async { - return Failure(NetworkErrors.serviceUnavailable); - }); - final result = await mock.getCardanoStakedAdaStakeAddress( + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.serviceUnavailable), + null, + ), + ); + final result = await repository.getCardanoStakedAdaStakeAddress( stakeAddress: validStakeAddress, ); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.serviceUnavailable)); }); + test('getCardanoSyncState success', () async { + const blockHash = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + final syncState = SyncState( slotNumber: 5, - blockHash: - '0x0000000000000000000000000000000000000000000000000000000000000000', + blockHash: blockHash, lastUpdated: DateTime.utc(1970), ); - when(mock.getCardanoSyncState()).thenAnswer( - (_) async => Success(syncState), + final repository = setupRepository( + chopper.Response(http.Response('', HttpStatus.ok), syncState), ); - final result = await mock.getCardanoSyncState(); + final result = await repository.getCardanoSyncState(); expect(result.isSuccess, true); expect(result.success, equals(syncState)); }); + test('getCardanoSyncState Server Error', () async { - when(mock.getCardanoSyncState()).thenAnswer( - (_) async => Failure(NetworkErrors.internalServerError), + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.internalServerError), + null, + ), ); - final result = await mock.getCardanoSyncState(); + final result = await repository.getCardanoSyncState(); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.internalServerError)); }); test('getCardanoSyncState Service Unavailable', () async { - when(mock.getCardanoSyncState()).thenAnswer( - (_) async => Failure(NetworkErrors.serviceUnavailable), + final repository = setupRepository( + chopper.Response( + http.Response('', HttpStatus.serviceUnavailable), + null, + ), ); - final result = await mock.getCardanoSyncState(); + final result = await repository.getCardanoSyncState(); expect(result.isFailure, true); expect(result.failure, equals(NetworkErrors.serviceUnavailable)); }); - }); } diff --git a/catalyst_voices/packages/catalyst_voices_repositories/test/src/catalyst_data_gateway_repository/catalyst_data_gateway_repository_test.mocks.dart b/catalyst_voices/packages/catalyst_voices_repositories/test/src/catalyst_data_gateway_repository/catalyst_data_gateway_repository_test.mocks.dart deleted file mode 100644 index b64461a2d..000000000 --- a/catalyst_voices/packages/catalyst_voices_repositories/test/src/catalyst_data_gateway_repository/catalyst_data_gateway_repository_test.mocks.dart +++ /dev/null @@ -1,201 +0,0 @@ -// Mocks generated by Mockito 5.4.4 from annotations -// in catalyst_voices_repositories/test/src/catalyst_data_gateway_repository_test.dart. -// Do not manually edit this file. - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i4; - -import 'package:catalyst_voices_models/catalyst_voices_models.dart' as _i5; -import 'package:catalyst_voices_repositories/src/catalyst_data_gateway_repository.dart' - as _i3; -import 'package:catalyst_voices_services/generated/catalyst_gateway/cat_gateway_api.enums.swagger.dart' - as _i7; -import 'package:catalyst_voices_services/generated/catalyst_gateway/cat_gateway_api.swagger.dart' - as _i6; -import 'package:mockito/mockito.dart' as _i1; -import 'package:result_type/result_type.dart' as _i2; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: deprecated_member_use -// ignore_for_file: deprecated_member_use_from_same_package -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types -// ignore_for_file: subtype_of_sealed_class - -class _FakeResult_0 extends _i1.SmartFake implements _i2.Result { - _FakeResult_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -/// A class which mocks [CatalystDataGatewayRepository]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockCatalystDataGatewayRepository extends _i1.Mock - implements _i3.CatalystDataGatewayRepository { - @override - _i4.Future<_i2.Result> getHealthStarted() => - (super.noSuchMethod( - Invocation.method( - #getHealthStarted, - [], - ), - returnValue: _i4.Future<_i2.Result>.value( - _FakeResult_0( - this, - Invocation.method( - #getHealthStarted, - [], - ), - )), - returnValueForMissingStub: - _i4.Future<_i2.Result>.value( - _FakeResult_0( - this, - Invocation.method( - #getHealthStarted, - [], - ), - )), - ) as _i4.Future<_i2.Result>); - - @override - _i4.Future<_i2.Result> getHealthReady() => - (super.noSuchMethod( - Invocation.method( - #getHealthReady, - [], - ), - returnValue: _i4.Future<_i2.Result>.value( - _FakeResult_0( - this, - Invocation.method( - #getHealthReady, - [], - ), - )), - returnValueForMissingStub: - _i4.Future<_i2.Result>.value( - _FakeResult_0( - this, - Invocation.method( - #getHealthReady, - [], - ), - )), - ) as _i4.Future<_i2.Result>); - - @override - _i4.Future<_i2.Result> getHealthLive() => - (super.noSuchMethod( - Invocation.method( - #getHealthLive, - [], - ), - returnValue: _i4.Future<_i2.Result>.value( - _FakeResult_0( - this, - Invocation.method( - #getHealthLive, - [], - ), - )), - returnValueForMissingStub: - _i4.Future<_i2.Result>.value( - _FakeResult_0( - this, - Invocation.method( - #getHealthLive, - [], - ), - )), - ) as _i4.Future<_i2.Result>); - - @override - _i4.Future<_i2.Result<_i6.StakeInfo, _i5.NetworkErrors>> - getCardanoStakedAdaStakeAddress({ - required String? stakeAddress, - _i7.Network? network = _i7.Network.mainnet, - DateTime? dateTime, - }) => - (super.noSuchMethod( - Invocation.method( - #getCardanoStakedAdaStakeAddress, - [], - { - #stakeAddress: stakeAddress, - #network: network, - #dateTime: dateTime, - }, - ), - returnValue: - _i4.Future<_i2.Result<_i6.StakeInfo, _i5.NetworkErrors>>.value( - _FakeResult_0<_i6.StakeInfo, _i5.NetworkErrors>( - this, - Invocation.method( - #getCardanoStakedAdaStakeAddress, - [], - { - #stakeAddress: stakeAddress, - #network: network, - #dateTime: dateTime, - }, - ), - )), - returnValueForMissingStub: - _i4.Future<_i2.Result<_i6.StakeInfo, _i5.NetworkErrors>>.value( - _FakeResult_0<_i6.StakeInfo, _i5.NetworkErrors>( - this, - Invocation.method( - #getCardanoStakedAdaStakeAddress, - [], - { - #stakeAddress: stakeAddress, - #network: network, - #dateTime: dateTime, - }, - ), - )), - ) as _i4.Future<_i2.Result<_i6.StakeInfo, _i5.NetworkErrors>>); - - @override - _i4.Future<_i2.Result<_i6.SyncState, _i5.NetworkErrors>> getCardanoSyncState( - {_i7.Network? network = _i7.Network.mainnet}) => - (super.noSuchMethod( - Invocation.method( - #getCardanoSyncState, - [], - {#network: network}, - ), - returnValue: - _i4.Future<_i2.Result<_i6.SyncState, _i5.NetworkErrors>>.value( - _FakeResult_0<_i6.SyncState, _i5.NetworkErrors>( - this, - Invocation.method( - #getCardanoSyncState, - [], - {#network: network}, - ), - )), - returnValueForMissingStub: - _i4.Future<_i2.Result<_i6.SyncState, _i5.NetworkErrors>>.value( - _FakeResult_0<_i6.SyncState, _i5.NetworkErrors>( - this, - Invocation.method( - #getCardanoSyncState, - [], - {#network: network}, - ), - )), - ) as _i4.Future<_i2.Result<_i6.SyncState, _i5.NetworkErrors>>); -} diff --git a/catalyst_voices/packages/catalyst_voices_services/test/src/catalyst_voices_services_test.dart b/catalyst_voices/packages/catalyst_voices_services/test/src/catalyst_voices_services_test.dart deleted file mode 100644 index ab73b3a23..000000000 --- a/catalyst_voices/packages/catalyst_voices_services/test/src/catalyst_voices_services_test.dart +++ /dev/null @@ -1 +0,0 @@ -void main() {} diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform/io_platform.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform/io_platform.dart index 5b3db720f..6377e4512 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform/io_platform.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform/io_platform.dart @@ -27,16 +27,16 @@ final class CatalystPlatform { static bool get isWindows => Platform.isWindows; static Map get identifiers => { - PlatformKey.android: isAndroid, - PlatformKey.desktop: isDesktop, - PlatformKey.fuchsia: isFuchsia, - PlatformKey.iOS: isIOS, - PlatformKey.linux: isLinux, - PlatformKey.macOS: isMacOS, - PlatformKey.mobile: isMobile, - PlatformKey.mobileWeb: isMobileWeb, - PlatformKey.web: isWeb, - PlatformKey.webDesktop: isWebDesktop, - PlatformKey.windows: isWindows, - }; + PlatformKey.android: isAndroid, + PlatformKey.desktop: isDesktop, + PlatformKey.fuchsia: isFuchsia, + PlatformKey.iOS: isIOS, + PlatformKey.linux: isLinux, + PlatformKey.macOS: isMacOS, + PlatformKey.mobile: isMobile, + PlatformKey.mobileWeb: isMobileWeb, + PlatformKey.web: isWeb, + PlatformKey.webDesktop: isWebDesktop, + PlatformKey.windows: isWindows, + }; } diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform/web_platform.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform/web_platform.dart index a7a9d074d..e9e4c62e7 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform/web_platform.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform/web_platform.dart @@ -34,20 +34,20 @@ final class CatalystPlatform { ]; return mobileIdentifiers.any(userAgent.contains); } - + static Map get identifiers => { - PlatformKey.android: isAndroid, - PlatformKey.desktop: isDesktop, - PlatformKey.fuchsia: isFuchsia, - PlatformKey.iOS: isIOS, - PlatformKey.linux: isLinux, - PlatformKey.macOS: isMacOS, - PlatformKey.mobile: isMobile, - PlatformKey.mobileWeb: isMobileWeb, - PlatformKey.web: isWeb, - PlatformKey.webDesktop: isWebDesktop, - PlatformKey.windows: isWindows, - }; + PlatformKey.android: isAndroid, + PlatformKey.desktop: isDesktop, + PlatformKey.fuchsia: isFuchsia, + PlatformKey.iOS: isIOS, + PlatformKey.linux: isLinux, + PlatformKey.macOS: isMacOS, + PlatformKey.mobile: isMobile, + PlatformKey.mobileWeb: isMobileWeb, + PlatformKey.web: isWeb, + PlatformKey.webDesktop: isWebDesktop, + PlatformKey.windows: isWindows, + }; const CatalystPlatform._(); } diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform_aware_builder/platform_aware_builder.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform_aware_builder/platform_aware_builder.dart index 8a4442651..cfcf9266e 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform_aware_builder/platform_aware_builder.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/platform_aware_builder/platform_aware_builder.dart @@ -2,21 +2,21 @@ import 'package:catalyst_voices_shared/src/platform/catalyst_platform.dart'; import 'package:catalyst_voices_shared/src/platform/platform_key.dart'; import 'package:flutter/widgets.dart'; -// A [PlatformAwareBuilder] is a StatelessWidget that is aware of the current +// A [PlatformAwareBuilder] is a StatelessWidget that is aware of the current // platform. // // This is an abstract widget that has a required argument [builder] that can -// consume platform-specific data automatically based on platform that is +// consume platform-specific data automatically based on platform that is // detected. // // The platform detection happens in [CatalystPlatform]. // -// The widget accepts an argument for each specific platform defined in +// The widget accepts an argument for each specific platform defined in // [PlatformKey]. The platform specific [data] is selected when two conditions // are verified at the same time: // - the platform is detected // - the platform-specific argument is present -// In case those conditions are not verified the [other] argument is used (and +// In case those conditions are not verified the [other] argument is used (and // because of this it is required). // The type of the platform specific data is generic. // @@ -63,19 +63,19 @@ class PlatformAwareBuilder extends StatelessWidget { T? windows, required T other, }) : _platformData = { - PlatformKey.android: android, - PlatformKey.desktop: desktop, - PlatformKey.fuchsia: fuchsia, - PlatformKey.iOS: iOS, - PlatformKey.linux: linux, - PlatformKey.macOS: macOS, - PlatformKey.mobile: mobile, - PlatformKey.mobileWeb: mobileWeb, - PlatformKey.web: web, - PlatformKey.webDesktop: webDesktop, - PlatformKey.windows: windows, - PlatformKey.other: other, - }; + PlatformKey.android: android, + PlatformKey.desktop: desktop, + PlatformKey.fuchsia: fuchsia, + PlatformKey.iOS: iOS, + PlatformKey.linux: linux, + PlatformKey.macOS: macOS, + PlatformKey.mobile: mobile, + PlatformKey.mobileWeb: mobileWeb, + PlatformKey.web: web, + PlatformKey.webDesktop: webDesktop, + PlatformKey.windows: windows, + PlatformKey.other: other, + }; @override Widget build(BuildContext context) { @@ -84,13 +84,13 @@ class PlatformAwareBuilder extends StatelessWidget { T _getPlatformData() { final currentPlatformKey = CatalystPlatform.identifiers.entries - .firstWhere( - // We select the platform only if the platform-specific data - // is also present. - (entry) => entry.value && (_platformData[entry.key] != null), - orElse: () => const MapEntry(PlatformKey.other, true), - ) - .key; + .firstWhere( + // We select the platform only if the platform-specific data + // is also present. + (entry) => entry.value && (_platformData[entry.key] != null), + orElse: () => const MapEntry(PlatformKey.other, true), + ) + .key; return _platformData[currentPlatformKey]!; } } diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_builder.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_builder.dart index aec88b142..55da2f90d 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_builder.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_builder.dart @@ -1,17 +1,17 @@ import 'package:catalyst_voices_shared/src/responsive/responsive_breakpoint_key.dart'; import 'package:flutter/widgets.dart'; -// A [ResponsiveBuilder] is a StatelessWidget that is aware about the current +// A [ResponsiveBuilder] is a StatelessWidget that is aware about the current // device breakpoint. // // This is an abstract widget that has a required argument [builder] that can -// consume breakpoint-specific data automatically based on breakpoint that is +// consume breakpoint-specific data automatically based on breakpoint that is // detected. // -// The breakpoint is identified by using the screen width exposed by MediaQuery +// The breakpoint is identified by using the screen width exposed by MediaQuery // of the context. // -// The widget accepts an argument for each breakpoint defined in +// The widget accepts an argument for each breakpoint defined in // [ResponsiveBreakpointKey]. The breakpoint specific [data] is selected when: // - the breakpoint is detected // - the breakpoint-specific data argument is present @@ -32,7 +32,7 @@ import 'package:flutter/widgets.dart'; // ); // // or to have a specific padding: -// +// // ```dart // ResponsiveBuilder( // xs: EdgeInsets.all(4.0), @@ -57,12 +57,12 @@ class ResponsiveBuilder extends StatelessWidget { T? lg, required T other, }) : _responsiveData = { - ResponsiveBreakpointKey.xs: xs, - ResponsiveBreakpointKey.sm: sm, - ResponsiveBreakpointKey.md: md, - ResponsiveBreakpointKey.lg: lg, - ResponsiveBreakpointKey.other: other, - }; + ResponsiveBreakpointKey.xs: xs, + ResponsiveBreakpointKey.sm: sm, + ResponsiveBreakpointKey.md: md, + ResponsiveBreakpointKey.lg: lg, + ResponsiveBreakpointKey.other: other, + }; @override Widget build(BuildContext context) { @@ -73,18 +73,17 @@ class ResponsiveBuilder extends StatelessWidget { final screenWidth = MediaQuery.sizeOf(context).width; final breakpointKey = _breakpoints.entries - .firstWhere( - (entry) => ( - screenWidth >= entry.value.min && - screenWidth <= entry.value.max && - _responsiveData[entry.key] != null - ), - orElse: () => const MapEntry( - ResponsiveBreakpointKey.other, (min: 0, max: 0), - ), - ) - .key; - return _responsiveData[breakpointKey]!; + .firstWhere( + (entry) => (screenWidth >= entry.value.min && + screenWidth <= entry.value.max && + _responsiveData[entry.key] != null), + orElse: () => const MapEntry( + ResponsiveBreakpointKey.other, + (min: 0, max: 0), + ), + ) + .key; + return _responsiveData[breakpointKey]!; } final Map _breakpoints = { diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_child.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_child.dart index c7330ce7a..ec07aa2fb 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_child.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_child.dart @@ -2,17 +2,17 @@ import 'package:catalyst_voices_shared/src/responsive/responsive_breakpoint_key. import 'package:catalyst_voices_shared/src/responsive/responsive_builder.dart'; import 'package:flutter/material.dart'; -// A [ResponsiveChild] is a StatelessWidget that selects a WidgetBuilder based +// A [ResponsiveChild] is a StatelessWidget that selects a WidgetBuilder based // on the current screen size and execute it. // This is a simple wrapper around ResponsiveBuilder to simplify development and // make it explicit for a reader. // -// The possible arguments are [xs], [sm], [md], [lg], [other] following the +// The possible arguments are [xs], [sm], [md], [lg], [other] following the // the ResponsiveBuilder arguments. // [other] is required and acts as fallback. // // Example usage: -// +// // ```dart // ResponsiveChild( // xs: (context) => const Text('Simple text for extra small screens.'), @@ -43,12 +43,12 @@ class ResponsiveChild extends StatelessWidget { WidgetBuilder? lg, required WidgetBuilder other, }) : _widgets = { - ResponsiveBreakpointKey.xs: xs, - ResponsiveBreakpointKey.sm: sm, - ResponsiveBreakpointKey.md: md, - ResponsiveBreakpointKey.lg: lg, - ResponsiveBreakpointKey.other: other, - }; + ResponsiveBreakpointKey.xs: xs, + ResponsiveBreakpointKey.sm: sm, + ResponsiveBreakpointKey.md: md, + ResponsiveBreakpointKey.lg: lg, + ResponsiveBreakpointKey.other: other, + }; @override Widget build(BuildContext context) { diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_padding.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_padding.dart index 2007d5029..a19087b5b 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_padding.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_padding.dart @@ -5,16 +5,16 @@ import 'package:flutter/widgets.dart'; // A [ResponsivePadding] is a StatelessWidget that applies a padding based on // the current screen size. // -// The widget wraps its [child] in a ResponsiveBuilder that calculates the +// The widget wraps its [child] in a ResponsiveBuilder that calculates the // proper padding value based on the screen size and wraps it again in a Padding // to display the selected padding value. // -// The possible arguments are [xs], [sm], [md], [lg], [other] following the +// The possible arguments are [xs], [sm], [md], [lg], [other] following the // Material design standards and the ResponsiveBuilder arguments. // Each screen size has a default value to simplify widget usage. // // Example usage: -// +// // ```dart // ResponsivePadding( // xs: const EdgeInsets.all(4.0), @@ -38,12 +38,12 @@ class ResponsivePadding extends StatelessWidget { EdgeInsets lg = const EdgeInsets.all(16), EdgeInsets other = const EdgeInsets.all(8), }) : _paddings = { - ResponsiveBreakpointKey.xs: xs, - ResponsiveBreakpointKey.sm: sm, - ResponsiveBreakpointKey.md: md, - ResponsiveBreakpointKey.lg: lg, - ResponsiveBreakpointKey.other: other, - }; + ResponsiveBreakpointKey.xs: xs, + ResponsiveBreakpointKey.sm: sm, + ResponsiveBreakpointKey.md: md, + ResponsiveBreakpointKey.lg: lg, + ResponsiveBreakpointKey.other: other, + }; @override Widget build(BuildContext context) { @@ -59,5 +59,4 @@ class ResponsivePadding extends StatelessWidget { other: _paddings[ResponsiveBreakpointKey.other]!, ); } - } diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/platform_aware_builder_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/platform_aware_builder_test.dart index 0cec3272b..cc020619a 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/test/src/platform_aware_builder_test.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/platform_aware_builder_test.dart @@ -3,15 +3,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - Widget buildApp() => MaterialApp( - home: Scaffold( - body: PlatformAwareBuilder( - other: 'other', - builder: (context, platformData) => Text(platformData!), - ), - ), - ); + home: Scaffold( + body: PlatformAwareBuilder( + other: 'other', + builder: (context, platformData) => Text(platformData!), + ), + ), + ); group('Test platform detection', () { testWidgets('PlatformWidgetBuilder fallbacks to other', (tester) async { @@ -20,7 +19,6 @@ void main() { expect(find.byType(Text), findsOneWidget); // Check the output contains the platform that was tested. expect(find.text('other'), findsOneWidget); - }); }); } diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_builder_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_builder_test.dart index f786cd82b..a3d1cb13f 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_builder_test.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_builder_test.dart @@ -6,14 +6,15 @@ void main() { Widget buildApp( Size size, Widget child, - ) => MediaQuery( - data: MediaQueryData(size: size), - child: MaterialApp( - home: Scaffold( - body: child, - ), - ), - ); + ) => + MediaQuery( + data: MediaQueryData(size: size), + child: MaterialApp( + home: Scaffold( + body: child, + ), + ), + ); group('Test screen sizes with Text child', () { final sizesToTest = { @@ -25,77 +26,73 @@ void main() { }; for (final entry in sizesToTest.entries) { - testWidgets( - 'ResponsiveBuilder adapts to screen of width $entry.key', - (tester) async { - await tester.pumpWidget( - buildApp( - Size.fromWidth(entry.key), - ResponsiveBuilder( - xs: 'Xs device', - sm: 'Small device', - md: 'Medium device', - lg: 'Large device', - other: 'Other device', - builder: (context, data) => Text(data!), - ), + testWidgets('ResponsiveBuilder adapts to screen of width $entry.key', + (tester) async { + await tester.pumpWidget( + buildApp( + Size.fromWidth(entry.key), + ResponsiveBuilder( + xs: 'Xs device', + sm: 'Small device', + md: 'Medium device', + lg: 'Large device', + other: 'Other device', + builder: (context, data) => Text(data!), ), - ); + ), + ); - final testedElement = find.byType(Text); - // Verify the Widget renders properly - expect(testedElement, findsOneWidget); - // Verify the proper text is rendered - expect(find.text(entry.value), findsOneWidget); - } - ); + final testedElement = find.byType(Text); + // Verify the Widget renders properly + expect(testedElement, findsOneWidget); + // Verify the proper text is rendered + expect(find.text(entry.value), findsOneWidget); + }); } }); group('Test screen sizes with specific Padding', () { final sizesToTest = { 280.0: const EdgeInsets.all(2), - 620.0: const EdgeInsets.all(4), + 620.0: const EdgeInsets.all(4), 1280.0: const EdgeInsets.all(8), 1600.0: const EdgeInsets.all(16), 3000.0: const EdgeInsets.all(32), }; for (final entry in sizesToTest.entries) { - testWidgets( - 'ResponsiveBuilder adapts to screen of width $entry.key', - (tester) async { - await tester.pumpWidget( - buildApp( - Size.fromWidth(entry.key), - ResponsiveBuilder( - xs: const EdgeInsets.all(2), - sm: const EdgeInsets.all(4), - md: const EdgeInsets.all(8), - lg: const EdgeInsets.all(16), - other: const EdgeInsets.all(32), - builder: (context, padding) => Padding( - padding: padding!, - child: const Text('Test'), - ), + testWidgets('ResponsiveBuilder adapts to screen of width $entry.key', + (tester) async { + await tester.pumpWidget( + buildApp( + Size.fromWidth(entry.key), + ResponsiveBuilder( + xs: const EdgeInsets.all(2), + sm: const EdgeInsets.all(4), + md: const EdgeInsets.all(8), + lg: const EdgeInsets.all(16), + other: const EdgeInsets.all(32), + builder: (context, padding) => Padding( + padding: padding!, + child: const Text('Test'), ), ), - ); + ), + ); - final testedElement = find.byType(Text); - // Verify the Widget renders properly - expect(testedElement, findsOneWidget); + final testedElement = find.byType(Text); + // Verify the Widget renders properly + expect(testedElement, findsOneWidget); - final paddingWidget = tester.widget( - find.ancestor( - of: testedElement, - matching: find.byType(Padding), - ), - ); - // Check that the padding corresponds - expect(paddingWidget.padding, entry.value); - } - ); + final paddingWidget = tester.widget( + find.ancestor( + of: testedElement, + matching: find.byType(Padding), + ), + ); + // Check that the padding corresponds + expect(paddingWidget.padding, entry.value); + }); } }); } diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_child_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_child_test.dart index eeb1cc5f5..0658fd898 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_child_test.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_child_test.dart @@ -4,89 +4,82 @@ import 'package:flutter_test/flutter_test.dart'; void main() { Widget buildApp(Size size) => MediaQuery( - data: MediaQueryData(size: size), - child: MaterialApp( - home: Scaffold( - body: ResponsiveChild( - xs: (context) => const Text('Simple text for extra small screens.'), - sm: (context) => const Padding( - padding: EdgeInsets.all(50), - child: Text('Text with padding for small screens.'), + data: MediaQueryData(size: size), + child: MaterialApp( + home: Scaffold( + body: ResponsiveChild( + xs: (context) => + const Text('Simple text for extra small screens.'), + sm: (context) => const Padding( + padding: EdgeInsets.all(50), + child: Text('Text with padding for small screens.'), + ), + md: (context) => const Column( + children: [ + Text('This is'), + Text('a set'), + Text('of Texts'), + Text('for medium screens.'), + ], + ), + other: (context) => const Text('The fallback widget.'), + ), ), - md: (context) => const Column( - children: [ - Text('This is'), - Text('a set'), - Text('of Texts'), - Text('for medium screens.'), - ], - ), - other: (context) => const Text('The fallback widget.'), ), - ), - ), - ); + ); group('Test screen sizes', () { - testWidgets( - 'ResponsiveChild outputs a text for extra-small screens', - (tester) async { - await tester.pumpWidget( - buildApp(const Size.fromWidth(280)), - ); - final testedElement = find.byType(Text); - expect(testedElement, findsOneWidget); - expect( - find.text('Simple text for extra small screens.'), - findsOneWidget, - ); - } - ); - testWidgets( - 'ResponsiveChild outputs a text with padding for small screens', - (tester) async { - await tester.pumpWidget( - buildApp(const Size.fromWidth(620)), - ); - final testedElement = find.byType(Text); - expect(testedElement, findsOneWidget); - expect( - find.text('Text with padding for small screens.'), - findsOneWidget, - ); - final paddingWidget = tester.widget( - find.ancestor( - of: testedElement, - matching: find.byType(Padding), - ), - ); - expect(paddingWidget.padding, const EdgeInsets.all(50)); - } - ); - testWidgets( - 'ResponsiveChild outputs four texts for medium screens', - (tester) async { - await tester.pumpWidget( - buildApp(const Size.fromWidth(1280)), - ); - final testedElements = find.byType(Text); - expect(testedElements, findsExactly(4)); - expect(find.text('This is'), findsOneWidget); - expect(find.text('a set'), findsOneWidget); - expect(find.text('of Texts'), findsOneWidget); - expect(find.text('for medium screens.'), findsOneWidget); - } - ); - testWidgets( - 'ResponsiveChild fallback to other for large screens', - (tester) async { - await tester.pumpWidget( - buildApp(const Size.fromWidth(1600)), - ); - final testedElements = find.byType(Text); - expect(testedElements, findsOneWidget); - expect(find.text('The fallback widget.'), findsOneWidget); - } - ); + testWidgets('ResponsiveChild outputs a text for extra-small screens', + (tester) async { + await tester.pumpWidget( + buildApp(const Size.fromWidth(280)), + ); + final testedElement = find.byType(Text); + expect(testedElement, findsOneWidget); + expect( + find.text('Simple text for extra small screens.'), + findsOneWidget, + ); + }); + testWidgets('ResponsiveChild outputs a text with padding for small screens', + (tester) async { + await tester.pumpWidget( + buildApp(const Size.fromWidth(620)), + ); + final testedElement = find.byType(Text); + expect(testedElement, findsOneWidget); + expect( + find.text('Text with padding for small screens.'), + findsOneWidget, + ); + final paddingWidget = tester.widget( + find.ancestor( + of: testedElement, + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsets.all(50)); + }); + testWidgets('ResponsiveChild outputs four texts for medium screens', + (tester) async { + await tester.pumpWidget( + buildApp(const Size.fromWidth(1280)), + ); + final testedElements = find.byType(Text); + expect(testedElements, findsExactly(4)); + expect(find.text('This is'), findsOneWidget); + expect(find.text('a set'), findsOneWidget); + expect(find.text('of Texts'), findsOneWidget); + expect(find.text('for medium screens.'), findsOneWidget); + }); + testWidgets('ResponsiveChild fallback to other for large screens', + (tester) async { + await tester.pumpWidget( + buildApp(const Size.fromWidth(1600)), + ); + final testedElements = find.byType(Text); + expect(testedElements, findsOneWidget); + expect(find.text('The fallback widget.'), findsOneWidget); + }); }); } diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_padding_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_padding_test.dart index 972173b82..18c88bd25 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_padding_test.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_padding_test.dart @@ -3,23 +3,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - Widget buildApp(Size size) => MediaQuery( - data: MediaQueryData(size: size), - child: MaterialApp( - home: Scaffold( - body: ResponsivePadding( - xs: const EdgeInsets.all(2), - sm: const EdgeInsets.symmetric(vertical: 3), - md: const EdgeInsets.symmetric(horizontal: 4), - lg: const EdgeInsets.only(top: 5), - child: const Text('Test data!'), + data: MediaQueryData(size: size), + child: MaterialApp( + home: Scaffold( + body: ResponsivePadding( + xs: const EdgeInsets.all(2), + sm: const EdgeInsets.symmetric(vertical: 3), + md: const EdgeInsets.symmetric(horizontal: 4), + lg: const EdgeInsets.only(top: 5), + child: const Text('Test data!'), + ), + ), ), - ), - ), - ); + ); group('Test screen sizes', () { - final sizesToTest = { 280.0: const EdgeInsets.all(2), 620.0: const EdgeInsets.symmetric(vertical: 3), @@ -28,27 +26,24 @@ void main() { }; for (final entry in sizesToTest.entries) { - testWidgets( - 'ResponsivePadding adapts to screen of width $entry.key', - (tester) async { - await tester.pumpWidget( - buildApp(Size.fromWidth(entry.key)), - ); + testWidgets('ResponsivePadding adapts to screen of width $entry.key', + (tester) async { + await tester.pumpWidget( + buildApp(Size.fromWidth(entry.key)), + ); - final testedElement = find.byType(Text); - // Verify the Widget renders properly - expect(testedElement, findsOneWidget); - final paddingWidget = tester.widget( - find.ancestor( - of: testedElement, - matching: find.byType(Padding), - ), - ); - // Check that the padding corresponds - expect(paddingWidget.padding, entry.value); - } - ); + final testedElement = find.byType(Text); + // Verify the Widget renders properly + expect(testedElement, findsOneWidget); + final paddingWidget = tester.widget( + find.ancestor( + of: testedElement, + matching: find.byType(Padding), + ), + ); + // Check that the padding corresponds + expect(paddingWidget.padding, entry.value); + }); } - }); } diff --git a/catalyst_voices/packages/catalyst_voices_view_models/test/src/catalyst_voices_view_models_test.dart b/catalyst_voices/packages/catalyst_voices_view_models/test/src/catalyst_voices_view_models_test.dart deleted file mode 100644 index b0d5ab372..000000000 --- a/catalyst_voices/packages/catalyst_voices_view_models/test/src/catalyst_voices_view_models_test.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:test/test.dart'; - -void main() { - group('CatalystVoicesViewModels', () {}); -} diff --git a/catalyst_voices/test/app/view/app_test.dart b/catalyst_voices/test/app/view/app_test.dart index ab73b3a23..28bfe199c 100644 --- a/catalyst_voices/test/app/view/app_test.dart +++ b/catalyst_voices/test/app/view/app_test.dart @@ -1 +1,11 @@ -void main() {} +import 'package:catalyst_voices/pages/coming_soon/coming_soon.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + testWidgets('placeholder_test', (tester) async { + await tester.pumpApp(const ComingSoonPage()); + await tester.pumpAndSettle(); + }); +} diff --git a/melos.yaml b/melos.yaml index ca213ddd4..8d7cfb2b6 100644 --- a/melos.yaml +++ b/melos.yaml @@ -2,7 +2,7 @@ name: catalyst_voices repository: https://github.com/input-output-hk/catalyst-voices packages: - - catalyst_voices/* + - catalyst_voices - catalyst_voices/packages/** - catalyst_voices_packages/** - utilities/* @@ -13,7 +13,7 @@ command: workspaceChangelog: true bootstrap: environment: - sdk: '>=3.3.0 <4.0.0' + sdk: ">=3.3.0 <4.0.0" flutter: 3.19.5 dependencies: bloc_concurrency: ^0.2.2 @@ -29,52 +29,61 @@ command: mocktail: ^1.0.1 scripts: - lint: - run: melos run analyze - description: Run all static analysis checks. - - format: - run: melos exec dart format . --fix - description: Run `dart format` for all packages. - - format-check: - run: melos exec dart format . --set-exit-if-changed - description: Run `dart format` checks for all packages. - - analyze: - # We are setting the concurrency to 1 because a higher concurrency can crash - # the analysis server on low performance machines (like GitHub Actions). - run: | - melos exec -c 1 -- \ - dart analyze . --fatal-infos - description: | - Run `dart analyze` in all packages. - - Note: you can also rely on your IDEs Dart Analysis / Issues window. - metrics: run: | - melos exec -c 1 --ignore="*example*" -- \ + melos exec -c 1 -- \ flutter pub run dart_code_metrics:metrics analyze description: | Run `dart_code_metrics` in all packages. - Note: you can also rely on your IDEs Dart Analysis / Issues window. + format: + run: | + melos exec -c 1 --dir-exists="lib" -- dart format lib --fix + melos exec -c 1 --dir-exists="test" -- dart format test --fix + melos exec -c 1 --dir-exists="integration_test" -- dart format integration_test --fix + description: Run `dart format` for all packages. + + format-check: + run: | + melos exec -c 1 --dir-exists="lib" -- dart format lib --set-exit-if-changed + melos exec -c 1 --dir-exists="test" -- dart format test --set-exit-if-changed + melos exec -c 1 --dir-exists="integration_test" -- dart format integration_test --set-exit-if-changed + description: Run `dart format` checks for all packages. + test:select: - run: melos exec -- flutter test + run: | + melos exec -c 1 --dir-exists="test" -- flutter test description: Run `flutter test` for selected packages. test: - run: melos run test:select --no-select - description: Run all Flutter tests in this project. + run: | + melos run test:select --no-select + description: Run `flutter test` for all packages. - coverage: + test-report: run: | - melos exec -- flutter test --coverage && - melos exec -- genhtml coverage/lcov.info --output-directory=coverage/ - description: Generate coverage for the selected package. + # cleanup from previous build and create new folders + rm -fR $MELOS_ROOT_PATH/test_reports + mkdir $MELOS_ROOT_PATH/test_reports + + # run tests, generate junit reports in /test_reports/ & code coverage report + melos exec -c 1 --dir-exists="test" -- \ + "flutter test --reporter expanded --coverage --machine | tojunit --output MELOS_ROOT_PATH/test_reports/MELOS_PACKAGE_NAME.junit-report.xml" - build:pub_get:all: - run: flutter pub get - exec: - concurrency: 6 - description: Install all dependencies \ No newline at end of file + # removes code coverage for generated code + melos exec -c 1 --dir-exists="test" -- \ + lcov --remove coverage/lcov.info -o coverage/lcov.info --ignore-errors unused,unused \ + '*.swagger.dart' \ + '*.chopper.dart' \ + '*.g.dart' \ + '*.freezed.dart' \ + 'lib/generated/**' + + # converts coverage to a HTML report + melos exec -c 1 --dir-exists="test" -- \ + "genhtml coverage/lcov.info --output-directory=coverage/" + description: | + Run `flutter test` for all packages and generate junit and coverage reports. + The test reports are saved in /test_reports folder. + The coverages are saved for each package separately in /coverage folder. diff --git a/utilities/catalyst_voices_remote_widgets/test/src/catalyst_voices_remote_widgets_test.dart b/utilities/catalyst_voices_remote_widgets/test/src/catalyst_voices_remote_widgets_test.dart deleted file mode 100644 index ab73b3a23..000000000 --- a/utilities/catalyst_voices_remote_widgets/test/src/catalyst_voices_remote_widgets_test.dart +++ /dev/null @@ -1 +0,0 @@ -void main() {}