import { pretty, simpleCopy } from '../src/utils/helpers'; import { type AuditMessage, validateMessage, buildMessageForJS, } from '../src/models/audit'; const valids: AuditMessage[] = [ { client: 'flashaudit-node-client', product: 'automatic-test', user: '01', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { stringField: {old: 'before', new: 'after'}, numericField: {old: 10, new: 20}, booleanField: {old: false, new: true}, nullField: {old: null, new: 10}, onlyOld: {old: 10}, onlyNew: {new: 10}, items: { // group of items that belong to searchable. itemId1: { // added item with its starting fields and values. itemfield1: 'abc', itemfield2: [123, 456, 789], itemfield3: [{a: 10, b: 20}, {a: 11, b: 21}, {a: 12, b: 22}], }, itemId2: { // changed item. onlyOld: {old: 10}, onlyNew: {new: 10}, itemfield2: {old: false, new: true}, itemfield3: { old: [{a: 10, b: 20}, {a: 11, b: 21}, {a: 12, b: 22}], new: [{a: 10, b: 20}, {a: 12, b: 22}], }, }, itemId3: null, // deleted item. }, }, }, { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'trigger', values: { cmd: 'speedtest', query: "onu onlines", targets: ['flashman', 'google'], things: [{a: 10}, {b: false}], }, }, { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', }, { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', values: {a: 10}, }, { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'create', values: { id: 'aa:bb:cc:dd:ee:ff', date: 1234567896766, arr: [1,2,3,4], lan_devices: { itemId1: { // added item with its starting fields and values. itemfield1: 'abc', itemfield2: [123, 456, 789], itemfield3: [{a: 10, b: 20}, {a: 11, b: 21}, {a: 12, b: 22}], }, }, }, }, ]; const invalids = [ { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { field: { old: {a: 10}, // 'field' should be flat. new: {a: 20}, }, }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { items: { itemId1: { subfield1: {old: 220, new: 20}, subfield3: 'abc', // mixed edit and create structure for item. }, }, }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { wifi_ssid: {old: 'my first wifi name', new: 'second wifi name'}, wifi_ssid_prefix: {new: 'second wifi name'}, items: [{ // Group of items should be an Object, not an Array. itemId1: { subfield1: {old: 10, new: 20}, }, }], }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { // only 'new' or 'old' keys allowed when 'operation' is "edit". field: {old: 'before', new: 'after', other: 'xxxxx'}, }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { // 'field' should be flat when its not a group of items. field: { // If 'field' is a group of items, 'subfield' should be an item id and its value should // be an Object with the item's fields. // An Object with 'old' and 'new' keys is only allowed as value of an item field. subfield: {old: 'my first wifi name', new: 'second wifi name'}, }, }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'trigger', values: { // missing 'values.cmd' String field when 'operation' is "trigger". query: "onu onlines", targets: ['flashman', 'google'], things: [{a: 10}, {b: false}], }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'trigger', values: { cmd: 'some event', parameter: {a: 10}, // parameter should be flat. }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'create', values: { field: {old: 220, new: 20}, // edit structure in create 'operation'. }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'create', values: { items: { itemId1: { itemfield1: {old: 220, new: 20}, // edit structure in create 'operation'. }, }, }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'create', values: { // 'field' should be flat when it's not a group of items. field: { // if 'field' is a group of items, 'subfield' should be an // item id and its value should be an map with its fields. subfield: 10, }, }, }, fs: 'all', }, { v: { product: 'automatic-test', // missing 'client' field. user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { field: {old: 10, new: 20} }, }, fs: 'all', }, { v: { client: 123123123, product: 'automatic-test', // 'client' should be a String. user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { field: {old: 10, new: 20} }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', // missing 'product' field. user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { field: {old: 10, new: 20} }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: ['automatic-test'], // 'product' should be a String. user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', values: { field: {old: 10, new: 20} }, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', // missing attribute 'version' of type Number. user: '0', date: Date.now(), object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', }, fs: 'validateMessage', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', // missing 'date' field. user: '0', version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', }, fs: 'validateMessage', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', // 'date' is not a number or a Date Object. user: '0', date: new Date().toISOString(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', }, fs: 'validateMessage', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', // 'date' is an old value and won't pass sane date check. user: '0', date: new Date('2021'), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', }, fs: 'validateMessage', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', // missing 'searchable' field. operation: 'delete', }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: 'aa:bb:cc:dd:ee:ff', // 'searchable' should be a String[]. operation: 'delete', }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: [], // 'searchable' cannot be em empty Array. operation: 'delete', }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, // 'searchable' should be an array of only strings. object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff', 112312], operation: 'delete', }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, searchable: 'aa:bb:cc:dd:ee:ff', // missing 'object' field. operation: 'delete', }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 1242342, searchable: 'aa:bb:cc:dd:ee:ff', // 'object' should be a String. operation: 'delete', }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', // missing 'user' field. date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', // user should be a String. user: 1233453453, date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '1233453453', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], // 'operation' field is missing. values: {a: 10}, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '1233453453', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'dsdsds', // 'operation' has unrecognized name. }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '1233453453', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 131231, // 'operation' should be a String. }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'create', // those empty 'values' fields will be cleaned and 'values' will become undefined, // which is invalid for 'operation' "create". values: {arr: [], lan_devices: {}}, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'edit', // those empty 'values' fields will be cleaned and 'values' will become undefined, // which is invalid for 'operation' "edit". values: {arr: [], lan_devices: {}}, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'trigger', // those empty 'values' fields will be cleaned and 'values' will become undefined, // which is invalid for 'operation' "trigger". values: {arr: [], lan_devices: {}}, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'delete', // array elements should be String, Boolean, Number or Null only. values: {arr: [{a: 10, b: 'abc'}], lan_devices: {}}, }, fs: 'all', }, { v: { client: 'flashaudit-node-client', product: 'automatic-test', user: '0', date: Date.now(), version: 1, object: 'cpe', searchable: ['aa:bb:cc:dd:ee:ff'], operation: 'create', values: {a :10}, dsds: 12121, // extra property; }, fs: 'validateMessage', }, ]; let success = true; // testing valid AuditMessages. for (let i = 0; i < valids.length; i++) { let m = valids[i]; const functions = []; let err1 = validateMessage(simpleCopy(m)); if (err1) functions.push('validateMessage()'); let [new2, err2] = buildMessageForJS(m.client, m.product, m.user, m.object, m.searchable, m.operation, m.values); if (err2 && !new2) functions.push('buildMessageForJS()'); // We can't check against the model 'audit.build' function because the typescript transpiler // will complain against forced type errors and this test will not even run. if (functions.length > 0) { success = false; console.log( `Valid Audit register ${i+1}, out of ${valids.length}, `+ `was not validateMessaged for functions [${functions.join(', ')}]:`, pretty(m, true), err1 || err2, ); } } // testing invalid AuditMessages. for (let i = 0; i < invalids.length; i++) { let invalid = invalids[i] let m = invalid.v; let fs = invalid.fs; const functions = []; if (fs === 'all' || fs === 'validateMessage') { let err = validateMessage(simpleCopy(m)); if (!err) functions.push('validateMessage()'); } if (fs === 'all' || fs === 'buildMessageForJS') { let [newM, err] = buildMessageForJS( m.client, m.product, m.user, m.object, m.searchable, m.operation, m.values, ); if (!err && newM) functions.push(`buildMessageForJS()`); } // We can't check against the model 'audit.build' function because the typescript transpiler // will complain against forced type errors and this test will not even run. if (functions.length > 0) { success = false; console.log( `Invalid Audit register ${i+1}, out of ${invalids.length}, `+ `was not invalidated for functions [${functions.join(', ')}]:`, pretty(m, true), ); } } export default success;