proxy-tracker
Version:
Nato per separare in modo semplice il controllo degli errori e il logging dalla logica dell'applicazione. Facilita l'uso di Proxy con handler complessi scrivendoli in una forma standardizzata. Permette di inserire funzioni "spia" tramite Proxy
208 lines (182 loc) • 11.7 kB
JavaScript
/* global Promise, describe, it, __dirname, process, Reflect, Function*/
const {expect, assert} = require('chai');
const util = require('util');
const ChangeEnv = require('change-env')(require);
const ENVIRONMENT = process.env.NODE_ENV;
const {ProxyTracker, ProxyExtension} = require(`../proxy-tracker.js`);
const errors = require('../src/errors.js').errors.message;
const t = {entity_obj: {prop: {value: 5}, met: function(){return {prop_nested: {value: 'value'}, met_nested: function(){return {prop_nested2: 'value'};}, met_nested2(){return 8;}};},
met_in_FOR: function(){return {prop_nested: {value: 'value'}, met_nested: function(){return {prop_nested2: 'value'};}, met_nested2(){return 8;}};}}};
Object.freeze(t);
function cbs(bridge, trap){
this.cb_ret = function (...args){bridge.push(`${trap} callback called`); return Reflect[trap](...args);};
this.cb = function (...args){bridge.push(`${trap} callback called`); let [, ...trap_args] = args; return Reflect[trap](...trap_args);};
this.cb1 = function (...args){bridge.push(`${trap}`);};
this.cb01 = function cb01(){};
this.cb02 = function cb02(){};
return this;
}
describe('disabling proxy feature for trap', () => {
let bridge;
beforeEach(()=>{
bridge = [];
});
it('ProxyTracker: handler without key FOR doesnt throw error and work normally', () => {
const handler = {get: {apply: [cbs(bridge, 'apply').cb, {get: cbs(bridge, 'get').cb}]}};
const proxy_entity = new ProxyTracker(t.entity_obj, handler);
proxy_entity.met; expect(bridge).to.eql([]);
let value = proxy_entity.met(); expect(bridge).to.eql(['apply callback called']);
expect(util.types.isProxy(value)).to.be.true;
let value_nested = value.met_nested; expect(bridge).to.eql(['apply callback called', 'get callback called']);
expect(util.types.isProxy(value_nested)).to.be.false;
});
it('ProxyExtension: handler without key FOR doesnt throw error and work normally', () => {
const handler = {get: {apply: [cbs(bridge, 'apply').cb_ret, {get: cbs(bridge, 'get').cb_ret}]}};
const proxy_entity = new ProxyExtension(t.entity_obj, handler);
proxy_entity.met; expect(bridge).to.eql([]);
let value = proxy_entity.met(); expect(bridge).to.eql(['apply callback called']);
expect(util.types.isProxy(value)).to.be.true;
let value_nested = value.met_nested; expect(bridge).to.eql(['apply callback called', 'get callback called']);
expect(util.types.isProxy(value_nested)).to.be.false;
});
it('proxy tracker: handler with key FOR abilits handler only for name prop in FOR', () => {
const handler = {get: {FOR: 'met_in_FOR', apply: [cbs(bridge, 'apply').cb, {get: cbs(bridge, 'get').cb}]}};
const proxy_entity = new ProxyTracker(t.entity_obj, handler);
proxy_entity.met; expect(bridge).to.eql([]);
let value = proxy_entity.met(); expect(bridge).to.eql([]);
expect(util.types.isProxy(value)).to.be.false;
let value_nested = value.met_nested; expect(bridge).to.eql([]);
expect(util.types.isProxy(value_nested)).to.be.false;
proxy_entity.met_in_FOR; expect(bridge).to.eql([]);
value = proxy_entity.met_in_FOR(); expect(bridge).to.eql(['apply callback called']);
expect(util.types.isProxy(value)).to.be.true;
value_nested = value.met_nested; expect(bridge).to.eql(['apply callback called', 'get callback called']);
expect(util.types.isProxy(value_nested)).to.be.false;
});
it('proxy tracker: hander with and without FOR. return expected', () => {
const handler = [{get: {FOR: 'met_in_FOR', apply: cbs(bridge, 'apply FOR').cb1}},
{get: {apply: cbs(bridge, 'apply each').cb1}}];
const proxy_entity = new ProxyTracker(t.entity_obj, ...handler);
bridge_expect = [];
proxy_entity.met(); bridge_expect.push('apply each'); expect(bridge).to.eql(bridge_expect);
proxy_entity.met_in_FOR(); bridge_expect.push('apply each'); bridge_expect.push('apply FOR'); expect(bridge).to.eql(bridge_expect);
});
it('proxy tracker: handler with FOR in apply', () => {
const handler = {apply: {FOR: 'met', apply: cbs(bridge, 'apply').cb1}};
const proxy_entity = new ProxyTracker(function met(){return function nested_met(){return 'value';};}, handler);
bridge_expect = [];
let value = proxy_entity(); expect(bridge).to.eql(bridge_expect);
value(); bridge_expect.push('apply'); expect(bridge).to.eql(bridge_expect);
const proxy_entity2 = new ProxyTracker(function other_met(){return function nested_met(){return 'value';};}, handler);
value = proxy_entity2(); expect(bridge).to.eql(bridge_expect);
value(); expect(bridge).to.eql(bridge_expect);
});
it('proxy tracker: handler with key FOR complex case', () => {
const handler = [{get: [{FOR: 'met_in_FOR', apply: [cbs(bridge, 'apply').cb1,
{get: [cbs(bridge, 'get nested').cb1,
{FOR: 'met_nested', apply: cbs(bridge, 'apply nested').cb1}
]
}]
}, {apply: cbs(bridge, 'apply for each in first get').cb1}]
}, {get: {FOR: ['met_in_FOR', 'prop'], get: cbs(bridge, 'get in second handler').cb1}}];
const proxy_entity = new ProxyTracker(t.entity_obj, ...handler);
const bridge_expect = [];
proxy_entity.met; expect(bridge).to.eql([]);
let value = proxy_entity.met(); bridge_expect.push('apply for each in first get'); expect(bridge).to.eql(bridge_expect);
expect(util.types.isProxy(value)).to.be.false;
proxy_entity.met_in_FOR; expect(bridge).to.eql(bridge_expect);
let val_temp = proxy_entity.prop; expect(bridge).to.eql(bridge_expect);
expect(util.types.isProxy(val_temp)).to.be.true;
proxy_entity.prop.value; bridge_expect.push('get in second handler'); expect(bridge).to.eql(bridge_expect);
value = proxy_entity.met_in_FOR(); bridge_expect.push('apply for each in first get'); bridge_expect.push('apply'); expect(bridge).to.eql(bridge_expect);
expect(util.types.isProxy(value)).to.be.true;
val_temp = value.prop_nested; bridge_expect.push('get nested'); expect(bridge).to.eql(bridge_expect);
expect(util.types.isProxy(val_temp)).to.be.false;
val_temp = value.met_nested; bridge_expect.push('get nested'); expect(bridge).to.eql(bridge_expect);
expect(util.types.isProxy(val_temp)).to.be.true;
val_temp = value.met_nested2(); bridge_expect.push('get nested'); expect(bridge).to.eql(bridge_expect);
expect(util.types.isProxy(val_temp)).to.be.false;
val_temp = value.met_nested(); bridge_expect.push('get nested'); bridge_expect.push('apply nested'); expect(bridge).to.eql(bridge_expect);
expect(util.types.isProxy(val_temp)).to.be.false;
});
});
describe('internal handler track generator', () => {
let bridge;
beforeEach(()=>{
bridge = [];
});
ChangeEnv('dev', ()=>{
const {generaHandlerForProxyTrack} = require(`../proxy-tracker.js`).test;
const NAME = generaHandlerForProxyTrack.CONST.NAME;
it('name: single value return expected', () => {
const cb01 = cbs(bridge).cb01;
const cb02 = cbs(bridge).cb02;
const handler = {get: [cb01, {FOR: 'prop1', get: cb02}]};
const expected = {get: {cbs: [cb01], hds: undefined, FOR: [{[NAME]: 'prop1', get: {cbs: [cb02], hds: undefined}}]}};
expect(generaHandlerForProxyTrack(handler)).to.eql(expected);
});
it('name: array of value return expected', () => {
const cb01 = cbs(bridge).cb01;
const cb02 = cbs(bridge).cb02;
const handler = {get: [cb01, {FOR: ['prop1', 'prop2'], get: cb02}]};
const expected = {get: {cbs: [cb01], hds: undefined, FOR: [{[NAME]: 'prop1', get: {cbs: [cb02], hds: undefined}}, {[NAME]: 'prop2', get: {cbs: [cb02], hds: undefined}}]}};
expect(generaHandlerForProxyTrack(handler)).to.eql(expected);
});
it('complex case', () => {
const cb01 = cbs(bridge).cb01;
const cb02 = cbs(bridge).cb02;
const handler = [{get: [cb01, {FOR: ['prop1', 'prop2'], get: cb02}]},
{get: cb02},
{get: {FOR: 'prop3', apply: cb02}},
{has: [cb01, cb02]},
{apply: {FOR: 'prop1', get: cb01}},
{get: {FOR: 'prop1', get: cb01, has: cb02}},
{get: {FOR: ['prop1', 'prop3'], get: cb01, apply: cb02}},
{apply: cb01},
{set: {FOR: 'prop1', get: cb01}},
{set: {has: cb01}},
{trap1: {trap2: [cb01, {trap3: cb02}, {trap3: cb02}, {trap3: {trap4: cb01}}, {trap3: {FOR: 'prop8', trap8: {trap9: cb02}}}]}}
];
const expected = {get: {cbs: [cb01], hds: undefined, FOR: [{[NAME]: 'prop1', get: {cbs: [cb02], hds: undefined}}, {[NAME]: 'prop2', get: {cbs: [cb02], hds: undefined}}]}};
expected.get.cbs.push(cb02);
expected.get.FOR.push({[NAME]: 'prop3', apply: {cbs: [cb02], hds: undefined}});
expected.has = {cbs: [cb01, cb02], hds: undefined};
expected.apply = {cbs: [], hds: undefined, FOR: [{[NAME]: 'prop1', get: {cbs: [cb01], hds: undefined}}]};
expected.get.FOR[0].get.cbs.push(cb01); expected.get.FOR[0].has = {cbs: [cb02], hds: undefined};
expected.get.FOR[0].get.cbs.push(cb01); expected.get.FOR[0].apply = {cbs: [cb02], hds: undefined};
expected.get.FOR[2].get = {cbs: [cb01], hds: undefined}; expected.get.FOR[2].apply.cbs.push(cb02);
expected.apply.cbs.push(cb01);
expected.set = {cbs: [], hds: undefined, FOR: [{[NAME]: 'prop1', get: {cbs: [cb01], hds: undefined}}]};
expected.set.hds = {has: {cbs: [cb01], hds: undefined}};
expected.trap1 = {cbs: [], hds: {trap2: {cbs: [cb01], hds: {trap3: {cbs: [cb02, cb02], hds: {trap4: {cbs: [cb01], hds: undefined}},
FOR: [{[NAME]: 'prop8', trap8: {cbs: [], hds: {trap9:{cbs: [cb02], hds: undefined} } }}]
}}}}};
expect(generaHandlerForProxyTrack(...handler)).to.eql(expected);
});
describe('wrong value', () => {
for(let wrong of [{key: 'value'}, function(){return 'value';}, 0,-8,+5,true, false]){
it(`name of FOR ${wrong} that isnt string or undefined throws error`, () => {
const handler = {get: {FOR: wrong, get: function(){}}};
expect(()=>{generaHandlerForProxyTrack(handler);}).to.throw(errors.name_for_in_handler_isnt_string);
});
}
});
}, `../proxy-tracker.js`);
});
describe('internal handler generator: extractReturningTrapsFromFOR', () => {
ChangeEnv('dev', ()=>{
const {generaHandlerForProxy} = require(`../proxy-tracker.js`).test;
const extractReturningTrapsFromFOR = generaHandlerForProxy.extractReturningTrapsFromFOR;
const trapList = generaHandlerForProxy.default_trapList;
const NAME = Symbol('NAME');
it('return expected', () => {
const handler_track = {get: {cbs: [], hds: undefined, FOR: [{[NAME]: 'prop1', get: {cbs: [], hds: undefined}}, {[NAME]: 'prop2', apply: {cbs: [], hds: undefined}}]}};
const handlersListByFOR = extractReturningTrapsFromFOR({NAME}, handler_track.get.FOR, trapList);
const expected = {prop1: {get: Function}, prop2: {apply: Function}};
for(let prop in expected){
expect(handlersListByFOR).to.have.property(prop).that.is.an('object');
expect(handlersListByFOR[prop]).to.have.all.keys(expected[prop]);
}
});
}, `../proxy-tracker.js`);
});