{"size": 6902, "ext": "js", "max_stars_repo_path": "bootstrap/test/PinningTest.js", "max_stars_repo_name": "lcoenen/fluence", "max_stars_repo_stars_event_min_datetime": "2019-05-30T14:42:54.000Z", "max_stars_repo_stars_event_max_datetime": "2019-05-30T14:42:54.000Z", "max_issues_repo_path": "bootstrap/test/PinningTest.js", "max_issues_repo_name": "lcoenen/fluence", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "bootstrap/test/PinningTest.js", "max_forks_repo_name": "lcoenen/fluence", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": 1, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 38.1325966851, "max_line_length": 105, "alphanum_fraction": 0.6298174442}
/*
 * Copyright (C) 2017  Fluence Labs Limited
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var FluenceContract = artifacts.require("./Network.sol");
const utils = require("./Utils.js");
const truffleAssert = require('truffle-assertions');
const assert = require("chai").assert;
const { shouldFail, expectEvent } = require('openzeppelin-test-helpers');

contract('Fluence (pinning)', function ([_, owner, whitelisted, anyone]) {
    beforeEach(async function() {
      global.contract = await FluenceContract.new({ from: owner });
    });

    async function addPinnedNodes(count, ports = 1) {
        return utils.addPinnedNodes(global.contract, count, "127.0.0.1", owner, ports);
    }

    async function addNodes(count, ports = 1) {
        return utils.addNodes(global.contract, count, "127.0.0.1", anyone, ports);
    }

    async function addApp(count, ids = []) {
        return await utils.addApp(global.contract, count, owner, ids);
    }
    
    it("Should form cluster of pinned nodes and ignore other nodes", async function() {
        let cluster = 5;
        // add some nodes before pinned so order doesn't matter
        await addNodes(cluster);
        
        var result = await addPinnedNodes(3);
        
        // add some nodes in the middle of pinned nodes so order doesn't matter
        await addNodes(1);
        
        // add pinned nodes & concatenate results
        result = [...(await addPinnedNodes(cluster - 3)), ...result];

        // add some nodes after pinned so order doesn't matter
        await addNodes(cluster);
        
        let pinnedNodes = result.map(r => r.nodeID);
        let app = await addApp(cluster, pinnedNodes);
        let receipt = app.receipt;
        
        let event = expectEvent.inLogs(receipt.logs, utils.appDeployedEvent).args;
        assert.deepEqual(event.nodeIDs, pinnedNodes);
        let appID = event.appID;

        let clusterInfo = await global.contract.getApp(appID);
        let clusterSize = clusterInfo[3];
        let appPinToNodes = clusterInfo[5];
        let nodeIDs = clusterInfo[7];

        assert.equal(clusterSize, cluster);
        assert.deepEqual(appPinToNodes, pinnedNodes);
        assert.deepEqual(nodeIDs, pinnedNodes);
    });

    it("Should get correct apps after adding", async function() {
        let count = 5;
        let pinnedCount = 2;
        let pinned = await addPinnedNodes(pinnedCount);
        let pinnedNodeIDs = pinned.map(r => r.nodeID);
        let appWithPins = await addApp(count, pinnedNodeIDs);
        let app = await addApp(count, []);

        let appIDs = await global.contract.getAppIDs();
        assert.equal(appIDs.length, 2); // deployed 2 apps

        let apps = await Promise.all(appIDs.map(async id => global.contract.getApp(id)));
        assert.equal(apps.length, 2);

        let pinnedApp = apps.find(a => a[0] === appWithPins.storageHash);
        assert.notEqual(pinnedApp, undefined);
        assert.deepEqual(pinnedApp[5], pinnedNodeIDs);

        let unpinnedApp = apps.find(a => a[0] === app.storageHash);
        assert.notEqual(unpinnedApp, undefined);
        assert.equal(unpinnedApp[5].length, 0);
    });

    it("Should use both public & private nodes for pinned app", async function() {
        let count = 5;
        let pinnedCount = 2;

        let pinned = await addPinnedNodes(pinnedCount);
        let pinnedNodeIDs = pinned.map(r => r.nodeID);
        let add = await addApp(count, pinnedNodeIDs);

        // Cluster isn't formed yet
        truffleAssert.eventNotEmitted(add.receipt, utils.appDeployedEvent, () => true);

        // App is enqueued
        expectEvent.inLogs(add.logs, utils.appEnqueuedEvent, { storageHash: add.storageHash });

        // Add remaining public nodes
        let result = await addNodes(count - pinnedCount);
        let receipt = result.pop();

        // Cluster is formed
        let nodeIDs = expectEvent.inLogs(receipt.logs, utils.appDeployedEvent).args.nodeIDs;
        pinnedNodeIDs.forEach(id => assert.include(nodeIDs, id));
    });

    it("Should enqueue several apps & deploy them in a single tx", async function() {
        // Number of nodes required by apps
        let count = 6;

        // Number of apps
        let apps = 5;
        
        // Adding pinned nodes, apps will require 1 more node to form a cluster
        let results = await addPinnedNodes(count - 1, ports = apps);
        let pinnedNodeIDs = results.map(r => r.nodeID);

        // Adding apps
        for (let i = 0; i < apps; i++) {
            let result = await addApp(count, pinnedNodeIDs);

            // App should be enqueued
            expectEvent.inLogs(result.logs, utils.appEnqueuedEvent, { storageHash: result.storageHash });
        }

        // adding 1 node that can host `apps` apps. should trigger ClusterFormed
        let receipts = await addNodes(1, ports = apps);

        var eventCount = 0;
        // TODO: replace with openzeppelin's expectEvent?
        truffleAssert.eventEmitted(receipts.pop(), utils.appDeployedEvent, ev => {
            assert.equal(ev.nodeIDs.length, count);
            // should be deployed on all pinned nodes + one public node (not checked here)
            pinnedNodeIDs.forEach(id => 
                assert.include(ev.nodeIDs, id)
            );
            eventCount += 1;
            return true;
        });

        assert.equal(eventCount, apps);
    });

    it("Should revert on pinToNodes > clusterSize", async function() {
        await shouldFail.reverting(
            addApp(5, utils.generateNodeIDs(10)),
            "number of pinTo nodes should be less or equal to the desired clusterSize"
        );
    });

    it("Should revert on pinning to non-registered node", async function() {
        await shouldFail.reverting(
            addApp(5, [web3.utils.fromAscii("non-existent-node-id")]),
            "Can pin only to registered nodes"
        );
    });

    it("Should revert on pinning to non-owned node", async function() {
        let result = await utils.addPinnedNodes(global.contract, 1, "127.0.0.1", anyone, 1);
        let nodeID = result.pop().nodeID;
        await shouldFail.reverting(
            addApp(5, [nodeID]),
            "Can pin only to nodes you own"
        );
    });
});
