Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions crates/xmtp_cryptography/src/basic_credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
30 changes: 23 additions & 7 deletions sdks/js/browser-sdk/test/DeviceSync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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);
Expand All @@ -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();

Expand Down
35 changes: 25 additions & 10 deletions sdks/js/browser-sdk/test/Preferences.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
createRegisteredClient,
createSigner,
sleep,
waitFor,
} from "@test/helpers";

describe("Preferences", () => {
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions sdks/js/browser-sdk/test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));

export const waitFor = async (
condition: () => boolean,
condition: () => boolean | Promise<boolean>,
{ 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`);
}
Expand Down
Loading