iota-gateway
Advanced tools
Comparing version 0.0.1 to 0.1.0
@@ -0,15 +1,28 @@ | ||
/// <reference types="node" /> | ||
import { Transaction, Hash } from 'iota-tangle'; | ||
import { EventEmitter } from 'events'; | ||
import { Neighbor } from './neighbor'; | ||
import { Transport } from './transport'; | ||
export declare class Gateway { | ||
export interface Data { | ||
transaction: Transaction; | ||
requestHash: Hash; | ||
} | ||
export declare class Gateway extends EventEmitter { | ||
private _neighbors; | ||
private _transports; | ||
private _neighborsTransportsMap; | ||
private _isRunning; | ||
private _transports; | ||
private _onTransportReceive; | ||
private _onTransportError; | ||
constructor(params: { | ||
transports: Transport[]; | ||
neighbors?: Neighbor[]; | ||
transports?: Transport[]; | ||
}); | ||
send(transaction: Transaction, params: { | ||
neighbor: string; | ||
requestHash: Hash; | ||
}): Promise<void>; | ||
readonly isRunning: boolean; | ||
getNeighbor(neighborAddress: string): Neighbor | null; | ||
addNeighbor(neighbor: Neighbor): Promise<void>; | ||
removeNeighbor(neighbor: Neighbor): Promise<void>; | ||
run(): Promise<void>; | ||
stop(): Promise<void>; | ||
shutdown(): Promise<void>; | ||
send(data: Data, neighborAddress: string): Promise<void>; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
class Gateway { | ||
const events_1 = require("events"); | ||
class Gateway extends events_1.EventEmitter { | ||
constructor(params) { | ||
super(); | ||
this._isRunning = false; | ||
this._transports = params.transports; | ||
this._onTransportReceive = null; | ||
this._onTransportError = null; | ||
const transports = params.transports || []; | ||
const neighbors = params.neighbors || []; | ||
if (!transports.length) { | ||
throw new Error('You should provide at least one transport for the gateway!'); | ||
} | ||
const neighborsTransportsMap = new Map(); | ||
NEIGHBOR_LOOP: for (const neighbor of neighbors) { | ||
for (const transport of transports) { | ||
if (transport.supports(neighbor)) { | ||
neighborsTransportsMap.set(neighbor, transport); | ||
continue NEIGHBOR_LOOP; | ||
} | ||
} | ||
throw new Error(`Couldn't find a transport for the neighbor '${neighbor.address}'!`); | ||
} | ||
this._transports = transports; | ||
this._neighbors = neighbors; | ||
this._neighborsTransportsMap = neighborsTransportsMap; | ||
} | ||
async send(transaction, params) { | ||
get isRunning() { | ||
return this._isRunning; | ||
} | ||
getNeighbor(neighborAddress) { | ||
for (const neighbor of this._neighbors) { | ||
if (neighbor.match(neighborAddress)) { | ||
return neighbor; | ||
} | ||
} | ||
return null; | ||
} | ||
async addNeighbor(neighbor) { | ||
if (this._neighborsTransportsMap.has(neighbor)) { | ||
throw new Error(`Couldn't add a neighbor: the neighbor '${neighbor.address}' already exists!`); | ||
} | ||
for (const transport of this._transports) { | ||
if (transport.supports(neighbor)) { | ||
if (this._isRunning) { | ||
await transport.addNeighbor(neighbor); | ||
} | ||
this._neighborsTransportsMap.set(neighbor, transport); | ||
this._neighbors.push(neighbor); | ||
return; | ||
} | ||
} | ||
throw new Error(`Couldn't find a transport for the neighbor '${neighbor.address}'!`); | ||
} | ||
async removeNeighbor(neighbor) { | ||
if (!this._neighborsTransportsMap.has(neighbor)) { | ||
throw new Error(`Couldn't remove a neighbor: the neighbor '${neighbor.address}' doesn't exists!`); | ||
} | ||
if (this._isRunning) { | ||
await this._neighborsTransportsMap.get(neighbor).removeNeighbor(neighbor); | ||
} | ||
this._neighborsTransportsMap.delete(neighbor); | ||
this._neighbors.splice(this._neighbors.indexOf(neighbor), 1); | ||
} | ||
async run() { | ||
@@ -15,18 +71,65 @@ if (this._isRunning) { | ||
try { | ||
await Promise.all(this._transports.map(t => t.run(() => { }))); | ||
await Promise.all(this._transports.map(async (transport) => { | ||
await transport.run(); | ||
})); | ||
await Promise.all(this._neighbors.map(async (neighbor) => { | ||
await this._neighborsTransportsMap.get(neighbor).addNeighbor(neighbor); | ||
})); | ||
const onTransportReceive = (data, neighbor) => this.emit('receive', data, neighbor.address); | ||
const onTransportError = (error) => this.emit('error', error); | ||
for (const transport of this._transports) { | ||
transport.on('receive', onTransportReceive); | ||
transport.on('error', onTransportError); | ||
} | ||
this._onTransportReceive = onTransportReceive; | ||
this._onTransportError = onTransportError; | ||
} | ||
catch (error) { | ||
await Promise.all(this._transports.map(t => t.stop())); | ||
await Promise.all(this._neighbors.map(async (neighbor) => { | ||
try { | ||
await this._neighborsTransportsMap.get(neighbor).removeNeighbor(neighbor); | ||
} | ||
catch (error) { } | ||
})); | ||
await Promise.all(this._transports.map(async (transport) => { | ||
try { | ||
await transport.shutdown(); | ||
} | ||
catch (error) { } | ||
})); | ||
throw error; | ||
} | ||
this.emit('run'); | ||
this._isRunning = true; | ||
} | ||
async stop() { | ||
async shutdown() { | ||
if (!this._isRunning) { | ||
throw new Error('The gateway is not running!'); | ||
} | ||
await Promise.all(this._transports.map(t => t.stop())); | ||
await Promise.all(this._neighbors.map((neighbor) => { | ||
return this._neighborsTransportsMap.get(neighbor).removeNeighbor(neighbor); | ||
})); | ||
await Promise.all(this._transports.map((transport) => { | ||
return transport.shutdown(); | ||
})); | ||
for (const transport of this._transports) { | ||
transport.removeListener('receive', this._onTransportReceive); | ||
transport.removeListener('error', this._onTransportError); | ||
} | ||
this._onTransportReceive = null; | ||
this._onTransportError = null; | ||
this.emit('shutdown'); | ||
this._isRunning = false; | ||
} | ||
async send(data, neighborAddress) { | ||
if (!this._isRunning) { | ||
throw new Error("Can't send a data: the gateway is not running!"); | ||
} | ||
const neighbor = this.getNeighbor(neighborAddress); | ||
if (!neighbor) { | ||
throw new Error(`Neighbor is not found for address '${neighborAddress}'!`); | ||
} | ||
await this._neighborsTransportsMap.get(neighbor).send(data, neighbor); | ||
} | ||
} | ||
exports.Gateway = Gateway; |
@@ -1,5 +0,4 @@ | ||
export { Gateway } from './gateway'; | ||
export { Gateway, Data } from './gateway'; | ||
export { Neighbor } from './neighbor'; | ||
export { Transport } from './transport'; | ||
export { Packer } from './packer'; | ||
export { Transport, ReceiveCallback } from './transport'; | ||
export { TcpTransport } from './tcp-transport'; | ||
export { UdpTransport } from './udp-transport'; |
@@ -5,9 +5,7 @@ "use strict"; | ||
exports.Gateway = gateway_1.Gateway; | ||
var neighbor_1 = require("./neighbor"); | ||
exports.Neighbor = neighbor_1.Neighbor; | ||
var transport_1 = require("./transport"); | ||
exports.Transport = transport_1.Transport; | ||
var packer_1 = require("./packer"); | ||
exports.Packer = packer_1.Packer; | ||
var transport_1 = require("./transport"); | ||
exports.Transport = transport_1.Transport; | ||
var tcp_transport_1 = require("./tcp-transport"); | ||
exports.TcpTransport = tcp_transport_1.TcpTransport; | ||
var udp_transport_1 = require("./udp-transport"); | ||
exports.UdpTransport = udp_transport_1.UdpTransport; |
/// <reference types="node" /> | ||
import { Transaction, Hash, Factory } from 'iota-tangle'; | ||
import { Factory } from 'iota-tangle'; | ||
import { Data } from './gateway'; | ||
export declare const TRANSCTION_OFFSET = 0; | ||
@@ -8,6 +9,2 @@ export declare const TRANSACTION_SIZE: number; | ||
export declare const PACKET_SIZE: number; | ||
export interface PacketData { | ||
transaction: Transaction; | ||
requestHash: Hash; | ||
} | ||
export declare class Packer { | ||
@@ -19,4 +16,4 @@ private _factory; | ||
readonly packetSize: number; | ||
pack(data: PacketData): Buffer; | ||
unpack(packet: Buffer): PacketData; | ||
pack(data: Data): Buffer; | ||
unpack(packet: Buffer): Data; | ||
} |
@@ -1,15 +0,13 @@ | ||
import { Transaction, Hash } from 'iota-tangle'; | ||
export interface ReceiveCallback { | ||
(transaction: Transaction, params: { | ||
neighbor: string; | ||
requestHash?: Hash; | ||
}): void; | ||
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import { Data } from './gateway'; | ||
import { Neighbor } from './neighbor'; | ||
export declare abstract class Transport extends EventEmitter { | ||
readonly abstract isRunning: boolean; | ||
abstract supports(neighbor: Neighbor): boolean; | ||
abstract run(): Promise<void>; | ||
abstract shutdown(): Promise<void>; | ||
abstract addNeighbor(neighbor: Neighbor): Promise<void>; | ||
abstract removeNeighbor(neighbor: Neighbor): Promise<void>; | ||
abstract send(data: Data, neighbor: Neighbor): Promise<void>; | ||
} | ||
export declare abstract class Transport { | ||
abstract send(transaction: Transaction, {neighbor: string, requestHash: Hash}: { | ||
neighbor: any; | ||
requestHash: any; | ||
}): Promise<void>; | ||
abstract run(cb: ReceiveCallback): Promise<void>; | ||
abstract stop(): Promise<void>; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
class Transport { | ||
const events_1 = require("events"); | ||
class Transport extends events_1.EventEmitter { | ||
} | ||
exports.Transport = Transport; |
{ | ||
"name": "iota-gateway", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "Network gateway for IOTA", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
import { Transaction, Hash } from 'iota-tangle' | ||
import { EventEmitter } from 'events' | ||
import { Neighbor } from './neighbor' | ||
import { Transport } from './transport' | ||
import { Packer } from './packer' | ||
export class Gateway { | ||
export interface Data { transaction: Transaction, requestHash: Hash } | ||
private _isRunning: boolean = false | ||
export class Gateway extends EventEmitter { | ||
private _neighbors: Neighbor[] | ||
private _transports: Transport[] | ||
private _neighborsTransportsMap: Map<Neighbor, Transport> | ||
private _isRunning: boolean = false | ||
private _onTransportReceive: ((data: Data, neighbor: Neighbor) => void)|null = null | ||
private _onTransportError: ((error: any) => void)|null = null | ||
constructor(params: { | ||
transports: Transport[] | ||
neighbors?: Neighbor[] | ||
transports?: Transport[] | ||
}) { | ||
this._transports = params.transports | ||
super() | ||
const transports = params.transports || [] | ||
const neighbors = params.neighbors || [] | ||
if (!transports.length) { | ||
throw new Error('You should provide at least one transport for the gateway!') | ||
} | ||
const neighborsTransportsMap = new Map<Neighbor, Transport>() | ||
NEIGHBOR_LOOP: for (const neighbor of neighbors) { | ||
for (const transport of transports) { | ||
if (transport.supports(neighbor)) { | ||
neighborsTransportsMap.set(neighbor, transport) | ||
continue NEIGHBOR_LOOP | ||
} | ||
} | ||
throw new Error(`Couldn't find a transport for the neighbor '${neighbor.address}'!`) | ||
} | ||
this._transports = transports | ||
this._neighbors = neighbors | ||
this._neighborsTransportsMap = neighborsTransportsMap | ||
} | ||
async send(transaction: Transaction, params: { neighbor: string, requestHash: Hash }): Promise<void> { | ||
get isRunning(): boolean { | ||
return this._isRunning | ||
} | ||
getNeighbor(neighborAddress: string): Neighbor|null { | ||
for (const neighbor of this._neighbors) { | ||
if (neighbor.match(neighborAddress)) { | ||
return neighbor | ||
} | ||
} | ||
return null | ||
} | ||
async addNeighbor(neighbor: Neighbor): Promise<void> { | ||
if (this._neighborsTransportsMap.has(neighbor)) { | ||
throw new Error(`Couldn't add a neighbor: the neighbor '${neighbor.address}' already exists!`) | ||
} | ||
for (const transport of this._transports) { | ||
if (transport.supports(neighbor)) { | ||
if (this._isRunning) { | ||
await transport.addNeighbor(neighbor) | ||
} | ||
this._neighborsTransportsMap.set(neighbor, transport) | ||
this._neighbors.push(neighbor) | ||
return | ||
} | ||
} | ||
throw new Error(`Couldn't find a transport for the neighbor '${neighbor.address}'!`) | ||
} | ||
async removeNeighbor(neighbor: Neighbor): Promise<void> { | ||
if (!this._neighborsTransportsMap.has(neighbor)) { | ||
throw new Error(`Couldn't remove a neighbor: the neighbor '${neighbor.address}' doesn't exists!`) | ||
} | ||
if (this._isRunning) { | ||
await this._neighborsTransportsMap.get(neighbor).removeNeighbor(neighbor) | ||
} | ||
this._neighborsTransportsMap.delete(neighbor) | ||
this._neighbors.splice(this._neighbors.indexOf(neighbor), 1) | ||
} | ||
async run(): Promise<void> { | ||
@@ -27,12 +108,43 @@ if (this._isRunning) { | ||
try { | ||
await Promise.all(this._transports.map(t => t.run(() => {}))) | ||
await Promise.all(this._transports.map(async (transport: Transport) => { | ||
await transport.run() | ||
})) | ||
await Promise.all(this._neighbors.map(async (neighbor: Neighbor) => { | ||
await this._neighborsTransportsMap.get(neighbor).addNeighbor(neighbor) | ||
})) | ||
const onTransportReceive = (data: Data, neighbor: Neighbor) => this.emit('receive', data, neighbor.address) | ||
const onTransportError = (error: any) => this.emit('error', error) | ||
for (const transport of this._transports) { | ||
transport.on('receive', onTransportReceive) | ||
transport.on('error', onTransportError) | ||
} | ||
this._onTransportReceive = onTransportReceive | ||
this._onTransportError = onTransportError | ||
} catch (error) { | ||
await Promise.all(this._transports.map(t => t.stop())) | ||
await Promise.all(this._neighbors.map(async (neighbor: Neighbor) => { | ||
try { | ||
await this._neighborsTransportsMap.get(neighbor).removeNeighbor(neighbor) | ||
} catch (error) {} | ||
})) | ||
await Promise.all(this._transports.map(async (transport: Transport) => { | ||
try { | ||
await transport.shutdown() | ||
} catch (error) {} | ||
})) | ||
throw error | ||
} | ||
this.emit('run') | ||
this._isRunning = true | ||
} | ||
async stop(): Promise<void> { | ||
async shutdown(): Promise<void> { | ||
if (!this._isRunning) { | ||
@@ -42,6 +154,36 @@ throw new Error('The gateway is not running!') | ||
await Promise.all(this._transports.map(t => t.stop())) | ||
await Promise.all(this._neighbors.map((neighbor: Neighbor) => { | ||
return this._neighborsTransportsMap.get(neighbor).removeNeighbor(neighbor) | ||
})) | ||
await Promise.all(this._transports.map((transport: Transport) => { | ||
return transport.shutdown() | ||
})) | ||
for (const transport of this._transports) { | ||
transport.removeListener('receive', this._onTransportReceive) | ||
transport.removeListener('error', this._onTransportError) | ||
} | ||
this._onTransportReceive = null | ||
this._onTransportError = null | ||
this.emit('shutdown') | ||
this._isRunning = false | ||
} | ||
async send(data: Data, neighborAddress: string): Promise<void> { | ||
if (!this._isRunning) { | ||
throw new Error("Can't send a data: the gateway is not running!") | ||
} | ||
const neighbor = this.getNeighbor(neighborAddress) | ||
if (!neighbor) { | ||
throw new Error(`Neighbor is not found for address '${neighborAddress}'!`) | ||
} | ||
await this._neighborsTransportsMap.get(neighbor).send(data, neighbor) | ||
} | ||
} |
@@ -1,6 +0,4 @@ | ||
export { Gateway } from './gateway' | ||
export { Gateway, Data } from './gateway' | ||
export { Neighbor } from './neighbor' | ||
export { Transport } from './transport' | ||
export { Packer } from './packer' | ||
export { Transport, ReceiveCallback } from './transport' | ||
export { TcpTransport } from './tcp-transport' | ||
export { UdpTransport } from './udp-transport' | ||
import { Transaction, Hash, Factory } from 'iota-tangle' | ||
import { Data } from './gateway' | ||
@@ -11,7 +12,2 @@ export const TRANSCTION_OFFSET = 0 | ||
export interface PacketData { | ||
transaction: Transaction | ||
requestHash: Hash | ||
} | ||
export class Packer { | ||
@@ -28,3 +24,3 @@ private _factory: Factory | ||
pack(data: PacketData): Buffer { | ||
pack(data: Data): Buffer { | ||
const buffer = Buffer.alloc(PACKET_SIZE) | ||
@@ -38,3 +34,3 @@ | ||
unpack(packet: Buffer): PacketData { | ||
unpack(packet: Buffer): Data { | ||
const transactionBytes = packet.slice(TRANSCTION_OFFSET, TRANSCTION_OFFSET + TRANSACTION_SIZE) | ||
@@ -41,0 +37,0 @@ const hashBytes = packet.slice(REQUEST_HASH_OFFSET, REQUEST_HASH_OFFSET + REQUEST_HASH_SIZE) |
import { Transaction, Hash } from 'iota-tangle' | ||
import { EventEmitter } from 'events' | ||
import { parse as parseUrl } from 'url' | ||
import { Data } from './gateway' | ||
import { Neighbor } from './neighbor' | ||
export interface ReceiveCallback { | ||
(transaction: Transaction, params: { neighbor: string, requestHash?: Hash }): void | ||
export abstract class Transport extends EventEmitter { | ||
abstract get isRunning(): boolean | ||
abstract supports(neighbor: Neighbor): boolean | ||
abstract async run(): Promise<void> | ||
abstract async shutdown(): Promise<void> | ||
abstract async addNeighbor(neighbor: Neighbor): Promise<void> | ||
abstract async removeNeighbor(neighbor: Neighbor): Promise<void> | ||
abstract async send(data: Data, neighbor: Neighbor): Promise<void> | ||
} | ||
export abstract class Transport { | ||
abstract async send(transaction: Transaction, { neighbor: string, requestHash: Hash }): Promise<void> | ||
abstract async run(cb: ReceiveCallback): Promise<void> | ||
abstract async stop(): Promise<void> | ||
} |
import { expect, use }from 'chai' | ||
import { spy, stub } from 'sinon' | ||
import { spy, stub, SinonSpy } from 'sinon' | ||
@@ -7,5 +7,8 @@ use(require('chai-as-promised')) | ||
import { Gateway } from '../src/gateway' | ||
import { Transaction, Hash } from 'iota-tangle' | ||
import { Gateway, Data } from '../src/gateway' | ||
import { Transport } from '../src/transport' | ||
import { TransportStub } from './utils' | ||
import { Neighbor } from '../src/neighbor' | ||
import { TransportStub, NeighborStub, generateHash, generateTransaction } from './utils' | ||
import { setTimeout } from 'timers'; | ||
@@ -15,16 +18,16 @@ describe("Gateway", () => { | ||
let transports: Transport[] | ||
let neighbors: Neighbor[] | ||
beforeEach(() => { | ||
neighbors = [ | ||
new NeighborStub({ address: 'address1' }), | ||
new NeighborStub({ address: 'address2' }), | ||
] | ||
beforeEach(() => { | ||
transports = [ | ||
new TransportStub(), | ||
new TransportStub(), | ||
new TransportStub({ supports: (n) => n === neighbors[0] }), | ||
new TransportStub({ supports: (n) => n === neighbors[1] || n.address.startsWith('1234') }), | ||
] | ||
for (const transport of transports) { | ||
transport.run = spy() | ||
transport.stop = spy() | ||
} | ||
gateway = new Gateway({ transports }) | ||
gateway = new Gateway({ neighbors, transports }) | ||
}) | ||
@@ -34,4 +37,21 @@ | ||
describe("run()", () => { | ||
it("should call run() method of all gateway's transports", async () => { | ||
for (let transport of transports) { | ||
let runEventCallback: SinonSpy | ||
let receiveEventCallback: SinonSpy | ||
let errorEventCallback: SinonSpy | ||
beforeEach(() => { | ||
for (const transport of transports) { | ||
spy(transport, 'run') | ||
spy(transport, 'addNeighbor') | ||
spy(transport, 'removeNeighbor') | ||
spy(transport, 'shutdown') | ||
} | ||
gateway.on('run', runEventCallback = spy()) | ||
gateway.on('receive', receiveEventCallback = spy()) | ||
gateway.on('error', errorEventCallback = spy()) | ||
}) | ||
it("should launch all transports", async () => { | ||
for (const transport of transports) { | ||
expect(transport.run).to.not.have.been.called | ||
@@ -43,6 +63,68 @@ } | ||
for (let transport of transports) { | ||
expect(transport.run).to.have.been.called | ||
expect(transport.run).to.have.been.calledWith() | ||
} | ||
}) | ||
it("should make isRunning flag return true", async () => { | ||
expect(gateway.isRunning).to.be.false | ||
await expect(gateway.run()).to.be.fulfilled | ||
expect(gateway.isRunning).to.be.true | ||
}) | ||
it("should add neighbors to transports", async () => { | ||
for (const transport of transports) { | ||
expect(transport.addNeighbor).to.not.have.been.called | ||
} | ||
await expect(gateway.run()).to.be.fulfilled | ||
expect(transports[0].addNeighbor).to.have.been.calledWith(neighbors[0]) | ||
expect(transports[1].addNeighbor).to.have.been.calledWith(neighbors[1]) | ||
expect(transports[0].addNeighbor).to.not.have.been.calledWith(neighbors[1]) | ||
expect(transports[1].addNeighbor).to.not.have.been.calledWith(neighbors[0]) | ||
}) | ||
it("should emit 'run' event", async () => { | ||
expect(runEventCallback).to.not.have.been.called | ||
await expect(gateway.run()).to.be.fulfilled | ||
expect(runEventCallback).to.have.been.called | ||
}) | ||
it("should start receiving data from transports", async () => { | ||
await expect(gateway.run()).to.be.fulfilled | ||
expect(receiveEventCallback).to.not.have.been.called | ||
const data = { transaction: generateTransaction(), requestHash: generateHash() } | ||
const neighbor = neighbors[0] | ||
transports[0].emit('receive', data, neighbor) | ||
expect(receiveEventCallback).to.have.been.called | ||
const [emittedData, emittedNeighborAddress] = receiveEventCallback.args[0] | ||
expect(emittedData).to.equal(data) | ||
expect(emittedNeighborAddress).to.equal(neighbor.address) | ||
}) | ||
it("should start receiving errors from transports", async () => { | ||
await expect(gateway.run()).to.be.fulfilled | ||
expect(errorEventCallback).to.not.have.been.called | ||
const error = new Error('Some error') | ||
transports[0].emit('error', error) | ||
expect(errorEventCallback).to.have.been.called | ||
const [emittedError] = errorEventCallback.args[0] | ||
expect(emittedError).to.equal(error) | ||
}) | ||
it("should be rejected if some of the transports run() calls was rejected", async () => { | ||
@@ -55,4 +137,4 @@ transports[0].run = stub().resolves() | ||
it("should call stop() methods of all gateway's transports " + | ||
"if some of the transports run() calls was rejected", async () => { | ||
it("should shutdown all the gateway's transports " + | ||
"if there were an error while lanching the gatway", async () => { | ||
@@ -63,3 +145,3 @@ transports[0].run = stub().rejects() | ||
for (let transport of transports) { | ||
expect(transport.stop).to.not.have.been.called | ||
expect(transport.removeNeighbor).to.not.have.been.called | ||
} | ||
@@ -70,6 +152,25 @@ | ||
for (let transport of transports) { | ||
expect(transport.stop).to.have.been.called | ||
expect(transport.shutdown).to.have.been.called | ||
} | ||
}) | ||
it("should remove neighbors from all transports " + | ||
"if there were an error while lanching the gatway", async () => { | ||
transports[0].addNeighbor = stub().rejects() | ||
transports[1].addNeighbor = stub().resolves() | ||
for (let transport of transports) { | ||
expect(transport.removeNeighbor).to.not.have.been.called | ||
} | ||
await expect(gateway.run()).to.be.rejected | ||
expect(transports[0].removeNeighbor).to.have.been.calledWith(neighbors[0]) | ||
expect(transports[1].removeNeighbor).to.have.been.calledWith(neighbors[1]) | ||
expect(transports[0].removeNeighbor).to.not.have.been.calledWith(neighbors[1]) | ||
expect(transports[1].removeNeighbor).to.not.have.been.calledWith(neighbors[0]) | ||
}) | ||
it("should be rejected if server is already started", async () => { | ||
@@ -81,29 +182,274 @@ await expect(gateway.run()).to.not.be.rejected | ||
describe("stop()", () => { | ||
it("should call stop() method of all gateway's transports", async () => { | ||
describe("shutdown()", () => { | ||
let receiveEventCallback: SinonSpy | ||
let errorEventCallback: SinonSpy | ||
let shutdownEventCallback: SinonSpy | ||
beforeEach(async () => { | ||
await gateway.run() | ||
}) | ||
beforeEach(() => { | ||
for (const transport of transports) { | ||
spy(transport, 'removeNeighbor') | ||
spy(transport, 'shutdown') | ||
} | ||
gateway.on('receive', receiveEventCallback = spy()) | ||
gateway.on('error', errorEventCallback = spy()) | ||
gateway.on('shutdown', shutdownEventCallback = spy()) | ||
}) | ||
it("should shutdown all the gateway's transports", async () => { | ||
for (let transport of transports) { | ||
expect(transport.stop).to.not.have.been.called | ||
expect(transport.shutdown).to.not.have.been.called | ||
} | ||
await expect(gateway.stop()).to.be.fulfilled | ||
await expect(gateway.shutdown()).to.be.fulfilled | ||
for (let transport of transports) { | ||
expect(transport.stop).to.have.been.called | ||
expect(transport.shutdown).to.have.been.called | ||
} | ||
}) | ||
it("should be rejected if some of the transports stop() calls was rejected", async () => { | ||
transports[0].stop = stub().resolves() | ||
transports[1].stop = stub().rejects() | ||
it("should make isRunning flag return false", async () => { | ||
expect(gateway.isRunning).to.be.true | ||
await expect(gateway.shutdown()).to.be.fulfilled | ||
expect(gateway.isRunning).to.be.false | ||
}) | ||
await expect(gateway.stop()).to.be.rejected | ||
it("should remove neighbors from all transports ", async () => { | ||
for (let transport of transports) { | ||
expect(transport.removeNeighbor).to.not.have.been.called | ||
} | ||
await expect(gateway.shutdown()).to.be.fulfilled | ||
expect(transports[0].removeNeighbor).to.have.been.calledWith(neighbors[0]) | ||
expect(transports[1].removeNeighbor).to.have.been.calledWith(neighbors[1]) | ||
expect(transports[0].removeNeighbor).to.not.have.been.calledWith(neighbors[1]) | ||
expect(transports[1].removeNeighbor).to.not.have.been.calledWith(neighbors[0]) | ||
}) | ||
it("should stop receiving data from transports", async () => { | ||
await expect(gateway.shutdown()).to.be.fulfilled | ||
expect(receiveEventCallback).to.not.have.been.called | ||
const data = { transaction: generateTransaction(), requestHash: generateHash() } | ||
const neighbor = neighbors[0] | ||
transports[0].emit('receive', data, neighbor) | ||
expect(receiveEventCallback).to.not.have.been.called | ||
}) | ||
it("should stop receiving errors from transports", async () => { | ||
await expect(gateway.shutdown()).to.be.fulfilled | ||
expect(errorEventCallback).to.not.have.been.called | ||
const error = new Error('Some error') | ||
transports[0].once('error', () => {}) | ||
transports[0].emit('error', error) | ||
expect(errorEventCallback).to.not.have.been.called | ||
}) | ||
it("should emit 'shutdown' event", async () => { | ||
expect(shutdownEventCallback).to.not.have.been.called | ||
await expect(gateway.shutdown()).to.be.fulfilled | ||
expect(shutdownEventCallback).to.have.been.called | ||
}) | ||
it("should be rejected if some of the transports shutdown() method calls was rejected", async () => { | ||
transports[0].shutdown = stub().resolves() | ||
transports[1].shutdown = stub().rejects() | ||
await expect(gateway.shutdown()).to.be.rejected | ||
}) | ||
it("should be rejected if the gateway is not running", async () => { | ||
await expect(gateway.stop()).to.be.rejected | ||
await expect(gateway.shutdown()).to.be.fulfilled | ||
await expect(gateway.shutdown()).to.be.rejected | ||
}) | ||
}) | ||
describe("send(data, neighbor)", () => { | ||
let data: Data | ||
let receiveEventCallback: SinonSpy | ||
let errorEventCallback: SinonSpy | ||
let shutdownEventCallback: SinonSpy | ||
beforeEach(async () => { | ||
data = { transaction: generateTransaction(), requestHash: generateHash() } | ||
await gateway.run() | ||
}) | ||
beforeEach(() => { | ||
for (const transport of transports) { | ||
spy(transport, 'send') | ||
} | ||
}) | ||
it("should delegate sending of data to the specified neighbor", async () => { | ||
expect(transports[0].send).to.not.have.been.called | ||
expect(transports[0].send).to.not.have.been.called | ||
await expect(gateway.send(data, neighbors[0].address)).to.have.been.fulfilled | ||
expect(transports[0].send).to.have.been.calledWith(data, neighbors[0]) | ||
expect(transports[1].send).to.not.have.been.called | ||
}) | ||
it("shoudl be rejected if the gateway is not running'", async () => { | ||
await expect(gateway.shutdown()).to.have.been.fulfilled | ||
await expect(gateway.send(data, neighbors[0].address)).to.have.been.rejected | ||
}) | ||
it("should be rejected if a neighbor doesn't exist for specified address", async () => { | ||
await expect(gateway.send(data, '1234.1234.1234.1243')).to.have.been.rejected | ||
for (const transport of transports) { | ||
expect(transport.send).to.not.have.been.called | ||
} | ||
}) | ||
}) | ||
describe("addNeighbor(neighbor)", () => { | ||
let neighbor: Neighbor | ||
beforeEach(async () => { | ||
neighbor = new NeighborStub({ address: '1234.1234.1234.1234' }) | ||
await gateway.run() | ||
for (const transport of transports) { | ||
spy(transport, 'addNeighbor') | ||
} | ||
}) | ||
it("should add neighbor to the gateway", async () => { | ||
expect(gateway.getNeighbor(neighbor.address)).to.be.null | ||
await expect(gateway.addNeighbor(neighbor)).to.be.fulfilled | ||
expect(gateway.getNeighbor(neighbor.address)).to.equal(neighbor) | ||
}) | ||
it("should add specified neighbor to the transport if the gateway is running", async () => { | ||
expect(transports[0].addNeighbor).to.not.have.been.called | ||
expect(transports[1].addNeighbor).to.not.have.been.called | ||
await expect(gateway.addNeighbor(neighbor)).to.be.fulfilled | ||
expect(transports[0].addNeighbor).to.not.have.been.called | ||
expect(transports[1].addNeighbor).to.have.been.calledWith(neighbor) | ||
}) | ||
it("should not add specified neighbor to the transport if the gateway is not running", async () => { | ||
await expect(gateway.shutdown()).to.be.fulfilled | ||
expect(transports[0].addNeighbor).to.not.have.been.called | ||
expect(transports[1].addNeighbor).to.not.have.been.called | ||
await expect(gateway.addNeighbor(neighbor)).to.be.fulfilled | ||
expect(transports[0].addNeighbor).to.not.have.been.called | ||
expect(transports[1].addNeighbor).to.not.have.been.called | ||
}) | ||
it("should be rejected if there were an error while adding neighbor to the transport ", async () => { | ||
transports[1].addNeighbor = stub().rejects() | ||
expect(transports[0].addNeighbor).to.not.have.been.called | ||
expect(transports[1].addNeighbor).to.not.have.been.called | ||
await expect(gateway.addNeighbor(neighbor)).to.be.rejected | ||
expect(transports[0].addNeighbor).to.not.have.been.called | ||
expect(transports[1].addNeighbor).to.have.been.calledWith(neighbor) | ||
}) | ||
it("should be rejected if there is no transport that supports specified neighbor", async () => { | ||
await expect(gateway.addNeighbor(new NeighborStub({ address: '4321.12.12.12' }))).to.be.rejected | ||
}) | ||
it("should be rejected if there specified neighbor has been already added to the gateway", async () => { | ||
await expect(gateway.addNeighbor(neighbor)).to.be.fulfilled | ||
await expect(gateway.addNeighbor(neighbor)).to.be.rejected | ||
}) | ||
}) | ||
describe("removeNeighbor(neighbor)", () => { | ||
let neighbor: Neighbor | ||
beforeEach(async () => { | ||
neighbor = new NeighborStub({ address: '1234.1234.1234.1234' }) | ||
await gateway.addNeighbor(neighbor) | ||
for (const transport of transports) { | ||
spy(transport, 'removeNeighbor') | ||
} | ||
}) | ||
it("should remove the neighbor from the gateway", async () => { | ||
expect(gateway.getNeighbor(neighbor.address)).to.equal(neighbor) | ||
await expect(gateway.removeNeighbor(neighbor)).to.be.fulfilled | ||
expect(gateway.getNeighbor(neighbor.address)).to.be.null | ||
}) | ||
it("should be rejected if specified neighbor doesn't exist", async () => { | ||
await expect(gateway.removeNeighbor(new NeighborStub({ address: '1234.555.55.55' }))).to.be.rejected | ||
}) | ||
it("should remove the neighbor from the transport if the gateway is running", async () => { | ||
await expect(gateway.run()).to.be.fulfilled | ||
expect(transports[0].removeNeighbor).to.not.have.been.called | ||
expect(transports[1].removeNeighbor).to.not.have.been.called | ||
await expect(gateway.removeNeighbor(neighbor)).to.be.fulfilled | ||
expect(transports[0].removeNeighbor).to.not.have.been.called | ||
expect(transports[1].removeNeighbor).to.have.been.calledWith(neighbor) | ||
}) | ||
it("should be rejected if there were an error while removing the neighbor from the transport", async () => { | ||
transports[1].removeNeighbor = stub().rejects() | ||
await expect(gateway.run()).to.be.fulfilled | ||
expect(transports[0].removeNeighbor).to.not.have.been.called | ||
expect(transports[1].removeNeighbor).to.not.have.been.called | ||
await expect(gateway.removeNeighbor(neighbor)).to.be.rejected | ||
expect(transports[0].removeNeighbor).to.not.have.been.called | ||
expect(transports[1].removeNeighbor).to.have.been.calledWith(neighbor) | ||
}) | ||
it("should not remove the neighbor from the transport if the gateway is not running", async () => { | ||
expect(transports[0].removeNeighbor).to.not.have.been.called | ||
expect(transports[1].removeNeighbor).to.not.have.been.called | ||
await expect(gateway.removeNeighbor(neighbor)).to.be.fulfilled | ||
expect(transports[0].removeNeighbor).to.not.have.been.called | ||
expect(transports[1].removeNeighbor).to.not.have.been.called | ||
}) | ||
}) | ||
}) |
@@ -14,2 +14,3 @@ import { expect } from 'chai' | ||
beforeEach(() => { | ||
@@ -24,2 +25,3 @@ serializer = new Serializer() | ||
describe('pack(packetData)', () => { | ||
@@ -40,2 +42,3 @@ it('should pack the specified transaction and transaction hash into a packet of bytes', () => { | ||
describe('unpack(packet)', () => { | ||
@@ -42,0 +45,0 @@ it('should unpack the specified packet', () => { |
import { Transaction, Hash, Factory, Serializer } from 'iota-tangle' | ||
import { Transport, ReceiveCallback } from '../src/transport' | ||
import { Data } from '../src/gateway' | ||
import { Transport } from '../src/transport' | ||
import { Neighbor } from '../src/neighbor' | ||
@@ -7,6 +9,2 @@ const serializer = new Serializer() | ||
export class TransportStub extends Transport { | ||
async run(cb: ReceiveCallback): Promise<void> {} | ||
async stop(): Promise<void> {} | ||
} | ||
@@ -21,2 +19,3 @@ export function generateTransaction(): Transaction { | ||
export function generateHash(): Hash { | ||
@@ -30,44 +29,46 @@ const buffer = Buffer.alloc(49) | ||
// export function generateTransaction(params: any = {}) { | ||
// if (params.message && params.message.length < 2187) { | ||
// params.message = params.message.padEnd(2187, '9') | ||
// } | ||
// return Object.assign({}, { | ||
// hash: 'IBQHNXPILYG9K9PFBZYZDTVHHQNEBENUKRAVMO9DJEGQ9VDIUAWU9FORBFZDSDBWOLWNFGCYVLEG99999', | ||
// address: 'CQNSCEPCDQNMWFYIVRZDSYKPFQLKPOSIXFZCKGTGDROGMDXMYHOR9JRUDZLXHOALXEAZWWLEXRJBLCWVC', | ||
// branch: 'EMEKOFNNGJDAZJWPWIZHIYQQCFWLBNNVCAEEJ9RDML9SHXUTDOKKBMRGHZWJIGYHUEKSZMMTAIWS99999', | ||
// trunk: 'YFEPOAXZKPAXQHMPIFGTNMTVFY9OHIBOIUVQVZYVNMEERVQCPU9UPJOGVM9D9X9YFLLFCSFVOXLK99999', | ||
// bundle: 'QLXDGLLXQIHPXYEKBPFHOSDFIXQRJCZVYVC9TOKBLRWYH9TWXCNGKI9N9RDBHLUBZGGFVWGIZU9Y9VDQW', | ||
// message: ( | ||
// 'MINEIOTADOTCOM9MANUAL9PAYOUT999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '9999999999999999999999999999999999999999999999999999999999999999999999999999999' + | ||
// '999999999999999999999999999999999999999999999999999999' | ||
// ) | ||
// }, params) | ||
// } | ||
export class NeighborStub extends Neighbor { | ||
private _address: string | ||
constructor(params: { address: string }) { | ||
super() | ||
this._address = params.address | ||
} | ||
get address(): string { | ||
return this._address | ||
} | ||
} | ||
export class TransportStub extends Transport { | ||
private _isRunning = false | ||
private _supports: Function | ||
get isRunning() { | ||
return this._isRunning | ||
} | ||
constructor(params: { supports: Function }) { | ||
super() | ||
this._supports = params.supports | ||
} | ||
supports(neighbor: Neighbor): boolean { | ||
return this._supports(neighbor) | ||
} | ||
async addNeighbor(neighbor: Neighbor): Promise<void> {} | ||
async removeNeighbor(neighbor: Neighbor): Promise<void> {} | ||
async send(data: Data, neighbor: Neighbor): Promise<void> {} | ||
async run(): Promise<void> { | ||
this._isRunning = true | ||
} | ||
async shutdown(): Promise<void> { | ||
this._isRunning = false | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
55173
1063
26
3