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 }