diff --git a/crates/xmtp_cryptography/src/basic_credential.rs b/crates/xmtp_cryptography/src/basic_credential.rs index ee8c8b458c..f40e5eb0a8 100644 --- a/crates/xmtp_cryptography/src/basic_credential.rs +++ b/crates/xmtp_cryptography/src/basic_credential.rs @@ -328,12 +328,15 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] - fn secret_key_can_not_be_exposed() { - let keypair = XmtpInstallationCredential::new(); - let secret = keypair.0.as_ref(); + fn test_from_raw_mismatched_public_key() { + // Generate two different keypairs using the existing API + let keypair_a = SignatureKeyPair::new(SignatureScheme::ED25519).unwrap(); + let keypair_b = SignatureKeyPair::new(SignatureScheme::ED25519).unwrap(); + + let private_key_a = keypair_a.private().to_vec(); + let public_key_b = keypair_b.public().to_vec(); - assert_ne!(keypair.public_bytes(), secret.as_bytes()); - assert_ne!(keypair.public_slice(), secret.as_bytes()); - assert_ne!(keypair.verifying_key().as_bytes(), &secret.to_bytes()); + let result = XmtpInstallationCredential::from_raw(&private_key_a, &public_key_b); + assert!(result.is_err()); } } diff --git a/sdks/js/browser-sdk/test/DeviceSync.test.ts b/sdks/js/browser-sdk/test/DeviceSync.test.ts index 5f59f9e386..e9cfcf369d 100644 --- a/sdks/js/browser-sdk/test/DeviceSync.test.ts +++ b/sdks/js/browser-sdk/test/DeviceSync.test.ts @@ -2,7 +2,12 @@ import { ConsentEntityType, ConsentState } from "@xmtp/wasm-bindings"; import { describe, expect, it } from "vitest"; import { HistorySyncUrls } from "@/constants"; import { uuid } from "@/utils/uuid"; -import { createRegisteredClient, createSigner, sleep } from "@test/helpers"; +import { + createRegisteredClient, + createSigner, + sleep, + waitFor, +} from "@test/helpers"; describe("DeviceSync", () => { it("should sync consent across installations", async () => { @@ -130,11 +135,6 @@ describe("DeviceSync", () => { const messagesBefore = await group2Before!.messages(); expect(messagesBefore.length).toBe(2); - await sleep(1000); - await alix.syncAllDeviceSyncGroups(); - await sleep(1000); - await alix2.syncAllDeviceSyncGroups(); - // list available archives - may fail in some environments try { const archives = await alix2.listAvailableArchives(7); @@ -143,7 +143,23 @@ describe("DeviceSync", () => { // listAvailableArchives may not be fully supported in all test environments } - await alix2.processSyncArchive("123"); + // The archive payload propagates to alix2 over the network asynchronously, + // so a single device-sync pass can race ahead of delivery and leave + // processSyncArchive unable to find the payload (DeviceSyncError::MissingPayload). + // Re-sync the device-sync groups and retry until the pinned payload arrives. + await waitFor( + async () => { + await alix.syncAllDeviceSyncGroups(); + await alix2.syncAllDeviceSyncGroups(); + try { + await alix2.processSyncArchive("123"); + return true; + } catch { + return false; + } + }, + { timeout: 30000, interval: 1000 }, + ); await sleep(1000); await alix2.conversations.syncAll(); diff --git a/sdks/js/browser-sdk/test/Preferences.test.ts b/sdks/js/browser-sdk/test/Preferences.test.ts index 13186d5e75..41c8f323b3 100644 --- a/sdks/js/browser-sdk/test/Preferences.test.ts +++ b/sdks/js/browser-sdk/test/Preferences.test.ts @@ -10,6 +10,7 @@ import { createRegisteredClient, createSigner, sleep, + waitFor, } from "@test/helpers"; describe("Preferences", () => { @@ -205,20 +206,34 @@ describe("Preferences", () => { }); await client3.conversations.syncAll(); - await sleep(1000); - await client1.conversations.syncAll(); - await sleep(1000); await client2.conversations.syncAll(); - await sleep(1000); - - setTimeout(() => { - void stream.end(); - }, 100); + // Collect updates concurrently while the preference changes propagate. const preferences: UserPreferenceUpdate[] = []; - for await (const update of stream) { - preferences.push(...update); + const collecting = (async () => { + for await (const update of stream) { + preferences.push(...update); + } + })(); + + // Four updates are expected: two consent updates (the group and the + // inbox-id consent changes) and two HMAC-key updates (one per new + // installation). These propagate over the network asynchronously, so + // re-sync until the stream has observed all of them rather than racing a + // fixed delay, which can cut off the last update and yield only 3. + try { + await waitFor( + async () => { + await client1.conversations.syncAll(); + return preferences.length >= 4; + }, + { timeout: 30000, interval: 1000 }, + ); + } finally { + await stream.end(); + await collecting; } + expect(preferences.length).toBe(4); const consentUpdate1 = preferences[0] as Extract< UserPreferenceUpdate, diff --git a/sdks/js/browser-sdk/test/helpers.ts b/sdks/js/browser-sdk/test/helpers.ts index 840521c118..2da91e98db 100644 --- a/sdks/js/browser-sdk/test/helpers.ts +++ b/sdks/js/browser-sdk/test/helpers.ts @@ -23,11 +23,11 @@ export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const waitFor = async ( - condition: () => boolean, + condition: () => boolean | Promise, { timeout = 2000, interval = 10 } = {}, ) => { const start = Date.now(); - while (!condition()) { + while (!(await condition())) { if (Date.now() - start > timeout) { throw new Error(`waitFor timed out after ${timeout}ms`); }