<commit_before>export { testCorrectness };

//import * as RandTools from "../util/RandTools.js";
import * as TensorPlaceholder from "../Conv/TensorPlaceholder.js";
import * as Operation from "../Conv/Operation.js";

/**
 * For testing Operation.Base.
 *
 */
class Case {

  /**
   * @param {number} caseId  The id of this test case.
   */
  constructor( caseId, bInput0, bInput1, outputTensorCount, bKeepInputTensor0, bKeepInputTensor1 ) {
    this.caseId = caseId;

    let input0;
    if ( bInput0 )
      input0 = new TensorPlaceholder.Base();

    let input1;
    if ( bInput1 )
      input1 = new TensorPlaceholder.Base();

    this.operation = new Operation.Base( input0, input1, outputTensorCount );
    this.operation.setKeepInputTensor( bKeepInputTensor0, bKeepInputTensor1 );

    tf.tidy( () => {

      let memoryInfo_apply_before = tf.memory(); // Test memory leakage of .apply.

      let numTensors_delta = 0;
      if ( input0 ) {
        input0.realTensor = tf.randomNormal( Case.testTensorShape );
        ++numTensors_delta;
      }

      if ( input1 ) {
        input1.realTensor = tf.randomNormal( Case.testTensorShape );
        ++numTensors_delta;
      }

      this.operation.apply();

//!!! ...unfinished... (2022/06/02)

      let memoryInfo_apply_after = tf.memory();

      let numTensors_result = ( memoryInfo_apply_before.numTensors + numTensors_delta );
      tf.util.assert( ( memoryInfo_apply_after.numTensors == ( memoryInfo_apply_before.numTensors + numTensors_delta ) ),
        `jsPerf_Operation.Case() memory leak. `
          + `( this.caseId == ${this.caseId} ). `
          + `result tensor count (${memoryInfo_apply_after.numTensors}) `
          + `should be ( ${numTensors_result} ) = ( ${memoryInfo_apply_before.numTensors} + ${numTensors_delta} ).`
       );
    });

//!!! ...unfinished... (2022/06/02)
}

Case.testTensorShape = [ 1 ];

function testCorrectness() {

//!!! ...unfinished... (2022/06/02)
//   let casesArray = [
//     new Case(  0, [  1,  2 ], [  3,  4 ],  5, [  3,  3 ], [  4,  6 ], [  3,  8 ], [   5,  10 ] ),
/
//   ];

  let caseId = -1;
  let bInput0, bInput1, outputTensorCount, bKeepInputTensor0, bKeepInputTensor1;

  for ( let nInput0 = 0; nInput0 <= 1; ++nInput0 ) {
    for ( let nInput1 = 0; nInput1 <= 1; ++nInput1 ) {
      for ( let outputTensorCount = 0; outputTensorCount <= 2; ++outputTensorCount ) {
        for ( let nKeepInputTensor0 = 0; nKeepInputTensor0 <= 1; ++nKeepInputTensor0 ) {
          for ( let nKeepInputTensor1 = 0; nKeepInputTensor1 <= 1; ++nKeepInputTensor1 ) {

            ++caseId;

            bInput0 = ( nInput0 != 0 );
            bInput1 = ( nInput1 != 0 );

            if ( !bInput0 && !bInput1 )
              continue; // Operation should have at least one input.

            bKeepInputTensor0 = ( nKeepInputTensor0 != 0 );
            bKeepInputTensor1 = ( nKeepInputTensor1 != 0 );

            let testCase = new Case( caseId, bInput0, bInput1, outputTensorCount, bKeepInputTensor0, bKeepInputTensor1 );
          }
        }
      }
    }
  }
}
<commit_msg>Update jsPerf_Operation.js<commit_after>export { testCorrectness };

//import * as RandTools from "../util/RandTools.js";
import * as TensorPlaceholder from "../Conv/TensorPlaceholder.js";
import * as Operation from "../Conv/Operation.js";

/**
 * For testing Operation.Base.
 *
 */
class Case {

  /**
   * @param {number} caseId  The id of this test case.
   */
  constructor( caseId, bInput0, bInput1, outputTensorCount, bKeepInputTensor0, bKeepInputTensor1 ) {
    this.caseId = caseId;

    let input0;
    if ( bInput0 )
      input0 = new TensorPlaceholder.Base();

    let input1;
    if ( bInput1 )
      input1 = new TensorPlaceholder.Base();

    this.operation = new Operation.Base( input0, input1, outputTensorCount );
    this.operation.setKeepInputTensor( bKeepInputTensor0, bKeepInputTensor1 );

    tf.tidy( () => {

      let memoryInfo_apply_before = tf.memory(); // Test memory leakage of .apply.

      let numTensors_delta = 0;
      if ( input0 ) {
        input0.realTensor = tf.randomNormal( Case.testTensorShape );
        ++numTensors_delta;
      }

      if ( input1 ) {
        input1.realTensor = tf.randomNormal( Case.testTensorShape );
        ++numTensors_delta;
      }

      this.operation.apply();

//!!! ...unfinished... (2022/06/02)

      let memoryInfo_apply_after = tf.memory();

      let numTensors_result = ( memoryInfo_apply_before.numTensors + numTensors_delta );
      tf.util.assert( ( memoryInfo_apply_after.numTensors == ( memoryInfo_apply_before.numTensors + numTensors_delta ) ),
        `jsPerf_Operation.Case() memory leak. `
          + `( this.caseId == ${this.caseId} ). `
          + `result tensor count (${memoryInfo_apply_after.numTensors}) `
          + `should be ( ${numTensors_result} ) = ( ${memoryInfo_apply_before.numTensors} + ${numTensors_delta} ).`
       );
    });

//!!! ...unfinished... (2022/06/02)
}

Case.testTensorShape = [ 1 ];

function testCorrectness() {

//!!! ...unfinished... (2022/06/02)
//   let casesArray = [
//     new Case(  0, [  1,  2 ], [  3,  4 ],  5, [  3,  3 ], [  4,  6 ], [  3,  8 ], [   5,  10 ] ),
/
//   ];

  let caseId = -1;
  let bInput0, bInput1, outputTensorCount, bKeepInputTensor0, bKeepInputTensor1;

  for ( let nInput0 = 0; nInput0 <= 1; ++nInput0 ) {
    for ( let nInput1 = 0; nInput1 <= 1; ++nInput1 ) {
      for ( let outputTensorCount = 0; outputTensorCount <= 2; ++outputTensorCount ) {
        for ( let nKeepInputTensor0 = 0; nKeepInputTensor0 <= 1; ++nKeepInputTensor0 ) {
          for ( let nKeepInputTensor1 = 0; nKeepInputTensor1 <= 1; ++nKeepInputTensor1 ) {

            ++caseId;

            bInput0 = ( nInput0 != 0 );
            bInput1 = ( nInput1 != 0 );

            //!!! (2022/06/02 Remarked) It could be supported. Just do nothing.
            //if ( !bInput0 && !bInput1 )
            //  continue; // Operation should have at least one input.

            bKeepInputTensor0 = ( nKeepInputTensor0 != 0 );
            bKeepInputTensor1 = ( nKeepInputTensor1 != 0 );

            let testCase = new Case( caseId, bInput0, bInput1, outputTensorCount, bKeepInputTensor0, bKeepInputTensor1 );
          }
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013-2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([
    'js-whatever/js/list-view',
    'backbone',
    'js-testing/backbone-mock-factory'
], function(ListView, Backbone, mockFactory) {

    var itemTemplate = _.template('<p id="item-<%=data.id%>" class="<%=className%>"><%-data.age%>
');
    var itemTemplateOptions = {className: 'my-class'};

    describe('List view', function() {
        beforeEach(function() {
            this.collection = new Backbone.Collection([
                {id: 'Fred', age: 32},
                {id: 'George', age: 46}
            ], {comparator: 'age'});
        });

        it('should call the correct method on the correctItemView on model change events when constructed with a collectionChangeEvents map', function() {
            var ItemView = mockFactory.getView(['changeAge']);

            var listView = new ListView({
                collection: this.collection,
                collectionChangeEvents: {age: 'changeAge'},
                ItemView: ItemView
            });

            listView.render();

            this.collection.get('Fred').set('age', 33);

            var fredView = ItemView.instances[0];
            expect(fredView.constructorArgs[0].model.id).toEqual('Fred');
            expect(fredView.changeAge).toHaveBeenCalled();

            expect(ItemView.instances[1].changeAge).not.toHaveBeenCalled();
        });

        describe('with default ItemView', function() {
            beforeEach(function() {
                this.list = new ListView({
                    collection: this.collection,
                    itemOptions: {
                        template: itemTemplate,
                        templateOptions: itemTemplateOptions
                    }
                });

                this.list.render();
            });

            it('should populate itself with existing collection items on initialize and render', function() {
                var $ps = this.list.$('p');
                expect($ps).toHaveLength(2);
                expect($ps).toHaveClass('my-class');

                expect($ps.eq(0)).toHaveText('32');
                expect($ps.eq(0)).toHaveId('item-Fred');
                expect($ps.eq(1)).toHaveText('46');
                expect($ps.eq(1)).toHaveId('item-George');
            });

            it('should set a data-id attribute on each item', function() {
                expect(this.list.$el.children().eq(0)).toHaveAttr('data-id', 'Fred');
            });

            it('should respond to collection "add" events', function() {
                this.collection.add({id: 'Penny', age: 26});

                var $ps = this.list.$('p');
                expect($ps).toHaveLength(3);
                expect($ps).toHaveClass('my-class');

                expect($ps.eq(0)).toHaveText('26');
                expect($ps.eq(0)).toHaveId('item-Penny');

                this.collection.add({id: 'Polly', age: 65});

                expect(this.list.$('p').eq(3)).toHaveId('item-Polly');
            });

            it('should respond to collection "remove" events', function() {
                this.collection.remove(this.collection.get('Fred'));

                var $ps = this.list.$('p');
                expect($ps).toHaveLength(1);
                expect($ps).toHaveText('46');
                expect($ps).toHaveId('item-George');
            });

            it('should respond to collection "change" events', function() {
                this.collection.get('Fred').set('age', 33);

                var $ps = this.list.$('p');
                expect($ps).toHaveLength(2);

                var $p = $ps.filter('#item-Fred');
                expect($p).toHaveText('33');
            });

            it('should respond to collection "sort" events', function() {
                this.collection.get('Fred').set('age', 105);
                this.collection.sort();

                var $ps = this.list.$('p');
                expect($ps.eq(0)).toHaveId('item-George');
                expect($ps.eq(1)).toHaveId('item-Fred');
            });

            it('should respond to collection "reset" events', function() {
                this.collection.reset([{id: 'Adrian', age: 106}]);

                expect(this.list.$('p')).toHaveLength(1);
                expect(this.list.$('p')).toHaveId('item-Adrian');
            });
        });

        describe('with event proxying', function() {
            beforeEach(function() {
                this.ItemView = mockFactory.getView();

                this.listView = new ListView({
                    collection: this.collection,
                    ItemView: this.ItemView,
                    proxyEvents: ['my-event']
                });

                this.listView.render();

                var eventArguments = this.eventArguments = [];

                this.listView.on('item:my-event', function() {
                    eventArguments.push(arguments);
                });
            });

            it('should proxy ItemView events specified in the proxyEvents option', function() {
                this.ItemView.instances[1].trigger('my-event', 123, 'abc');

                expect(this.eventArguments.length).toBe(1);
                expect(this.eventArguments[0]).toEqual([123, 'abc']);
            });

            it('should stop proxying events when the ItemView is removed', function() {
                this.collection.remove(this.collection.at(0));
                this.ItemView.instances[0].trigger('my-event', 123);

                expect(this.eventArguments.length).toBe(0);

                this.ItemView.instances[1].trigger('my-event', 123);

                expect(this.eventArguments.length).toBe(1);
            });

            it('should stop proxying events when the ListView is removed', function() {
                this.listView.remove();

                this.ItemView.instances[0].trigger('my-event');
                this.ItemView.instances[1].trigger('my-event');

                expect(this.eventArguments.length).toBe(0);
            });
        });
    });

});
<commit_msg>Fixed typo in test name.<commit_after>/*
 * Copyright 2013-2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([
    'js-whatever/js/list-view',
    'backbone',
    'js-testing/backbone-mock-factory'
], function(ListView, Backbone, mockFactory) {

    var itemTemplate = _.template('<p id="item-<%=data.id%>" class="<%=className%>"><%-data.age%>
');
    var itemTemplateOptions = {className: 'my-class'};

    describe('List view', function() {
        beforeEach(function() {
            this.collection = new Backbone.Collection([
                {id: 'Fred', age: 32},
                {id: 'George', age: 46}
            ], {comparator: 'age'});
        });

        it('should call the correct method on the correct ItemView on model change events when constructed with a collectionChangeEvents map', function() {
            var ItemView = mockFactory.getView(['changeAge']);

            var listView = new ListView({
                collection: this.collection,
                collectionChangeEvents: {age: 'changeAge'},
                ItemView: ItemView
            });

            listView.render();

            this.collection.get('Fred').set('age', 33);

            var fredView = ItemView.instances[0];
            expect(fredView.constructorArgs[0].model.id).toEqual('Fred');
            expect(fredView.changeAge).toHaveBeenCalled();

            expect(ItemView.instances[1].changeAge).not.toHaveBeenCalled();
        });

        describe('with default ItemView', function() {
            beforeEach(function() {
                this.list = new ListView({
                    collection: this.collection,
                    itemOptions: {
                        template: itemTemplate,
                        templateOptions: itemTemplateOptions
                    }
                });

                this.list.render();
            });

            it('should populate itself with existing collection items on initialize and render', function() {
                var $ps = this.list.$('p');
                expect($ps).toHaveLength(2);
                expect($ps).toHaveClass('my-class');

                expect($ps.eq(0)).toHaveText('32');
                expect($ps.eq(0)).toHaveId('item-Fred');
                expect($ps.eq(1)).toHaveText('46');
                expect($ps.eq(1)).toHaveId('item-George');
            });

            it('should set a data-id attribute on each item', function() {
                expect(this.list.$el.children().eq(0)).toHaveAttr('data-id', 'Fred');
            });

            it('should respond to collection "add" events', function() {
                this.collection.add({id: 'Penny', age: 26});

                var $ps = this.list.$('p');
                expect($ps).toHaveLength(3);
                expect($ps).toHaveClass('my-class');

                expect($ps.eq(0)).toHaveText('26');
                expect($ps.eq(0)).toHaveId('item-Penny');

                this.collection.add({id: 'Polly', age: 65});

                expect(this.list.$('p').eq(3)).toHaveId('item-Polly');
            });

            it('should respond to collection "remove" events', function() {
                this.collection.remove(this.collection.get('Fred'));

                var $ps = this.list.$('p');
                expect($ps).toHaveLength(1);
                expect($ps).toHaveText('46');
                expect($ps).toHaveId('item-George');
            });

            it('should respond to collection "change" events', function() {
                this.collection.get('Fred').set('age', 33);

                var $ps = this.list.$('p');
                expect($ps).toHaveLength(2);

                var $p = $ps.filter('#item-Fred');
                expect($p).toHaveText('33');
            });

            it('should respond to collection "sort" events', function() {
                this.collection.get('Fred').set('age', 105);
                this.collection.sort();

                var $ps = this.list.$('p');
                expect($ps.eq(0)).toHaveId('item-George');
                expect($ps.eq(1)).toHaveId('item-Fred');
            });

            it('should respond to collection "reset" events', function() {
                this.collection.reset([{id: 'Adrian', age: 106}]);

                expect(this.list.$('p')).toHaveLength(1);
                expect(this.list.$('p')).toHaveId('item-Adrian');
            });
        });

        describe('with event proxying', function() {
            beforeEach(function() {
                this.ItemView = mockFactory.getView();

                this.listView = new ListView({
                    collection: this.collection,
                    ItemView: this.ItemView,
                    proxyEvents: ['my-event']
                });

                this.listView.render();

                var eventArguments = this.eventArguments = [];

                this.listView.on('item:my-event', function() {
                    eventArguments.push(arguments);
                });
            });

            it('should proxy ItemView events specified in the proxyEvents option', function() {
                this.ItemView.instances[1].trigger('my-event', 123, 'abc');

                expect(this.eventArguments.length).toBe(1);
                expect(this.eventArguments[0]).toEqual([123, 'abc']);
            });

            it('should stop proxying events when the ItemView is removed', function() {
                this.collection.remove(this.collection.at(0));
                this.ItemView.instances[0].trigger('my-event', 123);

                expect(this.eventArguments.length).toBe(0);

                this.ItemView.instances[1].trigger('my-event', 123);

                expect(this.eventArguments.length).toBe(1);
            });

            it('should stop proxying events when the ListView is removed', function() {
                this.listView.remove();

                this.ItemView.instances[0].trigger('my-event');
                this.ItemView.instances[1].trigger('my-event');

                expect(this.eventArguments.length).toBe(0);
            });
        });
    });

});
<|endoftext|>


<commit_before>import { extend } from './util'
import { UPDATE_SELF } from './constants'

class ComponentWrapper {
  constructor (ComponentClass, props, context) {
    this.component = new ComponentClass(props, context)
    this.name = this.component.constructor.name
  }

  init () {
    this.component.__ref = this.component.props.ref
    delete this.component.props.ref
    this.component.mount()
    let domNode = this.component.dom
    if (!domNode) {
      return null
    }
    domNode._component = this.component
    domNode._componentConstructor = this.component.constructor
    return domNode
  }

  update (previous) {
    const props = this.component.props
    this.component = previous.component
    this.component.props = extend(this.component.props, props)
    this.component.update(UPDATE_SELF)
    if (this.component.dom) {
      this.component.dom._component = this.component
      this.component.dom._componentConstructor = this.component.constructor
    }
    return this.component.dom
  }

  destroy (domNode) {
    this.component.unmount()
    delete domNode.component
  }
}

ComponentWrapper.prototype.type = 'Widget'

export default ComponentWrapper<commit_msg>fix:兼容IE8<commit_after>import { extend } from './util'
import { UPDATE_SELF } from './constants'

class ComponentWrapper {
  constructor (ComponentClass, props, context) {
    this.component = new ComponentClass(props, context)
    this.name = this.component.constructor.name
  }

  init () {
    this.component.__ref = this.component.props.ref
    delete this.component.props.ref
    this.component.mount()
    let domNode = this.component.dom
    if (!domNode) {
      return null
    }
    domNode._component = this.component
    domNode._componentConstructor = this.component.constructor
    return domNode
  }

  update (previous) {
    const props = this.component.props
    this.component = previous.component
    this.component.props = extend(this.component.props, props)
    this.component.update(UPDATE_SELF)
    if (this.component.dom) {
      this.component.dom._component = this.component
      this.component.dom._componentConstructor = this.component.constructor
    }
    return this.component.dom
  }

  destroy (domNode) {
    this.component.unmount()
    if (domNode.component) {
      delete domNode.component
    }
  }
}

ComponentWrapper.prototype.type = 'Widget'

export default ComponentWrapper<|endoftext|>


<commit_before>/* Contains extra test cases for parts of DOM Level 2 that aren't covered by
 * the standard W3C test cases. */
exports.tests = {
  create_empty_document: function () {
    var dom = new DOMImplementation();
    var document = dom.createDocument(null, null, null);
    assertEquals("Document shouldn't contain any nodes",
        0, document.childNodes.length);
  },
  create_document_with_namespaceURI_but_not_qualifiedName: function () {
    var dom = new DOMImplementation();
    assertThrows('createDocument accepted invalid arguments',
        function () {
          dom.createDocument("http://example.org/motorcycle", null, null);
        },
        DOMException, DOMException.NAMESPACE_ERR);
  },
  doctype_ownerDocument: function () {
    var dom = new DOMImplementation();
    var doctype = dom.createDocumentType("bananas");
    var document = dom.createDocument(null, null, doctype);
    assertEquals('Doctype does not belong to the document',
        document, doctype.ownerDocument);
  },
  doctype_child_of_ownerDocument: function () {
    var dom = new DOMImplementation();
    var doctype = dom.createDocumentType("hatstand");
    var document = dom.createDocument(null, null, doctype);
    assertEquals('Doctype is not a child of the document',
        doctype, document.firstChild);
  }
}
<commit_msg>removed old file<commit_after><|endoftext|>


<commit_before>'use strict';

// ------- Imports -------------------------------------------------------------

const test = require('ava');
const chai = require('chai');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');

const Dequeuer = require('../../src/lib/Dequeuer');
const FreeFormMessage = require('../../src/messages/FreeFormMessage');
const HooksHelper = require('../helpers/HooksHelper');
const MessageFactoryHelper = require('../helpers/MessageFactoryHelper');

// ------- Init ----------------------------------------------------------------

chai.should();
chai.use(sinonChai);

// Setup blink app for each test.
test.beforeEach(HooksHelper.createRandomQueue);
test.afterEach.always(HooksHelper.destroyRandomQueue);

// ------- Tests ---------------------------------------------------------------

/**
 * Dequeuer: constructor()
 */
test('Dequeuer: Test class interface', (t) => {
  const dequeuer = new Dequeuer(t.context.queue);
  dequeuer.should.respondTo('dequeue');
  dequeuer.should.respondTo('executeCallback');
  dequeuer.should.respondTo('processCallbackResult');
  dequeuer.should.respondTo('processCallbackError');
  dequeuer.should.respondTo('extractOrDiscard');
  dequeuer.should.respondTo('unpack');
  dequeuer.should.respondTo('validate');
  dequeuer.should.respondTo('log');
});

/**
 * Dequeuer: executeCallback()
 */
test('Dequeuer.executeCallback(): should ack processed message', async (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure ack() will be called.
  const ackStub = sinon.stub(queue, 'ack').returns(null);

  // Create dequeue callback and spu on it.
  const callback = async () => true;
  const callbackSpy = sinon.spy(callback);

  // Prepare random message to make dequeur think it fot it from Rabbit.
  const message = MessageFactoryHelper.getRandomMessage();

  // Dequeue test message.
  const dequeuer = new Dequeuer(queue, callbackSpy);
  const result = await dequeuer.executeCallback(message);
  result.should.be.true;

  // Ensure callback has been called.
  callbackSpy.should.have.been.calledOnce;

  // Make sure the message has been acknowledged.
  ackStub.should.have.been.calledWith(message);

  // Cleanup.
  ackStub.restore();
});

/**
 * Dequeuer: executeCallback()
 */
test('Dequeuer/executeCallback(): ensure ack expected unprocessed message', async (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure ack() will be called.
  const ackStub = sinon.stub(queue, 'ack').returns(null);

  // Create dequeue callback and spu on it.
  const callback = async () => false;
  const callbackSpy = sinon.spy(callback);

  // Prepare random message to make dequeur think it fot it from Rabbit.
  const message = MessageFactoryHelper.getRandomMessage();

  // Dequeue test message.
  const dequeuer = new Dequeuer(queue, callbackSpy);
  const result = await dequeuer.executeCallback(message);
  result.should.be.true;

  // Ensure callback has been called.
  callbackSpy.should.have.been.calledOnce;

  // Make sure the message has been acknowledged.
  ackStub.should.have.been.calledWith(message);

  // Cleanup.
  ackStub.restore();
});

/**
 * Dequeuer: executeCallback()
 */
test('Dequeuer.executeCallback(): ensure nack on unexpected error thrown from callback', async (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Create dequeue callback and spu on it.
  const callback = async () => {
    throw new Error('Testing unexpected exception from worker callback');
  };
  const callbackSpy = sinon.spy(callback);

  // Prepare random message to make dequeur think it fot it from Rabbit.
  const message = MessageFactoryHelper.getRandomMessage();

  // Dequeue test message.
  const dequeuer = new Dequeuer(queue, callbackSpy);
  const result = await dequeuer.executeCallback(message);
  result.should.be.false;

  // Ensure callback has been called.
  callbackSpy.should.have.been.calledOnce;

  // Ensure the message has been nacked.
  nackStub.should.have.been.calledWith(message);

  // Cleanup.
  nackStub.restore();
});

/**
 * Dequeuer.extractOrDiscard()
 */
test('Dequeuer.extractOrDiscard(): ensure nack on incorrect JSON payload', (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Ensure Message.fromRabbitMessage throws MessageParsingBlinkError
  const blinkParsingErrorSpy = sinon.spy(queue.messageClass, 'parseIncomingPayload');

  // Create deliberaly incorrect JSON and feed it to extractOrDiscard.
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage('{incorrect-json}');
  const dequeuer = new Dequeuer(queue);
  const result = dequeuer.extractOrDiscard(rabbitMessage);
  result.should.be.false;

  // Ensure the message been nacked.
  nackStub.should.have.been.calledOnce;

  // Ensure MessageParsingBlinkError has been thrown.
  blinkParsingErrorSpy.should.have.thrown('MessageParsingBlinkError');

  // Cleanup.
  nackStub.restore();
  blinkParsingErrorSpy.restore();
});

/**
 * Dequeuer.extractOrDiscard()
 */
test('Dequeuer.extractOrDiscard(): ensure nack on unknown unpack error', (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Simulate unknown error in Message.fromRabbitMessage().
  // This method is static so we can stub it on Message constructor itself.
  const fromRabbitMessageStub = sinon.stub(queue.messageClass, 'fromRabbitMessage');
  fromRabbitMessageStub.throws(() => {
    // Fake unexpected error thrown from Message.fromRabbitMessage().
    const error = new Error('Testing unexpected exception from Message.fromRabbitMessage()');
    return error;
  });

  // Create random valid message.
  // It will be successfully unpacked from JSON,  but discarded anyways
  //  because we've overridden Message.fromRabbitMessage().
  const message = MessageFactoryHelper.getRandomMessage();
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage(message.toString());

  const dequeuer = new Dequeuer(queue);
  const result = dequeuer.extractOrDiscard(rabbitMessage);
  result.should.be.false;

  // Ensure the message been nacked.
  nackStub.should.have.been.calledOnce;

  // Cleanup.
  nackStub.restore();
  fromRabbitMessageStub.restore();
});

/**
 * Dequeuer.extractOrDiscard()
 */
test('Dequeuer.extractOrDiscard(): ensure nack on invalid message', (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Create invalid message (data expected to be an object)
  // and ensure it has been rejected with MessageValidationBlinkError.
  const invalidMessage = new FreeFormMessage({
    data: [],
    meta: {},
  });
  const validateSpy = sinon.spy(invalidMessage, 'validate');

  // Feed invalid message to Dequeuer.
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage(invalidMessage.toString());
  const dequeuer = new Dequeuer(queue);

  // Stub Dequeuer.unpack() to make it return message we actually spy on.
  const unpackStub = sinon.stub(dequeuer, 'unpack');
  unpackStub.callsFake(() => invalidMessage);

  const result = dequeuer.extractOrDiscard(rabbitMessage);
  result.should.be.false;

  // Ensure the message been nacked.
  nackStub.should.have.been.calledOnce;

  // Ensure MessageValidationBlinkError has been thrown.
  validateSpy.should.have.thrown('MessageValidationBlinkError');

  // Cleanup.
  nackStub.restore();
  validateSpy.restore();
  unpackStub.restore();
});

/**
 * Dequeuer.extractOrDiscard()
 */
test('Dequeuer.extractOrDiscard(): ensure nack on unknown error in Message.validate()', (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Create invalid message (data expected to be an object)
  // and ensure it has been rejected with MessageValidationBlinkError.
  const invalidMessage = new FreeFormMessage({
    data: [],
    meta: {},
  });
  const validateStub = sinon.stub(invalidMessage, 'validate');
  validateStub.throws(() => {
    // Fake unexpected error thrown from Message.fromRabbitMessage().
    const error = new Error('Testing unexpected exception from Message.validate()');
    return error;
  });

  // Feed invalid message to Dequeuer.
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage(invalidMessage.toString());
  const dequeuer = new Dequeuer(queue);

  // Stub Dequeuer.unpack() to make it return message we actually spy on.
  const unpackStub = sinon.stub(dequeuer, 'unpack');
  unpackStub.callsFake(() => invalidMessage);

  const result = dequeuer.extractOrDiscard(rabbitMessage);
  result.should.be.false;

  // Ensure the message been nacked.
  nackStub.should.have.been.calledOnce;

  // Cleanup.
  nackStub.restore();
  validateStub.restore();
  unpackStub.restore();
});

/**
 * Dequeuer.dequeue()
 */
test('Dequeuer.dequeue(): ensure executeCallback() is called on valid message', async (t) => {
  const queue = t.context.queue;

  // Create random Rabbit message and send it to .
  const message = MessageFactoryHelper.getRandomMessage();
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage(message.toString());
  const dequeuer = new Dequeuer(queue);

  // Stub dequeuer.executeCallback()
  const executeCallbackStub = sinon.stub(dequeuer, 'executeCallback').resolves(true);
  await dequeuer.dequeue(rabbitMessage);

  // ExecuteCallback has been called.
  executeCallbackStub.should.have.been.calledOnce;

  // Cleanup.
  executeCallbackStub.restore();
});

// ------- End -----------------------------------------------------------------
<commit_msg>Ensure Dequeuer.executeCallback() executes retry manager when retry is requested<commit_after>'use strict';

// ------- Imports -------------------------------------------------------------

const test = require('ava');
const chai = require('chai');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');

const BlinkRetryError = require('../../src/errors/BlinkRetryError');
const Dequeuer = require('../../src/lib/Dequeuer');
const RetryManager = require('../../src/lib/RetryManager');
const FreeFormMessage = require('../../src/messages/FreeFormMessage');
const HooksHelper = require('../helpers/HooksHelper');
const MessageFactoryHelper = require('../helpers/MessageFactoryHelper');

// ------- Init ----------------------------------------------------------------

chai.should();
chai.use(sinonChai);

// Setup blink app for each test.
test.beforeEach(HooksHelper.createRandomQueue);
test.afterEach.always(HooksHelper.destroyRandomQueue);

// ------- Tests ---------------------------------------------------------------

/**
 * Dequeuer: constructor()
 */
test('Dequeuer: Test class interface', (t) => {
  const dequeuer = new Dequeuer(t.context.queue);
  dequeuer.should.respondTo('dequeue');
  dequeuer.should.respondTo('executeCallback');
  dequeuer.should.respondTo('processCallbackResult');
  dequeuer.should.respondTo('processCallbackError');
  dequeuer.should.respondTo('extractOrDiscard');
  dequeuer.should.respondTo('unpack');
  dequeuer.should.respondTo('validate');
  dequeuer.should.respondTo('log');
});

/**
 * Dequeuer: executeCallback()
 */
test('Dequeuer.executeCallback(): should ack processed message', async (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure ack() will be called.
  const ackStub = sinon.stub(queue, 'ack').returns(null);

  // Create dequeue callback and spu on it.
  const callback = async () => true;
  const callbackSpy = sinon.spy(callback);

  // Prepare random message to make dequeur think it fot it from Rabbit.
  const message = MessageFactoryHelper.getRandomMessage();

  // Dequeue test message.
  const dequeuer = new Dequeuer(queue, callbackSpy);
  const result = await dequeuer.executeCallback(message);
  result.should.be.true;

  // Ensure callback has been called.
  callbackSpy.should.have.been.calledOnce;

  // Make sure the message has been acknowledged.
  ackStub.should.have.been.calledWith(message);

  // Cleanup.
  ackStub.restore();
});

/**
 * Dequeuer: executeCallback()
 */
test('Dequeuer/executeCallback(): ensure ack expected unprocessed message', async (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure ack() will be called.
  const ackStub = sinon.stub(queue, 'ack').returns(null);

  // Create dequeue callback and spu on it.
  const callback = async () => false;
  const callbackSpy = sinon.spy(callback);

  // Prepare random message to make dequeur think it fot it from Rabbit.
  const message = MessageFactoryHelper.getRandomMessage();

  // Dequeue test message.
  const dequeuer = new Dequeuer(queue, callbackSpy);
  const result = await dequeuer.executeCallback(message);
  result.should.be.true;

  // Ensure callback has been called.
  callbackSpy.should.have.been.calledOnce;

  // Make sure the message has been acknowledged.
  ackStub.should.have.been.calledWith(message);

  // Cleanup.
  ackStub.restore();
});

/**
 * Dequeuer: executeCallback()
 */
test('Dequeuer.executeCallback(): ensure nack on unexpected error thrown from callback', async (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Create dequeue callback and spu on it.
  const callback = async () => {
    throw new Error('Testing unexpected exception from worker callback');
  };
  const callbackSpy = sinon.spy(callback);

  // Prepare random message to make dequeur think it fot it from Rabbit.
  const message = MessageFactoryHelper.getRandomMessage();

  // Dequeue test message.
  const dequeuer = new Dequeuer(queue, callbackSpy);
  const result = await dequeuer.executeCallback(message);
  result.should.be.false;

  // Ensure callback has been called.
  callbackSpy.should.have.been.calledOnce;

  // Ensure the message has been nacked.
  nackStub.should.have.been.calledWith(message);

  // Cleanup.
  nackStub.restore();
});

/**
 * Dequeuer: executeCallback() retry
 */
test('Dequeuer.executeCallback(): ensure RetryManager is called when a retry is requested', async (t) => {
  const queue = t.context.queue;
  // Ensure message is not acked or nacked when retry error is called.
  const ackStub = sinon.stub(queue, 'ack').returns(null);
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Create random message and BlinkRetryError based on it.
  const message = MessageFactoryHelper.getRandomMessage();
  const retryError = new BlinkRetryError('Testing BlinkRetryError', message);

  // Simulate callback throwing the retry error during message processing.
  const callback = async () => {
    throw retryError;
  };
  const callbackSpy = sinon.spy(callback);

  // Create retry manager and spy on it.
  const retryManager = new RetryManager();
  const retryStub = sinon.stub(retryManager, 'retry').returns(true);

  // Execute callback using dequeuer.
  const dequeuer = new Dequeuer(queue, callbackSpy, retryManager);
  const result = await dequeuer.executeCallback(message);
  // Retry has been succesfully scheduled.
  result.should.be.true;

  // Ensure callback has been called.
  callbackSpy.should.have.been.calledOnce;

  // Ensure the message has been sent to RetryManager.
  retryStub.should.have.been.calledWith(message, retryError);

  // Ensure ack or nack hasn't been executed.
  nackStub.should.not.have.been.called;
  ackStub.should.not.have.been.called;

  // Cleanup.
  retryStub.restore();
  nackStub.restore();
  ackStub.restore();
});

/**
 * Dequeuer.extractOrDiscard()
 */
test('Dequeuer.extractOrDiscard(): ensure nack on incorrect JSON payload', (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Ensure Message.fromRabbitMessage throws MessageParsingBlinkError
  const blinkParsingErrorSpy = sinon.spy(queue.messageClass, 'parseIncomingPayload');

  // Create deliberaly incorrect JSON and feed it to extractOrDiscard.
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage('{incorrect-json}');
  const dequeuer = new Dequeuer(queue);
  const result = dequeuer.extractOrDiscard(rabbitMessage);
  result.should.be.false;

  // Ensure the message been nacked.
  nackStub.should.have.been.calledOnce;

  // Ensure MessageParsingBlinkError has been thrown.
  blinkParsingErrorSpy.should.have.thrown('MessageParsingBlinkError');

  // Cleanup.
  nackStub.restore();
  blinkParsingErrorSpy.restore();
});

/**
 * Dequeuer.extractOrDiscard()
 */
test('Dequeuer.extractOrDiscard(): ensure nack on unknown unpack error', (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Simulate unknown error in Message.fromRabbitMessage().
  // This method is static so we can stub it on Message constructor itself.
  const fromRabbitMessageStub = sinon.stub(queue.messageClass, 'fromRabbitMessage');
  fromRabbitMessageStub.throws(() => {
    // Fake unexpected error thrown from Message.fromRabbitMessage().
    const error = new Error('Testing unexpected exception from Message.fromRabbitMessage()');
    return error;
  });

  // Create random valid message.
  // It will be successfully unpacked from JSON,  but discarded anyways
  //  because we've overridden Message.fromRabbitMessage().
  const message = MessageFactoryHelper.getRandomMessage();
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage(message.toString());

  const dequeuer = new Dequeuer(queue);
  const result = dequeuer.extractOrDiscard(rabbitMessage);
  result.should.be.false;

  // Ensure the message been nacked.
  nackStub.should.have.been.calledOnce;

  // Cleanup.
  nackStub.restore();
  fromRabbitMessageStub.restore();
});

/**
 * Dequeuer.extractOrDiscard()
 */
test('Dequeuer.extractOrDiscard(): ensure nack on invalid message', (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Create invalid message (data expected to be an object)
  // and ensure it has been rejected with MessageValidationBlinkError.
  const invalidMessage = new FreeFormMessage({
    data: [],
    meta: {},
  });
  const validateSpy = sinon.spy(invalidMessage, 'validate');

  // Feed invalid message to Dequeuer.
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage(invalidMessage.toString());
  const dequeuer = new Dequeuer(queue);

  // Stub Dequeuer.unpack() to make it return message we actually spy on.
  const unpackStub = sinon.stub(dequeuer, 'unpack');
  unpackStub.callsFake(() => invalidMessage);

  const result = dequeuer.extractOrDiscard(rabbitMessage);
  result.should.be.false;

  // Ensure the message been nacked.
  nackStub.should.have.been.calledOnce;

  // Ensure MessageValidationBlinkError has been thrown.
  validateSpy.should.have.thrown('MessageValidationBlinkError');

  // Cleanup.
  nackStub.restore();
  validateSpy.restore();
  unpackStub.restore();
});

/**
 * Dequeuer.extractOrDiscard()
 */
test('Dequeuer.extractOrDiscard(): ensure nack on unknown error in Message.validate()', (t) => {
  const queue = t.context.queue;
  // Override queue method to ensure nack() will be called.
  const nackStub = sinon.stub(queue, 'nack').returns(null);

  // Create invalid message (data expected to be an object)
  // and ensure it has been rejected with MessageValidationBlinkError.
  const invalidMessage = new FreeFormMessage({
    data: [],
    meta: {},
  });
  const validateStub = sinon.stub(invalidMessage, 'validate');
  validateStub.throws(() => {
    // Fake unexpected error thrown from Message.fromRabbitMessage().
    const error = new Error('Testing unexpected exception from Message.validate()');
    return error;
  });

  // Feed invalid message to Dequeuer.
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage(invalidMessage.toString());
  const dequeuer = new Dequeuer(queue);

  // Stub Dequeuer.unpack() to make it return message we actually spy on.
  const unpackStub = sinon.stub(dequeuer, 'unpack');
  unpackStub.callsFake(() => invalidMessage);

  const result = dequeuer.extractOrDiscard(rabbitMessage);
  result.should.be.false;

  // Ensure the message been nacked.
  nackStub.should.have.been.calledOnce;

  // Cleanup.
  nackStub.restore();
  validateStub.restore();
  unpackStub.restore();
});

/**
 * Dequeuer.dequeue()
 */
test('Dequeuer.dequeue(): ensure executeCallback() is called on valid message', async (t) => {
  const queue = t.context.queue;

  // Create random Rabbit message and send it to .
  const message = MessageFactoryHelper.getRandomMessage();
  const rabbitMessage = MessageFactoryHelper.getFakeRabbitMessage(message.toString());
  const dequeuer = new Dequeuer(queue);

  // Stub dequeuer.executeCallback()
  const executeCallbackStub = sinon.stub(dequeuer, 'executeCallback').resolves(true);
  await dequeuer.dequeue(rabbitMessage);

  // ExecuteCallback has been called.
  executeCallbackStub.should.have.been.calledOnce;

  // Cleanup.
  executeCallbackStub.restore();
});

// ------- End -----------------------------------------------------------------
<|endoftext|>


<commit_before>// DecentCMS (c) 2014 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

// TODO: add support for part name matches

/**
 * @description
 * Uses placement.js and placement.json files found at the
 * root of modules to dispatch shapes into.
 *
 * The placement.js files should export a function(scope, rootShape, shapes)
 * that should splice shapes out of the shapes array if and when it decides
 * to place them on the rootShape.
 *
 * The placement.json files contain an object that may have one "matches"
 * property that contains an array of objects that must expose a path and
 * an order property. They can have any combination of id, type, and
 * displayType regular expressions that shapes will be matched against.
 *
 * Besides the matches array, the top object in the placement.json file
 * can have properties whose names are shape types, that have a path and
 * an order properties.
 * @constructor
 */
function FilePlacementStrategy(scope) {
  this.scope = scope;
  var shapeHelper = scope.require('shape');
  var placement = this.placement = {};
  var placementHandlers = this.placementHandlers = [];
  var fileResolution = scope.require('file-resolution');
  var placementFiles = fileResolution.all(/placement\.(json|js)/);
  placementFiles.reverse();
  for (var i = 0; i < placementFiles.length; i++) {
    var placementObject = require(placementFiles[i]);
    if (placementFiles[i].substr(-3) === '.js') {
      placementHandlers.push(placementObject);
    }
    else {
      for (var shape in placementObject) {
        if (shape === 'matches') {
          placementObject.matches.forEach(function forEachMatch(match) {
            var idExpression = match.id ? new RegExp(match.id) : null;
            var nameExpression = match.name ? new RegExp(match.name) : null;
            var shapeTypeExpression = match.type ? new RegExp(match.type) : null;
            var displayTypeExpression = match.displayType ? new RegExp(match.displayType) : null;
            // Find other custom match properties
            var customMatchExpressions = [];
            Object.getOwnPropertyNames(match).forEach(function(propertyName) {
              if (['path', 'order', 'id', 'name', 'type', 'displayType'].indexOf(propertyName) === -1) {
                customMatchExpressions.push({
                  propertyPath: propertyName.split('.'),
                  expression: new RegExp(match[propertyName])
                });
              }
            });
            var path = match.path;
            var order = match.order;

            placementHandlers.push(function (scope, rootShape, shapes) {
              for (var k = 0; k < shapes.length; k++) {
                var shapeToPlace = shapes[k];
                if (idExpression
                  && (!shapeToPlace.id || !idExpression.test(shapeToPlace.id))) {
                  continue;
                }
                if (nameExpression
                  && (!shapeToPlace.meta || !shapeToPlace.meta.name
                  || !nameExpression.test(shapeToPlace.meta.name))) {
                  continue;
                }
                if (shapeTypeExpression
                  && (!shapeToPlace.meta
                  || !shapeToPlace.meta.type
                  || !shapeTypeExpression.test(shapeToPlace.meta.type))) {
                  continue;
                }
                if (displayTypeExpression
                  && (!shapeToPlace.temp
                  || !shapeToPlace.temp.displayType
                  || !displayTypeExpression.test(shapeToPlace.temp.displayType))) {
                  continue;
                }
                // If any custom match
                var customMatch = true;
                for (var l = 0; l < customMatchExpressions.length; l++) {
                  var customMatchExpression = customMatchExpressions[l];
                  var propertyPath = customMatchExpression.propertyPath;
                  var obj = shapeToPlace;
                  for (var m = 0; m < propertyPath.length; m++) {
                    obj = obj[propertyPath[m]];
                    if (!obj) break;
                  }
                  if (!obj || !customMatchExpression.expression.test(obj)) {
                    customMatch = false;
                    break;
                  }
                }
                if (!customMatch) continue;
                // We have a match. Place the shape, and remove it from the list.
                shapeHelper.place(rootShape, path, shapeToPlace, order);
                shapes.splice(k--, 1);
              }
            });
          });
        }
        else {
          placement[shape] = placementObject[shape];
        }
      }
    }
  }
}
FilePlacementStrategy.service = 'placement-strategy';
FilePlacementStrategy.scope = 'shell';
FilePlacementStrategy.feature = 'file-placement-strategy';
FilePlacementStrategy.dependencies = ['decent-core-io'];

/**
 * Dispatches a list of shapes under a root shape, by handing over the task
 * to the placement strategies discovered by the constructor.
 * @param {object} context The context.
 * @param {object} context.shape
 * The root shape under which the shapes must be placed.
 * @param {Array} context.shapes
 * The list of shapes that must be placed.
 * @param {Function} done The callback.
 */
FilePlacementStrategy.prototype.placeShapes = function filePlace(context, done) {
  var rootShape = context.shape;
  var shapes = context.shapes;
  if (!shapes) return;
  var shapeHelper = this.scope.require('shape');
  // If shapes have placement built-in, use that.
  for (i = 0; i < shapes.length; i++) {
    var shape = shapes[i];
    if (shape.meta && shape.meta.placement) {
      var placement = shape.meta.placement;
      if (typeof(placement) === 'string') {
        var splitPlacement = placement.split(':');
        if (splitPlacement.length === 2) {
          shapeHelper.place(
            rootShape,
            splitPlacement[0] || 'main',
            shape,
            splitPlacement[1] || 'after');
          shapes.splice(i--, 1);
        }
      }
      else {
        shapeHelper.place(
          rootShape,
          placement.path || 'main',
          shape,
          placement.order || 'after');
        shapes.splice(i--, 1);
      }
    }
  }
  // Handlers have the priority on placing shapes
  for (var i = 0; i < this.placementHandlers.length; i++) {
    this.placementHandlers[i](this.scope, rootShape, shapes);
  }
  for (i = 0; i < shapes.length; i++) {
    var shape = shapes[i];
    if (shape.meta && shape.meta.type && this.placement[shape.meta.type]) {
      var placementItem = this.placement[shape.meta.type];
      shapeHelper.place(rootShape, placementItem.path, shape, placementItem.order);
      shapes.splice(i--, 1);
    }
  }
  done();
};

module.exports = FilePlacementStrategy;<commit_msg>Allow placement to add an alternate to the shape being placed.<commit_after>// DecentCMS (c) 2014 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

// TODO: add support for part name matches

/**
 * @description
 * Uses placement.js and placement.json files found at the
 * root of modules to dispatch shapes into.
 *
 * The placement.js files should export a function(scope, rootShape, shapes)
 * that should splice shapes out of the shapes array if and when it decides
 * to place them on the rootShape.
 *
 * The placement.json files contain an object that may have one "matches"
 * property that contains an array of objects that must expose a path and
 * an order property. They can have any combination of id, type, and
 * displayType regular expressions that shapes will be matched against.
 *
 * Besides the matches array, the top object in the placement.json file
 * can have properties whose names are shape types, that have a path and
 * an order properties.
 * @constructor
 */
function FilePlacementStrategy(scope) {
  this.scope = scope;
  var shapeHelper = scope.require('shape');
  var placement = this.placement = {};
  var placementHandlers = this.placementHandlers = [];
  var fileResolution = scope.require('file-resolution');
  var placementFiles = fileResolution.all(/placement\.(json|js)/);
  placementFiles.reverse();
  for (var i = 0; i < placementFiles.length; i++) {
    var placementObject = require(placementFiles[i]);
    if (placementFiles[i].substr(-3) === '.js') {
      placementHandlers.push(placementObject);
    }
    else {
      for (var shape in placementObject) {
        if (shape === 'matches') {
          placementObject.matches.forEach(function forEachMatch(match) {
            var idExpression = match.id ? new RegExp(match.id) : null;
            var nameExpression = match.name ? new RegExp(match.name) : null;
            var shapeTypeExpression = match.type ? new RegExp(match.type) : null;
            var displayTypeExpression = match.displayType ? new RegExp(match.displayType) : null;
            // Find other custom match properties
            var customMatchExpressions = [];
            Object.getOwnPropertyNames(match).forEach(function(propertyName) {
              if (['path', 'order', 'id', 'name', 'type', 'displayType'].indexOf(propertyName) === -1) {
                customMatchExpressions.push({
                  propertyPath: propertyName.split('.'),
                  expression: new RegExp(match[propertyName])
                });
              }
            });
            var path = match.path;
            var order = match.order;

            placementHandlers.push(function (scope, rootShape, shapes) {
              for (var k = 0; k < shapes.length; k++) {
                var shapeToPlace = shapes[k];
                if (idExpression
                  && (!shapeToPlace.id || !idExpression.test(shapeToPlace.id))) {
                  continue;
                }
                if (nameExpression
                  && (!shapeToPlace.meta || !shapeToPlace.meta.name
                  || !nameExpression.test(shapeToPlace.meta.name))) {
                  continue;
                }
                if (shapeTypeExpression
                  && (!shapeToPlace.meta
                  || !shapeToPlace.meta.type
                  || !shapeTypeExpression.test(shapeToPlace.meta.type))) {
                  continue;
                }
                if (displayTypeExpression
                  && (!shapeToPlace.temp
                  || !shapeToPlace.temp.displayType
                  || !displayTypeExpression.test(shapeToPlace.temp.displayType))) {
                  continue;
                }
                // If any custom match
                var customMatch = true;
                for (var l = 0; l < customMatchExpressions.length; l++) {
                  var customMatchExpression = customMatchExpressions[l];
                  var propertyPath = customMatchExpression.propertyPath;
                  var obj = shapeToPlace;
                  for (var m = 0; m < propertyPath.length; m++) {
                    obj = obj[propertyPath[m]];
                    if (!obj) break;
                  }
                  if (!obj || !customMatchExpression.expression.test(obj)) {
                    customMatch = false;
                    break;
                  }
                }
                if (!customMatch) continue;
                // We have a match. Place the shape, and remove it from the list.
                shapeHelper.place(rootShape, path, shapeToPlace, order);
                shapes.splice(k--, 1);
              }
            });
          });
        }
        else {
          placement[shape] = placementObject[shape];
        }
      }
    }
  }
}
FilePlacementStrategy.service = 'placement-strategy';
FilePlacementStrategy.scope = 'shell';
FilePlacementStrategy.feature = 'file-placement-strategy';
FilePlacementStrategy.dependencies = ['decent-core-io'];

/**
 * Dispatches a list of shapes under a root shape, by handing over the task
 * to the placement strategies discovered by the constructor.
 * @param {object} context The context.
 * @param {object} context.shape
 * The root shape under which the shapes must be placed.
 * @param {Array} context.shapes
 * The list of shapes that must be placed.
 * @param {Function} done The callback.
 */
FilePlacementStrategy.prototype.placeShapes = function filePlace(context, done) {
  var rootShape = context.shape;
  var shapes = context.shapes;
  if (!shapes) return;
  var shapeHelper = this.scope.require('shape');
  // If shapes have placement built-in, use that.
  for (i = 0; i < shapes.length; i++) {
    var shape = shapes[i];
    if (shape.meta && shape.meta.placement) {
      var placement = shape.meta.placement;
      if (typeof(placement) === 'string') {
        var splitPlacement = placement.split(':');
        if (splitPlacement.length === 2) {
          shapeHelper.place(
            rootShape,
            splitPlacement[0] || 'main',
            shape,
            splitPlacement[1] || 'after');
          shapes.splice(i--, 1);
        }
      }
      else {
        shapeHelper.place(
          rootShape,
          placement.path || 'main',
          shape,
          placement.order || 'after');
        shapes.splice(i--, 1);
      }
    }
  }
  // Handlers have the priority on placing shapes
  for (var i = 0; i < this.placementHandlers.length; i++) {
    this.placementHandlers[i](this.scope, rootShape, shapes);
  }
  for (i = 0; i < shapes.length; i++) {
    var shape = shapes[i];
    if (shape.meta && shape.meta.type && this.placement[shape.meta.type]) {
      var placementItem = this.placement[shape.meta.type];
      if (placementItem.alternate) {
        shapeHelper.alternates(shape).push(placementItem.alternate);
      }
      shapeHelper.place(rootShape, placementItem.path, shape, placementItem.order);
      shapes.splice(i--, 1);
    }
  }
  done();
};

module.exports = FilePlacementStrategy;<|endoftext|>


<commit_before>/* Janus Event Tracer (C) 2022 QXIP BV */

/* eslint-disable camelcase */
/* eslint-disable semi */
/* eslint quotes: 0 */
/* eslint-disable quote-props */
/* eslint-disable dot-notation */

var base_filter = require('@pastash/pastash').base_filter
var util = require('util')
var logger = require('@pastash/pastash').logger

const QuickLRU = require("quick-lru");

const recordCache = require("record-cache");
const fetch = require('cross-fetch');

const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const { MeterProvider } = require('@opentelemetry/sdk-metrics-base');

function nano_now (date) { return parseInt(date.toString().padEnd(16, '0')) }
function just_now (date) { return nano_now(date || new Date().getTime()) }
function spanid () { return Math.floor(10000000 + Math.random() * 90000000).toString() }

function FilterAppJanusTracer () {
  base_filter.BaseFilter.call(this);
  this.mergeConfig({
    name: 'AppJanusTracer',
    optional_params: ['debug', 'cacheSize', 'cacheAge', 'endpoint', 'bypass', 'port', 'metrics', 'service_name', 'interval'],
    default_values: {
      'cacheSize': 50000,
      'cacheAge': 60000,
      'endpoint': 'http://localhost:3100/tempo/api/push',
      'metrics': false,
      'service_name': 'pastash-janus',
      'interval': 10000,
      'port': 9090,
      'bypass': true,
      'debug': false
    },
    start_hook: this.start.bind(this)
  });
}

util.inherits(FilterAppJanusTracer, base_filter.BaseFilter);

FilterAppJanusTracer.prototype.start = async function (callback) {
  // Event cache
  var cache = recordCache({
    maxSize: this.cacheSize,
    maxAge: this.cacheAge,
    onStale: false
  });
  this.cache = cache;

  // Session cache
  var sessions = recordCache({
    maxSize: this.cacheSize,
    maxAge: this.cacheAge,
    onStale: false
  });
  this.sessions = sessions;
  this.lru = new QuickLRU({ maxSize: 10000, maxAge: 3600000, onEviction: false });

  if (this.metrics) {
    // Initialize Service
    const options = { port: this.port, startServer: true };
    const exporter = new PrometheusExporter(options);

    // Register the exporter
    this.meter = new MeterProvider({
      exporter,
      interval: this.interval
    }).getMeter(this.service_name)
    this.counters = {};

    // Register counters
    this.counters['s'] = this.meter.createUpDownCounter('sessions', {
      description: 'Session Counters'
    });
    this.counters['e'] = this.meter.createUpDownCounter('events', {
      description: 'Event Counters'
    });

    logger.info('Initialized Janus Prometheus Exporter :' + this.port + '/metrics');
  }
  logger.info('Initialized App Janus Span Tracer with: ' + this.endpoint);
  callback();
};

FilterAppJanusTracer.prototype.process = function (data) {
  // bypass
  if (this.bypass) this.emit('output', data)
  if (!data.message) return;
  var event = {};
  var line = JSON.parse(data.message);
  //logger.info('Incoming line', line.type, line.event)
  /* Ignore all other events */
  if (line.type === 128 || line.type === 8 || line.type === 16 || line.type === 32) return;
  logger.info('Filtered to 1, 2, 64', line.type, line.session_id, line.handle_id)
  /*
  TYPE 1

  Create Session and Destroy Session events are tracked
  */
  if (line.type == 1) {
    event = {
      name: line.event.name,
      event: line.event.name,
      session_id: line.session_id,
      id: line.session_id,
      spanId: spanid(),
      timestamp: line.timestamp || nano_now(new Date().getTime())
    }
    event.traceId = event.session_id
    event.duration = 1000
    /* CREATE event */
    if (event.name === "created") {
      // create root span
      this.lru.set(event.session_id, event);
      // start root trace, do not update
      this.sessions.add(event.session_id, just_now(event.timestamp));
      this.sessions.add('uuid_' + event.session_id, event.traceId)
      this.sessions.add('span_' + event.session_id, event.spanId)
      this.sessions.add('parent_' + event.session_id, event.spanId)
      if (this.metrics) this.counters['s'].add(1, line.event);
      logger.info('type 1 created sending', event)
      tracegen(event, this.endpoint)
    /* DESTROY event */
    } else if (event.name === "destroyed") {
      const previous_ts = this.sessions.get(event.session_id, 1)[0] || nano_now(new Date().getTime());
      event.duration = just_now(event.timestamp) - parseInt(previous_ts);
      /* name the event Session */
      event.name = "Session " + event.session_id
      // delete root span
      this.lru.delete(event.session_id);
      // end root trace
      this.sessions.remove(event.session_id);
      this.sessions.remove('uuid_' + event.session_id);
      if (this.metrics) this.counters['s'].add(-1, line.event);
      logger.info('type 1 destroyed sending', event)
      tracegen(event, this.endpoint)
    }
    logger.info('type 1 sending', event)
    tracegen(event, this.endpoint)
  /*
  TYPE 2

  Client Attachment and Detachment is tracked
  */
  } else if (line.type == 2) {
    event = {
      name: line.event.name,
      event: line.event.name,
      session_id: line.session_id,
      id: line.session_id,
      spanId: spanid(),
      timestamp: line.timestamp || nano_now(new Date().getTime())
    }
    if (!line.event.data) return;
    // session tracing + reset
    event.traceId = this.sessions.get('uuid_' + event.session_id, 1)[0] || line.session_id;
    event.spanId = this.sessions.get('span_' + event.session_id, 1)[0] || spanid();
    var previous_ts = this.sessions.get(event.session_id, 1)[0] || nano_now(new Date().getTime());
    event.duration = just_now(line.timestamp) - parseInt(previous_ts);

    if (event.name === "attached") {
      event.parentId = this.sessions.get("parent_" + event.session_id, 1)[0]
      // session_id, handle_id, opaque_id
      this.sessions.add(event.session_id, just_now(line.timestamp));
      logger.info('type 2 attached sending', event)
      tracegen(event, this.endpoint)
    } else if (event.name === "detached") {
      event.parentId = this.sessions.get("parent_" + event.session_id, 1)[0]
      // session_id, handle_id, opaque_id
      this.sessions.remove(event.handle_id);
      logger.info('type 2 detached sending', event)
      tracegen(event, this.endpoint)
    }
    logger.info('type 2 sending', event)
    tracegen(event, this.endpoint)
  /*
  TYPE 64

  Users Joining or Leaving Sessions
  */
  } else if (line.type == 64) {
    event = {
      name: line.event.plugin,
      event: line.event.data.event,
      id: line.event.data.id,
      spanId: spanid(),
      timestamp: line.timestamp || nano_now(new Date().getTime())
    }
    if (!line.event.data) return;

    logger.info("trace 64: ", line)
    if (event.event === "joined") {
      event.session_id = line.session_id
      event.traceId = this.sessions.get('uuid_' + event.session_id, 1)[0] || line.session_id;
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
      // session_id, handle_id, opaque_id, event.data.id
      // correlate: session_id --> event.data.id
      this.cache.add(event.session_id, just_now(event.timestamp));
      this.cache.add("uuid_" + event.id, event.session_id);
      this.lru.set(event.id, event.session_id);
      // increase tag counter
      if (this.metrics) this.counters['e'].add(1, line.event.data);
      logger.info('type 64 joined sending', event)
      tracegen(event, this.endpoint)
    } else if (event.event === "configured") {
      event.session_id = line.session_id
      event.traceId = this.sessions.get('uuid_' + event.session_id, 1)[0] || line.session_id;
      event.spanId = this.sessions.get('span_' + event.session_id, 1)[0] || spanid();
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
      // session_id, handle_id, opaque_id, event.data.id
    } else if (event.event === "published") {
      event.session_id = line.session_id
      event.traceId = this.sessions.get('uuid_' + event.session_id, 1)[0] || line.session_id;
      event.spanId = this.sessions.get('span_' + event.session_id, 1)[0] || spanid();
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
      // session_id, handle_id, opaque_id, event.data.id
      this.cache.add(event.id, event.session_id);
      this.lru.set(event.id, event.session_id);
    } else if (event.event === "unpublished") {
      // correlate: event.data.id --> session_id
      event.session_id = line.event.data.id
      const previous_ts = this.sessions.get(event.session_id, 1)[0] || nano_now(new Date().getTime());
      event.duration = just_now(event.timestamp) - parseInt(previous_ts);
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
    } else if (event.event === "leaving") {
      // correlate: event.data.id --> session_id
      event.session_id = this.cache.get("uuid_" + event.id, 1)[0]
      event.traceId = event.session_id
      const previous_ts = this.sessions.get(event.session_id, 1)[0] || nano_now(new Date().getTime());
      event.duration = just_now(event.timestamp) - parseInt(previous_ts);
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
      // decrease tag counter
      if (this.metrics) this.counters['e'].add(-1, line.event.data);
      logger.info('type 64 leaving sending', event)
      tracegen(event, this.endpoint)
    }
    logger.info('type 64 sending', event)
    tracegen(event, this.endpoint)
  }
};

exports.create = function () {
  return new FilterAppJanusTracer();
};

// TODO: replace trace mocker with opentelemetry-js
// Link: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-exporter-zipkin/README.md

async function tracegen (event, endpoint) {
  // mock a zipkin span
  var trace = [{
    "id": event.spanId || spanid(),
    "traceId": event.traceId.toString(),
    "timestamp": nano_now(event.timestamp),
    "duration": event.duration,
    "name": event.event,
    "localEndpoint": {
      "serviceName": event.name
    }
  }]
  if (event.parentId) { trace[0].parentId = event.parentId }
  if (event.tags) {
    trace[0].tags = event.tags
  } else {
    trace[0].tags = { event: 'hello' }
  }

  logger.info("trace: ", trace);
  // send event to endpoint
  if (endpoint) {
    const response = fetch(endpoint, {
      method: 'POST',
      body: JSON.stringify(trace),
      headers: { 'Content-Type': 'application/json' }
    })
      .then(res => {
        return res
      })
      .catch(err => {
        logger.error(err)
      });
    if (this.debug) logger.info(response);
  }
}
<commit_msg>quick experiment<commit_after>/* Janus Event Tracer (C) 2022 QXIP BV */

/* eslint-disable camelcase */
/* eslint-disable semi */
/* eslint quotes: 0 */
/* eslint-disable quote-props */
/* eslint-disable dot-notation */

var base_filter = require('@pastash/pastash').base_filter
var util = require('util')
var logger = require('@pastash/pastash').logger

const QuickLRU = require("quick-lru");

const recordCache = require("record-cache");
const fetch = require('cross-fetch');

const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const { MeterProvider } = require('@opentelemetry/sdk-metrics-base');

function nano_now (date) { return parseInt(date.toString().padEnd(16, '0')) }
function just_now (date) { return nano_now(date || new Date().getTime()) }
function spanid () { return Math.floor(10000000 + Math.random() * 90000000).toString() }

function FilterAppJanusTracer () {
  base_filter.BaseFilter.call(this);
  this.mergeConfig({
    name: 'AppJanusTracer',
    optional_params: ['debug', 'cacheSize', 'cacheAge', 'endpoint', 'bypass', 'port', 'metrics', 'service_name', 'interval'],
    default_values: {
      'cacheSize': 50000,
      'cacheAge': 60000,
      'endpoint': 'http://localhost:3100/tempo/api/push',
      'metrics': false,
      'service_name': 'pastash-janus',
      'interval': 10000,
      'port': 9090,
      'bypass': true,
      'debug': false
    },
    start_hook: this.start.bind(this)
  });
}

util.inherits(FilterAppJanusTracer, base_filter.BaseFilter);

FilterAppJanusTracer.prototype.start = async function (callback) {
  // Event cache
  var cache = recordCache({
    maxSize: this.cacheSize,
    maxAge: this.cacheAge,
    onStale: false
  });
  this.cache = cache;

  // Session cache
  var sessions = recordCache({
    maxSize: this.cacheSize,
    maxAge: this.cacheAge,
    onStale: false
  });
  this.sessions = sessions;
  this.lru = new QuickLRU({ maxSize: 10000, maxAge: 3600000, onEviction: false });

  if (this.metrics) {
    // Initialize Service
    const options = { port: this.port, startServer: true };
    const exporter = new PrometheusExporter(options);

    // Register the exporter
    this.meter = new MeterProvider({
      exporter,
      interval: this.interval
    }).getMeter(this.service_name)
    this.counters = {};

    // Register counters
    this.counters['s'] = this.meter.createUpDownCounter('sessions', {
      description: 'Session Counters'
    });
    this.counters['e'] = this.meter.createUpDownCounter('events', {
      description: 'Event Counters'
    });

    logger.info('Initialized Janus Prometheus Exporter :' + this.port + '/metrics');
  }
  logger.info('Initialized App Janus Span Tracer with: ' + this.endpoint);
  callback();
};

FilterAppJanusTracer.prototype.process = function (data) {
  // bypass
  if (this.bypass) this.emit('output', data)
  if (!data.message) return;
  var event = {};
  var line = JSON.parse(data.message);
  //logger.info('Incoming line', line.type, line.event)
  /* Ignore all other events */
  if (line.type === 128 || line.type === 8 || line.type === 16 || line.type === 32) return;
  logger.info('Filtered to 1, 2, 64', line.type, line.session_id, line.handle_id)
  /*
  TYPE 1

  Create Session and Destroy Session events are tracked
  */
  if (line.type == 1) {
    event = {
      name: line.event.name,
      event: line.event.name,
      session_id: line.session_id,
      id: line.session_id,
      spanId: spanid(),
      timestamp: line.timestamp || nano_now(new Date().getTime())
    }
    event.traceId = event.session_id
    event.duration = 1000
    /* CREATE event */
    if (event.name === "created") {
      // create root span
      this.lru.set(event.session_id, event);
      // start root trace, do not update
      this.sessions.add(event.session_id, just_now(event.timestamp));
      this.sessions.add('uuid_' + event.session_id, event.traceId)
      this.sessions.add('span_' + event.session_id, event.spanId)
      this.sessions.add('parent_' + event.session_id, event.spanId)
      if (this.metrics) this.counters['s'].add(1, line.event);
      logger.info('type 1 created sending', event)
      tracegen(event, this.endpoint)
    /* DESTROY event */
    } else if (event.name === "destroyed") {
      const previous_ts = this.sessions.get(event.session_id, 1)[0] || nano_now(new Date().getTime());
      // crazy experiment
      event.spanId = this.sessions.get('parent_' + event.session_id, 1)[0]
      event.duration = just_now(event.timestamp) - parseInt(previous_ts);
      /* name the event Session */
      event.name = "Session " + event.session_id
      // delete root span
      this.lru.delete(event.session_id);
      // end root trace
      this.sessions.remove(event.session_id);
      this.sessions.remove('uuid_' + event.session_id);
      if (this.metrics) this.counters['s'].add(-1, line.event);
      logger.info('type 1 destroyed sending', event)
      tracegen(event, this.endpoint)
    }
    logger.info('type 1 sending', event)
    tracegen(event, this.endpoint)
  /*
  TYPE 2

  Client Attachment and Detachment is tracked
  */
  } else if (line.type == 2) {
    event = {
      name: line.event.name,
      event: line.event.name,
      session_id: line.session_id,
      id: line.session_id,
      spanId: spanid(),
      timestamp: line.timestamp || nano_now(new Date().getTime())
    }
    if (!line.event.data) return;
    // session tracing + reset
    event.traceId = this.sessions.get('uuid_' + event.session_id, 1)[0] || line.session_id;
    event.spanId = this.sessions.get('span_' + event.session_id, 1)[0] || spanid();
    var previous_ts = this.sessions.get(event.session_id, 1)[0] || nano_now(new Date().getTime());
    event.duration = just_now(line.timestamp) - parseInt(previous_ts);

    if (event.name === "attached") {
      event.parentId = this.sessions.get("parent_" + event.session_id, 1)[0]
      // session_id, handle_id, opaque_id
      this.sessions.add(event.session_id, just_now(line.timestamp));
      logger.info('type 2 attached sending', event)
      tracegen(event, this.endpoint)
    } else if (event.name === "detached") {
      event.parentId = this.sessions.get("parent_" + event.session_id, 1)[0]
      // session_id, handle_id, opaque_id
      this.sessions.remove(event.handle_id);
      logger.info('type 2 detached sending', event)
      tracegen(event, this.endpoint)
    }
    logger.info('type 2 sending', event)
    tracegen(event, this.endpoint)
  /*
  TYPE 64

  Users Joining or Leaving Sessions
  */
  } else if (line.type == 64) {
    event = {
      name: line.event.plugin,
      event: line.event.data.event,
      id: line.event.data.id,
      spanId: spanid(),
      timestamp: line.timestamp || nano_now(new Date().getTime())
    }
    if (!line.event.data) return;

    logger.info("trace 64: ", line)
    if (event.event === "joined") {
      event.session_id = line.session_id
      event.traceId = this.sessions.get('uuid_' + event.session_id, 1)[0] || line.session_id;
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
      // session_id, handle_id, opaque_id, event.data.id
      // correlate: session_id --> event.data.id
      this.cache.add(event.session_id, just_now(event.timestamp));
      this.cache.add("uuid_" + event.id, event.session_id);
      this.lru.set(event.id, event.session_id);
      // increase tag counter
      if (this.metrics) this.counters['e'].add(1, line.event.data);
      logger.info('type 64 joined sending', event)
      tracegen(event, this.endpoint)
    } else if (event.event === "configured") {
      event.session_id = line.session_id
      event.traceId = this.sessions.get('uuid_' + event.session_id, 1)[0] || line.session_id;
      event.spanId = this.sessions.get('span_' + event.session_id, 1)[0] || spanid();
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
      // session_id, handle_id, opaque_id, event.data.id
    } else if (event.event === "published") {
      event.session_id = line.session_id
      event.traceId = this.sessions.get('uuid_' + event.session_id, 1)[0] || line.session_id;
      event.spanId = this.sessions.get('span_' + event.session_id, 1)[0] || spanid();
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
      // session_id, handle_id, opaque_id, event.data.id
      this.cache.add(event.id, event.session_id);
      this.lru.set(event.id, event.session_id);
    } else if (event.event === "unpublished") {
      // correlate: event.data.id --> session_id
      event.session_id = line.event.data.id
      const previous_ts = this.sessions.get(event.session_id, 1)[0] || nano_now(new Date().getTime());
      event.duration = just_now(event.timestamp) - parseInt(previous_ts);
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
    } else if (event.event === "leaving") {
      // correlate: event.data.id --> session_id
      event.session_id = this.cache.get("uuid_" + event.id, 1)[0]
      event.traceId = event.session_id
      const previous_ts = this.sessions.get(event.session_id, 1)[0] || nano_now(new Date().getTime());
      event.duration = just_now(event.timestamp) - parseInt(previous_ts);
      event.parentId = this.sessions.get('parent_' + event.session_id, 1)[0] || spanid();
      // decrease tag counter
      if (this.metrics) this.counters['e'].add(-1, line.event.data);
      logger.info('type 64 leaving sending', event)
      tracegen(event, this.endpoint)
    }
    logger.info('type 64 sending', event)
    tracegen(event, this.endpoint)
  }
};

exports.create = function () {
  return new FilterAppJanusTracer();
};

// TODO: replace trace mocker with opentelemetry-js
// Link: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-exporter-zipkin/README.md

async function tracegen (event, endpoint) {
  // mock a zipkin span
  var trace = [{
    "id": event.spanId || spanid(),
    "traceId": event.traceId.toString(),
    "timestamp": nano_now(event.timestamp),
    "duration": event.duration,
    "name": event.event,
    "localEndpoint": {
      "serviceName": event.name
    }
  }]
  if (event.parentId) { trace[0].parentId = event.parentId }
  if (event.tags) {
    trace[0].tags = event.tags
  } else {
    trace[0].tags = { event: 'hello' }
  }

  logger.info("trace: ", trace);
  // send event to endpoint
  if (endpoint) {
    const response = fetch(endpoint, {
      method: 'POST',
      body: JSON.stringify(trace),
      headers: { 'Content-Type': 'application/json' }
    })
      .then(res => {
        return res
      })
      .catch(err => {
        logger.error(err)
      });
    if (this.debug) logger.info(response);
  }
}
<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var redis = require('./mocks/redis');

var Repository = require('../src/repository');
var TestModel = require('./mocks/test-model');

describe('Repository', function () {

    describe('#get', function () {
        it('Should return model object from db', function (done) {
            var test_schema = {
                id: 1,
                name: 'test_schema'
            };

            redis.store['testmodel:info:' + test_schema.id] = JSON.stringify(test_schema);

            var repository = new Repository(TestModel, redis);

            repository.get(test_schema.id).then(function(test_model) {
                assert.equal(test_model.schema.id, test_schema.id);
                assert.equal(test_model.schema.name, test_schema.name);

                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    describe('#save', function () {
        it('Should save model into db', function (done) {
            redis.store = [];

            var test_schema = {
                name: 'test_schema'
            };

            var repository = new Repository(TestModel, redis);
            var test_model = new TestModel(test_schema);

            repository.save(test_model).then(function (id) {
                assert.equal(id, 1);

                assert.equal(JSON.parse(redis.store[TestModel.name.toLowerCase() + ':info:' + id]).id, 1);

                done();
            }).catch(function (err) {
                done(err);
            });
        });

        it('Should return error because schema is invalid', function (done) {
            redis.store = [];

            var test_schema = {
                wrong_field: 'test_schema'
            };

            var repository = new Repository(TestModel, redis);
            var test_model = new TestModel(test_schema);

            repository.save(test_model).then(function () {
                done(new Error('Must be done with error'));
            }).catch(function (err) {
                assert.equal(err.message, 'Wrong schema');

                done();
            });
        });
    });
});<commit_msg>Тесты для апдейта объекта в бд<commit_after>'use strict';

var assert = require('assert');
var redis = require('./mocks/redis');

var Repository = require('../src/repository');
var TestModel = require('./mocks/test-model');

describe('Repository', function () {

    describe('#get', function () {
        it('Should return model object from db', function (done) {
            var test_schema = {
                id: 1,
                name: 'test_schema'
            };

            redis.store['testmodel:info:' + test_schema.id] = JSON.stringify(test_schema);

            var repository = new Repository(TestModel, redis);

            repository.get(test_schema.id).then(function(test_model) {
                assert.equal(test_model.schema.id, test_schema.id);
                assert.equal(test_model.schema.name, test_schema.name);

                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    describe('#save', function () {
        it('Should save model into db', function (done) {
            redis.store = [];

            var test_schema = {
                name: 'test_schema'
            };

            var repository = new Repository(TestModel, redis);
            var test_model = new TestModel(test_schema);

            repository.save(test_model).then(function (id) {
                assert.equal(id, 1);

                assert.equal(JSON.parse(redis.store[TestModel.name.toLowerCase() + ':info:' + id]).id, 1);

                done();
            }).catch(function (err) {
                done(err);
            });
        });

        it('Should return error because schema is invalid', function (done) {
            redis.store = [];

            var test_schema = {
                wrong_field: 'test_schema'
            };

            var repository = new Repository(TestModel, redis);
            var test_model = new TestModel(test_schema);

            repository.save(test_model).then(function () {
                done(new Error('Must be done with error'));
            }).catch(function (err) {
                assert.equal(err.message, 'Wrong schema');

                done();
            });
        });

        it('Should update object in db', function (done) {
            redis.store = [];

            var test_schema_id = 1;

            var test_schema = {
                id: test_schema_id,
                name: 'test_schema'
            };

            var updated_test_schema = {
                id: test_schema_id,
                name: 'updated_test_schema'
            };

            redis.store['testmodel:info:' + test_schema_id] = JSON.stringify(test_schema);

            var repository = new Repository(TestModel, redis);

            repository.save(new TestModel(updated_test_schema)).then(function (id) {
                assert.equal(id, test_schema_id);

                assert.equal(JSON.parse(redis.store['testmodel:info:' + test_schema_id]).id, test_schema_id);
                assert.equal(JSON.parse(redis.store['testmodel:info:' + test_schema_id]).name, updated_test_schema.name);

                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });
});<|endoftext|>


<commit_before>alert("Hello!");
<commit_msg>find the report<commit_after>var frame = $('frame[name="main"]', top.document);
frame.on("load", function(){
  var frameDocument = frame[0].contentDocument;
  console.log(frameDocument);
  var jDoc = $(frameDocument);
  console.log(jDoc.find('title').html());
  if(jDoc.find('title').html()=='成绩单'){
    if(jDoc.find('body').children().eq(1)
    .children().html() ==
    "注意：申请办理成绩单的学生，请于提交申请之后尽快在本页面的最下方进行成绩单的确认。确认之后，已支付的成绩单办理申请方可生效。"){
      alert("这是成绩单！");
    }
    // $(frameDocument).find("body").html("
蛤蛤
");
  }
});
<|endoftext|>