github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/blockapps-ba-master/server/lib/project/test/projectManager.test.js (about)

     1  require('co-mocha');
     2  const ba = require('blockapps-rest');
     3  const rest = ba.rest;
     4  const common = ba.common;
     5  const config = common.config;
     6  const util = common.util;
     7  const should = common.should;
     8  const assert = common.assert;
     9  const constants = common.constants;
    10  const Promise = common.Promise;
    11  const BigNumber = common.BigNumber;
    12  
    13  const projectJs = require('../project');
    14  const projectManagerJs = require('../projectManager');
    15  const userManagerJs = require('../../user/userManager');
    16  const ErrorCodes = rest.getEnums(`${config.libPath}/common/ErrorCodes.sol`).ErrorCodes;
    17  const ProjectState = rest.getEnums(`${config.libPath}/project/contracts/ProjectState.sol`).ProjectState;
    18  const ProjectEvent = rest.getEnums(`${config.libPath}/project/contracts/ProjectEvent.sol`).ProjectEvent;
    19  const BidState = rest.getEnums(`${config.libPath}/bid/contracts/BidState.sol`).BidState;
    20  const UserRole = rest.getEnums(`${config.libPath}/user/contracts/UserRole.sol`).UserRole;
    21  
    22  const adminName = util.uid('Admin');
    23  const adminPassword = '1234';
    24  
    25  describe('ProjectManager tests', function() {
    26    this.timeout(config.timeout);
    27  
    28    let admin;
    29    let contract;
    30    let userManagerContract;
    31    // get ready:  admin-user and manager-contract
    32    before(function* () {
    33      admin = yield rest.createUser(adminName, adminPassword);
    34      contract = yield projectManagerJs.uploadContract(admin);
    35      userManagerContract = yield userManagerJs.uploadContract(admin);
    36    });
    37  
    38    it('Create Project', function* () {
    39      const projectArgs = createProjectArgs();
    40  
    41      // create project
    42      const project = yield contract.createProject(projectArgs);
    43      assert.equal(project.name, projectArgs.name, 'name');
    44      assert.equal(project.buyer, projectArgs.buyer, 'buyer');
    45    });
    46  
    47    it('Create Project - illegal name', function* () {
    48      const projectArgs = createProjectArgs();
    49      projectArgs.name = '123456789012345678901234567890123';
    50      let project;
    51      try {
    52        // create with illegal name - should fail
    53        project = yield contract.createProject(projectArgs);
    54      } catch(error) {
    55        // error should be ERROR
    56        const errorCode = error.message;
    57        assert.equal(errorCode, ErrorCodes.ERROR, `Unexpected error ${JSON.stringify(error,null,2)}`);
    58      }
    59      // did not FAIL - that is an error
    60      assert.isUndefined(project, `Illegal project name was not detected: ${projectArgs.username}`);
    61    });
    62  
    63    it('Test exists()', function* () {
    64      const projectArgs = createProjectArgs();
    65      // should not exists
    66      const doesNotExist = yield contract.exists(projectArgs.name);
    67      assert.isDefined(doesNotExist, 'should be defined');
    68      assert.isNotOk(doesNotExist, 'should not exist');
    69      // create project
    70      const project = yield contract.createProject(projectArgs);
    71      // // should exist
    72      const exists = yield contract.exists(projectArgs.name);
    73      assert.equal(exists, true, 'should exist');
    74    });
    75  
    76    it('Test exists() with special characters', function* () {
    77      const projectArgs = createProjectArgs();
    78      projectArgs.name += ' ? # % ! * ';
    79      // should not exists
    80      const doesNotExist = yield contract.exists(projectArgs.name);
    81      assert.isDefined(doesNotExist, 'should be defined');
    82      assert.isNotOk(doesNotExist, 'should not exist');
    83      // create project
    84      const project = yield contract.createProject(projectArgs);
    85      // // should exist
    86      const exists = yield contract.exists(projectArgs.name);
    87      assert.equal(exists, true, 'should exist');
    88    });
    89  
    90    it('Create Duplicate Project', function* () {
    91      const projectArgs = createProjectArgs();
    92      // create project
    93      const project = yield contract.createProject(projectArgs);
    94      // create a duplicate - should FAIL
    95      let dupProject;
    96      try {
    97        dupProject = yield contract.createProject(projectArgs);
    98      } catch(error) {
    99        const errorCode = parseInt(error.message);
   100        // error should be EXISTS
   101        assert.equal(errorCode, ErrorCodes.EXISTS, 'error should be EXISTS' + JSON.stringify(error));
   102        }
   103      // did not FAIL - that is an error
   104      assert.isUndefined(dupProject, 'creating duplicate project should fail');
   105    });
   106  
   107    it('Get Project', function* () {
   108      const projectArgs = createProjectArgs();
   109      // create project
   110      yield contract.createProject(projectArgs);
   111      const project = yield contract.getProject(projectArgs.name);
   112      assert.equal(project.name, projectArgs.name, 'should have a name');
   113    });
   114  
   115    it('Get non exisiting project', function* () {
   116      const projectArgs = createProjectArgs();
   117      let nonExistingProject;
   118      try {
   119        nonExistingProject = yield contract.getProject(projectArgs.name);
   120      } catch(error) {
   121        const errorCode = error.message;
   122        // error should be NOT_FOUND
   123        assert.equal(errorCode, ErrorCodes.NOT_FOUND, 'error should be NOT_FOUND' + JSON.stringify(error));
   124      }
   125      // did not FAIL - that is an error
   126      assert.isUndefined(nonExistingProject, 'getting non-existing project should fail');
   127    });
   128  
   129    it('Get Projects', function* () {
   130      const projectArgs = createProjectArgs();
   131      // get projects - should not exist yet
   132      {
   133        const projects = yield contract.getProjects();
   134        const found = projects.filter(function(project) {
   135          return project.name === projectArgs.name;
   136        });
   137        assert.equal(found.length, 0, 'project list should NOT contain ' + projectArgs.name);
   138      }
   139      // create project
   140      yield contract.createProject(projectArgs);
   141      {
   142        // get projects - should exist
   143        const projects = yield contract.getProjects();
   144        const found = projects.filter(function(project) {
   145          return project.name === projectArgs.name;
   146        });
   147        assert.equal(found.length, 1, 'project list should contain ' + projectArgs.name);
   148      }
   149    });
   150  
   151    it.skip('get project leading zeros - load test - skipped', function *() {
   152      this.timeout(60*60*1000);
   153  
   154      const count = 16*4; // leading 0 once every 16
   155      const uid = util.uid();
   156      // create projects
   157      const projects = [];
   158      for (let i = 0; i < count; i++) {
   159        const projectArgs = createProjectArgs(uid);
   160        projectArgs.name += '_' + i;
   161        const project = yield contract.createProject(projectArgs);
   162        projects.push(project);
   163      }
   164  
   165      // get all projects
   166      const resultProjects = yield contract.getProjects();
   167      const comparator = function(projectA, projectB) { return projectA.name == projectB.name; };
   168      const notFound = util.filter.isContained(projects, resultProjects, comparator, true);
   169      assert.equal(notFound.length, 0, JSON.stringify(notFound));
   170    });
   171  
   172  
   173    it('Get Projects by buyer', function* () {
   174      const uid = util.uid();
   175  
   176      const mod = 3;
   177      const count = 2 * mod;
   178      const projectsArgs = Array.apply(null, {
   179        length: count
   180      }).map(function(item, index) {
   181        const projectArgs = createProjectArgs(uid);
   182        projectArgs.name += index;
   183        projectArgs.buyer += index%mod;
   184        return projectArgs;
   185      });
   186  
   187      // all create project
   188      for (let projectArgs of projectsArgs) {
   189        const project = yield contract.createProject(projectArgs);
   190      }
   191      // get projects by buyer - should find that name in there
   192      const buyerName = projectsArgs[0].buyer;
   193      const projects = yield contract.getProjectsByBuyer(buyerName);
   194      assert.equal(projects.length, count/mod, '# of found projects');
   195    });
   196  
   197    it('Get Projects by name', function* () {
   198      const uid = util.uid();
   199  
   200      const count = 3
   201      const projectsArgs = Array.apply(null, {
   202        length: count
   203      }).map(function(item, index) {
   204        const projectArgs = createProjectArgs(uid);
   205        projectArgs.name += index;
   206        return projectArgs;
   207      });
   208  
   209      // all create project
   210      for (let projectArgs of projectsArgs) {
   211        const project = yield contract.createProject(projectArgs);
   212      }
   213      // get projects by buyer - should find that name in there
   214      const names = projectsArgs.map(projectArgs => {
   215        return projectArgs.name;
   216      });
   217      for (let i = projectsArgs.length; i < 539; i++) { // push the csv size boundry
   218        names.push(projectsArgs[0].name + i);
   219      }
   220  
   221      const projects = yield contract.getProjectsByName(names);
   222      assert.equal(projects.length, projectsArgs.length, '# of found projects');
   223    });
   224  
   225    it('Get Projects by state', function* () {
   226      const uid = util.uid();
   227  
   228      const count = 8;
   229      const changed = Math.floor(count/2);
   230      const projectsArgs = Array.apply(null, {
   231        length: count
   232      }).map(function(item, index) {
   233        const projectArgs = createProjectArgs(uid);
   234        projectArgs.name += index;
   235        return projectArgs;
   236      });
   237  
   238      // all create project
   239      for (let projectArgs of projectsArgs) {
   240        yield contract.createProject(projectArgs);
   241      }
   242      // change state for the first half
   243      const changedProjectsArgs = projectsArgs.slice(0,changed);
   244      for (let projectArgs of changedProjectsArgs) {
   245        const newState = yield contract.handleEvent(projectArgs.name, ProjectEvent.ACCEPT);
   246        assert.equal(newState, ProjectState.PRODUCTION, 'should be in PRODUCTION');
   247      }
   248  
   249      // get projects by state - should find that name in there
   250      const projects = yield contract.getProjectsByState(ProjectState.PRODUCTION);
   251      const comparator = function (memberA, memberB) {
   252        return memberA.name == memberB.name;
   253      };
   254      const notContained = util.filter.isContained(changedProjectsArgs, projects, comparator);
   255      // if found any items in the source list, that are not included in the query results
   256      assert.equal(notContained.length, 0, 'some projects were not found ' + JSON.stringify(notContained, null, 2));
   257    });
   258  
   259    it('ACCEPT an OPEN project - change to PRODUCTION', function* () {
   260      const projectArgs = createProjectArgs(util.uid());
   261      // create project
   262      yield contract.createProject(projectArgs);
   263      // set the state
   264      const newState = yield contract.handleEvent(projectArgs.name, ProjectEvent.ACCEPT);
   265      assert.equal(newState, ProjectState.PRODUCTION, 'handleEvent should return ProjectState.PRODUCTION');
   266      // check the new state
   267      const project = (yield rest.waitQuery(`${projectJs.contractName}?name=eq.${encodeURIComponent(projectArgs.name)}`, 1))[0];
   268      assert.equal(parseInt(project.state), ProjectState.PRODUCTION, 'ACCEPTED project should be in PRODUCTION');
   269    });
   270  });
   271  
   272  function createProjectArgs(_uid) {
   273    const uid = _uid || util.uid();
   274    const projectArgs = {
   275      name: 'P_ ?%#@!:* ' + uid.toString().substring(uid.length-5),
   276      buyer: 'Buyer_ ?%#@!:* ' + uid,
   277      description: 'description_ ?%#@!:* ' + uid,
   278      spec: 'spec_ ?%#@!:* ' + uid,
   279      price: 234,
   280  
   281      created: new Date().getTime(),
   282      targetDelivery: new Date().getTime() + 3 * 24*60*60*1000, // 3 days
   283  
   284      addressStreet: 'addressStreet_ ? ' + uid,
   285      addressCity: 'addressCity_ ? ' + uid,
   286      addressState: 'addressState_ ? ' + uid,
   287      addressZip: 'addressZip_ ? ' + uid,
   288    };
   289  
   290    return projectArgs;
   291  }
   292  
   293  describe('ProjectManager Life Cycle tests', function() {
   294    this.timeout(config.timeout);
   295  
   296    let admin;
   297    let contract;
   298    let userManagerContract;
   299  
   300    // get ready:  admin-user and manager-contract
   301    before(function* () {
   302      admin = yield rest.createUser(adminName, adminPassword);
   303      contract = yield projectManagerJs.uploadContract(admin);
   304      userManagerContract = yield userManagerJs.uploadContract(admin);
   305    });
   306  
   307    it('Create new Bid', function* () {
   308      const supplier = util.uid('Supplier1');
   309      const amount = 5678;
   310      const projectArgs = createProjectArgs();
   311  
   312      // create project
   313      const project = yield contract.createProject(projectArgs);
   314      // create bid
   315      const bid = yield contract.createBid(project.name, supplier, amount);
   316      assert.equal(bid.name, project.name, 'name');
   317      assert.equal(bid.supplier, supplier, 'supplier');
   318      assert.equal(bid.amount, amount, 'amount');
   319  
   320      // search by bid id
   321      const bidId = bid.id;
   322      {
   323        const bid = yield projectManagerJs.getBid(bidId);
   324        assert.equal(bid.name, project.name, 'name');
   325        assert.equal(bid.supplier, supplier, 'supplier');
   326        assert.equal(bid.amount, amount, 'amount');
   327      }
   328      // search by project name
   329      const bids = yield projectManagerJs.getBidsByName(project.name);
   330      assert.equal(bids.length, 1, 'one and only one');
   331  
   332      const projects = yield contract.getProjectsBySupplier(supplier);
   333      assert.equal(projects.length, 1, 'one and only one');
   334  
   335      const notFound = yield contract.getProjectsBySupplier(supplier+'z');
   336      assert.equal(notFound.length, 0, 'should not find any');
   337    });
   338  
   339    it('Accept a Bid.', function* () {
   340      const projectArgs = createProjectArgs();
   341      const supplier = 'Supplier1';
   342      const amount = 67;
   343  
   344      // create project
   345      const project = yield contract.createProject(projectArgs);
   346      // create bid
   347      const bid = yield contract.createBid(project.name, supplier, amount);
   348      // check that state is OPEN
   349      assert.equal(parseInt(bid.state), BidState.OPEN, 'state OPEN');
   350      // accept the bid
   351      const buyer = { // pretend the admin is the buyer
   352        username: admin.name,
   353        password: admin.password,
   354        account: admin.address,
   355      }
   356      const results = yield contract.acceptBid(buyer, bid.id, project.name);
   357      // get the bid again
   358      const newBid = yield projectManagerJs.getBid(bid.id);
   359      // check that state is ACCEPTED
   360      assert.equal(parseInt(newBid.state), BidState.ACCEPTED, 'state ACCEPTED');
   361      // check that query gets it
   362      const queryBid = yield projectManagerJs.getAcceptedBid(project.name);
   363      assert.equal(parseInt(queryBid.state), BidState.ACCEPTED, 'state ACCEPTED');
   364    });
   365  
   366    it('Accept a Bid - insufficient balance', function* () {
   367      const projectArgs = createProjectArgs();
   368      const supplier = 'Supplier1';
   369      const amount = 1000 + 67; // faucet allowance + more
   370  
   371      // create project
   372      const project = yield contract.createProject(projectArgs);
   373      // create bid
   374      const bid = yield contract.createBid(project.name, supplier, amount);
   375      // check that state is OPEN
   376      assert.equal(parseInt(bid.state), BidState.OPEN, 'state OPEN');
   377      // accept the bid
   378      const buyer = { // pretend the admin is the buyer
   379        username: admin.name,
   380        password: admin.password,
   381        account: admin.address,
   382      }
   383      let errorCode;
   384      try {
   385        yield contract.acceptBid(buyer, bid.id, project.name);
   386      } catch(error) {
   387        errorCode = parseInt(error.message);
   388      }
   389      // did not FAIL - that is an error
   390      assert.isDefined(errorCode, 'accepting a bid with insufficient balance should fail');
   391      // error should be INSUFFICIENT_BALANCE
   392      assert.equal(errorCode, ErrorCodes.INSUFFICIENT_BALANCE, 'error should be INSUFFICIENT_BALANCE.');
   393      // check that none was affected
   394      const bids = yield projectManagerJs.getBidsByName(project.name);
   395      bids.map(bid => {
   396        assert.equal(bid.state, BidState.OPEN);
   397      });
   398    });
   399  
   400    it('Accept a Bid and rejects the others', function* () {
   401      const uid = util.uid();
   402      const projectArgs = createProjectArgs(uid);
   403      const password = '1234';
   404      const amount = 32;
   405  
   406      // create project
   407      const project = yield contract.createProject(projectArgs);
   408      // create suppliers
   409      const suppliers = yield createSuppliers(3, password, uid);
   410      // create bids
   411      let bids = yield createMultipleBids(projectArgs.name, suppliers, amount);
   412      // accept one bid
   413      const buyer = { // pretend the admin is the buyer
   414        username: admin.name,
   415        password: admin.password,
   416        account: admin.address,
   417      }
   418      const acceptedBidId = bids[0].id;
   419      const result = yield contract.acceptBid(buyer, acceptedBidId, projectArgs.name);
   420      // get the bids
   421      bids = yield projectManagerJs.getBidsByName(projectArgs.name);
   422      assert.equal(bids.length, suppliers.length, 'should have created all bids');
   423      // check that the accepted bid is ACCEPTED and all others are REJECTED
   424      bids.map(bid => {
   425        if (bid.id === acceptedBidId) {
   426          assert.equal(parseInt(bid.state), BidState.ACCEPTED, 'bid should be ACCEPTED');
   427        } else {
   428          assert.equal(parseInt(bid.state), BidState.REJECTED, 'bid should be REJECTED');
   429        };
   430      });
   431    });
   432  
   433    function* createMultipleBids(projectName, suppliers, amount) {
   434      const bids = [];
   435      for (let supplier of suppliers) {
   436        const bid = yield contract.createBid(projectName, supplier.username, amount);
   437        bids.push(bid);
   438      }
   439      return bids;
   440    }
   441  
   442    it('Get bids by supplier', function* () {
   443      const projectArgs = createProjectArgs(util.uid());
   444      const supplier = 'Supplier1';
   445      const amount = 5678;
   446  
   447      // create project
   448      const project = yield contract.createProject(projectArgs);
   449      // create bid
   450      const bid = yield contract.createBid(project.name, supplier, amount);
   451      // get bids by supplier
   452      const bids = yield projectManagerJs.getBidsBySupplier(supplier);
   453      const filtered = bids.filter(function(bid) {
   454        return bid.supplier === supplier  &&  bid.name == projectArgs.name;
   455      });
   456      assert.equal(filtered.length, 1, 'one and only one');
   457    });
   458  
   459    it.skip('Accept a Bid (send funds into accepted bid), rejects the others, receive project, settle (send bid funds to supplier)', function* () {
   460      const uid = util.uid();
   461      const projectArgs = createProjectArgs(uid);
   462      const password = '1234';
   463      const amount = 23;
   464      const amountWei = new BigNumber(amount).times(constants.ETHER);
   465      const FAUCET_AWARD = new BigNumber(1000).times(constants.ETHER) ;
   466      const GAS_LIMIT = new BigNumber(100000000); // default in bockapps-rest
   467  
   468      // create buyer and suppliers
   469      const buyerArgs = createUserArgs(projectArgs.buyer, password, UserRole.BUYER);
   470      const buyer = yield userManagerContract.createUser(buyerArgs);
   471      buyer.password = password; // IRL this will be a prompt to the buyer
   472      // create suppliers
   473      const suppliers = yield createSuppliers(3, password, uid);
   474  
   475      // create project
   476      const project = yield contract.createProject(projectArgs);
   477      // create bids
   478      const createdBids = yield createMultipleBids(projectArgs.name, suppliers, amount);
   479      { // test
   480        const bids = yield projectManagerJs.getBidsByName(projectArgs.name);
   481        assert.equal(createdBids.length, bids.length, 'should find all the created bids');
   482      }
   483      // get the buyers balance before accepting a bid
   484      buyer.initialBalance = yield userManagerContract.getBalance(buyer.username);
   485      buyer.initialBalance.should.be.bignumber.eq(FAUCET_AWARD);
   486      // accept one bid (the first)
   487      const acceptedBid = createdBids[0];
   488      yield contract.acceptBid(buyer, acceptedBid.id, projectArgs.name);
   489      // get the buyers balance after accepting a bid
   490      buyer.balance = yield userManagerContract.getBalance(buyer.username);
   491      const delta = buyer.initialBalance.minus(buyer.balance);
   492      delta.should.be.bignumber.gte(amountWei); // amount + fee
   493      delta.should.be.bignumber.lte(amountWei.plus(GAS_LIMIT)); // amount + max fee (gas-limit)
   494      // get the bids
   495      const bids = yield projectManagerJs.getBidsByName(projectArgs.name);
   496      // check that the expected bid is ACCEPTED and all others are REJECTED
   497      bids.map(bid => {
   498        if (bid.id === acceptedBid.id) {
   499          assert.equal(parseInt(bid.state), BidState.ACCEPTED, 'bid should be ACCEPTED');
   500        } else {
   501          assert.equal(parseInt(bid.state), BidState.REJECTED, 'bid should be REJECTED');
   502        };
   503      });
   504      // deliver the project
   505      const projectState = yield contract.handleEvent(projectArgs.name, ProjectEvent.DELIVER);
   506      assert.equal(projectState, ProjectState.INTRANSIT, 'delivered project should be INTRANSIT ');
   507      // receive the project
   508      yield receiveProject(projectArgs.name);
   509  
   510      // get the suppliers balances
   511      for (let supplier of suppliers) {
   512        supplier.balance = yield userManagerContract.getBalance(supplier.username);
   513        if (supplier.username == acceptedBid.supplier) {
   514          // the winning supplier should have the bid amount minus the tx fee
   515          const delta = supplier.balance.minus(FAUCET_AWARD);
   516          const fee = new BigNumber(10000000);
   517          delta.should.be.bignumber.eq(amountWei.minus(fee));
   518        } else {
   519          // everyone else should have the otiginal value
   520          supplier.balance.should.be.bignumber.eq(FAUCET_AWARD);
   521        }
   522      }
   523    });
   524  
   525    function* createSuppliers(count, password, uid) {
   526      const suppliers = [];
   527      for (let i = 0 ; i < count; i++) {
   528        const name = `Supplier${i}_${uid}`;
   529        const supplierArgs = createUserArgs(name, password, UserRole.SUPPLIER);
   530        const supplier = yield userManagerContract.createUser(supplierArgs);
   531        suppliers.push(supplier);
   532      }
   533      return suppliers;
   534    }
   535  
   536    // throws: ErrorCodes
   537    function* receiveProject(projectName) {
   538      rest.verbose('receiveProject', projectName);
   539      // get the accepted bid
   540      const bid = yield projectManagerJs.getAcceptedBid(projectName);
   541      // get the supplier for the accepted bid
   542      const supplier = yield userManagerContract.getUser(bid.supplier);
   543      // Settle the project:  change state to RECEIVED and tell the bid to send the funds to the supplier
   544      yield contract.settleProject(projectName, supplier.account, bid.address);
   545    }
   546  
   547  });
   548  
   549  // function createUser(address account, string username, bytes32 pwHash, UserRole role) returns (ErrorCodes) {
   550  function createUserArgs(name, password, role) {
   551    const args = {
   552      username: name,
   553      password: password,
   554      role: role,
   555    }
   556    return args;
   557  }