From 2c9050824e426c6ccca0a26a222b4bbfe7c29c6a Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Thu, 7 Sep 2023 03:36:38 +0100 Subject: [PATCH] Test suite mostly ready --- packages/lib/src/subscriptions.ts | 24 +- test/lib/subscriptions.js | 577 +++++++++++++++++++++++------- test/mock.js | 4 +- 3 files changed, 473 insertions(+), 132 deletions(-) diff --git a/packages/lib/src/subscriptions.ts b/packages/lib/src/subscriptions.ts index 1127be05..c641923c 100644 --- a/packages/lib/src/subscriptions.ts +++ b/packages/lib/src/subscriptions.ts @@ -82,6 +82,9 @@ export class SubscriptionRequest { public channels: Array = [], public lastRequestTime: number = 0, ) { + if (!Link._eventsByName.has(eventName)) { + throw new Error(`Unregistered Event class ${eventName}`); + } } static jsonSchema = Type.Tuple([ @@ -114,7 +117,7 @@ export class SubscriptionController { constructor( private controller: any, // Controller | Link ) { - this.controller.handle(SubscriptionRequest, this._handleEvent.bind(this)); + this.controller.handle(SubscriptionRequest, this._handleRequest.bind(this)); } /** @@ -148,7 +151,7 @@ export class SubscriptionController { broadcast(event: Event) { const entry = Link._eventsByClass.get(event.constructor); if (!entry) { - throw new Error(`Unregistered Event class ${Event.name}`); + throw new Error(`Unregistered Event class ${event.constructor.name}`); } const eventData = this._events.get(entry.name); if (!eventData) { @@ -164,7 +167,7 @@ export class SubscriptionController { } } - async _handleEvent(event: SubscriptionRequest, src: Address, dst: Address) { + async _handleRequest(event: SubscriptionRequest, src: Address, dst: Address) { if (!Link._eventsByName.has(event.eventName)) { throw new Error(`Event ${event.eventName} is not a registered event`); } @@ -199,18 +202,19 @@ export class EventSubscriber { _channelCallbacks = new Map>>() lastResponse: T | null = null; // Does not work with Event lastResponseTime = 0; + control?: Link constructor( private event: EventClass, private prehandler?: (event: T) => V, - private control?: Link + control?: Link ) { const entry = Link._eventsByClass.get(this.event); if (!entry) { throw new Error(`Unregistered Event class ${this.event.name}`); } - if (this.control) { - this.control.handle(this.event, this._handle.bind(this)); + if (control) { + this.connectControl(control); } } @@ -237,7 +241,7 @@ export class EventSubscriber { subscribe(handler: EventSubscriberCallback) { this._callbacks.push(handler); - this._updateSubscription(); + return this._updateSubscription(); } subscribeToChannel(channel: string | number, handler: EventSubscriberCallback) { @@ -245,7 +249,7 @@ export class EventSubscriber { this._channelCallbacks.set(channel, []); } this._channelCallbacks.get(channel)!.push(handler); - this._updateSubscription(); + return this._updateSubscription(); } unsubscribe(handler: EventSubscriberCallback) { @@ -255,7 +259,7 @@ export class EventSubscriber { } this._callbacks.splice(index, 1); - this._updateSubscription(); + return this._updateSubscription(); } unsubscribeFromChannel(channel: string | number, handler: EventSubscriberCallback) { @@ -274,7 +278,7 @@ export class EventSubscriber { } else { channelCallbacks.splice(index, 1); } - this._updateSubscription(); + return this._updateSubscription(); } async _updateSubscription() { diff --git a/test/lib/subscriptions.js b/test/lib/subscriptions.js index 0fb8212f..586dba2a 100644 --- a/test/lib/subscriptions.js +++ b/test/lib/subscriptions.js @@ -1,231 +1,566 @@ "use strict"; +const events = require("events"); const assert = require("assert").strict; const lib = require("@clusterio/lib"); +const { MockController, MockConnector, MockControl } = require("../mock"); + +const addr = lib.Address.fromShorthand; describe("lib/subscriptions", function() { + class UnregisteredEvent { + static type = "event"; + static src = ["controller", "control"]; + static dst = ["controller", "control"]; + static permission = null; + } + + class RegisteredEvent { + static type = "event"; + static src = ["controller", "control"]; + static dst = ["controller", "control"]; + static permission = null; + } + lib.Link.register(RegisteredEvent); + + class ChannelEvent { + static type = "event"; + static src = ["controller", "control"]; + static dst = ["controller", "control"]; + static permission = null; + } + lib.Link.register(ChannelEvent); + + class StringPermissionEvent { + static type = "event"; + static src = ["controller", "control"]; + static dst = ["controller", "control"]; + static permission = "StringPermission"; + } + lib.Link.register(StringPermissionEvent); + + class FunctionPermissionEvent { + static type = "event"; + static src = ["controller", "control"]; + static dst = ["controller", "control"]; + static permission(user, message) { + user.checkPermission("FunctionPermission"); + }; + } + lib.Link.register(FunctionPermissionEvent); + + class MockUser { + checkPermission(permission) { + this.lastPermissionCheck = permission; + } + } + describe("class SubscriptionResponse", function() { - describe("constructor()", function() { - it("should be constructable without an event replay", function() { - assert(false); - }); - it("should be constructable with an event replay", function() { - assert(false); - }); - }); - - describe("toJSON()", function() { - it("should be serialisable to json without an event replay", function() { - assert(false); - }); - it("should be serialisable to json with an event replay", function() { - assert(false); - }); - }); - - describe("fromJSON()", function() { - it("should be deserialisable from json without an event replay", function() { - assert(false); - }); - it("should be deserialisable from json with an event replay", function() { - assert(false); - }); - }); - it("should be round trip json serialisable without an event replay", function() { - assert(false); + const response = new lib.SubscriptionResponse(); + const json = JSON.stringify(response); + assert.equal(typeof json, "string"); + const reconstructed = lib.SubscriptionResponse.fromJSON(JSON.parse(json)); + assert.deepEqual(reconstructed, response); }); it("should be round trip json serialisable with an event replay", function() { - assert(false); + const response = new lib.SubscriptionResponse(new RegisteredEvent()); + const json = JSON.stringify(response); + assert.equal(typeof json, "string"); + const reconstructed = lib.SubscriptionResponse.fromJSON(JSON.parse(json)); + assert.deepEqual(reconstructed, response); + }); + + it("should be round trip json serialisable with a null event replay", function() { + const response = new lib.SubscriptionResponse(null); + const json = JSON.stringify(response); + assert.equal(typeof json, "string"); + const reconstructed = lib.SubscriptionResponse.fromJSON(JSON.parse(json)); + assert.deepEqual(reconstructed, response); + }); + + it("should be throw when given an unregistered event", function() { + assert.throws( + () => new lib.SubscriptionResponse(new UnregisteredEvent()), + new Error(`Unregistered Event class ${UnregisteredEvent.name}`) + ); }); }); describe("class SubscriptionRequest", function() { - describe("constructor()", function() { - it("should be constructable with only an allChannels argument", function() { - assert(false); - }); - it("should be constructable with without a lastRequestTime", function() { - assert(false); - }); - it("should be constructable with with a lastRequestTime", function() { - assert(false); - }); - }); - describe("permission()", function() { it("should do nothing when the event has no permission property", function() { - assert(false); + const mockUser = new MockUser(); + const request = new lib.SubscriptionRequest(RegisteredEvent.name, true); + lib.SubscriptionRequest.permission(mockUser, new lib.MessageRequest( + 0, + addr({ controlId: 0 }), + addr("controller"), + lib.SubscriptionRequest.name, + JSON.stringify(request) + )); + assert.equal(mockUser.lastPermissionCheck, undefined); }); it("should check user permission when the permission property is a string", function() { - assert(false); + const mockUser = new MockUser(); + const request = new lib.SubscriptionRequest(StringPermissionEvent.name, true); + lib.SubscriptionRequest.permission(mockUser, new lib.MessageRequest( + 0, + addr({ controlId: 0 }), + addr("controller"), + lib.SubscriptionRequest.name, + request.toJSON() + )); + assert.equal(mockUser.lastPermissionCheck, "StringPermission"); }); it("should call the permission property when it is a function", function() { - assert(false); + const mockUser = new MockUser(); + const request = new lib.SubscriptionRequest(FunctionPermissionEvent.name, true); + lib.SubscriptionRequest.permission(mockUser, new lib.MessageRequest( + 0, + addr({ controlId: 0 }), + addr("controller"), + lib.SubscriptionRequest.name, + request.toJSON() + )); + assert.equal(mockUser.lastPermissionCheck, "FunctionPermission"); }); }); - it("should be serialisable to json", function() { - assert(false); - }); - - it("should be deserialisable from json", function() { - assert(false); - }); - it("should be round trip json serialisable", function() { - assert(false); + const request = new lib.SubscriptionRequest(RegisteredEvent.name, true, ["channelOne"], 123); + const json = JSON.stringify(request); + assert.equal(typeof json, "string"); + const reconstructed = lib.SubscriptionRequest.fromJSON(JSON.parse(json)); + assert.deepEqual(reconstructed, request); + }); + + it("should be throw when given an unregistered event", function() { + assert.throws( + () => new lib.SubscriptionRequest(UnregisteredEvent.name, true), + new Error(`Unregistered Event class ${UnregisteredEvent.name}`) + ); }); }); describe("class SubscriptionController", function() { + let mockController, subscriptions; + const connectorSetupDate = [{ + id: 1, + request: new lib.SubscriptionRequest(RegisteredEvent.name, true), + src: addr({ controlId: 1 }), + dst: addr("controller"), + }, { + id: 2, + request: new lib.SubscriptionRequest(RegisteredEvent.name, true), + src: addr({ controlId: 2 }), + dst: addr("controller"), + }, { + id: 3, + request: new lib.SubscriptionRequest(ChannelEvent.name, true), + src: addr({ controlId: 3 }), + dst: addr("controller"), + }, { + id: 4, + request: new lib.SubscriptionRequest(ChannelEvent.name, false, ["channelOne"]), + src: addr({ controlId: 4 }), + dst: addr("controller"), + }, { + id: 5, + request: new lib.SubscriptionRequest(ChannelEvent.name, false, ["channelTwo"]), + src: addr({ controlId: 5 }), + dst: addr("controller"), + }]; + + function awaitMessages(ids) { + return Promise.all(ids.map(function(id) { + const link = mockController.wsServer.controlConnections.get(id); + return events.once(link.connector, "send"); + })); + } + + function assertLastEvent(connectorId, Event) { + const link = mockController.wsServer.controlConnections.get(connectorId); + const lastMessage = link.connector.sentMessages.at(-1); + const name = Event.plugin ? `${Event.plugin}:${Event.name}` : Event.name; + assert.equal(lastMessage.name, name); + } + + function assertNoEvent(connectorId) { + const link = mockController.wsServer.controlConnections.get(connectorId); + assert.equal(link.connector.sentMessages.length, 0); + } + + beforeEach(function() { + mockController = new MockController(); + mockController.wsServer = { + controlConnections: new Map(connectorSetupDate.map(function (connectorData) { + return [connectorData.id, new lib.Link(new MockConnector(connectorData.src, connectorData.dst))]; + })), + }; + subscriptions = new lib.SubscriptionController(mockController); + }); + it("should handle the SubscriptionRequest event", function() { - assert(false); + assert.equal(mockController.handles.has(lib.SubscriptionRequest), true); }); describe("handle()", function() { it("should accept registered events", function() { - assert(false); + subscriptions.handle(RegisteredEvent); }); it("should not accept unregistered events", function() { - assert(false); + assert.throws( + () => subscriptions.handle(UnregisteredEvent), + new Error(`Unregistered Event class ${UnregisteredEvent.name}`) + ); }); it("should not accept events already handled by the class", function() { - assert(false); + subscriptions.handle(RegisteredEvent); + assert.throws( + () => subscriptions.handle(RegisteredEvent), + new Error(`Event ${RegisteredEvent.name} is already registered`) + ); }); }); describe("broadcast()", function() { + beforeEach(function() { + subscriptions.handle(RegisteredEvent); + subscriptions.handle(ChannelEvent, () => "channelOne"); + for (let connectorData of connectorSetupDate) { + subscriptions._handleRequest(connectorData.request, connectorData.src, connectorData.dst); + } + }); + it("should not accept unregistered events", function() { - assert(false); + assert.throws( + () => subscriptions.broadcast(new UnregisteredEvent()), + new Error(`Unregistered Event class ${UnregisteredEvent.name}`) + ); }); it("should not accept events not handled by the class", function() { - assert(false); + assert.throws( + () => subscriptions.broadcast(new StringPermissionEvent()), + new Error(`Event ${StringPermissionEvent.name} is not a registered as subscribable`) + ); }); - it("should notify all links who subscribed all notifications, when channels are disabled", function() { - assert(false); + it("should notify all links who subscribed to all channels", async function() { + const messages = awaitMessages([1, 2]); + subscriptions.broadcast(new RegisteredEvent()); + await messages; + assertLastEvent(1, RegisteredEvent); // RegisteredEvent: All + assertLastEvent(2, RegisteredEvent); // RegisteredEvent: All + assertNoEvent(3); // ChannelEvent: All + assertNoEvent(4); // ChannelEvent: channelOne + assertNoEvent(5); // ChannelEvent: channelTwo }); - it("should notify all links who subscribed all notifications, when channels are enabled", function() { - assert(false); + it("should not notify a link who unsubscribed from all all channels", async function() { + const messages = awaitMessages([2]); + const connectorData = connectorSetupDate[1]; + const request = new lib.SubscriptionRequest(RegisteredEvent.name, false); + await subscriptions._handleRequest(request, connectorData.src, connectorData.dst); + subscriptions.broadcast(new RegisteredEvent()); + await messages; + assertNoEvent(1); + assertLastEvent(2, RegisteredEvent); + assertNoEvent(3); + assertNoEvent(4); + assertNoEvent(5); }); - it("should not notify a link who unsubscribed from all notifications", function() { - assert(false); + it("should notify all links who subscribed the specific channel", async function() { + const messages = awaitMessages([3, 4]); + subscriptions.broadcast(new ChannelEvent()); + await messages; + assertNoEvent(1); + assertNoEvent(2); + assertLastEvent(3, ChannelEvent); + assertNoEvent(4, ChannelEvent); + assertNoEvent(5); }); - it("should notify all links who subscribed the specific channel", function() { - assert(false); + it("should not notify a link who unsubscribed from the specific channel", async function() { + const messages = awaitMessages([3]); + const connectorData = connectorSetupDate[4]; + const request = new lib.SubscriptionRequest(ChannelEvent.name, false, []); + await subscriptions._handleRequest(request, connectorData.src, connectorData.dst); + subscriptions.broadcast(new ChannelEvent()); + await messages; + assertNoEvent(1); + assertNoEvent(2); + assertLastEvent(3, ChannelEvent); + assertNoEvent(4); + assertNoEvent(5); }); - it("should not notify a link who did subscribe the specific channel", function() { - assert(false); - }); - it("should not notify a link who unsubscribed from the specific channel", function() { - assert(false); - }); - it("should not notify links which are closed or closing", function() { - assert(false); + it("should not notify links which are closed or closing", async function() { + const messages = awaitMessages([2]); + const link = mockController.wsServer.controlConnections.get(1); + link.connector.closing = true; + subscriptions.broadcast(new RegisteredEvent()); + await messages; + assertNoEvent(1); + assertLastEvent(2, RegisteredEvent); + assertNoEvent(3); + assertNoEvent(4); + assertNoEvent(5); }); }); - describe("_handleEvent()", function() { + describe("_handleRequest()", function() { + beforeEach(function() { + subscriptions.handle(RegisteredEvent); + subscriptions.handle(ChannelEvent, () => "channelOne"); + }); + it("should not accept subscriptions to unregistered events", function() { - assert(false); + const request = new lib.SubscriptionRequest(RegisteredEvent.name, true); + request.eventName = UnregisteredEvent.name; + assert.throws( + () => subscriptions._handleRequest(request, connectorSetupDate[0].src, connectorSetupDate[0].dst), + new Error(`Unregistered Event class ${UnregisteredEvent.name}`) + ); }); it("should not accept subscriptions to events not handled by the class", function() { - assert(false); + const request = new lib.SubscriptionRequest(RegisteredEvent.name, true); + request.eventName = StringPermissionEvent.name; + assert.throws( + () => subscriptions._handleRequest(request, connectorSetupDate[0].src, connectorSetupDate[0].dst), + new Error(`Unregistered Event class ${StringPermissionEvent.name}`) + ); }); - it("should accept a subscription to all notifications for an event", function() { - assert(false); + it("should accept a subscription to all channels", async function() { + const messages = awaitMessages([1]); + const request = new lib.SubscriptionRequest(RegisteredEvent.name, true); + await subscriptions._handleRequest(request, connectorSetupDate[0].src, connectorSetupDate[0].dst); + subscriptions.broadcast(new RegisteredEvent()); + await messages; + assertLastEvent(1, RegisteredEvent); }); - it("should accept a unsubscription from all notifications for an event", function() { - assert(false); + it("should accept a unsubscription from all channels", async function() { + const messages = awaitMessages([1]); + const request = new lib.SubscriptionRequest(RegisteredEvent.name, true); + await subscriptions._handleRequest(request, connectorSetupDate[0].src, connectorSetupDate[0].dst); + subscriptions.broadcast(new RegisteredEvent()); + await messages; + assertLastEvent(1, RegisteredEvent); + + const link = mockController.wsServer.controlConnections.get(1); + link.connector.sentMessages.pop(); + + const unsubRequest = new lib.SubscriptionRequest(RegisteredEvent.name, false); + await subscriptions._handleRequest(unsubRequest, connectorSetupDate[0].src, connectorSetupDate[0].dst); + subscriptions.broadcast(new RegisteredEvent()); + assertNoEvent(1); }); - it("should accept a subscription to a specific channel for an event", function() { - assert(false); + it("should accept a subscription to a specific channel", async function() { + const messages = awaitMessages([1]); + const request = new lib.SubscriptionRequest(ChannelEvent.name, false, ["channelOne"]); + await subscriptions._handleRequest(request, connectorSetupDate[0].src, connectorSetupDate[0].dst); + subscriptions.broadcast(new ChannelEvent()); + await messages; + assertLastEvent(1, ChannelEvent); }); - it("should accept a unsubscription from a specific channel for an event", function() { - assert(false); + it("should accept a unsubscription from a specific channel", async function() { + const messages = awaitMessages([1]); + const request = new lib.SubscriptionRequest(ChannelEvent.name, false, ["channelOne"]); + await subscriptions._handleRequest(request, connectorSetupDate[0].src, connectorSetupDate[0].dst); + subscriptions.broadcast(new ChannelEvent()); + await messages; + assertLastEvent(1, ChannelEvent); + + const link = mockController.wsServer.controlConnections.get(1); + link.connector.sentMessages.pop(); + + const unsubRequest = new lib.SubscriptionRequest(ChannelEvent.name, false, []); + await subscriptions._handleRequest(unsubRequest, connectorSetupDate[0].src, connectorSetupDate[0].dst); + subscriptions.broadcast(new ChannelEvent()); + assertNoEvent(1); }); - it("should accept a respond with an event replay when returned by the handler", function() { - assert(false); + it("should accept a respond with an event replay when returned by the handler", async function() { + const messages = awaitMessages([1]); + subscriptions.handle(StringPermissionEvent, undefined, async function() { + return new StringPermissionEvent(); + }); + + const request = new lib.SubscriptionRequest(StringPermissionEvent.name, true); + await subscriptions._handleRequest(request, connectorSetupDate[0].src, connectorSetupDate[0].dst); + subscriptions.broadcast(new StringPermissionEvent()); + await messages; + assertLastEvent(1, StringPermissionEvent); + + const link = mockController.wsServer.controlConnections.get(1); + const lastMessage = link.connector.sentMessages.pop(); + const response = lib.SubscriptionResponse.fromJSON(lastMessage.data); + assert.deepEqual(response, new lib.SubscriptionResponse(new StringPermissionEvent())); }); }); }); describe("class EventSubscriber", function() { + let channelEvent, mockControl, registeredEvent; + beforeEach(function() { + mockControl = new MockControl(new MockConnector( + addr({ controlId: 1 }), + addr("controller"), + )); + registeredEvent = new lib.EventSubscriber(RegisteredEvent, undefined, mockControl); + channelEvent = new lib.EventSubscriber(ChannelEvent, undefined, mockControl); + }); + + function assertLastRequest(request) { + const Request = request.constructor; + const lastMessage = mockControl.connector.sentMessages.at(-1); + const name = Request.plugin ? `${Request.plugin}:${Request.name}` : Request.name; + assert.equal(lastMessage.name, name); + assert.deepEqual(lastMessage.data, request); + } + describe("constructor()", function() { it("should not accept unregistered events", function() { - assert(false); + assert.throws( + () => new lib.EventSubscriber(UnregisteredEvent), + new Error(`Unregistered Event class ${UnregisteredEvent.name}`) + ); }); - it("should handle the provided event if a control link is given", function() { - assert(false); + it("should handle the provided event, if a control link is provided", function() { + assert.equal(mockControl._eventHandlers.has(RegisteredEvent), true); + assert.equal(mockControl._eventHandlers.has(ChannelEvent), true); }); - it("should call and use the return of a pre-handler if provided", function() { - assert(false); + it("should call and use the return of a pre-handler, if provided", function() { + let calledWith = null; + function prehandler() { return "PreHandlerReturn"; } + const eventSubscriber = new lib.EventSubscriber(StringPermissionEvent, prehandler, mockControl); + eventSubscriber.subscribe(function(value) { calledWith = value; }); + eventSubscriber._handle(new StringPermissionEvent()); + assert.equal(calledWith, "PreHandlerReturn"); }); }); describe("connectControl()", function() { it("should handle the provided event", function() { - assert(false); + const control = new MockControl(new MockConnector( + addr({ controlId: 1 }), + addr("controller"), + )); + registeredEvent.connectControl(control); + assert.equal(control._eventHandlers.has(RegisteredEvent), true); }); it("should do nothing if the control is already connected", function() { - assert(false); + assert.equal(mockControl._eventHandlers.has(RegisteredEvent), true); + registeredEvent.connectControl(mockControl); + assert.equal(mockControl._eventHandlers.has(RegisteredEvent), true); }); }); describe("subscribe()", function() { it("should allow subscriptions to an event", function() { - assert(false); - }); - it("should call all handlers who subscripted, when channels are disabled", function() { - assert(false); - }); - it("should call all handlers who subscripted, when channels are enabled", function() { - assert(false); + let calledWith = null; + let calledWithTwo = null; + const event = new RegisteredEvent(); + registeredEvent.subscribe(function(value) { calledWith = value; }); + registeredEvent.subscribe(function(value) { calledWithTwo = value; }); + registeredEvent._handle(event); + assert.deepEqual(calledWith, event); + assert.deepEqual(calledWithTwo, event); }); }); describe("subscribeToChannel()", function() { it("should allow subscriptions to a channel for an event", function() { - assert(false); - }); - it("should call all handlers who subscripted to the specific channel", function() { - assert(false); - }); - it("should not call a handler who did not subscribe to the specific channel", function() { - assert(false); + let calledWith = null; + let calledWithTwo = null; + const event = new ChannelEvent(); + channelEvent.subscribeToChannel("channelOne", function(value) { calledWith = value; }); + channelEvent.subscribeToChannel("channelTwo", function(value) { calledWithTwo = value; }); + channelEvent._handle(event); + assert.deepEqual(calledWith, event); + assert.deepEqual(calledWithTwo, null); }); }); describe("unsubscribe()", function() { it("should allow unsubscribing from an event", function() { - assert(false); + let calledWith = null; + let event = new RegisteredEvent(); + function callback(value) { calledWith = value; } + registeredEvent.subscribe(callback); + registeredEvent._handle(event); + assert.deepEqual(calledWith, event); + + calledWith = null; + event = new RegisteredEvent(); + registeredEvent.unsubscribe(callback); + registeredEvent._handle(event); + assert.equal(calledWith, null); }); it("should throw an error if the handler was not subscribed", function() { - assert(false); + function callback(value) { calledWith = value; } + assert.throws( + () => registeredEvent.unsubscribe(callback), + new Error("handler is not registered") + ); }); }); describe("unsubscribeFromChannel()", function() { it("should allow unsubscribing from a channel for an event", function() { - assert(false); + let calledWith = null; + let event = new ChannelEvent(); + function callback(value) { calledWith = value; } + channelEvent.subscribeToChannel("channelOne", callback); + channelEvent._handle(event); + assert.deepEqual(calledWith, event); + + calledWith = null; + event = new ChannelEvent(); + channelEvent.unsubscribeFromChannel("channelOne", callback); + channelEvent._handle(event); + assert.equal(calledWith, null); }); it("should throw an error if the handler was not subscribed", function() { - assert(false); + function callback(value) { calledWith = value; } + assert.throws( + () => channelEvent.unsubscribeFromChannel("channelOne", callback), + new Error("handler is not registered") + ); }); }); describe("_updateSubscription()", function() { - it("should correctly request a subscription for all channels", function() { - assert(false); + it("should correctly request a subscription for all channels", async function() { + const expected = new lib.SubscriptionRequest(RegisteredEvent.name, true); + registeredEvent.subscribe(() => true); + await events.once(mockControl.connector, "send"); + assertLastRequest(expected); }); - it("should correctly request a subscription for some channels", function() { - assert(false); + it("should correctly request a subscription for some channels", async function() { + const expected = new lib.SubscriptionRequest(ChannelEvent.name, false, ["channelOne"]); + channelEvent.subscribeToChannel("channelOne", () => true); + await events.once(mockControl.connector, "send"); + assertLastRequest(expected); }); - it("should correctly request a subscription for no channels", function() { - assert(false); + it("should correctly request a subscription for no channels", async function() { + const expected = new lib.SubscriptionRequest(RegisteredEvent.name, false); + function callback() {} + registeredEvent.subscribe(callback); + await events.once(mockControl.connector, "send"); + registeredEvent.unsubscribe(callback); + await events.once(mockControl.connector, "send"); + assertLastRequest(expected); }); - it("should call handlers when a replay event is returned", function() { - assert(false); + it("should call handlers when a replay event is returned", async function() { + let calledWith = null; + const event = new RegisteredEvent(); + const request = registeredEvent.subscribe(function(value) { calledWith = value; }); + await events.once(mockControl.connector, "send"); + + const responseMessage = new lib.MessageResponse( + 1, + mockControl.connector.dst, + mockControl.connector.src, + new lib.SubscriptionResponse(event) + ); + mockControl.connector.emit("message", responseMessage); + assert.deepEqual(await request, event); }); }); }); diff --git a/test/mock.js b/test/mock.js index 8ad5d751..1958ecfd 100644 --- a/test/mock.js +++ b/test/mock.js @@ -178,9 +178,11 @@ class MockController { ]); this.instances = new Map(); this.hosts = new Map(); + this.handles = new Map(); } - handle() { + handle(eventClass, handler) { + this.handles.set(eventClass, handler); } getControllerUrl() {