feat: added more docs/examples, cleanup of parse value, fix setting of default timeout and interval

This commit is contained in:
niftylettuce
2020-07-10 08:02:46 -05:00
parent a292778ba1
commit 17b297f46c
11 changed files with 198 additions and 57 deletions
+105 -30
View File
@@ -1,14 +1,25 @@
# [**bree**](https://github.com/breejs/bree)
[![build status](https://img.shields.io/travis/com/breejs/bree.svg)](https://travis-ci.com/breejs/bree)
[![code coverage](https://img.shields.io/codecov/c/github/breejs/bree.svg)](https://codecov.io/gh/breejs/bree)
[![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org)
[![license](https://img.shields.io/github/license/breejs/bree.svg)](LICENSE)
[![npm downloads](https://img.shields.io/npm/dt/bree.svg)](https://npm.im/bree)
> The best job scheduler for [Node.js][node] with support for [cron][], [ms][], and [human-friendly][human-interval] strings. Uses [workers][] and spawns sandboxed processes. Supports [async/await][async-await], [retries][p-retry], [throttling][p-throttle], [concurrency](#concurrency), and [cancelable][p-cancelable] jobs (graceful shutdown). Simple, fast, and the most lightweight tool for the job. Made for [Forward Email][forward-email] and [Lad][].
<h1 align="center">
<a href="https://jobscheduler.net"><img src="https://d1i8ikybhfrv4r.cloudfront.net/bree/bree.png" alt="bree" /></a>
</h1>
<div align="center">
<a href="https://slack.crocodilejs.com"><img src="https://slack.crocodilejs.com/badge.svg" alt="chat" /></a>
<a href="https://travis-ci.com/breejs/bree"><img src="https://travis-ci.com/breejs/bree.svg?branch=master" alt="build status" /></a>
<a href="https://codecov.io/github/breejs/bree"><img src="https://img.shields.io/codecov/c/github/breejs/bree/master.svg" alt="code coverage" /></a>
<a href="https://github.com/sindresorhus/xo"><img src="https://img.shields.io/badge/code_style-XO-5ed9c7.svg" alt="code style" /></a>
<a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/styled_with-prettier-ff69b4.svg" alt="styled with prettier" /></a>
<a href="https://lass.js.org"><img src="https://img.shields.io/badge/made_with-lass-95CC28.svg" alt="made with lass" /></a>
<a href="LICENSE"><img src="https://img.shields.io/github/license/breejs/bree.svg" alt="license" /></a>
</div>
<br />
<div align="center">
Bree is the best job scheduler for <a href="#">Node.js</a> with support for <a href="#">cron</a>, dates, <a href="#">ms</a>, <a href="#">later</a>, and <a href="#">human-friendly</a> strings.
</div>
<hr />
<div align="center">
Uses <a href="https://nodejs.org/api/worker_threads.html">workers</a> to spawn sandboxed processes, and supports <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function">async/await</a>, <a href="https://github.com/sindresorhus/p-retry">retries</a>, <a href="https://github.com/sindresorhus/p-throttle">throttling</a>, <a href="#concurrency">concurrency</a>, and <a href="#cancellation-retries-stalled-jobs-and-graceful-reloading">cancelable jobs with graceful shutdown</a>. Simple, fast, and the most lightweight tool for the job. Made for <a href="https://forwardemail.net">Forward Email</a> and <a href="https://lad.js.org">Lad</a>.
</div>
<hr />
<div align="center">:heart: Love this project? Support <a href="https://github.com/niftylettuce" target="_blank">@niftylettuce's</a> <a href="https://en.wikipedia.org/wiki/Free_and_open-source_software" target="_blank">FOSS</a> on <a href="https://patreon.com/niftylettuce" target="_blank">Patreon</a> or <a href="https://paypal.me/niftylettuce">PayPal</a> :unicorn:</div>
## Table of Contents
@@ -16,8 +27,11 @@
* [Foreword](#foreword)
* [Install](#install)
* [Usage and Examples](#usage-and-examples)
* [Instance Options](#instance-options)
* [Job Options](#job-options)
* [Job Interval and Timeout Values](#job-interval-and-timeout-values)
* [Cancellation, Retries, Stalled Jobs, and Graceful Reloading](#cancellation-retries-stalled-jobs-and-graceful-reloading)
* [Interval, Timeout, and Cron Validation](#interval-timeout-and-cron-validation)
* [Interval, Timeout, Date, and Cron Validation](#interval-timeout-date-and-cron-validation)
* [Writing jobs with Promises and async-await](#writing-jobs-with-promises-and-async-await)
* [Callbacks, Done, and Completion States](#callbacks-done-and-completion-states)
* [Long-running jobs](#long-running-jobs)
@@ -60,10 +74,12 @@ yarn add bree
The example below assumes that you have a directory `jobs` in the root of the directory from which you run this example. For example, if the example below is at `/path/to/script.js`, then `/path/to/jobs/` must also exist as a directory. If you wish to disable this feature, then pass `root: false` as an option.
Inside this `jobs` directory are individual scripts which are run using [Workers][] per optional timeouts, and additionally, an optional interval or cron expression. Examples to help clarify this are provided in the code snippet below.
Inside this `jobs` directory are individual scripts which are run using [Workers][] per optional timeouts, and additionally, an optional interval or cron expression. The example below contains comments, which help to clarify how this works.
The option `jobs` passed to a new instance of `Bree` (as shown below) is an Array. It contains values which can either be a String (name of a job in the `jobs` directory, which is run on boot) OR it can be an Object with `name`, `path`, `timeout`, and `interval` properties. If you do not supply a `path`, then the path is created using the root directory (defaults to `jobs`) in combination with the `name`. If you do not supply values for `timeout` and/nor `interval`, then these values are defaulted to `0` (which is the default for both, see [index.js](index.js) for more insight into configurable default options).
We have also documented all [Instance Options](#instance-options) and [Job Options](#job-options) in this README below. Be sure to read those sections so you have a complete understanding of how Bree works.
```js
const path = require('path');
@@ -77,7 +93,8 @@ const Cabin = require('cabin');
const Bree = require('bree');
//
// NOTE: see index.js for full list of options and defaults
// NOTE: see the "Instance Options" section below in this README
// for the complete list of options and their defaults
//
const bree = new Bree({
//
@@ -96,21 +113,22 @@ const bree = new Bree({
// doing so will allow you to keep your job configuration and the jobs
// themselves all in the same folder and very organized
//
// See the "Job Options" section below in this README
// for the complete list of job options and configurations
//
jobs: [
// runs `./jobs/foo.js` on start
'foo',
// runs `./jobs/foo-bar.js` on start
{
name: 'foo-bar',
timeout: 0
name: 'foo-bar'
},
// runs `./jobs/some-other-path.js` on start
{
name: 'beep',
path: path.join(__dirname, 'jobs', 'some-other-path'),
timeout: 0
path: path.join(__dirname, 'jobs', 'some-other-path')
},
// runs `./jobs/worker-1.js` on the last day of the month
@@ -137,7 +155,7 @@ const bree = new Bree({
cron: '15 10 ? * *'
},
// runs `./jobs/worker-5.js` on start after 10 minutes have elapsed
// runs `./jobs/worker-5.js` on after 10 minutes have elapsed
{
name: 'worker-5',
timeout: '10m'
@@ -198,14 +216,12 @@ const bree = new Bree({
// runs `./jobs/worker-13.js` on start and every 2 minutes
{
name: 'worker-13',
timeout: 0,
interval: '2m'
},
// runs `./jobs/worker-14.js` on start with custom `new Worker` options (see below)
{
name: 'worker-14',
timeout: 0,
// <https://nodejs.org/api/worker_threads.html#worker_threads_new_worker_filename_options>
worker: {
workerData: {
@@ -214,6 +230,21 @@ const bree = new Bree({
}
}
}
// runs `./jobs/worker-15.js` **NOT** on start, but every 2 minutes
{
name: 'worker-15',
timeout: false, // <-- specify `false` here to prevent default timeout (e.g. on start)
interval: '2m'
},
// runs `./jobs/worker-16.js` on January 1st, 2022
// and at midnight on the 1st of every month thereafter
{
name: 'worker-16',
date: dayjs('1-1-2022', 'M-D-YYYY').toDate(),
cron: '0 0 1 * *'
}
]
});
@@ -243,6 +274,52 @@ bree.run('beep');
```
## Instance Options
Here is the full list of options and their defaults. See [index.js](index.js) for more insight if necessary.
| Property | Type | Default Value | Description |
| ---------------------- | ------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `logger` | Object | `console` | This is the default logger. **We recommend using [Cabin][cabin]** instead of using `console` as your default logger. |
| `root` | String | `path.resolve('jobs')` | Set this value to `false` to prevent requiring a root directory of jobs (e.g. if your jobs are not all in one directory). |
| `timeout` | Number | `0` | Default timeout for jobs (e.g. a value of `0` means that jobs will start on boot by default unless a job has a property of `timeout` defined. Set this to `false` if you do not wish for a default value to be set for jobs. **This value does not apply to jobs with a property of `date` nor `cron`**. |
| `interval` | Number | `0` | Default interval for jobs (e.g. a value of `0` means that there is no interval, and a value greater than zero indicates a default interval will be set with this value). **This value does not apply to jobs with a property of `date` nor `cron`.** |
| `jobs` | Array | `[]` | Defaults to an empty Array, but if the `root` directory has a `index.js` file, then it will be used. This allows you to keep your jobs and job definition index in the same place. See [Job Options](#job-options) below, and [Usage and Examples](#usage-and-examples) above for more insight. |
| `hasSeconds` | Boolean | `false` | This value is passed to `later` for parsing jobs, and can be overriden on a per job basis. See [later cron parsing](https://bunkat.github.io/later/parsers.html#cron) documentation for more insight. |
| `cronValidate` | Object | `{}` | This value is passed to `cron-validate` for validation of cron expressions. See the [cron-validate](https://github.com/Airfooox/cron-validate) documentation for more insight. |
| `closeWorkerAfterMs` | Number | `0` | If you set a value greater than `0` here, then it will terminate workers after this specified time (in milliseconds). By default there is no termination done, and jobs can run for infinite periods of time. |
| `defaultExtension` | String | `js` | This value can either be `js` or `mjs`. The default is `js`, and is the default extension added to jobs that are simply defined with a name and without a path. For example, if you define a job `test`, then it will look for `/path/to/root/test.js` as the file used for workers. |
| `worker` | Object | `{}` | These are default options to pass when creating a `new Worker` instance. See the [Worker class](https://nodejs.org/api/worker_threads.html#worker_threads_new_worker_filename_options) documentation for more insight. |
| `outputWorkerMetadata` | Boolean | `false` | By default worker metadata is not passed to the second Object argument of `logger`. However if you set this to `true`, then `logger` will be invoked internally with two arguments (e.g. `logger.info('...', { worker: ... })`). This `worker` property contains `isMainThread` (Boolean), `resourceLimits` (Object), `threadId` (String), and `workerData` (Object) properties; all of which correspond to [Workers][] metadata. This can be overriden on a per job basis. |
## Job Options
See [Interval, Timeout, Date, and Cron Validate](#interval-timeout-date-and-cron-validation) below for more insight besides this table:
| Property | Type | Description |
| ---------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | String | The name of the job. This should match the base file path (e.g. `foo` if `foo.js` is located at `/path/to/jobs/foo.js`) unless `path` option is specified. A value of `index`, `index.js`, and `index.mjs` are reserved values and cannot be used here. |
| `path` | String | The path of the job used for spawning a new [Worker][workers] with. If not specified, then it defaults to the value for `name` plus the default file extension specified under [Instance Options](#instance-options). |
| `timeout` | Number, Object, String, or Boolean | Sets the duration in milliseconds before the job starts (it overrides the default inherited `timeout` as set in [Instance Options](#instance-options). A value of `0` indicates it will start immediately. This value can be a Number, String, or a Boolean of `false` (which indicates it will NOT inherit the default `timeout` from [Instance Options](#instance-options)). See [Job Interval and Timeout Values](#job-interval-and-timeout-values) below for more insight into how this value is parsed. |
| `interval` | Number, Object, or String | Sets the duration in milliseconds for the job to repeat itself, otherwise known as its interval (it overrides the default inherited `interval` as set in [Instance Options](#instance-options)). A value of `0` indicates it will not repeat and there will be no interval. If the value is greater than `0` then this value will be used as the interval. See [Job Interval and Timeout Values](#job-interval-and-timeout-values) below for more insight into how this value is parsed. |
| `date` | Date | This must be a valid JavaScript Date (we use `instance of Date` for comparison). If this value is in the past, then it is not run when jobs are started (or run manually). We recommend using [dayjs][] for creating this date, and then formatting it using the `toDate()` method (e.g. `dayjs().add('3, 'days').toDate()`). You could also use [moment][] or any other JavaScript date library, as long as you convert the value to a Date instance here. |
| `cron` | String | A cron expression to use as the job's interval, which is validated against [cron-validate][] and parsed by [later][]. |
| `hasSeconds` | Boolean | Overrides the [Instance Options](#instance-options) `hasSeconds` property if set. |
| `closeWorkerAfterMs` | Number | Overrides the [Instance Options](#instance-options) `closeWorkerAfterMs` property if set. |
| `worker` | Object | Overrides the [Instance Options](#instance-options) `worker` property if set. |
| `outputWorkerMetadata` | Boolean | Overrides the [Instance Options](#instance-options) `outputWorkerMetadata` property if set. |
## Job Interval and Timeout Values
These values can include Number, Object, and String variable types:
* Number values indicates the number of milliseconds for the timeout or interval
* Object values must be a [later][] schedule object value (e.g. `later.schedule(later.parse.cron('15 10 * * ? *')))`)
* String values can be either a [human-interval][] String or a [ms][] value (e.g. either [human-interval][] supports Strings such as `3 days and 4 hours`, and [ms][] supports short, human-friendly Strings such as `4h` for four hours)
## Cancellation, Retries, Stalled Jobs, and Graceful Reloading
We recommend that you listen for "cancel" event in your worker paths. Doing so will allow you to handle graceful cancellation of jobs. For example, you could use [p-cancelable][]
@@ -279,7 +356,7 @@ We leave it up to you to have as much fine-grained control as you wish.
See [@ladjs/graceful][lad-graceful] for more insight into how this package works.
## Interval, Timeout, and Cron Validation
## Interval, Timeout, Date, and Cron Validation
If you need help writing cron expressions, you can reference [crontab.guru](https://crontab.guru/).
@@ -393,6 +470,8 @@ Kudos to the authors of all these packages, however they did not work well enoug
##
<a href="#"><img src="https://d1i8ikybhfrv4r.cloudfront.net/bree/footer.png" alt="#" /></a>
[ms]: https://github.com/vercel/ms
[human-interval]: https://github.com/agenda/human-interval
@@ -433,14 +512,10 @@ Kudos to the authors of all these packages, however they did not work well enoug
[redis]: https://redis.io/
[p-throttle]: https://github.com/sindresorhus/p-throttle
[cron]: https://en.wikipedia.org/wiki/Cron
[node]: https://nodejs.org
[async-await]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
[mongodb]: https://www.mongodb.com/
[lad-graceful]: https://github.com/ladjs/graceful
[cabin]: https://cabinjs.com
[moment]: https://momentjs.com
Executable
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+3 -3
View File
@@ -5,8 +5,8 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<!-- TODO: <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> -->
<!-- TODO: <link rel="icon" href="/favicon.ico" type="image/x-icon"> -->
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<title>The best job scheduler for Node.js</title>
<link rel="stylesheet" href="https://unpkg.com/docute@3/dist/docute.css" type="text/css">
@@ -14,7 +14,7 @@
<meta property="og:description" content="Bree is the best job scheduler for JavaScript and Node.js. Built-in support for workers, cron expression syntax, human-friendly times, Dates, and more. Made by @niftylettuce.">
<meta property="og:site_name" content="Bree">
<meta property="og:type" content="website">
<!-- TODO: <meta property="og:image" content="https://raw.githubusercontent.com/cabinjs/cabin/master/media/cabin.png"> -->
<meta property="og:image" content="https://raw.githubusercontent.com/breejs/bree/master/media/bree.png">
</head>
<body>
+58 -23
View File
@@ -21,22 +21,42 @@ class Bree {
// set this to `false` to prevent requiring a root directory of jobs
// (e.g. if your jobs are not all in one directory)
root: resolve('jobs'),
// default timeout for jobs
// (set this to `false` if you do not wish for a default timeout to be set)
timeout: 0,
// default interval for jobs
// (set this to `0` for no interval, and > 0 for a default interval to be set)
interval: 0,
// this is an Array of your job definitions (see README for examples)
jobs: [],
// <https://bunkat.github.io/later/parsers.html#cron>
// (can be overridden on a job basis with same prop name)
hasSeconds: false,
// <https://github.com/Airfooox/cron-validate>
cronValidate: {},
// if you set a value > 0 here, then it will kill workers after this time (ms)
// if you set a value > 0 here, then it will terminate workers after this time (ms)
closeWorkerAfterMs: 0,
// could also be mjs if desired (?)
// could also be mjs if desired
// (this is the default extension if you just specify a job's name without ".js" or ".mjs")
defaultExtension: 'js',
// default worker options to pass to `new Worker`
// (can be overriden on a per job basis)
// <https://nodejs.org/api/worker_threads.html#worker_threads_new_worker_filename_options>
worker: {},
//
// if you set this to `true`, then a second arg is passed to log output
// and it will be an Object with `{ worker: Object }` set, for example:
// (see the documentation at <https://nodejs.org/api/worker_threads.html> for more insight)
//
// logger.info('...', {
// worker: {
// isMainThread: Boolean
// resourceLimits: Object,
// threadId: String,
// workerData: Object
// }
// });
//
outputWorkerMetadata: false,
...config
};
@@ -61,11 +81,11 @@ class Bree {
}
// validate timeout
this.config.timeout = this.getTimeout(this.config.timeout);
this.config.timeout = this.parseValue(this.config.timeout);
debug('timeout', this.config.timeout);
// validate interval
this.config.interval = this.getInterval(this.config.interval);
this.config.interval = this.parseValue(this.config.interval);
debug('interval', this.config.interval);
//
@@ -234,7 +254,7 @@ class Bree {
// validate timeout
if (typeof job.timeout !== 'undefined') {
try {
this.config.jobs[i].timeout = this.getTimeout(job.timeout);
this.config.jobs[i].timeout = this.parseValue(job.timeout);
} catch (err) {
errors.push(
combineErrors([
@@ -248,7 +268,7 @@ class Bree {
// validate interval
if (typeof job.interval !== 'undefined') {
try {
this.config.jobs[i].interval = this.getInterval(job.interval);
this.config.jobs[i].interval = this.parseValue(job.interval);
} catch (err) {
errors.push(
combineErrors([
@@ -263,7 +283,7 @@ class Bree {
if (typeof job.cron !== 'undefined') {
if (this.isSchedule(job.cron)) {
this.config.jobs[i].interval = job.cron;
delete this.config.jobs[i].cron;
// delete this.config.jobs[i].cron;
} else {
//
// validate cron pattern
@@ -283,7 +303,7 @@ class Bree {
);
if (schedule.isValid()) {
this.config.jobs[i].interval = schedule;
delete this.config.jobs[i].cron;
// delete this.config.jobs[i].cron;
} else {
errors.push(
new Error(
@@ -310,6 +330,30 @@ class Bree {
errors.push(
`${prefix} had an invalid closeWorkersAfterMs value of ${job.closeWorkersAfterMs} (it must be a finite number > 0`
);
// if timeout was undefined, cron was undefined,
// and date was undefined then set the default
// (as long as the default timeout is >= 0)
if (
Number.isFinite(this.config.timeout) &&
this.config.timeout >= 0 &&
typeof this.config.jobs[i].timeout === 'undefined' &&
typeof job.cron === 'undefined' &&
typeof job.date === 'undefined'
)
this.config.jobs[i].timeout = this.config.timeout;
// if interval was undefined, cron was undefined,
// and date was undefined then set the default
// (as long as the default interval is > 0)
if (
Number.isFinite(this.config.interval) &&
this.config.interval > 0 &&
typeof this.config.jobs[i].interval === 'undefined' &&
typeof job.cron === 'undefined' &&
typeof job.date === 'undefined'
)
this.config.jobs[i].interval = this.config.interval;
}
// don't allow a job to have the `index` file name
@@ -336,7 +380,9 @@ class Bree {
return value;
}
getTimeout(value) {
parseValue(value) {
if (value === false) return value;
if (this.isSchedule(value)) return value;
if (isSANB(value)) {
@@ -353,25 +399,14 @@ class Bree {
return value;
}
getInterval(value) {
if (this.isSchedule(value)) return value;
if (isSANB(value)) {
const schedule = later.schedule(later.parse.text(value));
if (schedule.isValid()) return schedule;
value = this.getHumanToMs(value);
}
// will throw error re-using existing logic
return this.getTimeout(value);
}
isSchedule(value) {
return typeof value === 'object' && Array.isArray(value.schedules);
}
getWorkerMetadata(name, meta = {}) {
if (!this.config.outputWorkerMetadata) return;
const job = this.config.jobs.find((j) => j.name === name);
if (!job) throw new Error(`Job "${name}" does not exist`);
if (!this.config.outputWorkerMetadata && !job.outputWorkerMetadata) return;
return this.workers[name]
? {
...meta,
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

+7 -1
View File
@@ -1,6 +1,6 @@
{
"name": "bree",
"description": "The best job scheduler for Node.js with support for cron, ms, and human-friendly strings. Uses workers and spawns sandboxed processes. Supports async/await, retries, throttling, concurrency, and cancelable promises (graceful shutdown). Simple, fast, and the most lightweight tool for the job. Made for Lad.",
"description": "The best job scheduler for Node.js with support for cron, dates, ms, later, and human-friendly strings. Uses workers to spawn sandboxed processes, and supports async/await, retries, throttling, concurrency, and cancelable promises (graceful shutdown). Simple, fast, and the most lightweight tool for the job. Made for Forward Email and Lad.",
"version": "1.0.0",
"author": "Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)",
"ava": {
@@ -47,6 +47,9 @@
"engines": {
"node": ">= 12.11.0"
},
"files": [
"index.js"
],
"homepage": "https://github.com/breejs/bree",
"husky": {
"hooks": {
@@ -72,6 +75,7 @@
"cronjob",
"crontab",
"date",
"dates",
"day",
"dayjs",
"delay",
@@ -88,12 +92,14 @@
"interval",
"job",
"jobs",
"js",
"koa",
"koatiming",
"lad",
"lass",
"later",
"moment",
"momentjs",
"mongo",
"mongodb",
"mongoose",
+25
View File
@@ -24,3 +24,28 @@ test('creates a basic job and runs it', async (t) => {
t.true(typeof bree.workers.basic === 'undefined');
bree.stop();
});
test.todo(
'throws an error with an invalid root directory (e.g. directory does not exist)'
);
test.todo('does not throw an error when root directory option is set to false');
test.todo('job with just a name');
test.todo(
'job with an invalid name throws an error (e.g. no matching file path)'
);
test.todo(
'job with a name and an invalid file path throws an error (e.g. file does not exist)'
);
test.todo('job with a timeout');
test.todo('job with an interval');
test.todo('job with an interval uses the default timeout');
test.todo('job with a timeout and an interval');
test.todo('job with a false timeout and an interval');
test.todo('job with cron');
test.todo('job with date');
test.todo('job with date in the past does not run');
test.todo('job with cron that is invalid cron expression throws an error');
test.todo('job with custom worker instance options');
test.todo('job that combines date and cron');
test.todo('job that combines date and interval');
test.todo('job that combines timeout and cron');