Skip to content

Commit

Permalink
fix: optin to websockets for the mediator live mode as an experiment,… (
Browse files Browse the repository at this point in the history
  • Loading branch information
elribonazo authored Apr 24, 2024
1 parent 11cc07c commit 5fe7f33
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 30 deletions.
4 changes: 3 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
"${fileBasenameNoExtension}",
"--colors",
"--workerThreads",
"--detectOpenHandles",
"--maxWorkers",
"1"
"1",
"./tests"
],
"skipFiles": [
"${workspaceRoot}/../../node_modules/**/*",
Expand Down
28 changes: 22 additions & 6 deletions src/prism-agent/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
AgentCredentials as AgentCredentialsClass,
AgentDIDHigherFunctions as AgentDIDHigherFunctionsClass,
AgentInvitations as AgentInvitationsClass,
AgentOptions,
EventCallback,
InvitationType,
ListenerKey,
Expand Down Expand Up @@ -86,8 +87,10 @@ export default class Agent
public readonly mediationHandler: MediatorHandler,
public readonly connectionManager: ConnectionsManager,
public readonly seed: Domain.Seed = apollo.createRandomSeed().seed,
public readonly api: Domain.Api = new ApiImpl()
public readonly api: Domain.Api = new ApiImpl(),
options?: AgentOptions
) {

this.pollux = new Pollux(castor);
this.agentCredentials = new AgentCredentials(
apollo,
Expand All @@ -104,7 +107,8 @@ export default class Agent
pluto,
this.agentCredentials,
mediationHandler,
[]
[],
options
);


Expand Down Expand Up @@ -147,6 +151,7 @@ export default class Agent
castor?: Domain.Castor;
mercury?: Domain.Mercury;
seed?: Domain.Seed;
options?: AgentOptions
}): Agent {
const mediatorDID = Domain.DID.from(params.mediatorDID);
const pluto = params.pluto;
Expand All @@ -170,7 +175,15 @@ export default class Agent
pollux,
seed
);
const manager = new ConnectionsManager(castor, mercury, pluto, agentCredentials, handler);
const manager = new ConnectionsManager(
castor,
mercury,
pluto,
agentCredentials,
handler,
[],
params.options
);

const agent = new Agent(
apollo,
Expand All @@ -180,7 +193,8 @@ export default class Agent
handler,
manager,
seed,
api
api,
params.options
);

return agent;
Expand Down Expand Up @@ -217,7 +231,8 @@ export default class Agent
mercury: Domain.Mercury,
connectionManager: ConnectionsManager,
seed?: Domain.Seed,
api?: Domain.Api
api?: Domain.Api,
options?: AgentOptions
) {
return new Agent(
apollo,
Expand All @@ -227,7 +242,8 @@ export default class Agent
connectionManager.mediationHandler,
connectionManager,
seed ? seed : apollo.createRandomSeed().seed,
api ? api : new ApiImpl()
api ? api : new ApiImpl(),
options
);
}

Expand Down
15 changes: 11 additions & 4 deletions src/prism-agent/connectionsManager/ConnectionsManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { uuid } from "@stablelib/uuid";
import { DID, Message, MessageDirection, Pollux } from "../../domain";

Check warning on line 1 in src/prism-agent/connectionsManager/ConnectionsManager.ts

View workflow job for this annotation

GitHub Actions / Build and test

'Pollux' is defined but never used
import { Castor } from "../../domain/buildingBlocks/Castor";
import { Mercury } from "../../domain/buildingBlocks/Mercury";
Expand All @@ -10,6 +9,7 @@ import { CancellableTask } from "../helpers/Task";
import {
AgentCredentials,
AgentMessageEvents as AgentMessageEventsClass,
AgentOptions,
ConnectionsManager as ConnectionsManagerClass,
ListenerKey,
MediatorHandler,
Expand Down Expand Up @@ -72,11 +72,16 @@ export class ConnectionsManager implements ConnectionsManagerClass {
public pluto: Pluto,
public agentCredentials: AgentCredentials,
public mediationHandler: MediatorHandler,
public pairings: DIDPair[] = []
public pairings: DIDPair[] = [],
public options?: AgentOptions
) {
this.events = new AgentMessageEvents();
}

get withWebsocketsExperiment() {
return this.options?.experiments?.liveMode === true
}

/**
* Asyncronously Start the mediator, just checking if we had one stored in Database and
* setting that one as default during the Agent start
Expand Down Expand Up @@ -263,8 +268,10 @@ export class ConnectionsManager implements ConnectionsManagerClass {
const currentMediator = this.mediationHandler.mediator.mediatorDID;
const resolvedMediator = await this.castor.resolveDID(currentMediator.toString());
const hasWebsocket = resolvedMediator.services.find(({ serviceEndpoint: { uri } }) =>
uri.startsWith("ws://") ||
uri.startsWith("wss://")
(
uri.startsWith("ws://") ||
uri.startsWith("wss://")
) && this.withWebsocketsExperiment
);
if (!hasWebsocket) {
const timeInterval = Math.max(iterationPeriod, 5) * 1000;
Expand Down
4 changes: 0 additions & 4 deletions src/prism-agent/helpers/Task.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
type Task<T> = (signal: AbortSignal) => Promise<T>;





export class CancellableTask<T> {
private period?: number;
private controller: AbortController;
Expand Down
8 changes: 8 additions & 0 deletions src/prism-agent/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ export enum InvitationTypes {
PRISM_ONBOARD,
}


export type AgentOptions = {
experiments?: {
liveMode?: boolean
}
}

export type InvitationType = PrismOnboardingInvitation | OutOfBandInvitation;

export class PrismOnboardingInvitation implements InvitationInterface {
Expand Down Expand Up @@ -111,6 +118,7 @@ export interface ConnectionsManager {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
cancellables: CancellableTask<any>[];

withWebsocketsExperiment: boolean;
stopAllEvents(): void;

addConnection(paired: DIDPair): Promise<void>;
Expand Down
225 changes: 225 additions & 0 deletions tests/agent/Agent.ConnectionsManager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/**
* @jest-environment node
*/
import chai from "chai";
import chaiAsPromised from "chai-as-promised";
import SinonChai from "sinon-chai";
import { Apollo, BasicMediatorHandler, Castor, ConnectionsManager, MediatorStore, Pluto } from "../../src";
import { Curve, KeyTypes, Mercury, Service, ServiceEndpoint } from "../../src/domain";
import { MercuryStub } from "./mocks/MercuryMock";
import { AgentCredentials } from "../../src/prism-agent/Agent.Credentials";
import { AgentOptions } from "../../src/prism-agent/types";

chai.use(SinonChai);
chai.use(chaiAsPromised);

const store: MediatorStore = null as any;
const mercury: Mercury = new MercuryStub();

const apollo = new Apollo();
const castor = new Castor(apollo)
const pluto: Pluto = null as any;
const agentCredentials: AgentCredentials = null as any;


async function createBasicMediationHandler(
ConnectionsManager: any,
BasicMediatorHandler: any,
services: Service[],
options?: AgentOptions
): Promise<
{
manager: ConnectionsManager,
handler: BasicMediatorHandler
}
> {

const seed = apollo.createRandomSeed().seed;
const keypair = apollo.createPrivateKey({
type: KeyTypes.EC,
curve: Curve.SECP256K1,
seed: Buffer.from(seed.value).toString("hex"),
});
const mediatorDID = await castor.createPrismDID(keypair.publicKey(), services);
const handler = new BasicMediatorHandler(
mediatorDID,
mercury,
store
);
handler.mediator = {
hostDID: mediatorDID,
routingDID: mediatorDID,
mediatorDID: mediatorDID
}
const manager = new ConnectionsManager(
castor,
mercury,
pluto,
agentCredentials,
handler,
[],
options
)
return {
manager,
handler
}
}


describe("ConnectionsManager tests", () => {

beforeEach(() => {
jest.mock('isows', () => ({
WebSocket: jest.fn(() => ({
addEventListener: jest.fn(),
send: jest.fn(),
close: jest.fn(),
})),
}));
})

afterEach(() => {
jest.restoreAllMocks();
});

it("Should use websockets if the mediator's did endpoint uri contains ws or wss and agent options have the opt in", async () => {
const services = [
new Service(
"#didcomm-1",
["DIDCommMessaging"],
new ServiceEndpoint("ws://localhost:12346")
)
];
const ConnectionsManager = jest.requireActual('../../src/prism-agent/connectionsManager/ConnectionsManager').ConnectionsManager;
const BasicMediatorHandler = jest.requireMock('../../src/prism-agent/mediator/BasicMediatorHandler').BasicMediatorHandler;
const { manager, handler } = await createBasicMediationHandler(
ConnectionsManager,
BasicMediatorHandler,
services,
{
experiments: {
liveMode: true
}
}
);
const listenUnread = jest.spyOn(handler, 'listenUnreadMessages')
expect(manager).toHaveProperty('withWebsocketsExperiment', true);
await manager.startFetchingMessages(1)
expect(listenUnread).toHaveBeenCalled();

manager.stopFetchingMessages()
})

it("Should not use websockets even if the mediator's did endpoint uri contains ws or wss if the agent options don't have the opt-in", async () => {
const services = [
new Service(
"#didcomm-1",
["DIDCommMessaging"],
new ServiceEndpoint("ws://localhost:12346")
)
];
const ConnectionsManager = jest.requireActual('../../src/prism-agent/connectionsManager/ConnectionsManager').ConnectionsManager;
const BasicMediatorHandler = jest.requireMock('../../src/prism-agent/mediator/BasicMediatorHandler').BasicMediatorHandler;
const { manager, handler } = await createBasicMediationHandler(
ConnectionsManager,
BasicMediatorHandler,
services
);
const listenUnread = jest.spyOn(handler, 'listenUnreadMessages')
expect(manager).toHaveProperty('withWebsocketsExperiment', false);

await manager.startFetchingMessages(1)

expect(listenUnread).not.toHaveBeenCalled()
manager.stopFetchingMessages()
})

it("Should not use websockets even if the mediator's did endpoint uri contains ws or wss if the agent options don't have the opt-in 1", async () => {
const services = [
new Service(
"#didcomm-1",
["DIDCommMessaging"],
new ServiceEndpoint("ws://localhost:12346")
)
];
const ConnectionsManager = jest.requireActual('../../src/prism-agent/connectionsManager/ConnectionsManager').ConnectionsManager;
const BasicMediatorHandler = jest.requireMock('../../src/prism-agent/mediator/BasicMediatorHandler').BasicMediatorHandler;
const { manager, handler } = await createBasicMediationHandler(
ConnectionsManager,
BasicMediatorHandler,
services,
{
experiments: {

}
}
);
const listenUnread = jest.spyOn(handler, 'listenUnreadMessages')
expect(manager).toHaveProperty('withWebsocketsExperiment', false);

await manager.startFetchingMessages(1)

expect(listenUnread).not.toHaveBeenCalled()
manager.stopFetchingMessages()
})

it("Should not use websockets even if the mediator's did endpoint uri contains ws or wss if the agent options don't have the opt-in 2", async () => {
const services = [
new Service(
"#didcomm-1",
["DIDCommMessaging"],
new ServiceEndpoint("ws://localhost:12346")
)
];
const ConnectionsManager = jest.requireActual('../../src/prism-agent/connectionsManager/ConnectionsManager').ConnectionsManager;
const BasicMediatorHandler = jest.requireMock('../../src/prism-agent/mediator/BasicMediatorHandler').BasicMediatorHandler;
const { manager, handler } = await createBasicMediationHandler(
ConnectionsManager,
BasicMediatorHandler,
services,
{
experiments: {
liveMode: false
}
}
);
const listenUnread = jest.spyOn(handler, 'listenUnreadMessages')
expect(manager).toHaveProperty('withWebsocketsExperiment', false);

await manager.startFetchingMessages(1)

expect(listenUnread).not.toHaveBeenCalled()
manager.stopFetchingMessages()
})

it("Should not use websockets if the mediator'd did endpoint uri does not contain ws or wss for more than the agent has opted in", async () => {
const services = [
new Service(
"#didcomm-1",
["DIDCommMessaging"],
new ServiceEndpoint("http://localhost:12346")
)
];
const ConnectionsManager = jest.requireActual('../../src/prism-agent/connectionsManager/ConnectionsManager').ConnectionsManager;
const BasicMediatorHandler = jest.requireMock('../../src/prism-agent/mediator/BasicMediatorHandler').BasicMediatorHandler;
const { manager, handler } = await createBasicMediationHandler(
ConnectionsManager,
BasicMediatorHandler,
services,
{
experiments: {
liveMode: true
}
}
);
const listenUnread = jest.spyOn(handler, 'listenUnreadMessages')
expect(manager).toHaveProperty('withWebsocketsExperiment', true);

await manager.startFetchingMessages(1)

expect(listenUnread).not.toHaveBeenCalled()
manager.stopFetchingMessages()
})

})
Loading

1 comment on commit 5fe7f33

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines Statements Branches Functions
Coverage: 71%
71.45% (1860/2603) 57.64% (731/1268) 74.69% (558/747)

JUnit

Tests Skipped Failures Errors Time
413 2 💤 0 ❌ 0 🔥 1m 1s ⏱️

Please sign in to comment.