<commit_before>describe('Core: Tasks', function(){
  var specHelper = require('../helpers/specHelper.js').specHelper;
  var apiObj = {};
  var rawAPI = {};
  var should = require("should");
  var taskOutput = [];

  var getAllQueueLengths = function(callback){
    rawAPI.tasks.countDelayedTasks(function(err, delayedCount){
      rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount){
        rawAPI.tasks.queueLength(rawAPI.tasks.queues.localQueue, function(err, localCount){
          var data = {
            delayed: delayedCount,
            global: globalCount,
            local: localCount
          }
          callback(null, data);
        });
      });
    });
  }

  before(function(done){
    this.timeout(10000)
    specHelper.stopServer(0, function(api){
      specHelper.prepare(0, function(api){
        rawAPI = api;
        apiObj = specHelper.cleanAPIObject(api);

        setTimeout(function(){

          rawAPI.tasks.taskProcessors.forEach(function(taskProcessor){
            taskProcessor.stop();
          });

          rawAPI.tasks.tasks['regular_any'] = {
            name: 'regular_any',
            description: 'task: ' + this.name,
            scope: 'any',
            frequency: 0,
            run: function(api, params, next){
              taskOutput.push(params.word);
              next();
            }
          }

          rawAPI.tasks.tasks['regular_all'] = {
            name: 'regular_all',
            description: 'task: ' + this.name,
            scope: 'all',
            frequency: 0,
            run: function(api, params, next){
              taskOutput.push(params.word);
              next();
            }
          }

          rawAPI.tasks.tasks['periodic_any'] = {
            name: 'periodic_any',
            description: 'task: ' + this.name,
            scope: 'any',
            frequency: 1000,
            run: function(api, params, next){
              taskOutput.push(params.word);
              next();
            }
          }

          rawAPI.tasks.tasks['periodic_all'] = {
            name: 'periodic_all',
            description: 'task: ' + this.name,
            scope: 'all',
            frequency: 1000,
            run: function(api, params, next){
              taskOutput.push(params.word);
              next();
            }
          }

          setTimeout(function(){
            // time for the workers to stop
            done();
          }, rawAPI.tasks.cycleTimeMS * 2 + 1);
        }, rawAPI.tasks.cycleTimeMS * 2 + 1);
      });
    });
  });

  after(function(done){
    specHelper.stopServer(0, function(api){
      delete rawAPI.tasks.tasks['regular_any'];
      delete rawAPI.tasks.tasks['regular_all'];
      delete rawAPI.tasks.tasks['periodic_any'];
      delete rawAPI.tasks.tasks['periodic_all'];
      done();
    })
  });

  it('setup worked', function(done){
    rawAPI.utils.hashLength(rawAPI.tasks.tasks).should.equal(4 + 1);
    rawAPI.tasks.taskProcessors.length.should.equal(1);
    rawAPI.tasks.taskProcessors.forEach(function(taskProcessor){
      taskProcessor.running.should.equal(false);
    });
    done();
  });

  it('a bad task definition causes an exception', function(done){
    // TODO
    done();
  });

  it('a bad task (no name) definition causes an exception', function(done){
    try{
      var t = new rawAPI.task();
    }catch(e){
      String(e).should.equal("Error: name is required");
      done();
    }
  });

  it('a bad task (unknwon task name) definition causes an exception', function(done){
    try{
      var t = new rawAPI.task({name: 'something_crazy'});
    }catch(e){
      String(e).should.equal("Error: task name, something_crazy, not found");
      done();
    }
  });

  it('all queues should start empty', function(done){
    rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount){
      rawAPI.tasks.queueLength(rawAPI.tasks.queues.localQueue, function(err, localCount){
        [globalCount, localCount].forEach(function(count){
          count.should.equal(0)
        })
        done();
      });
    });
  });

  it('all perioduc tasks should be enqueued when the server starts', function(done){
    rawAPI.tasks.seedPeriodicTasks(function(){
      rawAPI.tasks.countDelayedTasks(function(err, delayedTasksCount){
        delayedTasksCount.should.equal(2);
        done();
      });
    });
  });

  it('re-enquing a periodc task should fail (if it exists alread) via loader', function(done){
    rawAPI.tasks.seedPeriodicTasks(function(){
      rawAPI.tasks.countDelayedTasks(function(err, delayedTasksCount){
        delayedTasksCount.should.equal(2); // no change
        done();
      });
    });
  });

  it('re-enquing a periodc task should fail (if it exists alread) via direct enqueue', function(done){
    var t = new rawAPI.task({name: 'periodic_any'});
    t.enqueue(function(err, success){
      String(err).should.equal('Error: not enquing periodic task periodic_any: already in the queue');
      done();
    })
  });

   it('I can inspect the state of my current tasks, the local queue, and the global queue', function(done){
    rawAPI.tasks.getAllTasks(function(err, data){
      for(var i in data){
        var t = data[i];
        ( (['periodic_any', 'periodic_all'].indexOf(t.name) >= 0) ).should.be.true;
        t.periodic.should.equal(true);
        t.frequency.should.equal(1000);
        t.queue.indexOf('actionHero:tasks:delayed').should.equal(0);
        t.state.should.equal('delayed');
      }
      done();
    })
  });

  it('I can add many non-periodic task instances', function(done){
    var t = new rawAPI.task({name: 'regular_any'});
    t.enqueue(function(){
      var t = new rawAPI.task({name: 'regular_any'});
      t.enqueue(function(){
        rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount){
          globalCount.should.equal(2);
          rawAPI.tasks.getAllTasks(function(err, data){
            rawAPI.utils.hashLength(data).should.equal(4)
            done();
          });
        });
      });
    });
  });

  describe('full worker flow', function(){

    beforeEach(function(done){
      rawAPI.redis.client.flushdb(function(){
        done();
      });
    })

    it('general task flow works (normal)', function(done){
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'regular_any'});
      task.enqueue(function(err, resp){
        setTimeout(function(){
          getAllQueueLengths(function(err, data1){
            data1.delayed.should.equal(0);
            data1.local.should.equal(0);
            data1.global.should.equal(1);
            worker.process(function(){
              // move to local
              getAllQueueLengths(function(err, data3){
                data3.delayed.should.equal(0);
                data3.local.should.equal(1);
                data3.global.should.equal(0);
                worker.process(function(){
                  // move to processing and try to work it
                  // should be back in delayed
                  getAllQueueLengths(function(err, data4){
                    data4.delayed.should.equal(0);
                    data4.local.should.equal(0);
                    data4.global.should.equal(0);
                    done();
                  });
                });
              });
            });
          });
        }, 2);
      });
    });

  it('general task flow works (delayed)', function(done){
      this.timeout(5000);
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'regular_any', runAt: new Date().getTime() + 200});
      task.enqueue(function(err, resp){
        setTimeout(function(){
          getAllQueueLengths(function(err, data1){
            data1.delayed.should.equal(1);
            data1.local.should.equal(0);
            data1.global.should.equal(0);
            worker.process(function(){
              // move to global
              getAllQueueLengths(function(err, data2){
                data2.delayed.should.equal(0);
                data2.local.should.equal(0);
                data2.global.should.equal(1);
                worker.process(function(){
                  // move to local
                  getAllQueueLengths(function(err, data3){
                    data3.delayed.should.equal(0);
                    data3.local.should.equal(1);
                    data3.global.should.equal(0);
                    worker.process(function(){
                      // move to processing and try to work it
                      // should be back in delayed
                      getAllQueueLengths(function(err, data4){
                        data4.delayed.should.equal(0);
                        data4.local.should.equal(0);
                        data4.global.should.equal(0);
                        done();
                      });
                    });
                  });
                });
              });
            });
          });
        }, 1000);
      });
    });

    it('general task flow works (periodic)', function(done){
      this.timeout(10000);
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'periodic_any'});
      task.enqueue(function(err, resp){
        setTimeout(function(){
          getAllQueueLengths(function(err, data1){
            data1.delayed.should.equal(1);
            data1.local.should.equal(0);
            data1.global.should.equal(0);
            worker.process(function(){
              // move to global
              getAllQueueLengths(function(err, data2){
                data2.delayed.should.equal(0);
                data2.local.should.equal(0);
                data2.global.should.equal(1);
                worker.process(function(){
                  // move to local
                  getAllQueueLengths(function(err, data3){
                    data3.delayed.should.equal(0);
                    data3.local.should.equal(1);
                    data3.global.should.equal(0);
                    worker.process(function(){
                      // move to processing and try to work it
                      // should be back in delayed
                      getAllQueueLengths(function(err, data4){
                        data4.delayed.should.equal(1);
                        data4.local.should.equal(0);
                        data4.global.should.equal(0);
                        done();
                      });
                    });
                  });
                });
              });
            });
          });
        }, 4001);
      });
    });

  });

  // TODO
  // it('If I crash while working on a task, I will clear the crash on my next boot', function(done){
  //   rawAPI.redis.client.flushdb(function(){
  //     var t = new rawAPI.task({name: 'regular_any', runAt: new Date().getTime() - 1});
  //     t.enqueue(function(err, success){
  //       success.should.equal(true);
  //       rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount){
  //         globalCount.should.equal(1);
  //         rawAPI.tasks.changeQueue(rawAPI.tasks.queues.globalQueue, rawAPI.tasks.queues.processingQueue, function(err, task){
  //           task.name.should.equal('regular_any')
  //           rawAPI.tasks.setTaskData(task.id, {api_id: rawAPI.id, worker_id: 0, state: "processing"}, function(err, task){
  //             task.queue.should.equal('actionHero:tasks:processing')
  //             rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount2){
  //               globalCount2.should.equal(0)

  //               rawAPI.tasks.savePreviouslyCrashedTasks(function(){
  //                 rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount3){
  //                   globalCount3.should.equal(1);
  //                   done();
  //                 });
  //               });

  //             });
  //           });
  //         });
  //       });
  //     });
  //   });
  // });

  describe('busted periodic task', function(){

    var uncaughtExceptionHandlers;

    before(function(done){
      rawAPI.tasks.tasks['busted_task'] = {
        name: 'busted_task',
        description: 'task: ' + this.name,
        scope: 'any',
        frequency: 1,
        run: function(api, params, next){
          stuff = bad + thing;
          next();
        }
      }

      uncaughtExceptionHandlers = process.listeners("uncaughtException");
      uncaughtExceptionHandlers.forEach(function(e){
        process.removeListener("uncaughtException", e);
      });

      rawAPI.redis.client.flushdb(function(){
        done();
      });
    });

    after(function(done){
      delete rawAPI.tasks.tasks['busted_task'];
      uncaughtExceptionHandlers.forEach(function(e){
        process.on("uncaughtException", e);
      });
      done()
    });

    it('should start empty', function(done){
      getAllQueueLengths(function(err, data){
        data.delayed.should.equal(0);
        data.local.should.equal(0);
        data.global.should.equal(0);
        done();
      });
    });

    it('periodc tasks which return a failure will still be re-enqueued and tried again', function(done){
      this.timeout(15000);
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'busted_task'});
      task.enqueue(function(err, resp){
        setTimeout(function(){
          getAllQueueLengths(function(err, data1){
            data1.delayed.should.equal(1);
            data1.local.should.equal(0);
            data1.global.should.equal(0);
            worker.process(function(){
              // move to global
              getAllQueueLengths(function(err, data2){
                data2.delayed.should.equal(0);
                data2.local.should.equal(0);
                data2.global.should.equal(1);
                worker.process(function(){
                  // move to local
                  getAllQueueLengths(function(err, data3){
                    data3.delayed.should.equal(0);
                    data3.local.should.equal(1);
                    data3.global.should.equal(0);
                    worker.process(function(){
                      // move to processing and try to work it
                      // should be back in delayed
                      getAllQueueLengths(function(err, data4){
                        data4.delayed.should.equal(0);
                        data4.local.should.equal(0);
                        data4.global.should.equal(1);
                        done();
                      });
                    });
                  });
                });
              });
            });
          });
        }, 400);
      });
    });

  });

  it('enquing an "all" task will end up preformed by every server', function(done){
    // TODO
    done();
  });

  it('I will not process tasks with a runAt in the future', function(done){
    this.timeout(5000);
    rawAPI.redis.client.flushdb(function(){
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'regular_any', runAt: new Date().getTime() + 10000});
      task.enqueue(function(){
        rawAPI.tasks.countDelayedTasks(function(err, delayedCount){
          delayedCount.should.equal(1);
          setTimeout(function(){
            worker.process(function(){
              rawAPI.tasks.countDelayedTasks(function(err, delayedCount2){
                delayedCount.should.equal(1);
                done();
              });
            });
          }, 500)
        });
      });
    });
  });

  it('tasks can be passed params and taskWorkers can work on thier own', function(done){
    this.timeout(10000)
    taskOutput = [];
    rawAPI.redis.client.flushdb(function(){
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'regular_any', params: {word: 'TEST'}});
      task.enqueue(function(){
        worker.start();
        setTimeout(function(){
          taskOutput[0].should.equal('TEST');
          worker.stop();
          done();
        }, rawAPI.tasks.cycleTimeMS * 10 + 1)
      });
    });
  });

});<commit_msg>longer test timeout<commit_after>describe('Core: Tasks', function(){
  var specHelper = require('../helpers/specHelper.js').specHelper;
  var apiObj = {};
  var rawAPI = {};
  var should = require("should");
  var taskOutput = [];

  var getAllQueueLengths = function(callback){
    rawAPI.tasks.countDelayedTasks(function(err, delayedCount){
      rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount){
        rawAPI.tasks.queueLength(rawAPI.tasks.queues.localQueue, function(err, localCount){
          var data = {
            delayed: delayedCount,
            global: globalCount,
            local: localCount
          }
          callback(null, data);
        });
      });
    });
  }

  before(function(done){
    this.timeout(10000)
    specHelper.stopServer(0, function(api){
      specHelper.prepare(0, function(api){
        rawAPI = api;
        apiObj = specHelper.cleanAPIObject(api);

        setTimeout(function(){

          rawAPI.tasks.taskProcessors.forEach(function(taskProcessor){
            taskProcessor.stop();
          });

          rawAPI.tasks.tasks['regular_any'] = {
            name: 'regular_any',
            description: 'task: ' + this.name,
            scope: 'any',
            frequency: 0,
            run: function(api, params, next){
              taskOutput.push(params.word);
              next();
            }
          }

          rawAPI.tasks.tasks['regular_all'] = {
            name: 'regular_all',
            description: 'task: ' + this.name,
            scope: 'all',
            frequency: 0,
            run: function(api, params, next){
              taskOutput.push(params.word);
              next();
            }
          }

          rawAPI.tasks.tasks['periodic_any'] = {
            name: 'periodic_any',
            description: 'task: ' + this.name,
            scope: 'any',
            frequency: 1000,
            run: function(api, params, next){
              taskOutput.push(params.word);
              next();
            }
          }

          rawAPI.tasks.tasks['periodic_all'] = {
            name: 'periodic_all',
            description: 'task: ' + this.name,
            scope: 'all',
            frequency: 1000,
            run: function(api, params, next){
              taskOutput.push(params.word);
              next();
            }
          }

          setTimeout(function(){
            // time for the workers to stop
            done();
          }, rawAPI.tasks.cycleTimeMS * 2 + 1);
        }, rawAPI.tasks.cycleTimeMS * 2 + 1);
      });
    });
  });

  after(function(done){
    specHelper.stopServer(0, function(api){
      delete rawAPI.tasks.tasks['regular_any'];
      delete rawAPI.tasks.tasks['regular_all'];
      delete rawAPI.tasks.tasks['periodic_any'];
      delete rawAPI.tasks.tasks['periodic_all'];
      done();
    })
  });

  it('setup worked', function(done){
    rawAPI.utils.hashLength(rawAPI.tasks.tasks).should.equal(4 + 1);
    rawAPI.tasks.taskProcessors.length.should.equal(1);
    rawAPI.tasks.taskProcessors.forEach(function(taskProcessor){
      taskProcessor.running.should.equal(false);
    });
    done();
  });

  it('a bad task definition causes an exception', function(done){
    // TODO
    done();
  });

  it('a bad task (no name) definition causes an exception', function(done){
    try{
      var t = new rawAPI.task();
    }catch(e){
      String(e).should.equal("Error: name is required");
      done();
    }
  });

  it('a bad task (unknwon task name) definition causes an exception', function(done){
    try{
      var t = new rawAPI.task({name: 'something_crazy'});
    }catch(e){
      String(e).should.equal("Error: task name, something_crazy, not found");
      done();
    }
  });

  it('all queues should start empty', function(done){
    rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount){
      rawAPI.tasks.queueLength(rawAPI.tasks.queues.localQueue, function(err, localCount){
        [globalCount, localCount].forEach(function(count){
          count.should.equal(0)
        })
        done();
      });
    });
  });

  it('all perioduc tasks should be enqueued when the server starts', function(done){
    rawAPI.tasks.seedPeriodicTasks(function(){
      rawAPI.tasks.countDelayedTasks(function(err, delayedTasksCount){
        delayedTasksCount.should.equal(2);
        done();
      });
    });
  });

  it('re-enquing a periodc task should fail (if it exists alread) via loader', function(done){
    rawAPI.tasks.seedPeriodicTasks(function(){
      rawAPI.tasks.countDelayedTasks(function(err, delayedTasksCount){
        delayedTasksCount.should.equal(2); // no change
        done();
      });
    });
  });

  it('re-enquing a periodc task should fail (if it exists alread) via direct enqueue', function(done){
    var t = new rawAPI.task({name: 'periodic_any'});
    t.enqueue(function(err, success){
      String(err).should.equal('Error: not enquing periodic task periodic_any: already in the queue');
      done();
    })
  });

   it('I can inspect the state of my current tasks, the local queue, and the global queue', function(done){
    rawAPI.tasks.getAllTasks(function(err, data){
      for(var i in data){
        var t = data[i];
        ( (['periodic_any', 'periodic_all'].indexOf(t.name) >= 0) ).should.be.true;
        t.periodic.should.equal(true);
        t.frequency.should.equal(1000);
        t.queue.indexOf('actionHero:tasks:delayed').should.equal(0);
        t.state.should.equal('delayed');
      }
      done();
    })
  });

  it('I can add many non-periodic task instances', function(done){
    var t = new rawAPI.task({name: 'regular_any'});
    t.enqueue(function(){
      var t = new rawAPI.task({name: 'regular_any'});
      t.enqueue(function(){
        rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount){
          globalCount.should.equal(2);
          rawAPI.tasks.getAllTasks(function(err, data){
            rawAPI.utils.hashLength(data).should.equal(4)
            done();
          });
        });
      });
    });
  });

  describe('full worker flow', function(){

    beforeEach(function(done){
      rawAPI.redis.client.flushdb(function(){
        done();
      });
    })

    it('general task flow works (normal)', function(done){
      this.timeout(5000);
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'regular_any'});
      task.enqueue(function(err, resp){
        setTimeout(function(){
          getAllQueueLengths(function(err, data1){
            data1.delayed.should.equal(0);
            data1.local.should.equal(0);
            data1.global.should.equal(1);
            worker.process(function(){
              // move to local
              getAllQueueLengths(function(err, data3){
                data3.delayed.should.equal(0);
                data3.local.should.equal(1);
                data3.global.should.equal(0);
                worker.process(function(){
                  // move to processing and try to work it
                  // should be back in delayed
                  getAllQueueLengths(function(err, data4){
                    data4.delayed.should.equal(0);
                    data4.local.should.equal(0);
                    data4.global.should.equal(0);
                    done();
                  });
                });
              });
            });
          });
        }, 2);
      });
    });

  it('general task flow works (delayed)', function(done){
      this.timeout(5000);
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'regular_any', runAt: new Date().getTime() + 200});
      task.enqueue(function(err, resp){
        setTimeout(function(){
          getAllQueueLengths(function(err, data1){
            data1.delayed.should.equal(1);
            data1.local.should.equal(0);
            data1.global.should.equal(0);
            worker.process(function(){
              // move to global
              getAllQueueLengths(function(err, data2){
                data2.delayed.should.equal(0);
                data2.local.should.equal(0);
                data2.global.should.equal(1);
                worker.process(function(){
                  // move to local
                  getAllQueueLengths(function(err, data3){
                    data3.delayed.should.equal(0);
                    data3.local.should.equal(1);
                    data3.global.should.equal(0);
                    worker.process(function(){
                      // move to processing and try to work it
                      // should be back in delayed
                      getAllQueueLengths(function(err, data4){
                        data4.delayed.should.equal(0);
                        data4.local.should.equal(0);
                        data4.global.should.equal(0);
                        done();
                      });
                    });
                  });
                });
              });
            });
          });
        }, 1000);
      });
    });

    it('general task flow works (periodic)', function(done){
      this.timeout(10000);
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'periodic_any'});
      task.enqueue(function(err, resp){
        setTimeout(function(){
          getAllQueueLengths(function(err, data1){
            data1.delayed.should.equal(1);
            data1.local.should.equal(0);
            data1.global.should.equal(0);
            worker.process(function(){
              // move to global
              getAllQueueLengths(function(err, data2){
                data2.delayed.should.equal(0);
                data2.local.should.equal(0);
                data2.global.should.equal(1);
                worker.process(function(){
                  // move to local
                  getAllQueueLengths(function(err, data3){
                    data3.delayed.should.equal(0);
                    data3.local.should.equal(1);
                    data3.global.should.equal(0);
                    worker.process(function(){
                      // move to processing and try to work it
                      // should be back in delayed
                      getAllQueueLengths(function(err, data4){
                        data4.delayed.should.equal(1);
                        data4.local.should.equal(0);
                        data4.global.should.equal(0);
                        done();
                      });
                    });
                  });
                });
              });
            });
          });
        }, 4001);
      });
    });

  });

  // TODO
  // it('If I crash while working on a task, I will clear the crash on my next boot', function(done){
  //   rawAPI.redis.client.flushdb(function(){
  //     var t = new rawAPI.task({name: 'regular_any', runAt: new Date().getTime() - 1});
  //     t.enqueue(function(err, success){
  //       success.should.equal(true);
  //       rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount){
  //         globalCount.should.equal(1);
  //         rawAPI.tasks.changeQueue(rawAPI.tasks.queues.globalQueue, rawAPI.tasks.queues.processingQueue, function(err, task){
  //           task.name.should.equal('regular_any')
  //           rawAPI.tasks.setTaskData(task.id, {api_id: rawAPI.id, worker_id: 0, state: "processing"}, function(err, task){
  //             task.queue.should.equal('actionHero:tasks:processing')
  //             rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount2){
  //               globalCount2.should.equal(0)

  //               rawAPI.tasks.savePreviouslyCrashedTasks(function(){
  //                 rawAPI.tasks.queueLength(rawAPI.tasks.queues.globalQueue, function(err, globalCount3){
  //                   globalCount3.should.equal(1);
  //                   done();
  //                 });
  //               });

  //             });
  //           });
  //         });
  //       });
  //     });
  //   });
  // });

  describe('busted periodic task', function(){

    var uncaughtExceptionHandlers;

    before(function(done){
      rawAPI.tasks.tasks['busted_task'] = {
        name: 'busted_task',
        description: 'task: ' + this.name,
        scope: 'any',
        frequency: 1,
        run: function(api, params, next){
          stuff = bad + thing;
          next();
        }
      }

      uncaughtExceptionHandlers = process.listeners("uncaughtException");
      uncaughtExceptionHandlers.forEach(function(e){
        process.removeListener("uncaughtException", e);
      });

      rawAPI.redis.client.flushdb(function(){
        done();
      });
    });

    after(function(done){
      delete rawAPI.tasks.tasks['busted_task'];
      uncaughtExceptionHandlers.forEach(function(e){
        process.on("uncaughtException", e);
      });
      done()
    });

    it('should start empty', function(done){
      getAllQueueLengths(function(err, data){
        data.delayed.should.equal(0);
        data.local.should.equal(0);
        data.global.should.equal(0);
        done();
      });
    });

    it('periodc tasks which return a failure will still be re-enqueued and tried again', function(done){
      this.timeout(15000);
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'busted_task'});
      task.enqueue(function(err, resp){
        setTimeout(function(){
          getAllQueueLengths(function(err, data1){
            data1.delayed.should.equal(1);
            data1.local.should.equal(0);
            data1.global.should.equal(0);
            worker.process(function(){
              // move to global
              getAllQueueLengths(function(err, data2){
                data2.delayed.should.equal(0);
                data2.local.should.equal(0);
                data2.global.should.equal(1);
                worker.process(function(){
                  // move to local
                  getAllQueueLengths(function(err, data3){
                    data3.delayed.should.equal(0);
                    data3.local.should.equal(1);
                    data3.global.should.equal(0);
                    worker.process(function(){
                      // move to processing and try to work it
                      // should be back in delayed
                      getAllQueueLengths(function(err, data4){
                        data4.delayed.should.equal(0);
                        data4.local.should.equal(0);
                        data4.global.should.equal(1);
                        done();
                      });
                    });
                  });
                });
              });
            });
          });
        }, 400);
      });
    });

  });

  it('enquing an "all" task will end up preformed by every server', function(done){
    // TODO
    done();
  });

  it('I will not process tasks with a runAt in the future', function(done){
    this.timeout(5000);
    rawAPI.redis.client.flushdb(function(){
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'regular_any', runAt: new Date().getTime() + 10000});
      task.enqueue(function(){
        rawAPI.tasks.countDelayedTasks(function(err, delayedCount){
          delayedCount.should.equal(1);
          setTimeout(function(){
            worker.process(function(){
              rawAPI.tasks.countDelayedTasks(function(err, delayedCount2){
                delayedCount.should.equal(1);
                done();
              });
            });
          }, 500)
        });
      });
    });
  });

  it('tasks can be passed params and taskWorkers can work on thier own', function(done){
    this.timeout(10000)
    taskOutput = [];
    rawAPI.redis.client.flushdb(function(){
      var worker = new rawAPI.taskProcessor({id: 1});
      var task = new rawAPI.task({name: 'regular_any', params: {word: 'TEST'}});
      task.enqueue(function(){
        worker.start();
        setTimeout(function(){
          taskOutput[0].should.equal('TEST');
          worker.stop();
          done();
        }, rawAPI.tasks.cycleTimeMS * 10 + 1)
      });
    });
  });

});<|endoftext|>


<commit_before>var expect = require('expect.js')
  , Device = require('./fixture/device');

describe('Device', function() {

    describe('without Connection object', function() {

        var device;

        describe('creating a device object', function() {

            it('it should have all expected values', function() {

                device = new Device(true);
                expect(device.id).to.be.a('string');
                expect(device.open).to.be.a('function');
                expect(device.close).to.be.a('function');
                expect(device.send).to.be.a('function');

            });

            describe('calling open', function() {

                it('it should emit open', function(done) {

                    device.once('open', function() {
                        done();
                    });
                    device.open();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        device.open(function() {
                            done();
                        });

                    });

                });

            });

            describe('emitting send', function() {

                it('it should emit receive', function(done) {

                    device.once('receive', function() {
                        done();
                    });
                    device.emit('send', []);

                });

            });

            describe('calling send', function() {

                it('it should emit receive', function(done) {

                    device.once('receive', function() {
                        done();
                    });
                    device.send([]);

                });

            });

            describe('calling close', function() {

                it('it should emit close', function(done) {

                    device.once('close', function() {
                        done();
                    });
                    device.close();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        device.close(function() {
                            done();
                        });

                    });

                });

            });

        });

    });

    describe('with Connection object', function() {

        var device;

        describe('creating a device object', function() {

            it('it should have all expected values', function() {

                device = new Device();
                expect(device.id).to.be.a('string');
                expect(device.open).to.be.a('function');
                expect(device.close).to.be.a('function');
                expect(device.send).to.be.a('function');

            });

            describe('calling open', function() {

                it('it should emit open', function(done) {

                    device.once('open', function() {
                        done();
                    });
                    device.open();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        device.open(function() {
                            done();
                        });

                    });

                });

            });

            describe('emitting send', function() {

                it('it should emit receive', function(done) {

                    device.once('receive', function() {
                        done();
                    });
                    device.emit('send', []);

                });

            });

            describe('calling send', function() {

                it('it should emit receive', function(done) {

                    device.once('receive', function() {
                        done();
                    });
                    device.send();

                });

            });

            describe('calling close', function() {

                it('it should emit close', function(done) {

                    device.once('close', function() {
                        done();
                    });
                    device.close();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        device.close(function() {
                            done();
                        });

                    });

                });

            });

            describe('calling connect', function() {

                it('it should emit opening', function(done) {

                    device.once('opening', function() {
                        done();
                    });
                    device.connect();

                });

                it('it should emit open', function(done) {

                    device.once('open', function() {
                        done();
                    });
                    device.connect();

                });

                it('it should a new connection property with a generated id', function(done) {

                    device.once('open', function() {
                        expect(device.connection).to.be.an('object');
                        expect(device.connection.id).to.be.a('string');

                        done();
                    });
                    device.connect();

                });

                describe('with a callback', function() {

                    var device2 = new Device();

                    it('it should call the callback', function(done) {

                        device2.connect(function() {
                            done();
                        });

                    });

                });

            });

            describe('calling disconnect', function() {

                it('it should emit closing', function(done) {

                    device.once('closing', function() {
                        done();
                    });
                    device.disconnect();

                });

                it('it should emit close', function(done) {

                    device.once('close', function() {
                        done();
                    });
                    device.disconnect();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        var device2 = new Device();

                        device2.disconnect(function() {
                            done();
                        });

                    });

                });

            });

        });

    });

});<commit_msg>added more test expectations<commit_after>var expect = require('expect.js')
  , Device = require('./fixture/device');

describe('Device', function() {

    describe('without Connection object', function() {

        var device;

        describe('creating a device object', function() {

            it('it should have all expected values', function() {

                device = new Device(true);
                expect(device.id).to.be.a('string');
                expect(device.open).to.be.a('function');
                expect(device.close).to.be.a('function');
                expect(device.send).to.be.a('function');
                expect(device.set).to.be.a('function');
                expect(device.get).to.be.a('function');
                expect(device.toJSON).to.be.a('function');

            });

            describe('calling open', function() {

                it('it should emit open', function(done) {

                    device.once('open', function() {
                        done();
                    });
                    device.open();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        device.open(function() {
                            done();
                        });

                    });

                });

            });

            describe('emitting send', function() {

                it('it should emit receive', function(done) {

                    device.once('receive', function() {
                        done();
                    });
                    device.emit('send', []);

                });

            });

            describe('calling send', function() {

                it('it should emit receive', function(done) {

                    device.once('receive', function() {
                        done();
                    });
                    device.send([]);

                });

            });

            describe('calling close', function() {

                it('it should emit close', function(done) {

                    device.once('close', function() {
                        done();
                    });
                    device.close();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        device.close(function() {
                            done();
                        });

                    });

                });

            });

        });

    });

    describe('with Connection object', function() {

        var device;

        describe('creating a device object', function() {

            it('it should have all expected values', function() {

                device = new Device();
                expect(device.id).to.be.a('string');
                expect(device.open).to.be.a('function');
                expect(device.close).to.be.a('function');
                expect(device.send).to.be.a('function');

            });

            describe('calling open', function() {

                it('it should emit open', function(done) {

                    device.once('open', function() {
                        done();
                    });
                    device.open();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        device.open(function() {
                            done();
                        });

                    });

                });

            });

            describe('emitting send', function() {

                it('it should emit receive', function(done) {

                    device.once('receive', function() {
                        done();
                    });
                    device.emit('send', []);

                });

            });

            describe('calling send', function() {

                it('it should emit receive', function(done) {

                    device.once('receive', function() {
                        done();
                    });
                    device.send();

                });

            });

            describe('calling close', function() {

                it('it should emit close', function(done) {

                    device.once('close', function() {
                        done();
                    });
                    device.close();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        device.close(function() {
                            done();
                        });

                    });

                });

            });

            describe('calling connect', function() {

                it('it should emit opening', function(done) {

                    device.once('opening', function() {
                        done();
                    });
                    device.connect();

                });

                it('it should emit open', function(done) {

                    device.once('open', function() {
                        done();
                    });
                    device.connect();

                });

                it('it should a new connection property with a generated id', function(done) {

                    device.once('open', function() {
                        expect(device.connection).to.be.an('object');
                        expect(device.connection.id).to.be.a('string');

                        done();
                    });
                    device.connect();

                });

                describe('with a callback', function() {

                    var device2 = new Device();

                    it('it should call the callback', function(done) {

                        device2.connect(function() {
                            done();
                        });

                    });

                });

            });

            describe('calling disconnect', function() {

                it('it should emit closing', function(done) {

                    device.once('closing', function() {
                        done();
                    });
                    device.disconnect();

                });

                it('it should emit close', function(done) {

                    device.once('close', function() {
                        done();
                    });
                    device.disconnect();

                });

                describe('with a callback', function() {

                    it('it should call the callback', function(done) {

                        var device2 = new Device();

                        device2.disconnect(function() {
                            done();
                        });

                    });

                });

            });

        });

    });

});<|endoftext|>


<commit_before>module.exports = {
  schema: {
    enabled: { default: true }
  },

  init: function () {
    this.dVelocity = new THREE.Vector3();
    this.bindMethods();
  },

  play: function () {
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
    this.dVelocity.set(0, 0, 0);
  },

  remove: function () {
    this.pause();
  },

  addEventListeners: function () {
    var sceneEl = this.el.sceneEl;
    var canvasEl = sceneEl.canvas;

    if (!canvasEl) {
      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
      return;
    }

    canvasEl.addEventListener('touchstart', this.onTouchStart);
    canvasEl.addEventListener('touchend', this.onTouchEnd);
  },

  removeEventListeners: function () {
    var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
    if (!canvasEl) { return; }

    canvasEl.removeEventListener('touchstart', this.onTouchStart);
    canvasEl.removeEventListener('touchend', this.onTouchEnd);
  },

  isVelocityActive: function () {
    return this.data.enabled && this.isMoving;
  },

  getVelocityDelta: function () {
    this.dVelocity.z = this.isMoving ? -1 : 0;
    return this.dVelocity.clone();
  },

  bindMethods: function () {
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onTouchEnd = this.onTouchEnd.bind(this);
  },

  onTouchStart: function (e) {
    this.isMoving = true;
    e.preventDefault();
  },

  onTouchEnd: function (e) {
    this.isMoving = false;
    e.preventDefault();
  }
};
<commit_msg>Delete touch-controls.js<commit_after><|endoftext|>


<commit_before>(function ($) {
    $.fn.sortable = function () {
        return this.each(function () {

            var supports_touch = 'ontouchstart' in document.documentElement;
            var start_event_name = (supports_touch) ? 'touchstart' : 'mousedown';
            $(this).children()
                .on(start_event_name, onStart)
                .css({
                	cursor: "move",
                    	'user-select': 'none'
            	})
                .attr('unselectable', 'on')
                .on('selectstart', false);

            var el;
            var parentTop, parentBtm;
            var start_y;
            var elDistance = $(this).children(':nth-child(2)').offset().top - $(this).children(':nth-child(1)').offset().top;

            function onStart(e) {

                e = e.originalEvent;
                el = $(supports_touch ? e.touches[0].target : e.target);
                parentTop = el.parent().position().top;
                parentBtm = parentTop + el.parent().innerHeight() + el.height();
                el.addClass('inMotion').css({
                    position: 'relative',
                    'z-index': 1
                });

                /* Bind respective events based on start trigger */
                if (e.touches) {
                    start_y = e.touches[0].pageY;
                    $('body').on('touchmove.sortable', onMove).on('touchend.sortable', onEnd).on('touchcancel.sortable', onEnd);
                } else {
                    start_y = e.pageY;
                    $('body').on('mousemove.sortable', onMove).on('mouseup.sortable', onEnd).on('mouseleave.sortable', onEnd);
                }
            }

            function onMove(e) {
                var move;
                if (e) {
                    e = e.originalEvent;

                    var pageY = (e.touches) ? e.touches[0].pageY : e.pageY;

                    /* Limit dragging to constraints of parent */
                    if (pageY <= parentTop) {
                        pageY = parentTop;
                    } else if (pageY >= parentBtm) {
                        pageY = parentBtm;
                    }

                    /* Move item  */
                    move = pageY - start_y;
                    el.css('top', move);
                } else {
                    move = el.css('top').split('px')[0];
                }

                /* Re-order the list once item crosses over the neighboring elements */
                if (move < -elDistance) {

                    if (el.prev().length) {
                        el.siblings().css('position', 'relative');
                        if (!el.prev().is(':animated')) {

                            /* Animate and swap */
                            el.prev().animate({
                                'top': elDistance
                            }, 150, function () {
                                start_y = start_y - elDistance;
                                $(this).insertAfter(el).css({
                                    'top': ''
                                });
                                el.css('top', '+=' + elDistance);
                                onMove();
                            });
                        }
                    }
                } else if (move > elDistance) {

                    if (el.next().length) {
                        el.siblings().css('position', 'relative');
                        if (!el.next().is(':animated')) {

                            /* Animate and swap */

                            el.next().animate({
                                'top': -elDistance
                            }, 150, function () {
                                $(this).insertBefore(el).css({
                                    'top': ''
                                });
                                start_y = start_y + elDistance;
                                el.css('top', '-=' + elDistance);
                                onMove();
                            });
                        }
                    }
                }

                if (e) {
                    e.preventDefault();
                }
            }

            function onEnd(e) {
                $('body').off('.sortable');

                function complete() {
                    el.animate({'top': "-=" + el.css('top')}, 150, function () {
                        el.css('top', 'auto').removeClass('inMotion').css('z-index', '');
                    });
                }

                if (el.prev().is(':animated') || el.next().is(':animated')) {
                    setTimeout(function () { onEnd() }, 50);
                } else {
                    complete();
                }
            }
        });
    };
})(jQuery);
<commit_msg>Changed the way onEnd detects all animation is complete<commit_after>(function ($) {
    $.fn.sortable = function () {
        return this.each(function () {

            var supports_touch = 'ontouchstart' in document.documentElement;
            var start_event_name = (supports_touch) ? 'touchstart' : 'mousedown';
            $(this).children()
                .on(start_event_name, onStart)
                .css({
                cursor: "move",
                    'user-select': 'none'
            })
                .attr('unselectable', 'on')
                .on('selectstart', false);

            var el;
            var parentTop, parentBtm;
            var start_y;
            var elDistance = $(this).children(':nth-child(2)').offset().top - $(this).children(':nth-child(1)').offset().top;

            function onStart(e) {

                e = e.originalEvent;
                el = $(supports_touch ? e.touches[0].target : e.target);
                parentTop = el.parent().position().top;
                parentBtm = parentTop + el.parent().innerHeight() + el.height();
                el.addClass('inMotion').css({
                    position: 'relative',
                    'z-index': 1
                });

                /* Bind respective events based on start trigger */
                if (e.touches) {
                    start_y = e.touches[0].pageY;
                    $('body').on('touchmove.sortable', onMove).on('touchend.sortable', onEnd).on('touchcancel.sortable', onEnd);
                } else {
                    start_y = e.pageY;
                    $('body').on('mousemove.sortable', onMove).on('mouseup.sortable', onEnd).on('mouseleave.sortable', onEnd);
                }
            }

            function onMove(e) {
                var move;
                if (e) {
                    e = e.originalEvent;

                    var pageY = (e.touches) ? e.touches[0].pageY : e.pageY;

                    /* Limit dragging to constraints of parent */
                    if (pageY <= parentTop) {
                        pageY = parentTop;
                    } else if (pageY >= parentBtm) {
                        pageY = parentBtm;
                    }

                    /* Move item  */
                    move = pageY - start_y;
                    el.css('top', move);
                } else {
                    move = el.css('top').split('px')[0];
                }

                /* Re-order the list once item crosses over the neighboring elements */
                if (move < -elDistance) {

                    if (el.prev().length) {
                        el.siblings().css('position', 'relative');
                        if (!el.prev().is(':animated')) {

                            /* Animate and swap */
                            el.prev().animate({
                                'top': elDistance
                            }, 150, function () {
                                start_y = start_y - elDistance;
                                $(this).insertAfter(el).css({
                                    'top': ''
                                });
                                el.css('top', '+=' + elDistance);
                                onMove();
                            });
                        }
                    }
                } else if (move > elDistance) {

                    if (el.next().length) {
                        el.siblings().css('position', 'relative');
                        if (!el.next().is(':animated')) {

                            /* Animate and swap */

                            el.next().animate({ 'top': -elDistance}, 150, function () {
                                $(this).insertBefore(el).css({
                                    'top': ''
                                });
                                start_y = start_y + elDistance;
                                el.css('top', '-=' + elDistance);
                                onMove();
                            });
                        }
                    }
                }

                if (e) {
                    e.preventDefault();
                }
            }

            function onEnd(e) {
                $('body').off('.sortable');

                /* Execute onEnd only after all animation is complete */
                $('.sortable').children().promise().done(function() {
                    console.log('complete')
                    el.animate({ 'top': "-=" + el.css('top')}, 150, function () {
                        el.css('top', 'auto').removeClass('inMotion').css({
                            'z-index': ''
                        });
                    });
                });

            }
        });
    };
})(jQuery);
<|endoftext|>


<commit_before>var test = require('tape')
var find = require('array-find')
var crypto = require('crypto')
var extend = require('extend')
var memdown = require('memdown')
var collect = require('stream-collector')
var map = require('map-stream')
var safe = require('safecb')
var Q = require('q')
var DHT = require('bittorrent-dht')
var ChainedObj = require('chained-obj')
var Builder = ChainedObj.Builder
var kiki = require('kiki')
var toKey = kiki.toKey
var billPub = require('./fixtures/bill-pub.json')
var tedPub = require('./fixtures/ted-pub.json')
var billPriv = require('./fixtures/bill-priv')
var tedPriv = require('./fixtures/ted-priv')
var constants = require('tradle-constants')
var TYPE = constants.TYPE
// var ROOT_HASH = constants.ROOT_HASH
// var CUR_HASH = constants.CUR_HASH
// var PREV_HASH = constants.PREV_HASH
// var tedHash = tedPub[ROOT_HASH] = 'c67905793f6cc0f0ab8d20aecfec441932ffb13d'
// var billHash = billPub[ROOT_HASH] ='fb07729c0cef307ab7c28cb76088cc60dbc98cdd'
// var tedHash = 'c67905793f6cc0f0ab8d20aecfec441932ffb13d'
// var billHash = 'fb07729c0cef307ab7c28cb76088cc60dbc98cdd'
var Identity = require('midentity').Identity
var help = require('tradle-test-helpers')
var fakeKeeper = help.fakeKeeper
var fakeWallet = help.fakeWallet
// var bill = Identity.fromJSON(billPriv)
// var ted = Identity.fromJSON(tedPriv)
var networkName = 'testnet'
// var blockchain = new Fakechain({ networkName: networkName })
var Driver = require('../')

var driverBill
var driverTed
var reinitCount = 0

test('setup', function (t) {
  t.plan(1)
  t.timeoutAfter(2000)

  init(t.pass)
})

// reinitAndTest('self publish, edit, republish', function (t) {
//   publish(function () {
//     failToRepeatPublish(function () {
//       republish(function () {
//         readIdentities(t.end)
//       })
//     })
//   })

//   var identitiesChecked = 0

//   function publish (next) {
//     driverBill.publishMyIdentity()
//     driverTed.once('unchained', function (info) {
//       driverTed.lookupObject(info)
//         .done(function (chainedObj) {
//           t.deepEqual(chainedObj.parsed.data, driverBill.identityJSON)
//           next()
//         })
//     })
//   }

//   function failToRepeatPublish (next) {
//     driverTed.on('unchained', t.fail)
//     driverBill.publishMyIdentity()
//     driverBill.publishMyIdentity()
//     driverBill.publishMyIdentity()
//     setTimeout(function () {
//       driverTed.removeListener('unchained', t.fail)
//       next()
//     }, 3000)
//   }

//   function republish (next) {
//     driverBill.identityJSON.name.firstName = 'blah'
//     driverBill.publishMyIdentity()
//     driverTed.once('unchained', function (info) {
//       driverTed.lookupObject(info)
//         .done(function (chainedObj) {
//           var loaded = chainedObj.parsed.data
//           t.equal(loaded[PREV_HASH], driverBill.identityMeta[PREV_HASH])
//           t.equal(loaded[ROOT_HASH], driverBill.identityMeta[ROOT_HASH])
//           t.deepEqual(loaded, driverBill.identityJSON)
//           next()
//         })
//     })
//   }

//   function readIdentities (next) {
//     var bStream = driverBill.identities().createValueStream()
//     var tStream = driverTed.identities().createValueStream()
//     collect(bStream, checkIdentities)
//     collect(tStream, checkIdentities)
//   }

//   function checkIdentities (err, identities) {
//     if (err) throw err

//     t.equal(identities.length, 1)
//     identities.forEach(function (ident) {
//       // if (ident.name.firstName === driverBill.identityJSON.name.firstName) {
//       t.deepEqual(ident, driverBill.identityJSON)
//       // } else {
//       //   t.deepEqual(ident, driverTed.identityJSON)
//       // }
//     })

//     if (++identitiesChecked === 2) t.end()
//   }
// })

reinitAndTest('chained message', function (t) {
  t.plan(6)
  // t.timeoutAfter(15000)

  driverBill.publishMyIdentity()
  driverTed.publishMyIdentity()

  driverTed.on('unchained', onUnchained.bind(driverTed))
  driverBill.on('unchained', onUnchained.bind(driverBill))

  driverBill.on('message', function (obj) {
    driverBill.lookupObject(obj)
      .then(function (chainedObj) {
        checkMessage(chainedObj.data)
      })
      .done()
  })

  driverBill.on('resolved', function (obj) {
    driverBill.lookupObject(obj)
      .then(function (chainedObj) {
        checkMessage(chainedObj.data)
      })
      .done()
  })

  var billCoords = {
    fingerprint: billPub.pubkeys[0].fingerprint
  }

  var msg = { hey: 'ho' }
  msg[TYPE] = 'blahblah'

  var identitiesChained = 0
  var messagesChained = 0

  function onUnchained (info) {
    // var self = this
    this.lookupObject(info)
      .then(function (chainedObj) {
        var parsed = chainedObj.parsed
        // console.log(self.identityJSON.name.formatted, 'unchained', parsed.data[TYPE])
        if (parsed.data[TYPE] === Identity.TYPE) {
          // console.log(self.identityJSON.name.formatted, 'unchained', parsed.data.name.formatted)
          onIdentityChained(parsed.data)
        } else {
          checkMessage(parsed.data)
          if (++messagesChained === 2) {
            checkMessageDB()
          }
        }
      })
      .done()
  }

  function checkMessage (m) {
    // console.log('check')
    if (Buffer.isBuffer(m)) m = JSON.parse(m)

    // delete m[constants.SIG]
    t.deepEqual(m, msg)
  }

  function checkMessageDB () {
    collect(driverBill.messages().createValueStream().pipe(
      map(function (data, cb) {
        driverBill.lookupObject(data)
          .nodeify(cb)
      })
    ), checkLast)

    collect(driverTed.messages().createValueStream().pipe(
      map(function (data, cb) {
        driverTed.lookupObject(data)
          .nodeify(cb)
      })
    ), checkLast)

    function checkLast (err, messages) {
      if (err) throw err

      // console.log(JSON.stringify(messages, null, 2))
      checkMessage(messages.pop().parsed.data)
    }
  }

  function onIdentityChained (i) {
    if (++identitiesChained !== 4) return // 2 each, because both also detect themselves

    Builder()
      .data(msg)
      .signWith(getSigningKey(tedPriv))
      .build(function (err, result) {
        if (err) throw err

        driverTed.send({
          msg: result.form,
          to: [billCoords],
          chain: true
        }).done()
      })
  }
})

test('teardown', function (t) {
  t.timeoutAfter(10000)
  teardown(function () {
    t.end()
  })
})

function reinit (cb) {
  if (driverBill) {
    return teardown(function () {
      driverBill = driverTed = null
      reinit(cb)
    })
  } else {
    return init(cb)
  }
}

function init (cb) {
  reinitCount++

  var bill = Identity.fromJSON(billPub)
  var ted = Identity.fromJSON(tedPub)
  var keeper = fakeKeeper.empty()
  var billPort = 51086
  var tedPort = 51087
  var billWallet = walletFor(billPriv, null, 'messaging')
  var blockchain = billWallet.blockchain
  var tedWallet = walletFor(tedPriv, blockchain, 'messaging')
  var commonOpts = {
    networkName: networkName,
    keeper: keeper,
    blockchain: blockchain,
    leveldown: memdown,
    syncInterval: 1000
  }

  var billDHT = dhtFor(bill)
  billDHT.listen(billPort)

  var tedDHT = dhtFor(ted)
  tedDHT.listen(tedPort)

  billDHT.addNode('127.0.0.1:' + tedPort, tedDHT.nodeId)
  tedDHT.addNode('127.0.0.1:' + billPort, billDHT.nodeId)

  driverBill = new Driver(extend({
    pathPrefix: 'bill' + reinitCount,
    identity: bill,
    identityKeys: billPriv,
    // kiki: kiki.kiki(billPriv),
    wallet: billWallet,
    dht: billDHT,
    port: billPort
  }, commonOpts))

  driverTed = new Driver(extend({
    pathPrefix: 'ted' + reinitCount,
    identity: ted,
    identityKeys: tedPriv,
    // kiki: kiki.kiki(tedPriv),
    wallet: tedWallet,
    dht: tedDHT,
    port: tedPort
  }, commonOpts))

  ;['warn', 'error'].forEach(function (bad) {
    driverBill.on(bad, rethrow)
    driverTed.on(bad, rethrow)
  })

  var togo = 2

  driverBill.once('ready', finish)
  driverTed.once('ready', finish)

  function finish () {
    if (--togo === 0) cb()
  }
}

function teardown (cb) {
  Q.all([
    driverBill.destroy(),
    driverTed.destroy()
  ]).done(safe(cb))
}

function reinitAndTest (name, testFn) {
  test(name, function (t) {
    var ctx = this
    var args = arguments
    reinit(function () {
      testFn.apply(ctx, args)
    })
  })
}

function dhtFor (identity) {
  return new DHT({
    nodeId: nodeIdFor(identity),
    bootstrap: false
    // ,
    // bootstrap: ['tradle.io:25778']
  })
}

function nodeIdFor (identity) {
  return crypto.createHash('sha256')
    .update(identity.keys({ type: 'dsa' })[0].fingerprint())
    .digest()
    .slice(0, 20)
}

function walletFor (keys, blockchain, purpose) {
  return fakeWallet({
    blockchain: blockchain,
    unspents: [100000, 100000, 100000, 100000],
    priv: find(keys, function (k) {
      return k.type === 'bitcoin' &&
        k.networkName === networkName &&
        k.purpose === purpose
    }).priv
  })
}

function rethrow (err) {
  if (err) throw err
}

function getSigningKey (keys) {
  var key = find(keys, function (k) {
    return k.type === 'ec' && k.purpose === 'sign'
  })

  return key && toKey(key)
}
<commit_msg>uncomment publish test<commit_after>var test = require('tape')
var find = require('array-find')
var crypto = require('crypto')
var extend = require('extend')
var memdown = require('memdown')
var collect = require('stream-collector')
var map = require('map-stream')
var safe = require('safecb')
var Q = require('q')
var DHT = require('bittorrent-dht')
var ChainedObj = require('chained-obj')
var Builder = ChainedObj.Builder
var kiki = require('kiki')
var toKey = kiki.toKey
var billPub = require('./fixtures/bill-pub.json')
var tedPub = require('./fixtures/ted-pub.json')
var billPriv = require('./fixtures/bill-priv')
var tedPriv = require('./fixtures/ted-priv')
var constants = require('tradle-constants')
var TYPE = constants.TYPE
var ROOT_HASH = constants.ROOT_HASH
// var CUR_HASH = constants.CUR_HASH
var PREV_HASH = constants.PREV_HASH
// var tedHash = tedPub[ROOT_HASH] = 'c67905793f6cc0f0ab8d20aecfec441932ffb13d'
// var billHash = billPub[ROOT_HASH] ='fb07729c0cef307ab7c28cb76088cc60dbc98cdd'
// var tedHash = 'c67905793f6cc0f0ab8d20aecfec441932ffb13d'
// var billHash = 'fb07729c0cef307ab7c28cb76088cc60dbc98cdd'
var Identity = require('midentity').Identity
var help = require('tradle-test-helpers')
var fakeKeeper = help.fakeKeeper
var fakeWallet = help.fakeWallet
// var bill = Identity.fromJSON(billPriv)
// var ted = Identity.fromJSON(tedPriv)
var networkName = 'testnet'
// var blockchain = new Fakechain({ networkName: networkName })
var Driver = require('../')

var driverBill
var driverTed
var reinitCount = 0

test('setup', function (t) {
  t.plan(1)
  t.timeoutAfter(2000)

  init(t.pass)
})

reinitAndTest('self publish, edit, republish', function (t) {
  publish(function () {
    failToRepeatPublish(function () {
      republish(function () {
        readIdentities(t.end)
      })
    })
  })

  var identitiesChecked = 0

  function publish (next) {
    driverBill.publishMyIdentity()
    driverTed.once('unchained', function (info) {
      driverTed.lookupObject(info)
        .done(function (chainedObj) {
          t.deepEqual(chainedObj.parsed.data, driverBill.identityJSON)
          next()
        })
    })
  }

  function failToRepeatPublish (next) {
    driverTed.on('unchained', t.fail)
    driverBill.publishMyIdentity()
    driverBill.publishMyIdentity()
    driverBill.publishMyIdentity()
    setTimeout(function () {
      driverTed.removeListener('unchained', t.fail)
      next()
    }, 3000)
  }

  function republish (next) {
    driverBill.identityJSON.name.firstName = 'blah'
    driverBill.publishMyIdentity()
    driverTed.once('unchained', function (info) {
      driverTed.lookupObject(info)
        .done(function (chainedObj) {
          var loaded = chainedObj.parsed.data
          t.equal(loaded[PREV_HASH], driverBill.identityMeta[PREV_HASH])
          t.equal(loaded[ROOT_HASH], driverBill.identityMeta[ROOT_HASH])
          t.deepEqual(loaded, driverBill.identityJSON)
          next()
        })
    })
  }

  function readIdentities (next) {
    var bStream = driverBill.identities().createValueStream()
    var tStream = driverTed.identities().createValueStream()
    collect(bStream, checkIdentities)
    collect(tStream, checkIdentities)
  }

  function checkIdentities (err, identities) {
    if (err) throw err

    t.equal(identities.length, 1)
    identities.forEach(function (ident) {
      // if (ident.name.firstName === driverBill.identityJSON.name.firstName) {
      t.deepEqual(ident, driverBill.identityJSON)
      // } else {
      //   t.deepEqual(ident, driverTed.identityJSON)
      // }
    })

    if (++identitiesChecked === 2) t.end()
  }
})

reinitAndTest('chained message', function (t) {
  t.plan(6)
  t.timeoutAfter(15000)

  driverBill.publishMyIdentity()
  driverTed.publishMyIdentity()

  driverTed.on('unchained', onUnchained.bind(driverTed))
  driverBill.on('unchained', onUnchained.bind(driverBill))

  driverBill.on('message', function (obj) {
    driverBill.lookupObject(obj)
      .then(function (chainedObj) {
        checkMessage(chainedObj.data)
      })
      .done()
  })

  driverBill.on('resolved', function (obj) {
    driverBill.lookupObject(obj)
      .then(function (chainedObj) {
        checkMessage(chainedObj.data)
      })
      .done()
  })

  var billCoords = {
    fingerprint: billPub.pubkeys[0].fingerprint
  }

  var msg = { hey: 'ho' }
  msg[TYPE] = 'blahblah'

  var identitiesChained = 0
  var messagesChained = 0

  function onUnchained (info) {
    // var self = this
    this.lookupObject(info)
      .then(function (chainedObj) {
        var parsed = chainedObj.parsed
        // console.log(self.identityJSON.name.formatted, 'unchained', parsed.data[TYPE])
        if (parsed.data[TYPE] === Identity.TYPE) {
          // console.log(self.identityJSON.name.formatted, 'unchained', parsed.data.name.formatted)
          onIdentityChained(parsed.data)
        } else {
          checkMessage(parsed.data)
          if (++messagesChained === 2) {
            checkMessageDB()
          }
        }
      })
      .done()
  }

  function checkMessage (m) {
    // console.log('check')
    if (Buffer.isBuffer(m)) m = JSON.parse(m)

    // delete m[constants.SIG]
    t.deepEqual(m, msg)
  }

  function checkMessageDB () {
    collect(driverBill.messages().createValueStream().pipe(
      map(function (data, cb) {
        driverBill.lookupObject(data)
          .nodeify(cb)
      })
    ), checkLast)

    collect(driverTed.messages().createValueStream().pipe(
      map(function (data, cb) {
        driverTed.lookupObject(data)
          .nodeify(cb)
      })
    ), checkLast)

    function checkLast (err, messages) {
      if (err) throw err

      // console.log(JSON.stringify(messages, null, 2))
      checkMessage(messages.pop().parsed.data)
    }
  }

  function onIdentityChained (i) {
    if (++identitiesChained !== 4) return // 2 each, because both also detect themselves

    Builder()
      .data(msg)
      .signWith(getSigningKey(tedPriv))
      .build(function (err, result) {
        if (err) throw err

        driverTed.send({
          msg: result.form,
          to: [billCoords],
          chain: true
        }).done()
      })
  }
})

test('teardown', function (t) {
  t.timeoutAfter(10000)
  teardown(function () {
    t.end()
  })
})

function reinit (cb) {
  if (driverBill) {
    return teardown(function () {
      driverBill = driverTed = null
      reinit(cb)
    })
  } else {
    return init(cb)
  }
}

function init (cb) {
  reinitCount++

  var bill = Identity.fromJSON(billPub)
  var ted = Identity.fromJSON(tedPub)
  var keeper = fakeKeeper.empty()
  var billPort = 51086
  var tedPort = 51087
  var billWallet = walletFor(billPriv, null, 'messaging')
  var blockchain = billWallet.blockchain
  var tedWallet = walletFor(tedPriv, blockchain, 'messaging')
  var commonOpts = {
    networkName: networkName,
    keeper: keeper,
    blockchain: blockchain,
    leveldown: memdown,
    syncInterval: 1000
  }

  var billDHT = dhtFor(bill)
  billDHT.listen(billPort)

  var tedDHT = dhtFor(ted)
  tedDHT.listen(tedPort)

  billDHT.addNode('127.0.0.1:' + tedPort, tedDHT.nodeId)
  tedDHT.addNode('127.0.0.1:' + billPort, billDHT.nodeId)

  driverBill = new Driver(extend({
    pathPrefix: 'bill' + reinitCount,
    identity: bill,
    identityKeys: billPriv,
    // kiki: kiki.kiki(billPriv),
    wallet: billWallet,
    dht: billDHT,
    port: billPort
  }, commonOpts))

  driverTed = new Driver(extend({
    pathPrefix: 'ted' + reinitCount,
    identity: ted,
    identityKeys: tedPriv,
    // kiki: kiki.kiki(tedPriv),
    wallet: tedWallet,
    dht: tedDHT,
    port: tedPort
  }, commonOpts))

  ;['warn', 'error'].forEach(function (bad) {
    driverBill.on(bad, rethrow)
    driverTed.on(bad, rethrow)
  })

  var togo = 2

  driverBill.once('ready', finish)
  driverTed.once('ready', finish)

  function finish () {
    if (--togo === 0) cb()
  }
}

function teardown (cb) {
  Q.all([
    driverBill.destroy(),
    driverTed.destroy()
  ]).done(safe(cb))
}

function reinitAndTest (name, testFn) {
  test(name, function (t) {
    var ctx = this
    var args = arguments
    reinit(function () {
      testFn.apply(ctx, args)
    })
  })
}

function dhtFor (identity) {
  return new DHT({
    nodeId: nodeIdFor(identity),
    bootstrap: false
    // ,
    // bootstrap: ['tradle.io:25778']
  })
}

function nodeIdFor (identity) {
  return crypto.createHash('sha256')
    .update(identity.keys({ type: 'dsa' })[0].fingerprint())
    .digest()
    .slice(0, 20)
}

function walletFor (keys, blockchain, purpose) {
  return fakeWallet({
    blockchain: blockchain,
    unspents: [100000, 100000, 100000, 100000],
    priv: find(keys, function (k) {
      return k.type === 'bitcoin' &&
        k.networkName === networkName &&
        k.purpose === purpose
    }).priv
  })
}

function rethrow (err) {
  if (err) throw err
}

function getSigningKey (keys) {
  var key = find(keys, function (k) {
    return k.type === 'ec' && k.purpose === 'sign'
  })

  return key && toKey(key)
}
<|endoftext|>


<commit_before>function sum(x, y) {
  return x + y;
}

// This will do the calc for determining the actual number of groups
function getGroups(groups, items) {
 var length = items.length;

  if (!groups.length) {
    return [length];
  }

  var total = groups.reduce(sum, 0);

  if (length > total) {
    return groups.concat(length - total);
  }

  return groups;
}

// It does an uneven split of any generic item array into groups.
// The function signature uses an order that will be more "functional", so data last.
function segregate(condition, items) {
  if (Array.isArray(condition)) {
    var initialValue = {nextIndex: 0, groups: []};
    var distribution = getGroups(condition, items);

    return distribution.reduce(function(acc, count) {
      var nextIndex = acc.nextIndex;
      var groups = acc.groups;

      groups.push(items.slice(nextIndex, nextIndex + count))

      return {
        nextIndex: nextIndex + count,
        groups: groups
      };
    }, initialValue).groups;
  }

  if (typeof condition === 'function') {
    return items.reduce((acc, item) => {
      const key = condition(item);
      acc[key] = (acc[key] || []).concat(item);
      return acc;
    }, {});
  }
}

module.exports = segregate;
<commit_msg>Increase purity of segregate reducer logic<commit_after>function sum(x, y) {
  return x + y;
}

// This will do the calc for determining the actual number of groups
function getGroups(groups, items) {
 var length = items.length;

  if (!groups.length) {
    return [length];
  }

  var total = groups.reduce(sum, 0);

  if (length > total) {
    return groups.concat(length - total);
  }

  return groups;
}

// It does an uneven split of any generic item array into groups.
// The function signature uses an order that will be more "functional", so data last.
function segregate(condition, items) {
  var initialValue = {nextIndex: 0, groups: []};
  var distribution = getGroups(condition, items);

  return distribution.reduce(function(acc, count) {
    var nextIndex = acc.nextIndex;
    var groups = acc.groups;

    return {
      nextIndex: nextIndex + count,
      groups: groups.concat([items.slice(nextIndex, nextIndex + count)])
    };
  }, initialValue).groups;
}

module.exports = segregate;
<|endoftext|>


<commit_before>import flamegraph from 'flamegraph'
import { select } from 'd3-selection'

describe('flame graph library', () => {
    let chartElem

    beforeEach(() => {
        chartElem = document.createElement('div')
    })

    it('should generate a minimal graph with a single stack', () => {
        const chart = flamegraph()
        const stacks = {
            name: 'root',
            value: 1,
            children: []
        }

        select(chartElem).datum(stacks).call(chart)
        expect(chartElem).toMatchInlineSnapshot(`

              <svg
                class="partition d3-flame-graph"
                height="54"
                width="960"
              >
                <text
                  class="title"
                  fill="#808080"
                  text-anchor="middle"
                  x="480"
                  y="25"
                />
                <g
                  class="frame"
                  height="18"
                  name="root"
                  transform="translate(0,36)"
                  width="960"
                >
                  <rect
                    fill="rgb(217,157,38)"
                    height="18"
                  />
                  <title>
                    root (100.000%, 1 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="960"
                  >

                  </foreignobject>
                </g>
              </svg>

        `)
    })

    it('should generate a graph with multiple stacks, using the self value logic', () => {
        const sortByValue = (lhs, rhs) => {
            if (lhs.value === rhs.value) return 0
            if (lhs.value < rhs.value) return 1
            return -1
        }

        const chart = flamegraph().sort(sortByValue).selfValue(true)
        const stacks = {
            name: 'root',
            value: 0,
            children: [
                {
                    name: 'root.node1',
                    value: 2,
                    children: [
                        {
                            name: 'root.node1.node1',
                            value: 3
                        }
                    ]
                },
                {
                    name: 'root.node2',
                    value: 4
                }
            ]
        }

        select(chartElem).datum(stacks).call(chart)
        expect(chartElem).toMatchInlineSnapshot(`

              <svg
                class="partition d3-flame-graph"
                height="90"
                width="960"
              >
                <text
                  class="title"
                  fill="#808080"
                  text-anchor="middle"
                  x="480"
                  y="25"
                />
                <g
                  class="frame"
                  height="18"
                  name="root"
                  transform="translate(0,72)"
                  width="960"
                >
                  <rect
                    fill="rgb(217,157,38)"
                    height="18"
                  />
                  <title>
                    root (100.000%, 9 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="960"
                  >

                  </foreignobject>
                </g>
                <g
                  class="frame"
                  height="18"
                  name="root.node1"
                  transform="translate(0,54)"
                  width="533.3333333333334"
                >
                  <rect
                    fill="rgb(218,156,37)"
                    height="18"
                  />
                  <title>
                    root.node1 (55.556%, 5 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="533.3333333333334"
                  >

                  </foreignobject>
                </g>
                <g
                  class="frame"
                  height="18"
                  name="root.node2"
                  transform="translate(533.3333333333334,54)"
                  width="426.66666666666663"
                >
                  <rect
                    fill="rgb(218,156,37)"
                    height="18"
                  />
                  <title>
                    root.node2 (44.444%, 4 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="426.66666666666663"
                  >

                  </foreignobject>
                </g>
                <g
                  class="frame"
                  height="18"
                  name="root.node1.node1"
                  transform="translate(0,36)"
                  width="320.00000000000006"
                >
                  <rect
                    fill="rgb(218,156,37)"
                    height="18"
                  />
                  <title>
                    root.node1.node1 (33.333%, 3 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="320.00000000000006"
                  >

                  </foreignobject>
                </g>
              </svg>

        `)
    })
})
<commit_msg>test: specify jsdom test environment<commit_after>/**
 * @jest-environment jsdom
 */

import flamegraph from 'flamegraph'
import { select } from 'd3-selection'

describe('flame graph library', () => {
    let chartElem

    beforeEach(() => {
        chartElem = document.createElement('div')
    })

    it('should generate a minimal graph with a single stack', () => {
        const chart = flamegraph()
        const stacks = {
            name: 'root',
            value: 1,
            children: []
        }

        select(chartElem).datum(stacks).call(chart)
        expect(chartElem).toMatchInlineSnapshot(`

              <svg
                class="partition d3-flame-graph"
                height="54"
                width="960"
              >
                <text
                  class="title"
                  fill="#808080"
                  text-anchor="middle"
                  x="480"
                  y="25"
                />
                <g
                  class="frame"
                  height="18"
                  name="root"
                  transform="translate(0,36)"
                  width="960"
                >
                  <rect
                    fill="rgb(217,157,38)"
                    height="18"
                  />
                  <title>
                    root (100.000%, 1 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="960"
                  >

                  </foreignobject>
                </g>
              </svg>

        `)
    })

    it('should generate a graph with multiple stacks, using the self value logic', () => {
        const sortByValue = (lhs, rhs) => {
            if (lhs.value === rhs.value) return 0
            if (lhs.value < rhs.value) return 1
            return -1
        }

        const chart = flamegraph().sort(sortByValue).selfValue(true)
        const stacks = {
            name: 'root',
            value: 0,
            children: [
                {
                    name: 'root.node1',
                    value: 2,
                    children: [
                        {
                            name: 'root.node1.node1',
                            value: 3
                        }
                    ]
                },
                {
                    name: 'root.node2',
                    value: 4
                }
            ]
        }

        select(chartElem).datum(stacks).call(chart)
        expect(chartElem).toMatchInlineSnapshot(`

              <svg
                class="partition d3-flame-graph"
                height="90"
                width="960"
              >
                <text
                  class="title"
                  fill="#808080"
                  text-anchor="middle"
                  x="480"
                  y="25"
                />
                <g
                  class="frame"
                  height="18"
                  name="root"
                  transform="translate(0,72)"
                  width="960"
                >
                  <rect
                    fill="rgb(217,157,38)"
                    height="18"
                  />
                  <title>
                    root (100.000%, 9 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="960"
                  >

                  </foreignobject>
                </g>
                <g
                  class="frame"
                  height="18"
                  name="root.node1"
                  transform="translate(0,54)"
                  width="533.3333333333334"
                >
                  <rect
                    fill="rgb(218,156,37)"
                    height="18"
                  />
                  <title>
                    root.node1 (55.556%, 5 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="533.3333333333334"
                  >

                  </foreignobject>
                </g>
                <g
                  class="frame"
                  height="18"
                  name="root.node2"
                  transform="translate(533.3333333333334,54)"
                  width="426.66666666666663"
                >
                  <rect
                    fill="rgb(218,156,37)"
                    height="18"
                  />
                  <title>
                    root.node2 (44.444%, 4 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="426.66666666666663"
                  >

                  </foreignobject>
                </g>
                <g
                  class="frame"
                  height="18"
                  name="root.node1.node1"
                  transform="translate(0,36)"
                  width="320.00000000000006"
                >
                  <rect
                    fill="rgb(218,156,37)"
                    height="18"
                  />
                  <title>
                    root.node1.node1 (33.333%, 3 samples)
                  </title>
                  <foreignobject
                    height="18"
                    width="320.00000000000006"
                  >

                  </foreignobject>
                </g>
              </svg>

        `)
    })
})
<|endoftext|>