155 lines
4.6 KiB
TypeScript
155 lines
4.6 KiB
TypeScript
import { binToHex } from "@bitauth/libauth";
|
|
import { deserializeInvitation, serializeInvitation } from "@xo-cash/engine";
|
|
import type { XOInvitation, XOInvitationCommit } from "@xo-cash/types";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const client = vi.hoisted(() => ({
|
|
disconnect: vi.fn<() => Promise<void>>(),
|
|
onError: undefined as ((error: Error) => void) | undefined,
|
|
onMessage: undefined as ((message: string) => void) | undefined,
|
|
read: vi.fn<(resourceId: string) => Promise<unknown>>(),
|
|
subscribe: vi.fn<(resourceId: string) => Promise<void>>(),
|
|
write:
|
|
vi.fn<
|
|
(resourceId: string, value: Record<string, unknown>) => Promise<unknown>
|
|
>(),
|
|
}));
|
|
|
|
vi.mock("../../src/utils/syncing/index.js", () => ({
|
|
SSEClient: class {
|
|
constructor(
|
|
_url: string,
|
|
_privateKey: unknown,
|
|
options: {
|
|
onError?: (error: Error) => void;
|
|
onMessage?: (message: string) => void;
|
|
},
|
|
) {
|
|
client.onError = options.onError;
|
|
client.onMessage = options.onMessage;
|
|
}
|
|
|
|
disconnect = client.disconnect;
|
|
read = client.read;
|
|
subscribe = client.subscribe;
|
|
write = client.write;
|
|
},
|
|
}));
|
|
|
|
import { SyncServer } from "../../src/utils/sync-server.js";
|
|
|
|
const invitationIdentifier = "01".repeat(16);
|
|
const templateIdentifier = "02".repeat(32);
|
|
|
|
function makeCommit(commitIdentifier: string): XOInvitationCommit {
|
|
return {
|
|
commitIdentifier,
|
|
createdAtTimestamp: 1,
|
|
data: {},
|
|
} as XOInvitationCommit;
|
|
}
|
|
|
|
function makeInvitation(
|
|
commits: XOInvitationCommit[],
|
|
createdAtTimestamp = 1,
|
|
): XOInvitation {
|
|
return {
|
|
actionIdentifier: "send",
|
|
commits,
|
|
createdAtTimestamp,
|
|
invitationIdentifier,
|
|
templateIdentifier,
|
|
};
|
|
}
|
|
|
|
function invitationBlob(invitation: XOInvitation): Uint8Array {
|
|
return new TextEncoder().encode(serializeInvitation(invitation));
|
|
}
|
|
|
|
function encodeProtocolBlob(invitation: XOInvitation): string {
|
|
return `<Uint8Array: ${binToHex(invitationBlob(invitation))}>`;
|
|
}
|
|
|
|
describe("SyncServer v2 adapter", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
client.onError = undefined;
|
|
client.onMessage = undefined;
|
|
client.disconnect.mockResolvedValue();
|
|
client.subscribe.mockResolvedValue();
|
|
client.write.mockResolvedValue({});
|
|
});
|
|
|
|
it("scopes connection and writes to one invitation resource", async () => {
|
|
const sync = new SyncServer(
|
|
"https://v2.sync.xo.harvmaster.com",
|
|
invitationIdentifier,
|
|
new Uint8Array(32).fill(1),
|
|
);
|
|
const invitation = makeInvitation([makeCommit("commit-a")]);
|
|
|
|
await sync.connect();
|
|
await sync.publishInvitation(invitation);
|
|
await sync.disconnect();
|
|
|
|
expect(client.subscribe).toHaveBeenCalledWith(invitationIdentifier);
|
|
expect(client.write).toHaveBeenCalledWith(invitationIdentifier, invitation);
|
|
expect(client.disconnect).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("merges signer instances returned by a v2 resource read", async () => {
|
|
const older = makeInvitation([makeCommit("commit-a")], 1);
|
|
const newer = makeInvitation([makeCommit("commit-b")], 2);
|
|
client.read.mockResolvedValue([
|
|
{
|
|
blob: invitationBlob(older),
|
|
publicKey: "02aa",
|
|
resourceId: invitationIdentifier,
|
|
timestamp: 1,
|
|
},
|
|
{
|
|
blob: invitationBlob(newer),
|
|
publicKey: "02bb",
|
|
resourceId: invitationIdentifier,
|
|
timestamp: 2,
|
|
},
|
|
]);
|
|
const sync = new SyncServer(
|
|
"https://v2.sync.xo.harvmaster.com",
|
|
invitationIdentifier,
|
|
new Uint8Array(32).fill(1),
|
|
);
|
|
|
|
const invitation = await sync.getInvitation(invitationIdentifier);
|
|
|
|
expect(
|
|
invitation?.commits.map((commit) => commit.commitIdentifier).sort(),
|
|
).toEqual(["commit-a", "commit-b"]);
|
|
expect(invitation?.createdAtTimestamp).toBe(2);
|
|
});
|
|
|
|
it("translates live instance changes to the existing invitation event", () => {
|
|
const updated = makeInvitation([makeCommit("commit-live")]);
|
|
const sync = new SyncServer(
|
|
"https://v2.sync.xo.harvmaster.com",
|
|
invitationIdentifier,
|
|
new Uint8Array(32).fill(1),
|
|
);
|
|
const messages: Array<{ event?: string; data: XOInvitation }> = [];
|
|
sync.on("message", (message) => messages.push(message));
|
|
|
|
client.onMessage?.(
|
|
JSON.stringify({
|
|
blob: encodeProtocolBlob(updated),
|
|
publicKey: "02aa",
|
|
resourceId: invitationIdentifier,
|
|
timestamp: 2,
|
|
}),
|
|
);
|
|
|
|
expect(messages).toHaveLength(1);
|
|
expect(messages[0]?.event).toBe("invitation-updated");
|
|
expect(messages[0]?.data).toEqual(deserializeInvitation(serializeInvitation(updated)));
|
|
});
|
|
});
|