Update packages version
Add first test Add design pattern builder + Youtube builder
This commit is contained in:
		
							
								
								
									
										780
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										780
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -16,14 +16,15 @@
 | 
			
		||||
    "discord.js": "^12.0.1",
 | 
			
		||||
    "fs-extra": "^9.0.0",
 | 
			
		||||
    "jsonfile": "^6.0.1",
 | 
			
		||||
    "lodash": "^4.17.15",
 | 
			
		||||
    "pm2": "^4.2.3",
 | 
			
		||||
    "lodash": "^4.17.20",
 | 
			
		||||
    "pm2": "^4.5.1",
 | 
			
		||||
    "rss-parser": "^3.7.5"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "chai": "^4.2.0",
 | 
			
		||||
    "mocha": "^8.2.1",
 | 
			
		||||
    "proxyquire": "^2.1.3",
 | 
			
		||||
    "sinon": "^9.2.2"
 | 
			
		||||
    "sinon": "^9.2.2",
 | 
			
		||||
    "sinon-chai": "^3.5.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								src/Models/Builders/AbstractListenerRSSBuilder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/Models/Builders/AbstractListenerRSSBuilder.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
const ListenerRSS = require('../listener-rss');
 | 
			
		||||
 | 
			
		||||
class AbstractListnerRSSBuilder {
 | 
			
		||||
    _listenerRSS = undefined;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        if(this.constructor === AbstractListnerRSSBuilder)
 | 
			
		||||
            throw new Error('The Abstract class "AbstractListnerRSSBuilder" cannot be instantiated');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief first function to call after constructor. It's building the listener
 | 
			
		||||
     * @param infos Must to be an object ListenerRSSInfos
 | 
			
		||||
     */
 | 
			
		||||
    constructListener(infos) {
 | 
			
		||||
        this._listenerRSS = new ListenerRSS();
 | 
			
		||||
 | 
			
		||||
        this.setInfos(infos);
 | 
			
		||||
        this.setSpecificInfos();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief give the listener just build
 | 
			
		||||
     * @return ListenerRSS with all the setups datas
 | 
			
		||||
     * @exception if the listener isn't construct
 | 
			
		||||
     */
 | 
			
		||||
    get listenerRSS() {
 | 
			
		||||
        if(this._listenerRSS === undefined)
 | 
			
		||||
            throw new Error('the listener is not yet build');
 | 
			
		||||
        return this._listenerRSS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setInfos(infos) { // Nominal Infos (like name, addresse, and other)
 | 
			
		||||
        this._listenerRSS.name = infos._name;
 | 
			
		||||
        this._listenerRSS.address = infos._address;
 | 
			
		||||
        this._listenerRSS.timeloop = infos._timeloop;
 | 
			
		||||
        this._listenerRSS.name = infos._name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setSpecificInfos() { // More generic infos who's depend of platforms
 | 
			
		||||
        throw new Error('This function is not implemented');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = AbstractListnerRSSBuilder
 | 
			
		||||
							
								
								
									
										16
									
								
								src/Models/Builders/YoutubeListenerRSSBuilder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/Models/Builders/YoutubeListenerRSSBuilder.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
const AbstractListenerRSSBuilder = require('AbstractListenerRSSBuilder')
 | 
			
		||||
 | 
			
		||||
class YoutubeListenerRSSBuilder extends AbstractListenerRSSBuilder {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setSpecificInfos() {
 | 
			
		||||
        this.listenerRSS._customfields = [
 | 
			
		||||
            ['description', ['media:group', 'media:description']],
 | 
			
		||||
            ['icon', ['media:group', 'media:thumbnail']]
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = YoutubeListenerRSSBuilder
 | 
			
		||||
							
								
								
									
										42
									
								
								src/Models/listenerRSSInfos.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/Models/listenerRSSInfos.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
class ListenerRSSInfos {
 | 
			
		||||
    _name = undefined; // name of the listener
 | 
			
		||||
    _address = undefined; // feed's address
 | 
			
		||||
    _timeloop = 1 * 60; // update time RSS feed
 | 
			
		||||
    _customfields = [] // rss fields custom
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    set name(value) {
 | 
			
		||||
        this._name = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set address(value) {
 | 
			
		||||
        this._address = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set timeloop(value) {
 | 
			
		||||
        this._timeloop = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set customfields(value) {
 | 
			
		||||
        this._customfields = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get name() {
 | 
			
		||||
        return this._name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get address() {
 | 
			
		||||
        return this._address;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get timeloop() {
 | 
			
		||||
        return this._timeloop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get customfields() {
 | 
			
		||||
        return this._customfields;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = ListenerRSSInfos
 | 
			
		||||
							
								
								
									
										22
									
								
								src/listener-director.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/listener-director.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
const YoutubeListenerRSSBuilder = require('./Models/YoutubeListenerRSSBuilder');
 | 
			
		||||
 | 
			
		||||
class ListenerBuildDirector {
 | 
			
		||||
    _builder = undefined;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    constructor(builder) {
 | 
			
		||||
        this._builder = builder;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getListener() {
 | 
			
		||||
        return this._builder.listenerRSS();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    build(infos) {
 | 
			
		||||
        if(infos === undefined)
 | 
			
		||||
            throw new Error('infos must be initialized');
 | 
			
		||||
        this._builder.constructListener(infos);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = ListenerBuildDirector
 | 
			
		||||
							
								
								
									
										61
									
								
								src/listener-rss.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/listener-rss.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
const jsonFile = require('jsonfile');
 | 
			
		||||
const Parser = require('rss-parser');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListenerRss {
 | 
			
		||||
    name = undefined;
 | 
			
		||||
    address = undefined;
 | 
			
		||||
    timeloop = 5 * 60; // time in seconds
 | 
			
		||||
    customfields = [];
 | 
			
		||||
 | 
			
		||||
    // private fields
 | 
			
		||||
    parser = null;
 | 
			
		||||
    obj = null;
 | 
			
		||||
    loopRunning = false;
 | 
			
		||||
 | 
			
		||||
    constructor(info) {
 | 
			
		||||
        // set parser
 | 
			
		||||
        this.parser = new Parser({
 | 
			
		||||
            customFields : {
 | 
			
		||||
                item: info._customfields.map((elt) => {
 | 
			
		||||
                    return Array.isArray(elt[1]) ? elt[1][0] : elt[1];
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Set data
 | 
			
		||||
        this.name = info._name === undefined ? info._address : info._name; // if name is undefined let's take the address
 | 
			
		||||
        this.address = info._address;
 | 
			
		||||
        this.timeloop = info._timeloop;
 | 
			
		||||
        this.customfields = info._customfields;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fetchRSS() {
 | 
			
		||||
        return this.parser.parseURL(this.address)
 | 
			
		||||
            .catch((err) => { throw new Error('bad address or no access : ' + err);});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief call the callback function each looptime
 | 
			
		||||
     * @param callback function who's going to be called with the latest get
 | 
			
		||||
     */
 | 
			
		||||
    start(callback) {
 | 
			
		||||
        this.loopRunning = true;
 | 
			
		||||
 | 
			
		||||
        (async () => {
 | 
			
		||||
            while(this.loopRunning === true) {
 | 
			
		||||
                callback(await this.fetchRSS());
 | 
			
		||||
                await new Promise(res => setTimeout(res, this.timeloop * 1000));
 | 
			
		||||
            }
 | 
			
		||||
        })();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief stop the async function
 | 
			
		||||
     */
 | 
			
		||||
    stop() {
 | 
			
		||||
        this.loopRunning = false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = ListenerRss
 | 
			
		||||
@@ -1,59 +1,100 @@
 | 
			
		||||
const chai = require("chai");
 | 
			
		||||
const sinon = require("sinon");
 | 
			
		||||
const proxyquire = require("proxyquire");
 | 
			
		||||
const sinon_chai = require("sinon-chai");
 | 
			
		||||
chai.use(sinon_chai);
 | 
			
		||||
const Parser = require("rss-parser");
 | 
			
		||||
 | 
			
		||||
const Listeners = require('../src/listener-rss')
 | 
			
		||||
const ListenerRRSInfo = require('../src/Models/listenerRSSInfos')
 | 
			
		||||
const YtbBuilder = require('../src/Models/YoutubeListenerRSSBuilder')
 | 
			
		||||
const YtbDirector = require('../src/listener-director')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const expect = chai.expect;
 | 
			
		||||
 | 
			
		||||
describe("RSS Youtube", function () {
 | 
			
		||||
  let rssYoutubeService;
 | 
			
		||||
  let timecode;
 | 
			
		||||
  let routage;
 | 
			
		||||
  let parseUrlStub;
 | 
			
		||||
  let mockedInput = {
 | 
			
		||||
    items: [
 | 
			
		||||
      {
 | 
			
		||||
        id: "::account",
 | 
			
		||||
        url:
 | 
			
		||||
        isoDate: Date.now().toISOString(),
 | 
			
		||||
        "media:group": {
 | 
			
		||||
          "media:description": [
 | 
			
		||||
            "my description"
 | 
			
		||||
          ]
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
  };
 | 
			
		||||
describe("test class RSS: jsonfile", function () {
 | 
			
		||||
    let myListener = undefined;
 | 
			
		||||
 | 
			
		||||
  beforeEach(function () {
 | 
			
		||||
    timecode = {
 | 
			
		||||
      "lastCheck": "2020-11-12T17:44:11.713Z",
 | 
			
		||||
      "MyUser": "2020-11-12T17:44:11.713Z"
 | 
			
		||||
    };
 | 
			
		||||
    routage = {
 | 
			
		||||
      log: sinon.stub(),
 | 
			
		||||
      send: sinon.stub()
 | 
			
		||||
    let infosListener = new ListenerRRSInfo();
 | 
			
		||||
    infosListener.name = 'my-test-service';
 | 
			
		||||
    infosListener.address = 'fake.rss.service';
 | 
			
		||||
    infosListener.timeloop = 15;
 | 
			
		||||
 | 
			
		||||
        // parseURL tests
 | 
			
		||||
    let stubParser;
 | 
			
		||||
 | 
			
		||||
    const mockedRSSOutput = {
 | 
			
		||||
        items: [
 | 
			
		||||
            {
 | 
			
		||||
                'title': 'my title 00',
 | 
			
		||||
                'media:group': {
 | 
			
		||||
                    'media:description': 'my description 00',
 | 
			
		||||
                    'media:thumbnail': [{$: {height: 360, width: 420, url: 'my_image00.jpg'}}],
 | 
			
		||||
                },
 | 
			
		||||
                'link': 'my_url_00.com',
 | 
			
		||||
                'pubDate': 'myDate00'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                'title': 'my title 01',
 | 
			
		||||
                'media:group' : {
 | 
			
		||||
                    'media:description': 'my description 01',
 | 
			
		||||
                    'media:thumbnail': [{$: { height: 360, width: 420, url: 'my_image01.jpg'}}],
 | 
			
		||||
                },
 | 
			
		||||
                'link': 'my_url_01.com',
 | 
			
		||||
                'pubDate': 'myDate01'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                'title': 'my title 02',
 | 
			
		||||
                'media:group' : {
 | 
			
		||||
                    'media:description': 'my description 02',
 | 
			
		||||
                    'media:thumbnail': [{$: { height: 360, width: 420, url: 'my_image02.jpg'}}],
 | 
			
		||||
                },
 | 
			
		||||
                'link': 'my_url_02.com',
 | 
			
		||||
                'pubDate': 'myDate02'
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    parseUrlStub = sinon.stub(Parser.prototype, "parseURL")
 | 
			
		||||
      .callsFake((_, callback) => callback(null, "data"));
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
        // stubs
 | 
			
		||||
        stubParser = sinon.stub(Parser.prototype, 'parseURL')
 | 
			
		||||
            .withArgs(listenerInfo.address)
 | 
			
		||||
            .resolves(mockedRSSOutput);
 | 
			
		||||
 | 
			
		||||
    rssYoutubeService = proxyquire("../services/rss-youtube", {
 | 
			
		||||
      "../modules/routage": () => routage,
 | 
			
		||||
      "../db/rss-youtube.json": {
 | 
			
		||||
        config: {
 | 
			
		||||
          timeLoop: 5,
 | 
			
		||||
          urlFeeds: "https://www.youtube.com/feeds/videos.xml?channel_id=",
 | 
			
		||||
          sliceDescription: 142
 | 
			
		||||
        },
 | 
			
		||||
        feeds: {
 | 
			
		||||
          MyUser: "Some channel"
 | 
			
		||||
        },
 | 
			
		||||
        timecode
 | 
			
		||||
      }
 | 
			
		||||
        // constructor
 | 
			
		||||
        myListener = new Listeners();
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    afterEach(function () {
 | 
			
		||||
        // restore stubs
 | 
			
		||||
        Parser.prototype.parseURL.restore();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  afterEach(function () {
 | 
			
		||||
    Parser.prototype.parseURL.reset();
 | 
			
		||||
  });
 | 
			
		||||
    describe("Building Ytb listener", function () {
 | 
			
		||||
        it("The build without problems", function () {
 | 
			
		||||
            let builder = new YtbBuilder();
 | 
			
		||||
            let director = new YtbDirector(builder);
 | 
			
		||||
            director.build(infosListener);
 | 
			
		||||
            myListener = director.getListener();
 | 
			
		||||
 | 
			
		||||
            // assertions
 | 
			
		||||
            expect(stubParser).to.have.been.calledOnce;
 | 
			
		||||
            expect(stubParser).to.have.been.calledWith(infosListener._address);
 | 
			
		||||
            expect(myListener.customFields).to.eql([
 | 
			
		||||
                ['description', ['media:group', 'media:description']],
 | 
			
		||||
                ['icon', ['media:group', 'media:thumbnail']]
 | 
			
		||||
            ]);
 | 
			
		||||
            //TODO test les infos dans "myListener"
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    //Todo
 | 
			
		||||
    describe.skip("start", function () {
 | 
			
		||||
        it("Let's start the timer", function () {
 | 
			
		||||
            myListener.setDatas(listenerInfo);
 | 
			
		||||
            myListener.start();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user