diff --git a/.devcontainer/keycloak/import/tenant-90505c8a-23a9-4cdf-a26b-4e19f6a097d5.json b/.devcontainer/keycloak/import/tenant-90505c8a-23a9-4cdf-a26b-4e19f6a097d5.json index 0d02a4d5741..e71254f7904 100644 --- a/.devcontainer/keycloak/import/tenant-90505c8a-23a9-4cdf-a26b-4e19f6a097d5.json +++ b/.devcontainer/keycloak/import/tenant-90505c8a-23a9-4cdf-a26b-4e19f6a097d5.json @@ -2294,7 +2294,7 @@ { "id": "6d44ec97-33ea-4a03-b155-76cb191fc610", "name": "sign-key-ceremony", - "description": "Sign: confirm a key share", + "description": "Sign: confirm custody", "composite": false, "clientRole": false, "containerId": "9c67adb8-ae12-43ad-a3f4-2bc275fc888a", @@ -2303,7 +2303,7 @@ { "id": "e004425b-71da-4e36-9b24-6ff783c24716", "name": "sign-tally-key", - "description": "Sign: contribute a key share", + "description": "Sign: authorize tally custody", "composite": false, "clientRole": false, "containerId": "9c67adb8-ae12-43ad-a3f4-2bc275fc888a", diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c03cabe40ec..0961cc0cfbf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -249,7 +249,7 @@ jobs: CARGO_PROFILE_DEV_DEBUG: "0" CARGO_PROFILE_TEST_DEBUG: "0" CARGO_INCREMENTAL: "0" - WBRAID_BOARD_DATABASE_URL: postgres://test:test@127.0.0.1:5432/test + WBRAID_BOARD_DATABASE_URL: postgres://test:test@127.0.0.1:3322/test # The ballot box seal tests post to boards of their own on this server. services: postgres: diff --git a/docs/docusaurus/docs/02-election_managers/02-reference/02-election-event/16-signatures/06-election_management_election-event_signatures_permissions.md b/docs/docusaurus/docs/02-election_managers/02-reference/02-election-event/16-signatures/06-election_management_election-event_signatures_permissions.md index 8577444b0f9..1601e06c8c4 100644 --- a/docs/docusaurus/docs/02-election_managers/02-reference/02-election-event/16-signatures/06-election_management_election-event_signatures_permissions.md +++ b/docs/docusaurus/docs/02-election_managers/02-reference/02-election-event/16-signatures/06-election_management_election-event_signatures_permissions.md @@ -37,8 +37,8 @@ for these permissions, and how existing tenants get them, are in | `sign-transmit-results` | Sign: transmit results | Sign a results package. | | `sign-approve-voter` | Sign: approve a voter manually | Sign a manual voter approval. | | `sign-approve-configuration` | Sign: approve a configuration version | Sign a configuration version. | -| `sign-key-ceremony` | Sign: confirm a key share | Approve one's own public custody proof in the key ceremony. | -| `sign-tally-key` | Sign: contribute a key share | Approve one's own public custody proof bound to the tally session. | +| `sign-key-ceremony` | Sign: confirm custody | Approve one's own public custody proof in the key ceremony. | +| `sign-tally-key` | Sign: authorize tally custody | Approve one's own public custody proof bound to the tally session. | A `sign-` permission is what **Who can sign** shows for the action. Signing also needs access to the request's Post through the user's permission labels (an unlabelled diff --git a/hasura/metadata/actions.graphql b/hasura/metadata/actions.graphql index 24f91b8ede9..ac86a64fab6 100644 --- a/hasura/metadata/actions.graphql +++ b/hasura/metadata/actions.graphql @@ -1648,7 +1648,6 @@ type CustodyProofSignatureStatusOutput { type AuthorizeTallyCustodyOutput { is_valid: Boolean! signing_request: SigningRequestSummary - outcome: RestorePrivateKeyOutcome! } type InsertCastVoteOutput { diff --git a/packages/Dockerfile.cargo-packages b/packages/Dockerfile.cargo-packages index 7036a7d4313..e25f8529311 100644 --- a/packages/Dockerfile.cargo-packages +++ b/packages/Dockerfile.cargo-packages @@ -41,7 +41,7 @@ RUN case "${TARGETARCH}" in \ echo "JAVA_FILENAME=${JAVA_FILENAME}" >> /etc/profile.d/java.sh; \ apt-get update && \ apt-get install -y --no-install-recommends \ - m4 make chromium libssl-dev protobuf-compiler libprotobuf-dev curl pkg-config wget software-properties-common && \ + m4 make chromium libssl-dev protobuf-compiler libprotobuf-dev curl pkg-config wget software-properties-common unzip && \ wget https://download.java.net/java/GA/jdk${JAVA_VERSION}/${JAVA_BUILD}/GPL/${JAVA_FILENAME} \ && tar xvf ${JAVA_FILENAME} \ && mv jdk-${JAVA_VERSION} /usr/local/ \ @@ -51,4 +51,20 @@ RUN case "${TARGETARCH}" in \ cargo binstall -y cargo-watch && \ # cleanups apt-get clean && \ - apt-get autoremove \ No newline at end of file + apt-get autoremove + +# The PDF renderer starts Chrome with --single-process, which Debian's current +# Chromium no longer starts with. Where Chrome for Testing is built, it gets the +# headless shell pinned in .github/actions/setup-test-browser, found before +# chromium on the PATH; chromium stays for its libraries and for arm64. +ENV PDF_BROWSER_VERSION=153.0.8010.36 +ENV PDF_BROWSER_SHA256=a0079df5617da34bcd1debad18196568b072ec3d8ec57944008972a4fc970580 +RUN if [ "${TARGETARCH}" = "amd64" ]; then \ + curl --fail --location --silent --show-error --output /tmp/browser.zip \ + https://storage.googleapis.com/chrome-for-testing-public/${PDF_BROWSER_VERSION}/linux64/chrome-headless-shell-linux64.zip && \ + echo "${PDF_BROWSER_SHA256} /tmp/browser.zip" | sha256sum --check && \ + unzip -q /tmp/browser.zip -d /opt && \ + rm /tmp/browser.zip && \ + ln -s /opt/chrome-headless-shell-linux64/chrome-headless-shell /usr/local/bin/google-chrome-stable && \ + google-chrome-stable --version; \ + fi diff --git a/packages/admin-portal/rust/sequent-core-0.1.0.tgz b/packages/admin-portal/rust/sequent-core-0.1.0.tgz index f3ae2f4ae7c..068c1987c38 100644 Binary files a/packages/admin-portal/rust/sequent-core-0.1.0.tgz and b/packages/admin-portal/rust/sequent-core-0.1.0.tgz differ diff --git a/packages/admin-portal/src/components/keys-ceremony/AdminWizard.stories.tsx b/packages/admin-portal/src/components/keys-ceremony/AdminWizard.stories.tsx index 8f37583c919..ac930da1f1d 100644 --- a/packages/admin-portal/src/components/keys-ceremony/AdminWizard.stories.tsx +++ b/packages/admin-portal/src/components/keys-ceremony/AdminWizard.stories.tsx @@ -86,12 +86,6 @@ const meta = { current: {control: "select", options: ["none", ...Object.values(EStatus)]}, stored: {control: "select", options: Object.values(EStatus)}, }, - parameters: { - expectedFailure: { - reason: "The ceremony status chip puts white text on its light status colour, and the progress and logs accordions are two unnamed regions.", - a11y: ["color-contrast", "landmark-unique"], - }, - }, beforeEach: async ({args}) => { data = resourceBoundary({ [KEYS_CEREMONY_RESOURCE]: [statusCeremony(args.stored, args.automated)], @@ -128,7 +122,6 @@ export const CeremonyInProgress: Story = { export const Configure: Story = { args: {current: "none"}, - parameters: {expectedFailure: null}, play: async ({canvasElement}) => { const canvas = within(canvasElement) await expect( diff --git a/packages/admin-portal/src/components/keys-ceremony/StartStep.stories.tsx b/packages/admin-portal/src/components/keys-ceremony/StartStep.stories.tsx index 083ea1db9ee..0132510e0d8 100644 --- a/packages/admin-portal/src/components/keys-ceremony/StartStep.stories.tsx +++ b/packages/admin-portal/src/components/keys-ceremony/StartStep.stories.tsx @@ -41,9 +41,9 @@ export const Populated: Story = { // The signed-in trustee is named in bold. await expect(canvas.getByText(STORY_TRUSTEE, {selector: "strong"})).toBeVisible() expect(canvas.getAllByRole("listitem").map((item) => item.textContent)).toEqual([ - "Download your Encrypted Private Key.", - "Create multiple Backups of the Encrypted Private Key.", - "Check that the backups works well.", + "Download the public custody request.", + "Keep secure Backups of your local trustee.toml keys file.", + "Create and upload a signed custody proof using your local keys.", ]) expect(args.goNext).not.toHaveBeenCalled() expect(graphql.calls).toEqual([]) diff --git a/packages/admin-portal/src/resources/Settings/SettingsTrustees.stories.tsx b/packages/admin-portal/src/resources/Settings/SettingsTrustees.stories.tsx index 237b2d8fd56..f75b3fc970c 100644 --- a/packages/admin-portal/src/resources/Settings/SettingsTrustees.stories.tsx +++ b/packages/admin-portal/src/resources/Settings/SettingsTrustees.stories.tsx @@ -207,6 +207,10 @@ export const CreateATrustee: Story = { ).toHaveLength(1) await userEvent.type(form.getByRole("textbox", {name: "Name"}), "trustee3") await userEvent.type(form.getByRole("textbox", {name: "Public key"}), "a2V5IHRocmVl") + await userEvent.type( + form.getByRole("textbox", {name: "Share encryption public key"}), + "synthetic-share-encryption-key" + ) await userEvent.click(form.getByRole("button", {name: "Save"})) await waitFor(() => expect(data.writes).toEqual([ @@ -214,7 +218,12 @@ export const CreateATrustee: Story = { method: "create", resource: TRUSTEE_RESOURCE, params: { - data: {name: "trustee3", public_key: "a2V5IHRocmVl", tenant_id: TENANT_ID}, + data: { + name: "trustee3", + public_key: "a2V5IHRocmVl", + share_encryption_public_key: "synthetic-share-encryption-key", + tenant_id: TENANT_ID, + }, }, }, ]) diff --git a/packages/admin-portal/test/journeys/ceremonies.spec.ts b/packages/admin-portal/test/journeys/ceremonies.spec.ts index bed29db4663..fffce18a131 100644 --- a/packages/admin-portal/test/journeys/ceremonies.spec.ts +++ b/packages/admin-portal/test/journeys/ceremonies.spec.ts @@ -662,7 +662,7 @@ test.describe("manual native trustee ceremony", () => { page.waitForEvent("download"), page.getByRole("button", {name: "Download Custody Request", exact: true}).click(), ]) - expect(download.suggestedFilename()).toBe("custody-request.json") + expect(download.suggestedFilename()).toBe("Alice-custody-request.json") const downloadPath = await download.path() if (!downloadPath) throw new Error("Custody request download is missing") expect(await readFile(downloadPath, "utf8")).toBe(request) diff --git a/packages/admin-portal/test/journeys/events.spec.ts b/packages/admin-portal/test/journeys/events.spec.ts index 920b94eff00..743037a62f2 100644 --- a/packages/admin-portal/test/journeys/events.spec.ts +++ b/packages/admin-portal/test/journeys/events.spec.ts @@ -234,7 +234,7 @@ test("creates an event then refreshes the scoped tree after task completion", as tenant_id: TENANT_ID, name: "Created council", description: "Annual council election", - encryption_protocol: "RSA256", + encryption_protocol: "WBRAID", is_archived: false, presentation: { language_conf: {enabled_language_codes: ["en"], default_language_code: "en"}, diff --git a/packages/admin-portal/test/journeys/shell/routes.spec.ts b/packages/admin-portal/test/journeys/shell/routes.spec.ts index 233ba4a64ac..3c75abf0f24 100644 --- a/packages/admin-portal/test/journeys/shell/routes.spec.ts +++ b/packages/admin-portal/test/journeys/shell/routes.spec.ts @@ -99,10 +99,8 @@ export const SMOKE: Record = { "/trustee": { url: "/trustee", shows: async (page) => { - await heading(page, "Wbraid trustee") - await expect( - page.getByText(/trustee generate --trustee-config/) - ).toBeVisible() + await heading(page, "Trustee dashboard") + await expect(page.getByText(/trustee generate --trustee-config/)).toBeVisible() }, checkConsole: true, }, diff --git a/packages/admin-portal/test/journeys/tally/data.ts b/packages/admin-portal/test/journeys/tally/data.ts index 0575eddf753..08401031b84 100644 --- a/packages/admin-portal/test/journeys/tally/data.ts +++ b/packages/admin-portal/test/journeys/tally/data.ts @@ -302,6 +302,17 @@ export function tallyWorld(portal: AdminPortal) { }) table(portal, "sequent_backend_tally_session", () => state.sessions) table(portal, "sequent_backend_tally_session_execution", () => [execution]) + portal.graphql.on("ListTallyKeyRestoreState", () => ({ + data: { + sequent_backend_tally_session: state.sessions.map(({id, execution_status}) => ({ + id, + execution_status, + })), + sequent_backend_tally_session_execution: [ + {tally_session_id: execution.tally_session_id, status: execution.status}, + ], + }, + })) table(portal, "sequent_backend_tally_session_resolution", () => state.resolutions) table(portal, "sequent_backend_tally_session_contest", () => []) table(portal, "sequent_backend_results_event", () => diff --git a/packages/ballot-verifier/rust/sequent-core-0.1.0.tgz b/packages/ballot-verifier/rust/sequent-core-0.1.0.tgz index f3ae2f4ae7c..068c1987c38 100644 Binary files a/packages/ballot-verifier/rust/sequent-core-0.1.0.tgz and b/packages/ballot-verifier/rust/sequent-core-0.1.0.tgz differ diff --git a/packages/harvest/tests/support/fake_provider_routes.rs b/packages/harvest/tests/support/fake_provider_routes.rs index 59d30343f82..ce124a655eb 100644 --- a/packages/harvest/tests/support/fake_provider_routes.rs +++ b/packages/harvest/tests/support/fake_provider_routes.rs @@ -1640,15 +1640,25 @@ async fn adapters_send_and_check_through_the_routes_and_reports_reach_the_ledger assert_eq!(code_message["sender"], page_id.as_str()); assert_eq!(code_message["recipient"], psid.as_str()); assert_eq!(code_message["content"]["text"], "Your code is 482619."); + // The link is marked once the provider has accepted the code, which the + // provider's own list shows a moment earlier. let (state, bound): (String, Option) = { - let rows = rows::query( - &stack.services.hasura, - "SELECT state, page_scoped_id FROM sequent_backend.messenger_link - WHERE tenant_id = $1", - &[&Uuid::parse_str(&stack.event.tenant_id).unwrap()], - ) - .await; - (rows[0].get(0), rows[0].get(1)) + let deadline = Instant::now() + Duration::from_secs(20); + loop { + let rows = rows::query( + &stack.services.hasura, + "SELECT state, page_scoped_id FROM sequent_backend.messenger_link + WHERE tenant_id = $1", + &[&Uuid::parse_str(&stack.event.tenant_id).unwrap()], + ) + .await; + let link: (String, Option) = + (rows[0].get(0), rows[0].get(1)); + if link.0 != "PENDING" || Instant::now() >= deadline { + break link; + } + tokio::time::sleep(Duration::from_millis(100)).await; + } }; assert_eq!( (state.as_str(), bound.as_deref()), diff --git a/packages/orare/Cargo.toml b/packages/orare/Cargo.toml index d26003786ee..a384865d90a 100644 --- a/packages/orare/Cargo.toml +++ b/packages/orare/Cargo.toml @@ -32,13 +32,13 @@ clap = { version = "4.5", features = ["derive"] } # Dependencies used in the lambda_runtime proc macro anyhow = "1.0" -sequent-core = { path = "../sequent-core", features = ["reports", "log", "areas", "default_features"] } [features] default = [] -aws_lambda = ["sequent-core/s3"] +aws_lambda = [] openwhisk = [] [dev-dependencies] +sequent-core = { path = "../sequent-core", features = ["reports", "log", "areas", "default_features"] } aws_lambda_events = "0.16" lambda_runtime = { version = "0.13", features = ["anyhow"] } tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } diff --git a/packages/sequent-core/tests/wasm/wasm.rs b/packages/sequent-core/tests/wasm/wasm.rs index 8094dd069c1..707f7645672 100644 --- a/packages/sequent-core/tests/wasm/wasm.rs +++ b/packages/sequent-core/tests/wasm/wasm.rs @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: 2026 Sequent Tech Inc // SPDX-License-Identifier: AGPL-3.0-only use base64::{engine::general_purpose::STANDARD, Engine}; -use ed25519_dalek::{Signer, SigningKey}; use sequent_core::ballot::{ AuditableBallot, HashableBallot, SignedHashableBallot, }; use sequent_core::wasm::wasm::verify_ballot_signature_js; use serde_json::{json, Value}; +use voting_crypto::signing::{SigningKey, VerificationKey}; use wasm_bindgen::JsValue; use wasm_bindgen_test::*; use web_sys::js_sys::JSON; @@ -36,13 +36,13 @@ fn signed_fixture() -> Value { message.extend_from_slice(&(bytes.len() as u64).to_le_bytes()); message.extend_from_slice(bytes); } - let key = SigningKey::from_bytes(&[7; 32]); - let signature = key.sign(&message); + let key = SigningKey::from_bytes([7; 32]).unwrap(); + let signature = key.sign(&message).unwrap(); // RFC 8410 SubjectPublicKeyInfo prefix for an Ed25519 public key. let mut der = vec![ 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, ]; - der.extend_from_slice(key.verifying_key().as_bytes()); + der.extend_from_slice(&VerificationKey::from_sk(&key).unwrap().to_bytes()); value["voter_signing_pk"] = json!(STANDARD.encode(der)); value["voter_ballot_signature"] = json!(STANDARD.encode(signature.to_bytes())); diff --git a/packages/step-cli/tests/command_failures.rs b/packages/step-cli/tests/command_failures.rs index 6a20f969be8..2775fa648e3 100644 --- a/packages/step-cli/tests/command_failures.rs +++ b/packages/step-cli/tests/command_failures.rs @@ -193,20 +193,27 @@ tar -xf - -C "$MOCK_IMAGE_CONTEXT" fs::set_permissions(&docker, fs::Permissions::from_mode(0o700)).unwrap(); let context = root.join("context"); fs::create_dir(&context).unwrap(); - let output = Command::new(env!("CARGO_BIN_EXE_step-cli")) - .args(["load", "image", "--engine", "k6", "--tag", "synthetic:test"]) - .env( - "PATH", - format!("{}:{}", root.display(), std::env::var("PATH").unwrap()), - ) - .env("MOCK_IMAGE_CONTEXT", &context) - .output() - .unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + // The bundled files are unpacked into a cache of this test's own, under + // its home directory, then found there by the second build: a cache + // shared with other runs would decide which of the two paths a run takes. + for _ in 0..2 { + let output = Command::new(env!("CARGO_BIN_EXE_step-cli")) + .args(["load", "image", "--engine", "k6", "--tag", "synthetic:test"]) + .env( + "PATH", + format!("{}:{}", root.display(), std::env::var("PATH").unwrap()), + ) + .env_remove("XDG_CACHE_HOME") + .env("HOME", root) + .env("MOCK_IMAGE_CONTEXT", &context) + .output() + .unwrap(); + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + } let replay = fs::read_to_string(context.join("packages/voting-load/replay.k6.js")).unwrap(); assert!(replay.contains(r#"from "./url-1.0.0.js""#)); let parser = fs::read(context.join("packages/voting-load/url-1.0.0.js")).unwrap(); diff --git a/packages/ui-core/rust/sequent-core-0.1.0.tgz b/packages/ui-core/rust/sequent-core-0.1.0.tgz index f3ae2f4ae7c..068c1987c38 100644 Binary files a/packages/ui-core/rust/sequent-core-0.1.0.tgz and b/packages/ui-core/rust/sequent-core-0.1.0.tgz differ diff --git a/packages/voting-portal/rust/sequent-core-0.1.0.tgz b/packages/voting-portal/rust/sequent-core-0.1.0.tgz index f3ae2f4ae7c..068c1987c38 100644 Binary files a/packages/voting-portal/rust/sequent-core-0.1.0.tgz and b/packages/voting-portal/rust/sequent-core-0.1.0.tgz differ diff --git a/packages/windmill/external-bin/janitor/templates/COMELEC/keycloakAdmin.hbs b/packages/windmill/external-bin/janitor/templates/COMELEC/keycloakAdmin.hbs index 14afb90d27e..509a888c76b 100644 --- a/packages/windmill/external-bin/janitor/templates/COMELEC/keycloakAdmin.hbs +++ b/packages/windmill/external-bin/janitor/templates/COMELEC/keycloakAdmin.hbs @@ -2168,7 +2168,7 @@ { "id": "cf8c266f-056b-45e9-a395-56c1a3fafce0", "name": "sign-key-ceremony", - "description": "Sign: confirm a key share", + "description": "Sign: confirm custody", "composite": false, "clientRole": false, "containerId": "9c67adb8-ae12-43ad-a3f4-2bc275fc888a", @@ -2177,7 +2177,7 @@ { "id": "9f64321f-820e-4d9f-8de3-098cab9ae196", "name": "sign-tally-key", - "description": "Sign: contribute a key share", + "description": "Sign: authorize tally custody", "composite": false, "clientRole": false, "containerId": "9c67adb8-ae12-43ad-a3f4-2bc275fc888a", diff --git a/packages/windmill/src/services/ballot_styles/ballot_publication.rs b/packages/windmill/src/services/ballot_styles/ballot_publication.rs index fe9bf5687fe..fc86fa6a284 100644 --- a/packages/windmill/src/services/ballot_styles/ballot_publication.rs +++ b/packages/windmill/src/services/ballot_styles/ballot_publication.rs @@ -705,6 +705,14 @@ pub async fn update_publish_ballot( return Ok(()); } + validate_published_ballot_styles( + hasura_transaction, + &tenant_id, + &election_event_id, + &ballot_publication, + ) + .await?; + validate_published_slates( hasura_transaction, &tenant_id, diff --git a/packages/windmill/src/tasks/migrate_realm_permissions.rs b/packages/windmill/src/tasks/migrate_realm_permissions.rs index 01e54ff3526..f37d13a650d 100644 --- a/packages/windmill/src/tasks/migrate_realm_permissions.rs +++ b/packages/windmill/src/tasks/migrate_realm_permissions.rs @@ -102,8 +102,8 @@ pub const MIGRATED_PERMISSIONS: [(Permissions, &str); 25] = [ Permissions::SIGN_APPROVE_CONFIGURATION, "Sign: approve a configuration version", ), - (Permissions::SIGN_KEY_CEREMONY, "Sign: confirm a key share"), - (Permissions::SIGN_TALLY_KEY, "Sign: contribute a key share"), + (Permissions::SIGN_KEY_CEREMONY, "Sign: confirm custody"), + (Permissions::SIGN_TALLY_KEY, "Sign: authorize tally custody"), ( Permissions::MESSAGING_ACCOUNT_READ, "Read messaging accounts", diff --git a/packages/yarn.lock b/packages/yarn.lock index 6e3ad68af40..8af159a4e7c 100644 --- a/packages/yarn.lock +++ b/packages/yarn.lock @@ -16084,19 +16084,19 @@ sentence-case@^3.0.4: "sequent-core@file:./admin-portal/rust/sequent-core-0.1.0.tgz": version "0.1.0" - resolved "file:./admin-portal/rust/sequent-core-0.1.0.tgz#80fb45032be6e3271a1a841bcc7d522373756d69" + resolved "file:./admin-portal/rust/sequent-core-0.1.0.tgz#cf2287b734563ee8b8357722aacd5f34e07201c5" "sequent-core@file:./ballot-verifier/rust/sequent-core-0.1.0.tgz": version "0.1.0" - resolved "file:./ballot-verifier/rust/sequent-core-0.1.0.tgz#80fb45032be6e3271a1a841bcc7d522373756d69" + resolved "file:./ballot-verifier/rust/sequent-core-0.1.0.tgz#cf2287b734563ee8b8357722aacd5f34e07201c5" "sequent-core@file:./voting-portal/rust/sequent-core-0.1.0.tgz": version "0.1.0" - resolved "file:./voting-portal/rust/sequent-core-0.1.0.tgz#80fb45032be6e3271a1a841bcc7d522373756d69" + resolved "file:./voting-portal/rust/sequent-core-0.1.0.tgz#cf2287b734563ee8b8357722aacd5f34e07201c5" "sequent-core@file:voting-portal/rust/sequent-core-0.1.0.tgz": version "0.1.0" - resolved "file:voting-portal/rust/sequent-core-0.1.0.tgz#80fb45032be6e3271a1a841bcc7d522373756d69" + resolved "file:voting-portal/rust/sequent-core-0.1.0.tgz#cf2287b734563ee8b8357722aacd5f34e07201c5" serialize-javascript@^6.0.0, serialize-javascript@^6.0.2, serialize-javascript@^7.0.5: version "7.1.1" diff --git a/scripts/coverage/profiles.toml b/scripts/coverage/profiles.toml index 9ce3e5b63a2..3fdd24965a6 100644 --- a/scripts/coverage/profiles.toml +++ b/scripts/coverage/profiles.toml @@ -508,20 +508,27 @@ ELECTORAL_LOG_PG_DATABASE = "test" ELECTORAL_LOG_PG_SSLMODE = "disable" ENV_SLUG = "electoral-log-tests" WBRAID_BOARD_DATABASE_URL = "postgres://test:test@127.0.0.1:3322/test" + [profiles.voting-crypto] package = "voting-crypto" features = [] limitations = ["Application signing and ballot boundary contracts against wbraid's crypto implementation."] issue = "https://github.com/sequentech/meta/issues/12886" +[profiles.voting-crypto.scope_exceptions] + [profiles.protocol-board] package = "protocol-board" features = [] limitations = ["Native in-memory DKG, mixing, decryption and custody protocols."] issue = "https://github.com/sequentech/meta/issues/12886" +[profiles.protocol-board.scope_exceptions] + [profiles.trustee] package = "trustee" features = [] limitations = ["Native trustee sessions, persistence and custody tooling."] issue = "https://github.com/sequentech/meta/issues/12886" + +[profiles.trustee.scope_exceptions] diff --git a/scripts/coverage/test_summary.py b/scripts/coverage/test_summary.py index 2a5773e832d..36a4cf6d94e 100644 --- a/scripts/coverage/test_summary.py +++ b/scripts/coverage/test_summary.py @@ -165,12 +165,15 @@ def test_an_expected_job_without_a_verdict_is_reported_missing(self): ) # A job that stopped before ci.py ran uploads partial files at most. (self.root / "coverage-harvest" / "_temp").mkdir(parents=True) - output = self.summarize("coverage-tooling", "voting-crypto", "harvest", "harvest") + output = self.summarize( + "coverage-tooling", "voting-crypto", "harvest", "harvest" + ) rows = table_rows(output) self.assertEqual(list(rows), ["coverage-tooling", "harvest", "voting-crypto"]) self.assertEqual(rows["harvest"], ["—"] * 4 + ["MISSING"]) self.assertIn( - "- voting-crypto (MISSING): No readable verdict.json was downloaded for this job.", + "- voting-crypto (MISSING): " + "No readable verdict.json was downloaded for this job.", output, ) diff --git a/scripts/dev/test_rust_watch.py b/scripts/dev/test_rust_watch.py index 91e090590d6..abe11027dc5 100644 --- a/scripts/dev/test_rust_watch.py +++ b/scripts/dev/test_rust_watch.py @@ -5,6 +5,7 @@ import enum import json import os +import posixpath import shlex import shutil import subprocess @@ -87,7 +88,11 @@ def test_each_watcher_runs_its_service_with_its_own_feature_graph(self): self.assertEqual(command.count("-s"), 1) shell = shlex.split(command[command.index("-s") + 1]) self.assertEqual(shell[:2], ["sh", RESTART_WORKER]) - self.assertEqual(shell[2], f"../target/debug/{worker[0]}") + built = posixpath.relpath( + f"{configuration['environment']['CARGO_TARGET_DIR']}/debug/{worker[0]}", + configuration["working_dir"], + ) + self.assertEqual(shell[2], built) self.assertEqual(shell[3 : 2 + len(worker)], worker[1:]) self.assertNotIn("cargo", shell) # Keep cargo-watch's metadata-derived local dependency scope. diff --git a/scripts/e2e/journeys/test_journeys.py b/scripts/e2e/journeys/test_journeys.py index afb6808c07b..1bae3ef1a16 100644 --- a/scripts/e2e/journeys/test_journeys.py +++ b/scripts/e2e/journeys/test_journeys.py @@ -1035,10 +1035,9 @@ def message_hashes(name): (publication,) = recovered["sequent_backend_ballot_publication"] self.assertNotEqual(publication["id"], original_publication["id"]) self.assertTrue(publication["is_generated"]) - self.assertEqual( - publication["published_at"], original_publication["published_at"] - ) - self.assertTrue(publication["published_at"]) + self.assertTrue(original_publication["published_at"]) + # An import never publishes ballots: the publication comes back as a draft. + self.assertIsNone(publication["published_at"]) self.assertTrue( publication["annotations"]["ballot_files_v1"].startswith( f"tenant-{TENANT_ID}/event-{recovered_id}/publication-{publication['id']}/" @@ -1124,7 +1123,7 @@ def complete(): ) (publication,) = recovered["sequent_backend_ballot_publication"] self.assertTrue(publication["is_generated"]) - self.assertTrue(publication["published_at"]) + self.assertIsNone(publication["published_at"]) expected = sorted(sum(self.expected(key).values()) for key in ("A", "B", "C")) results = recovered["sequent_backend_results_contest"] self.assertEqual(sorted(r["total_votes"] for r in results), expected) @@ -1253,11 +1252,7 @@ def keys_ready(): self.assertEqual( [e["extensions"]["code"] for e in errors], ["PokValidationFailed"] ) - stored = self.admin.query( - "query ($event: uuid!) { sequent_backend_cast_vote(where: {election_event_id: {_eq: $event}}) { id } }", - {"event": event_id}, - )["sequent_backend_cast_vote"] - self.assertEqual(stored, []) + self.assertEqual(portal.status(token)["cast_votes"], []) receipt, errors = portal.cast(token, ballot) self.assertIsNone(errors) self.assertEqual(receipt["ballot_id"], ballot["ballotId"]) diff --git a/scripts/e2e/test_runner.py b/scripts/e2e/test_runner.py index 3278e3fbebe..b41dda56129 100644 --- a/scripts/e2e/test_runner.py +++ b/scripts/e2e/test_runner.py @@ -542,7 +542,10 @@ def test_each_contest_uses_its_own_selection_in_the_native_encoder(self): with tempfile.TemporaryDirectory() as directory: cli = MagicMock(directory=Path(directory), binary=Path("step-cli")) - portal = Portal("event", cli) + with patch.dict( + os.environ, {"VOTING_PORTAL_URL": "https://voting.invalid"} + ): + portal = Portal("event", cli) style = { "contests": [ {