moleculer
Version:
Fast & powerful microservices framework for Node.JS
1,174 lines (935 loc) • 30.9 kB
JavaScript
"use strict";
const ServiceRegistry = require("../../src/service-registry");
const ServiceBroker = require("../../src/service-broker");
const lolex = require("lolex");
// Registry strategies
const { STRATEGY_ROUND_ROBIN, STRATEGY_RANDOM } = require("../../src/constants");
// Circuit-breaker states
const { CIRCUIT_CLOSE, CIRCUIT_HALF_OPEN, CIRCUIT_OPEN } = require("../../src/constants");
describe("Test constructor", () => {
it("should create instance with default options", () => {
let registry = new ServiceRegistry();
expect(registry).toBeDefined();
expect(registry.opts).toEqual({"preferLocal": true, "strategy": STRATEGY_ROUND_ROBIN});
expect(registry.actions).toBeInstanceOf(Map);
expect(registry.services).toBeInstanceOf(Array);
});
it("should create instance with options", () => {
let opts = {
preferLocal: false,
strategy: STRATEGY_RANDOM
};
let registry = new ServiceRegistry(opts);
expect(registry).toBeDefined();
expect(registry.opts).toEqual({ preferLocal: false, strategy: STRATEGY_RANDOM});
});
it("should create instance with options via broker", () => {
const broker = new ServiceBroker({
registry: {
preferLocal: false,
strategy: STRATEGY_RANDOM
}
});
let registry = broker.serviceRegistry;
expect(registry).toBeDefined();
expect(registry.opts).toEqual({ preferLocal: false, strategy: STRATEGY_RANDOM});
});
});
describe("Test registry.init", () => {
const broker = new ServiceBroker();
it("should set broker to local var", () => {
let registry = broker.serviceRegistry;
expect(registry.broker).toBe(broker);
expect(registry.services.length).toBe(1);
expect(registry.services[0]).toBeInstanceOf(ServiceRegistry.ServiceItem);
expect(registry.services[0].nodeID).toBe(null);
expect(registry.services[0].name).toBe("$node");
expect(registry.services[0].version).toBeUndefined();
expect(registry.services[0].settings).toEqual({});
});
});
describe("Test registry.registerService", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
version: 2,
settings: {
a: 5
},
actions: {
find: {}
}
};
it("should find service, and push it if not found", () => {
registry.findServiceByNode = jest.fn();
expect(registry.services.length).toBe(0);
registry.registerService(null, service);
expect(registry.services.length).toBe(1);
expect(registry.services[0]).toBeInstanceOf(ServiceRegistry.ServiceItem);
});
it("should find service, and not push it if found", () => {
registry.services.length = 0;
registry.findServiceByNode = jest.fn(() => service);
expect(registry.services.length).toBe(0);
registry.registerService(null, service);
expect(registry.services.length).toBe(0);
});
});
describe("Test registry.unregisterServicesByNode", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
version: 2,
settings: {
a: 5
}
};
let action1 = {
name: "posts.find",
service
};
let action2 = {
name: "posts.get",
cache: true,
service
};
registry.registerService(null, service);
registry.registerService("node-2", service);
registry.registerAction(null, action1);
registry.registerAction(null, action2);
registry.registerAction("node-2", action1);
registry.registerAction("node-2", action2);
registry.unregisterAction = jest.fn();
it("should unregister actions & remove service", () => {
expect(registry.services.length).toBe(2);
registry.unregisterServicesByNode("node-2");
expect(registry.services.length).toBe(1);
expect(registry.unregisterAction).toHaveBeenCalledTimes(2);
expect(registry.unregisterAction).toHaveBeenCalledWith("node-2", action1);
expect(registry.unregisterAction).toHaveBeenCalledWith("node-2", action2);
});
});
describe("Test registry.findService", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let serviceV1 = {
name: "posts",
version: 1,
settings: {}
};
let serviceV2 = {
name: "posts",
version: 2,
settings: {}
};
let serviceOther = {
name: "users",
settings: {}
};
registry.registerService(null, serviceV1);
registry.registerService(null, serviceV2);
registry.registerService("node-2", serviceOther);
it("should find versioned service", () => {
expect(registry.findService("posts", 1).name).toBe("posts");
expect(registry.findService("posts", 1).version).toBe(1);
expect(registry.findService("posts", 1).nodeID).toBeNull();
expect(registry.findService("posts", 1).settings).toBe(serviceV1.settings);
expect(registry.findService("posts", 2).name).toBe("posts");
expect(registry.findService("posts", 2).version).toBe(2);
expect(registry.findService("posts", 2).settings).toBe(serviceV2.settings);
});
it("should find not versioned service", () => {
expect(registry.findService("users").name).toBe("users");
expect(registry.findService("users").version).toBeUndefined();
expect(registry.findService("users").nodeID).toBe("node-2");
expect(registry.findService("users").settings).toBe(serviceOther.settings);
expect(registry.findService("users", 2)).toBeUndefined();
});
});
describe("Test registry.findServiceByNode", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
registry.registerService(null, service);
registry.registerService("node-2", service);
registry.registerService("node-3", service);
it("should find not versioned service", () => {
expect(registry.findServiceByNode(null, "posts").nodeID).toBeNull();
expect(registry.findServiceByNode("node-3", "posts").nodeID).toBe("node-3");
expect(registry.findServiceByNode("node-2", "posts").nodeID).toBe("node-2");
expect(registry.findServiceByNode("node-123", "posts")).toBeUndefined();
});
});
describe("Test registry.registerAction", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "posts.find",
handler: jest.fn(),
service
};
it("should set local action and create an EndpointList", () => {
expect(registry.actions.size).toBe(0);
registry.registerService(null, service);
const res = registry.registerAction(null, action);
expect(res).toBe(true);
expect(registry.actions.size).toBe(1);
let endpoint = registry.findAction("posts.find");
expect(endpoint).toBeInstanceOf(ServiceRegistry.EndpointList);
expect(endpoint.count()).toBe(1);
expect(registry.services[0].actions["posts.find"]).toBeDefined();
});
it("should set local action but not create a new EndpointList", () => {
const res = registry.registerAction(null, action);
expect(res).toBe(false);
expect(registry.actions.size).toBe(1);
expect(registry.services[0].actions["posts.find"]).toBeDefined();
});
it("should set remote action but not create a new EndpointList", () => {
registry.registerService("server-2", service);
const res = registry.registerAction("server-2", action);
expect(res).toBe(true);
expect(registry.actions.size).toBe(1);
expect(registry.services[1].actions["posts.find"]).toBeDefined();
let endpoint = registry.findAction("posts.find");
expect(endpoint.count()).toBe(2);
});
});
describe("Test registry.unregisterAction", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "posts.find",
handler: jest.fn(),
service
};
registry.registerService(null, service);
registry.registerService("server-2", service);
registry.registerAction(null, action);
registry.registerAction("server-2", action);
it("should count endpoint equals 2", () => {
let endpoint = registry.findAction("posts.find");
expect(endpoint.count()).toBe(2);
});
it("should remove 'server-2' endpoint from list", () => {
registry.unregisterAction("server-2", action);
let endpoint = registry.findAction("posts.find");
expect(endpoint.count()).toBe(1);
});
});
describe("Test registry.findAction", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "posts.find",
handler: jest.fn(),
service
};
it("should returns undefined if action is not exist", () => {
let endpoint = registry.findAction("posts.find");
expect(endpoint).toBeUndefined();
});
it("should return the endpoint if action is exist", () => {
registry.registerService("server-2", service);
registry.registerAction("server-2", action);
let endpoint = registry.findAction("posts.find");
expect(endpoint).toBeDefined();
});
});
describe("Test registry.findAction with internal actions", () => {
const broker = new ServiceBroker({ internalActions: true, registry: { preferLocal: false } });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "posts.find",
handler: jest.fn(),
service
};
registry.registerService("server-2", service);
registry.registerAction("server-2", action);
it("should return the endpoint if action is exist", () => {
let endpoint = registry.findAction("$node.list").nextAvailable();
expect(endpoint).toBeDefined();
expect(endpoint.nodeID).toBeNull();
endpoint = registry.findAction("$node.list").nextAvailable();
expect(endpoint).toBeDefined();
expect(endpoint.nodeID).toBeNull();
});
});
describe("Test registry.getEndpointByNodeID", () => {
const broker = new ServiceBroker({ internalActions: true, registry: { preferLocal: false } });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "$node.list",
custom: 100,
handler: jest.fn(),
service
};
registry.registerService("server-2", service);
registry.registerAction("server-2", action);
it("check the count of nodes", () => {
let item = registry.findAction("$node.list");
expect(item.count()).toBe(2);
});
it("should return the endpoint if action is exist", () => {
let endpoint = registry.getEndpointByNodeID("$node.list", "server-2");
expect(endpoint).toBeDefined();
expect(endpoint.local).toBe(false);
expect(endpoint.action).toBeDefined();
expect(endpoint.action.custom).toBe(100);
});
it("should not return the endpoint if endpoint is not available", () => {
let endpoint = registry.getEndpointByNodeID("$node.list", "server-2");
endpoint.state = CIRCUIT_OPEN;
endpoint = registry.getEndpointByNodeID("$node.list", "server-2");
expect(endpoint).toBeUndefined();
});
it("should not return endpoint if action is not exist", () => {
let endpoint = registry.getEndpointByNodeID("math.pow", "server-2");
expect(endpoint).toBeUndefined();
});
it("should not return endpoint if node is not exist", () => {
let endpoint = registry.getEndpointByNodeID("$node.list", "server-123");
expect(endpoint).toBeUndefined();
});
});
describe("Test registry.hasAction", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "posts.find",
handler: jest.fn(),
service
};
it("should returns false if action is not exist", () => {
let res = registry.hasAction("posts.find");
expect(res).toBe(false);
});
it("should return the res if action is exist", () => {
registry.registerService("server-2", service);
registry.registerAction("server-2", action);
let res = registry.hasAction("posts.find");
expect(res).toBe(true);
});
});
describe("Test registry.count", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "posts.find",
handler: jest.fn(),
service
};
it("should returns with 0", () => {
expect(registry.actionCount()).toBe(0);
});
it("should return with 1", () => {
registry.registerService("server-2", service);
registry.registerAction("server-2", action);
expect(registry.actionCount()).toBe(1);
});
});
describe("Test registry.getServiceList", () => {
describe("Test without internal actions", () => {
const broker = new ServiceBroker({ internalActions: false });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "posts.find",
cache: true,
custom: 5,
handler: jest.fn(),
service
};
it("should returns empty list", () => {
expect(registry.getServiceList({})).toEqual([]);
});
it("should return empty list because only remote endpoint registered", () => {
registry.registerService("server-2", service);
registry.registerAction("server-2", action);
expect(registry.getServiceList({ onlyLocal: true })).toEqual([]);
});
it("should return action list", () => {
registry.registerService(null, service);
registry.registerAction(null, action);
expect(registry.getServiceList({
onlyLocal: true
})).toEqual([{
"name": "posts",
"settings": {},
"version": undefined,
"nodeID": null
}]);
});
it("should return all services", () => {
expect(registry.getServiceList({
onlyLocal: false
})).toEqual([{
"name": "posts",
"settings": {},
"version": undefined,
"nodeID": "server-2"
}, {
"name": "posts",
"settings": {},
"version": undefined,
"nodeID": null
}]);
});
it("should return all services with actions", () => {
expect(registry.getServiceList({
onlyLocal: false,
withActions: true
})).toEqual([{
"actions": {
"posts.find": {
"cache": true,
"custom": 5,
"name": "posts.find"
}
},
"name": "posts",
"settings": {},
"version": undefined,
"nodeID": "server-2"
}, {
"actions": {
"posts.find": {
"cache": true,
"custom": 5,
"name": "posts.find"
}
},
"name": "posts",
"settings": {},
"version": undefined,
"nodeID": null
}]);
});
});
describe("Test with internal actions", () => {
const broker = new ServiceBroker({ internalActions: true });
const registry = broker.serviceRegistry;
it("should returns the internal list", () => {
expect(registry.getServiceList({}).length).toBe(1);
expect(registry.getServiceList({})).toEqual([{"name": "$node", "nodeID": null, "settings": {}, "version": undefined}]);
expect(registry.getServiceList({ skipInternal: true })).toEqual([]);
});
});
});
describe("Test registry.getActionList", () => {
const broker = new ServiceBroker({ internalActions: true });
const registry = broker.serviceRegistry;
let service = {
name: "posts",
settings: {}
};
let action = {
name: "posts.find",
cache: true,
custom: 5,
handler: jest.fn(),
service
};
it("should return empty list", () => {
expect(registry.getActionList({ onlyLocal: true, skipInternal: true, withEndpoints: false })).toEqual([]);
});
it("should return internal actions", () => {
expect(registry.getActionList({ onlyLocal: false, skipInternal: false, withEndpoints: false })).toEqual([
{
"action": {
"cache": false,
"name": "$node.list"
},
"available": true,
"count": 1,
"hasLocal": true,
"name": "$node.list"
},
{
"action": {
"cache": false,
"name": "$node.services"
},
"available": true,
"count": 1,
"hasLocal": true,
"name": "$node.services"
},
{
"action": {
"cache": false,
"name": "$node.actions"
},
"available": true,
"count": 1,
"hasLocal": true,
"name": "$node.actions"
},
{
"action": {
"cache": false,
"name": "$node.health"
},
"available": true,
"count": 1,
"hasLocal": true,
"name": "$node.health"
}
]);
});
it("should return remote actions", () => {
registry.registerService("server-2", service);
registry.registerAction("server-2", action);
expect(registry.getActionList({ onlyLocal: false, skipInternal: true, withEndpoints: false })).toEqual([{"action": {"cache": true, "custom": 5, "name": "posts.find"}, "available": true, "count": 1, "hasLocal": false, "name": "posts.find"}]);
});
it("should return empty list because only remote endpoint registered", () => {
expect(registry.getActionList({ onlyLocal: true, skipInternal: true, withEndpoints: false })).toEqual([]);
});
it("should return action list", () => {
registry.registerService(null, service);
registry.registerAction(null, action);
expect(registry.getActionList({ onlyLocal: false, skipInternal: true, withEndpoints: false })).toEqual([{"action": {"cache": true, "custom": 5, "name": "posts.find"}, "available": true, "count": 2, "hasLocal": true, "name": "posts.find"}]);
});
it("should return action list", () => {
let svc = { name: "hello", settings: {} };
registry.registerService("server-3", svc);
registry.registerAction("server-3", {
name: "hello.world",
service: svc
});
expect(registry.getActionList({ onlyLocal: false, skipInternal: true, withEndpoints: true })).toEqual([
{
"action": {
"cache": true,
"custom": 5,
"name": "posts.find"
},
"available": true,
"count": 2,
"endpoints": [
{
"nodeID": "server-2",
"state": "close"
},
{
"nodeID": null,
"state": "close"
}
],
"hasLocal": true,
"name": "posts.find"
},
{
"action": {
"name": "hello.world"
},
"available": true,
"count": 1,
"endpoints": [
{
"nodeID": "server-3",
"state": "close"
}
],
"hasLocal": false,
"name": "hello.world"
}
]);
});
});
describe("Test EndpointList constructor", () => {
const broker = new ServiceBroker();
it("should create instance with default options", () => {
let list = new ServiceRegistry.EndpointList(broker);
expect(list).toBeDefined();
expect(list.list).toBeDefined();
expect(list.opts).toEqual({"preferLocal": true, "strategy": STRATEGY_ROUND_ROBIN});
expect(list.counter).toBe(0);
expect(list.localEndpoint).toBeNull();
expect(list.count()).toBe(0);
expect(list.hasLocal()).toBe(false);
});
it("should create instance with options", () => {
let opts = {
preferLocal: false,
strategy: STRATEGY_RANDOM
};
let list = new ServiceRegistry.EndpointList(broker, opts);
expect(list).toBeDefined();
expect(list.opts).toEqual({"preferLocal": false, "strategy": STRATEGY_RANDOM});
});
});
describe("Test EndpointList add methods", () => {
const broker = new ServiceBroker();
let list = new ServiceRegistry.EndpointList(broker);
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { c: 3 };
it("should add items and not found local item", () => {
expect(list.add("node1", obj1)).toBe(true); // remote
expect(list.add("node2", obj2)).toBe(true); // remote
expect(list.count()).toBe(2);
expect(list.hasLocal()).toBe(false);
expect(list.counter).toBe(0);
});
it("should add & found local item", () => {
list.add(null, obj3); // local
expect(list.localEndpoint).toBeDefined();
expect(list.localEndpoint.action).toBe(obj3);
expect(list.count()).toBe(3);
expect(list.hasLocal()).toBe(true);
expect(list.counter).toBe(0);
});
it("should re-add remote item", () => {
expect(list.add("node1", obj1)).toBe(false); // remote
expect(list.count()).toBe(3);
expect(list.hasLocal()).toBe(true);
expect(list.counter).toBe(0);
});
});
describe("Test EndpointList get methods with round-robin", () => {
const broker = new ServiceBroker();
let list = new ServiceRegistry.EndpointList(broker, {
strategy: STRATEGY_ROUND_ROBIN
});
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { c: 3 };
list.add("node1", obj1);
list.add("node2", obj2);
list.add("node3", obj3);
it("should return items", () => {
let ep = list.get();
expect(ep.action).toBe(obj1);
expect(list.counter).toBe(1);
ep = list.get();
expect(ep.action).toBe(obj2);
expect(list.counter).toBe(2);
ep = list.get();
expect(ep.action).toBe(obj3);
expect(list.counter).toBe(3);
ep = list.get();
expect(ep.action).toBe(obj1);
expect(list.counter).toBe(1);
});
});
describe("Test EndpointList nextAvailable methods with preferLocal", () => {
const broker = new ServiceBroker();
let list = new ServiceRegistry.EndpointList(broker, {
preferLocal: true,
strategy: STRATEGY_ROUND_ROBIN
});
let obj0 = { a: 0 };
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { c: 3 };
it("should return null if list is empty", () => {
let ep = list.nextAvailable();
expect(ep).toBeNull();
});
it("should return the first item if list is contains one item", () => {
list.add("node1", obj1);
let ep = list.nextAvailable();
expect(ep.action).toBe(obj1);
ep = list.nextAvailable();
expect(ep.action).toBe(obj1);
});
it("should return null if list is contains one item but unavailable", () => {
list.list[0].state = CIRCUIT_OPEN;
let ep = list.nextAvailable();
expect(ep).toBeNull();
list.list[0].state = CIRCUIT_CLOSE;
});
it("should return the local item if list is contains local item", () => {
list.add(null, obj0);
let ep = list.nextAvailable();
expect(ep.action).toBe(obj0);
ep = list.nextAvailable();
expect(ep.action).toBe(obj0);
});
it("should return the remote item if the local item is unavailable", () => {
list.list[1].state = CIRCUIT_OPEN;
let ep = list.nextAvailable();
expect(ep.action).toBe(obj1);
ep = list.nextAvailable();
expect(ep.action).toBe(obj1);
});
it("should return the remote items if list is NOT contains local item", () => {
list.removeByNode(null);
list.add("node2", obj2);
list.add("node3", obj3);
let ep = list.nextAvailable();
expect(ep.action).toBe(obj2);
ep = list.nextAvailable();
expect(ep.action).toBe(obj3);
ep = list.nextAvailable();
expect(ep.action).toBe(obj1);
});
it("should skip the unavailable items", () => {
list.removeByNode(null);
list.add("node2", obj2);
list.add("node3", obj3);
list.list[1].state = CIRCUIT_OPEN;
let ep = list.nextAvailable();
expect(ep.action).toBe(obj3);
// obj2 skipped
ep = list.nextAvailable();
expect(ep.action).toBe(obj1);
ep = list.nextAvailable();
expect(ep.action).toBe(obj3);
});
it("should returns null if every endpoints are unavailable", () => {
list.list[0].state = CIRCUIT_OPEN;
list.list[1].state = CIRCUIT_OPEN;
list.list[2].state = CIRCUIT_OPEN;
let ep = list.nextAvailable();
expect(ep).toBeNull();
ep = list.nextAvailable();
expect(ep).toBeNull();
});
});
describe("Test EndpointList getAction method", () => {
const broker = new ServiceBroker();
let list = new ServiceRegistry.EndpointList(broker);
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { c: 3 };
it("should return null", () => {
expect(list.getAction()).toBeNull();
expect(list.count()).toBe(0);
});
it("should return items", () => {
list.add("node1", obj1);
list.add("node2", obj2);
list.add("node3", obj3);
expect(list.count()).toBe(3);
expect(list.getAction()).toBe(obj1);
expect(list.getAction()).toBe(obj2);
expect(list.getAction()).toBe(obj3);
});
});
describe("Test EndpointList getLocalEndpoint & hasLocal methods", () => {
const broker = new ServiceBroker();
let list = new ServiceRegistry.EndpointList(broker);
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { c: 3 };
it("should return null", () => {
expect(list.hasLocal()).toBe(false);
expect(list.getLocalEndpoint()).toBeNull();
});
it("should return the local endpoint", () => {
list.add("node1", obj1);
list.add("node2", obj2);
list.add(null, obj3);
expect(list.hasLocal()).toBe(true);
expect(list.getLocalEndpoint().action).toBe(obj3);
expect(list.getLocalEndpoint().action).toBe(obj3);
});
it("should return null after remove", () => {
list.removeByNode(null);
expect(list.hasLocal()).toBe(false);
expect(list.getLocalEndpoint()).toBeNull();
});
});
describe("Test EndpointList removeByAction & removeByNode methods", () => {
const broker = new ServiceBroker();
let list = new ServiceRegistry.EndpointList(broker);
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { c: 3 };
it("should not throw Error", () => {
list.removeByAction();
list.removeByNode();
});
it("should remove item by action", () => {
list.add("node1", obj1);
list.add("node2", obj2);
list.add(null, obj3);
expect(list.count()).toBe(3);
list.removeByAction(obj2);
expect(list.count()).toBe(2);
});
it("should remove item by nodeID", () => {
expect(list.count()).toBe(2);
list.removeByNode(null);
expect(list.count()).toBe(1);
list.removeByNode("node1");
expect(list.count()).toBe(0);
});
});
describe("Test Endpoint constructor", () => {
const broker = new ServiceBroker();
broker.emit = jest.fn();
const action = {
name: "user.list",
handler: jest.fn()
};
it("should create a default instance", () => {
let item = new ServiceRegistry.Endpoint(broker, "node2", action);
expect(item).toBeDefined();
expect(item.broker).toBe(broker);
expect(item.nodeID).toBe("node2");
expect(item.action).toBe(action);
expect(item.local).toBe(false);
expect(item.state).toBe(CIRCUIT_CLOSE);
expect(item.failures).toBe(0);
expect(item.cbTimer).toBeNull();
});
it("should create a local instance", () => {
let item = new ServiceRegistry.Endpoint(broker, null, action);
expect(item).toBeDefined();
expect(item.broker).toBe(broker);
expect(item.nodeID).toBeNull();
expect(item.action).toBe(action);
expect(item.local).toBe(true);
expect(item.state).toBe(CIRCUIT_CLOSE);
expect(item.failures).toBe(0);
expect(item.cbTimer).toBeNull();
});
it("should update action", () => {
const action2 = {
name: "user.find"
};
let item = new ServiceRegistry.Endpoint(broker, null, action);
item.updateAction(action2);
expect(item.action).toBe(action2);
});
});
describe("Test Endpoint circuit methods", () => {
const broker = new ServiceBroker({
circuitBreaker: {
maxFailures: 2,
halfOpenTime: 5 * 1000
}
});
broker.emitLocal = jest.fn();
const action = {
name: "user.list",
handler: jest.fn()
};
let item;
beforeEach(() => {
item = new ServiceRegistry.Endpoint(broker, null, action);
});
it("test available", () => {
item.state = CIRCUIT_HALF_OPEN;
expect(item.available()).toBe(true);
item.state = CIRCUIT_OPEN;
expect(item.available()).toBe(false);
item.state = CIRCUIT_CLOSE;
expect(item.available()).toBe(true);
});
it("test failure", () => {
item.circuitOpen = jest.fn();
expect(item.failures).toBe(0);
expect(item.circuitOpen).toHaveBeenCalledTimes(0);
item.failure();
expect(item.failures).toBe(1);
expect(item.circuitOpen).toHaveBeenCalledTimes(0);
item.failure();
expect(item.failures).toBe(2);
expect(item.circuitOpen).toHaveBeenCalledTimes(1);
});
it("test circuitOpen", () => {
const clock = lolex.install();
item.state = CIRCUIT_CLOSE;
item.circuitHalfOpen = jest.fn();
item.circuitOpen();
expect(item.state).toBe(CIRCUIT_OPEN);
expect(item.cbTimer).toBeDefined();
expect(broker.emitLocal).toHaveBeenCalledTimes(1);
expect(broker.emitLocal).toHaveBeenCalledWith("circuit-breaker.open", { nodeID: null, action, failures: 0 });
// circuitHalfOpen
expect(item.circuitHalfOpen).toHaveBeenCalledTimes(0);
clock.tick(6 * 1000);
expect(item.circuitHalfOpen).toHaveBeenCalledTimes(1);
clock.uninstall();
});
it("test circuitOpen", () => {
broker.emitLocal.mockClear();
item.state = CIRCUIT_OPEN;
item.circuitHalfOpen();
expect(item.state).toBe(CIRCUIT_HALF_OPEN);
expect(broker.emitLocal).toHaveBeenCalledTimes(1);
expect(broker.emitLocal).toHaveBeenCalledWith("circuit-breaker.half-open", { nodeID: null, action });
});
it("test circuitOpen", () => {
broker.emitLocal.mockClear();
item.state = CIRCUIT_HALF_OPEN;
item.failures = 5;
item.circuitClose();
expect(item.state).toBe(CIRCUIT_CLOSE);
expect(item.failures).toBe(0);
expect(broker.emitLocal).toHaveBeenCalledTimes(1);
expect(broker.emitLocal).toHaveBeenCalledWith("circuit-breaker.close", { nodeID: null, action });
});
});
describe("Test ServiceItem constructor", () => {
const settings = {};
let item;
let action = {
name: "posts.find",
cache: true,
handler: jest.fn()
};
it("should create instance", () => {
item = new ServiceRegistry.ServiceItem(null, "posts", 2, settings);
expect(item).toBeDefined();
expect(item.nodeID).toBe(null);
expect(item.name).toBe("posts");
expect(item.version).toBe(2);
expect(item.settings).toBe(settings);
expect(item.local).toBe(true);
expect(item.actions).toEqual({});
});
it("should add action", () => {
item.addAction(action);
expect(item.actions["posts.find"]).toBe(action);
});
it("check isSame", () => {
expect(item.isSame("posts")).toBe(false);
expect(item.isSame("posts", null)).toBe(false);
expect(item.isSame("posts", 2)).toBe(true);
let item2 = new ServiceRegistry.ServiceItem(null, "users", undefined, settings);
expect(item2.isSame("users")).toBe(true);
expect(item2.isSame("users", null)).toBe(true);
expect(item2.isSame("users", 2)).toBe(false);
});
});