import moment from 'moment'; import {ParsedNode, parseNode, parseNodesJson, parseTimestamp} from "./monitoringService"; import {MAC, OnlineState, to} from "../types"; import Logger from '../logger'; import {MockLogger} from "../__mocks__/logger"; const mockedLogger = Logger as MockLogger; jest.mock('../logger'); jest.mock('../db/database'); const NODES_JSON_INVALID_VERSION = 1; const NODES_JSON_VALID_VERSION = 2; const TIMESTAMP_INVALID_STRING = "2020-01-02T42:99:23.000Z"; const TIMESTAMP_VALID_STRING = "2020-01-02T12:34:56.000Z"; beforeEach(() => { mockedLogger.reset(); }); test('parseTimestamp() should fail parsing non-string timestamp', () => { // given const timestamp = {}; // when const parsedTimestamp = parseTimestamp(timestamp); // then expect(parsedTimestamp.isValid()).toBe(false); }); test('parseTimestamp() should fail parsing empty timestamp string', () => { // given const timestamp = ""; // when const parsedTimestamp = parseTimestamp(timestamp); // then expect(parsedTimestamp.isValid()).toBe(false); }); test('parseTimestamp() should fail parsing invalid timestamp string', () => { // given const timestamp = TIMESTAMP_INVALID_STRING; // when const parsedTimestamp = parseTimestamp(timestamp); // then expect(parsedTimestamp.isValid()).toBe(false); }); test('parseTimestamp() should succeed parsing valid timestamp string', () => { // given const timestamp = TIMESTAMP_VALID_STRING; // when const parsedTimestamp = parseTimestamp(timestamp); // then expect(parsedTimestamp.isValid()).toBe(true); expect(parsedTimestamp.toISOString()).toEqual(timestamp); }); test('parseNode() should fail parsing node for undefined node data', () => { // given const importTimestamp = moment(); const nodeData = undefined; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for empty node data', () => { // given const importTimestamp = moment(); const nodeData = {}; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for empty node info', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: {} }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for non-string node id', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: 42 } }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for empty node id', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "" } }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for empty network info', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "1234567890ab", network: {} } }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for invalid mac', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "1234567890ab", network: { mac: "xxx" } } }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for missing flags', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "1234567890ab", network: { mac: "12:34:56:78:90:ab" } } }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for empty flags', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "1234567890ab", network: { mac: "12:34:56:78:90:ab" } }, flags: {} }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for missing last seen timestamp', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "1234567890ab", network: { mac: "12:34:56:78:90:ab" } }, flags: { online: true } }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should fail parsing node for invalid last seen timestamp', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "1234567890ab", network: { mac: "12:34:56:78:90:ab" } }, flags: { online: true }, lastseen: 42 }; // then expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); }); test('parseNode() should succeed parsing node without site and domain', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "1234567890ab", network: { mac: "12:34:56:78:90:ab" } }, flags: { online: true }, lastseen: TIMESTAMP_VALID_STRING }; // then const expectedParsedNode: ParsedNode = { mac: to("12:34:56:78:90:AB"), importTimestamp: importTimestamp, state: OnlineState.ONLINE, lastSeen: parseTimestamp(TIMESTAMP_VALID_STRING), site: to(""), domain: to(""), }; expect(parseNode(importTimestamp, nodeData)).toEqual(expectedParsedNode); }); test('parseNode() should succeed parsing node with site and domain', () => { // given const importTimestamp = moment(); const nodeData = { nodeinfo: { node_id: "1234567890ab", network: { mac: "12:34:56:78:90:ab" }, system: { site_code: "test-site", domain_code: "test-domain" } }, flags: { online: true }, lastseen: TIMESTAMP_VALID_STRING, }; // then const expectedParsedNode: ParsedNode = { mac: to("12:34:56:78:90:AB"), importTimestamp: importTimestamp, state: OnlineState.ONLINE, lastSeen: parseTimestamp(TIMESTAMP_VALID_STRING), site: to("test-site"), domain: to("test-domain") }; expect(parseNode(importTimestamp, nodeData)).toEqual(expectedParsedNode); }); test('parseNodesJson() should fail parsing empty string', () => { // given const json = ""; // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing malformed JSON', () => { // given const json = '{"version": 2]'; // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing JSON null', () => { // given const json = JSON.stringify(null); // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing JSON string', () => { // given const json = JSON.stringify("foo"); // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing JSON number', () => { // given const json = JSON.stringify(42); // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing empty JSON object', () => { // given const json = JSON.stringify({}); // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing for mismatching version', () => { // given const json = JSON.stringify({ version: NODES_JSON_INVALID_VERSION }); // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing for missing timestamp', () => { // given const json = JSON.stringify({ version: NODES_JSON_VALID_VERSION, nodes: [] }); // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing for invalid timestamp', () => { // given const json = JSON.stringify({ version: NODES_JSON_VALID_VERSION, timestamp: TIMESTAMP_INVALID_STRING, nodes: [] }); // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should fail parsing for nodes object instead of array', () => { // given const json = JSON.stringify({ version: NODES_JSON_VALID_VERSION, timestamp: TIMESTAMP_VALID_STRING, nodes: {} }); // then expect(() => parseNodesJson(json)).toThrowError(); }); test('parseNodesJson() should succeed parsing no nodes', () => { // given const json = JSON.stringify({ version: NODES_JSON_VALID_VERSION, timestamp: TIMESTAMP_VALID_STRING, nodes: [] }); // when const result = parseNodesJson(json); // then expect(result.importTimestamp.isValid()).toBe(true); expect(result.nodes).toEqual([]); expect(result.failedNodesCount).toEqual(0); expect(result.totalNodesCount).toEqual(0); }); test('parseNodesJson() should skip parsing invalid nodes', () => { // given const json = JSON.stringify({ version: NODES_JSON_VALID_VERSION, timestamp: TIMESTAMP_VALID_STRING, nodes: [ {}, { nodeinfo: { node_id: "1234567890ab", network: { mac: "12:34:56:78:90:ab" }, system: { site_code: "test-site", domain_code: "test-domain" } }, flags: { online: true }, lastseen: TIMESTAMP_INVALID_STRING, } ] }); // when const result = parseNodesJson(json); // then expect(result.importTimestamp.isValid()).toBe(true); expect(result.nodes).toEqual([]); expect(result.failedNodesCount).toEqual(2); expect(result.totalNodesCount).toEqual(2); expect(mockedLogger.getMessages('error', 'monitoring', 'parsing-nodes-json').length).toEqual(2); }); test('parseNodesJson() should parse valid nodes', () => { // given const json = JSON.stringify({ version: NODES_JSON_VALID_VERSION, timestamp: TIMESTAMP_VALID_STRING, nodes: [ {}, // keep an invalid one for good measure { nodeinfo: { node_id: "1234567890ab", network: { mac: "12:34:56:78:90:ab" }, system: { site_code: "test-site", domain_code: "test-domain" } }, flags: { online: true }, lastseen: TIMESTAMP_VALID_STRING, } ] }); // when const result = parseNodesJson(json); // then const expectedParsedNode: ParsedNode = { mac: to("12:34:56:78:90:AB"), importTimestamp: parseTimestamp(TIMESTAMP_VALID_STRING), state: OnlineState.ONLINE, lastSeen: parseTimestamp(TIMESTAMP_VALID_STRING), site: to("test-site"), domain: to("test-domain"), }; expect(result.importTimestamp.isValid()).toBe(true); expect(result.nodes).toEqual([expectedParsedNode]); expect(result.failedNodesCount).toEqual(1); expect(result.totalNodesCount).toEqual(2); expect(mockedLogger.getMessages('error', 'monitoring', 'parsing-nodes-json').length).toEqual(1); });