From 410046144611bcae89dbf67c9749202d8b61b8b8 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Fri, 6 Nov 2026 11:59:26 +0700 Subject: [PATCH] fix(linux): match the dialog tests to the TLS default and keep cloud stack tests out of CI --- .config/nextest.toml | 5 +- .../crates/app/src/ui/connect_dialog/tests.rs | 6 +- .../src/ui/preferences_dialog/account_page.rs | 142 +++++++++--------- linux/docs/testing.md | 2 +- 4 files changed, 83 insertions(+), 72 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index 4910032d63..5f6732a2b1 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -15,13 +15,16 @@ app = { max-threads = 1 } # tiberius 0.13 stops reading at the first end of message, so an attention # sent while SQL Server is still waiting is never acknowledged and the # connection is dropped: https://github.com/tiberius-rs/tiberius/issues/459. -# Remove once a release has the fix. +# Remove once a release has the fix. The tests against a tablepro-cloud +# stack run by hand (linux/docs/testing.md, tablepro-cloud stack): CI +# cannot reach the private backend. default-filter = """ not (package(tablepro-driver-mssql) & ( test(=cancellation::editor_waitfor_cancel_then_select1_same_client) | test(=cancellation::pooled_query_bound_waitfor_deadline_server_cancelled_client_reused) | test(=conformance_engine::suite::editor_cancel_reports_cancelled_and_session_stays_ready) )) +& not (test(/::cloud_stack::/) | binary(cloud_stack)) """ [[profile.default.overrides]] diff --git a/linux/crates/app/src/ui/connect_dialog/tests.rs b/linux/crates/app/src/ui/connect_dialog/tests.rs index 7956844f70..d56555fbf9 100644 --- a/linux/crates/app/src/ui/connect_dialog/tests.rs +++ b/linux/crates/app/src/ui/connect_dialog/tests.rs @@ -1569,6 +1569,7 @@ fn the_socket_switch_trades_the_host_for_the_socket_and_back() { let harness = dialog(); select(&harness.dialog, "mysql"); fill_network(&harness.dialog); + select_mode(&harness.dialog, TlsMode::Require); let imp = harness.dialog.imp(); assert!(imp.socket_row.is_visible()); assert!(imp.tls_section.is_visible() && imp.ssh_section.is_visible()); @@ -1593,7 +1594,7 @@ fn the_socket_switch_trades_the_host_for_the_socket_and_back() { assert!(imp.port_row.is_visible() && imp.tls_section.is_visible() && imp.ssh_section.is_visible()); assert_eq!( imp.tls_section.form_state().mode, - TlsMode::VerifyFull, + TlsMode::Require, "the network's TLS came back" ); } @@ -1614,9 +1615,10 @@ fn a_path_typed_as_the_host_is_a_socket_folder() { imp.host_row.set_text("db.example.com"); assert_eq!( imp.tls_section.form_state().mode, - TlsMode::VerifyFull, + TlsMode::Prefer, "a host typed over the socket kept the socket's TLS off" ); + select_mode(&harness.dialog, TlsMode::VerifyFull); imp.host_row.set_text("/var/run/postgresql"); select(&harness.dialog, "mongodb"); diff --git a/linux/crates/app/src/ui/preferences_dialog/account_page.rs b/linux/crates/app/src/ui/preferences_dialog/account_page.rs index b1a13fc405..271ddabd5b 100644 --- a/linux/crates/app/src/ui/preferences_dialog/account_page.rs +++ b/linux/crates/app/src/ui/preferences_dialog/account_page.rs @@ -2219,76 +2219,82 @@ mod tests { drop(window); } - #[gtk4::test] - #[ignore = "requires a tablepro-cloud stack"] - fn the_page_signs_in_through_the_browser_shows_the_account_and_signs_out() { - let read = - |name: &str| std::env::var(name).unwrap_or_else(|_| panic!("{name} is not set; see linux/docs/testing.md")); - let email = format!( - "linux-page-{}@{}", - &uuid::Uuid::new_v4().simple().to_string()[..8], - read("TABLEPRO_CLOUD_EMAIL_DOMAIN") - ); - let harness = Harness::new(None); - let endpoints = Endpoints::new(&read("TABLEPRO_CLOUD_ACCOUNT_URL"), &read("TABLEPRO_CLOUD_API_URL")) - .expect("the endpoints"); - let keys = - tablepro_account::EntitlementKeys::from_public_keys([read("TABLEPRO_CLOUD_ENTITLEMENT_KEY").as_str()]) - .expect("the stack's key"); - let machine = MachineId::derive(&format!("{}\n", uuid::Uuid::new_v4().simple())).expect("a machine ID"); - let browser = Arc::new(StackBrowser { - email: email.clone(), - profile: tempfile::tempdir().expect("a browser profile"), - }); - harness.ready_with(endpoints, keys, machine, browser); - let collector = crate::test_support::PostHogCollector::start(); - harness.share_usage_data(&collector); - let window = OwnedWindow::presented(adw::Window::builder().default_width(900).default_height(900).build()); - window.set_content(Some(&harness.page)); - let shows = |title: &str| { - harness.settle(); - descendants(&harness.page) - .into_iter() - .filter_map(|widget| widget.downcast::().ok()) - .any(|row| row.title() == title) - }; + // nextest leaves `::cloud_stack::` out of every run: CI cannot reach the private backend. + mod cloud_stack { + use super::*; + + #[gtk4::test] + #[ignore = "requires a tablepro-cloud stack"] + fn the_page_signs_in_through_the_browser_shows_the_account_and_signs_out() { + let read = |name: &str| { + std::env::var(name).unwrap_or_else(|_| panic!("{name} is not set; see linux/docs/testing.md")) + }; + let email = format!( + "linux-page-{}@{}", + &uuid::Uuid::new_v4().simple().to_string()[..8], + read("TABLEPRO_CLOUD_EMAIL_DOMAIN") + ); + let harness = Harness::new(None); + let endpoints = Endpoints::new(&read("TABLEPRO_CLOUD_ACCOUNT_URL"), &read("TABLEPRO_CLOUD_API_URL")) + .expect("the endpoints"); + let keys = + tablepro_account::EntitlementKeys::from_public_keys([read("TABLEPRO_CLOUD_ENTITLEMENT_KEY").as_str()]) + .expect("the stack's key"); + let machine = MachineId::derive(&format!("{}\n", uuid::Uuid::new_v4().simple())).expect("a machine ID"); + let browser = Arc::new(StackBrowser { + email: email.clone(), + profile: tempfile::tempdir().expect("a browser profile"), + }); + harness.ready_with(endpoints, keys, machine, browser); + let collector = crate::test_support::PostHogCollector::start(); + harness.share_usage_data(&collector); + let window = OwnedWindow::presented(adw::Window::builder().default_width(900).default_height(900).build()); + window.set_content(Some(&harness.page)); + let shows = |title: &str| { + harness.settle(); + descendants(&harness.page) + .into_iter() + .filter_map(|widget| widget.downcast::().ok()) + .any(|row| row.title() == title) + }; - harness.button(&harness.page, "Sign In…").emit_clicked(); - wait_until(Duration::from_secs(120), || shows("Starter") && shows("Ada's laptop")) - .expect("signed in, with the plan and this computer"); + harness.button(&harness.page, "Sign In…").emit_clicked(); + wait_until(Duration::from_secs(120), || shows("Starter") && shows("Ada's laptop")) + .expect("signed in, with the plan and this computer"); - let imp = harness.page.imp(); - assert_eq!(harness.titles(&imp.account_group), [email.as_str(), "Manage Account"]); - assert!( - harness - .row("Ada's laptop") - .subtitle() - .is_some_and(|text| text.starts_with("This computer")) - ); - // An admin's grant, as the browser step writes it: no cycle and no Billing…. - assert_eq!(harness.row("Starter").subtitle().as_deref(), Some("Personal · Active")); - assert!( - descendants(&harness.row("Starter")) - .iter() - .all(|widget| !widget.is::()) - ); - assert!( - harness - .titles(&imp.offline_group) - .iter() - .any(|title| title.starts_with("Verified")) - ); + let imp = harness.page.imp(); + assert_eq!(harness.titles(&imp.account_group), [email.as_str(), "Manage Account"]); + assert!( + harness + .row("Ada's laptop") + .subtitle() + .is_some_and(|text| text.starts_with("This computer")) + ); + // An admin's grant, as the browser step writes it: no cycle and no Billing…. + assert_eq!(harness.row("Starter").subtitle().as_deref(), Some("Personal · Active")); + assert!( + descendants(&harness.row("Starter")) + .iter() + .all(|widget| !widget.is::()) + ); + assert!( + harness + .titles(&imp.offline_group) + .iter() + .any(|title| title.starts_with("Verified")) + ); - harness.button(&imp.account_group.get(), "Sign Out").emit_clicked(); - wait_until(WAIT, || shows("Sign In to TablePro")).expect("signed out"); - wait_until(WAIT, || !collector.named("signed_out").is_empty()).expect("the sign-out sent"); - let identify = collector.named("$identify"); - assert_eq!(identify.len(), 1); - let account = identify[0]["distinct_id"].as_str().unwrap_or_default().to_owned(); - assert!(account.starts_with("acct_"), "{account}"); - assert_eq!(collector.named("signed_in")[0]["distinct_id"], account.as_str()); - assert_eq!(collector.named("signed_in")[0]["properties"]["method"], "interactive"); - assert_eq!(collector.named("signed_out")[0]["distinct_id"], account.as_str()); - drop(window); + harness.button(&imp.account_group.get(), "Sign Out").emit_clicked(); + wait_until(WAIT, || shows("Sign In to TablePro")).expect("signed out"); + wait_until(WAIT, || !collector.named("signed_out").is_empty()).expect("the sign-out sent"); + let identify = collector.named("$identify"); + assert_eq!(identify.len(), 1); + let account = identify[0]["distinct_id"].as_str().unwrap_or_default().to_owned(); + assert!(account.starts_with("acct_"), "{account}"); + assert_eq!(collector.named("signed_in")[0]["distinct_id"], account.as_str()); + assert_eq!(collector.named("signed_in")[0]["properties"]["method"], "interactive"); + assert_eq!(collector.named("signed_out")[0]["distinct_id"], account.as_str()); + drop(window); + } } } diff --git a/linux/docs/testing.md b/linux/docs/testing.md index 5bd744fc0b..d2c7a22976 100644 --- a/linux/docs/testing.md +++ b/linux/docs/testing.md @@ -288,7 +288,7 @@ Three reasons are allowed: - `#[ignore = "requires docker"]` for testcontainers suites. - `#[ignore = "requires a Secret Service"]` for tests that talk to a keyring. - `#[ignore = "requires a SQL Server on this Windows machine"]` for the SQL Server driver's integrated login through SSPI (`tests/integration/windows_server.rs`). SQL Server installs on x64 Windows only, so `core.yml`'s `sql-server-windows` job runs them on `windows-2025`: `tests/windows/sql-server.ps1 -Install` sets up SQL Server 2022 Express with a self-signed certificate, the `sspi` tests sign in over TDS 7.4 and strict TLS, `-ForceStrict` turns on Force Strict Encryption, and the `force_strict` test checks that a TDS 7.4 login is refused and a strict one gets in. The runner is in no domain, so SSPI signs in with NTLM. -- `#[ignore = "requires a tablepro-cloud stack"]` for the account's tests against a running backend ([below](#tablepro-cloud-stack)). +- `#[ignore = "requires a tablepro-cloud stack"]` for the account's tests against a running backend ([below](#tablepro-cloud-stack)). They live in a `cloud_stack` test binary or module, which nextest's default filter in `.config/nextest.toml` leaves out, so the Docker jobs' `--run-ignored only` never picks them up. The workspace denies `clippy::ignore_without_reason`, so a bare `#[ignore]` fails the build.