Skip to content

Commit 3ced778

Browse files
committed
feat: Initial implementation
0 parents  commit 3ced778

File tree

10 files changed

+257
-0
lines changed

10 files changed

+257
-0
lines changed

.github/workflows/test.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Tests
2+
on: [push, pull_request]
3+
4+
env:
5+
CI: true
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
- name: Setup Node.js
13+
uses: actions/setup-node@v1
14+
with:
15+
node-version: 14
16+
- run: npm install
17+
- name: Lint
18+
run: npm run -s pretest
19+
- name: Tests
20+
run: npm run -s tests-only
21+
- name: Coverage
22+
run: npm run -s posttest

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.tgz
2+
node_modules
3+
coverage

.npmignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.tgz
2+
.github
3+
nyc.config.js
4+
coverage
5+
test.js

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 CFWare, LLC
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# @cfware/integration-instance-base [![NPM Version][npm-image]][npm-url]
2+
3+
Integration Instance Base
4+
5+
[npm-image]: https://img.shields.io/npm/v/@cfware/integration-instance-base.svg
6+
[npm-url]: https://npmjs.org/package/@cfware/integration-instance-base

index.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import fs from 'fs/promises';
2+
import EventEmitter from 'events';
3+
4+
import pMap from 'p-map';
5+
6+
export class IntegrationInstanceBase extends EventEmitter {
7+
#resolveStopped;
8+
#instancesPath;
9+
#instances = {};
10+
11+
get instances() {
12+
return Object.keys(this.#instances);
13+
}
14+
15+
get startedMessage() {
16+
return 'Started daemons';
17+
}
18+
19+
stopped = new Promise(resolve => {
20+
this.#resolveStopped = resolve;
21+
});
22+
23+
constructor(instancesPath, instances) {
24+
super();
25+
26+
this.#instancesPath = instancesPath;
27+
Object.assign(this.#instances, instances);
28+
Object.assign(this, instances);
29+
}
30+
31+
async build() {
32+
await fs.rmdir(this.#instancesPath, {recursive: true});
33+
this.emit('status', 'Building daemons');
34+
await pMap(
35+
Object.values(this.#instances),
36+
instance => instance.build()
37+
);
38+
}
39+
40+
async start(...args) {
41+
await this.build();
42+
43+
this.emit('status', 'Starting daemons');
44+
await pMap(
45+
Object.values(this.#instances),
46+
instance => instance.start(...args)
47+
);
48+
this.emit('status', this.startedMessage);
49+
}
50+
51+
async stop() {
52+
this.emit('status', 'Stopping daemons');
53+
await pMap(
54+
Object.values(this.#instances),
55+
instance => instance.stop()
56+
);
57+
this.emit('status', 'Stopped daemons');
58+
59+
this.#resolveStopped();
60+
}
61+
62+
async checkStopped() {
63+
await pMap(
64+
Object.values(this.#instances),
65+
instance => instance.checkStopped?.()
66+
);
67+
}
68+
}

nyc.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {default} from '@cfware/nyc';

package.json

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "@cfware/integration-instance-base",
3+
"version": "0.1.0",
4+
"description": "Integration Instance Base",
5+
"type": "module",
6+
"main": "index.js",
7+
"exports": "./index.js",
8+
"scripts": {
9+
"pretest": "cfware-lint .",
10+
"tests-only": "nyc -s node --experimental-loader=@istanbuljs/esm-loader-hook test.js",
11+
"test": "npm run -s tests-only",
12+
"posttest": "nyc report --check-coverage"
13+
},
14+
"engines": {
15+
"node": ">=14.0.0"
16+
},
17+
"author": "Corey Farrell",
18+
"license": "MIT",
19+
"repository": {
20+
"type": "git",
21+
"url": "git+https://github.com/cfware/integration-instance-base.git"
22+
},
23+
"bugs": {
24+
"url": "https://github.com/cfware/integration-instance-base/issues"
25+
},
26+
"homepage": "https://github.com/cfware/integration-instance-base#readme",
27+
"dependencies": {
28+
"p-map": "^4.0.0"
29+
},
30+
"devDependencies": {
31+
"@cfware/lint": "^2.0.3",
32+
"@cfware/nyc": "^0.7.0",
33+
"@istanbuljs/esm-loader-hook": "^0.1.2",
34+
"libtap": "^0.3.0",
35+
"nyc": "^15.1.0"
36+
}
37+
}

test.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import fs from 'fs/promises';
2+
import path from 'path';
3+
4+
import t from 'libtap';
5+
import {IntegrationInstanceBase} from '@cfware/integration-instance-base';
6+
7+
class CounterInstance {
8+
counts = {
9+
build: 0,
10+
start: [],
11+
stop: 0,
12+
checkStopped: 0
13+
};
14+
15+
async build() {
16+
this.counts.build++;
17+
}
18+
19+
async start(...args) {
20+
this.counts.start.push(args);
21+
}
22+
23+
async stop() {
24+
this.counts.stop++;
25+
}
26+
27+
async checkStopped() {
28+
this.counts.checkStopped++;
29+
}
30+
}
31+
32+
t.type(IntegrationInstanceBase, 'function');
33+
34+
t.test('counters', async t => {
35+
const statuses = [];
36+
await fs.mkdir('instances', {recursive: true});
37+
38+
const counter1 = new CounterInstance();
39+
const counter2 = new CounterInstance();
40+
const integration = new IntegrationInstanceBase(path.resolve('instances'), {counter1, counter2});
41+
integration.on('status', status => statuses.push(status));
42+
t.same(integration.instances, ['counter1', 'counter2']);
43+
t.equal(integration.counter1, counter1);
44+
t.equal(integration.counter2, counter2);
45+
t.equal(integration.startedMessage, 'Started daemons');
46+
// eslint-disable-next-line promise/prefer-await-to-then
47+
integration.stopped.then(() => {
48+
statuses.push('resolved stopped');
49+
});
50+
51+
const counts = {
52+
...integration.counter1.counts,
53+
start: []
54+
};
55+
const checkCounts = () => {
56+
t.same(integration.counter1.counts, counts);
57+
t.same(integration.counter2.counts, counts);
58+
};
59+
60+
checkCounts();
61+
62+
await integration.build();
63+
counts.build++;
64+
checkCounts();
65+
t.same(statuses, ['Building daemons']);
66+
statuses.length = 0;
67+
68+
await integration.start('arg1', 'arg2');
69+
counts.build++;
70+
counts.start.push(['arg1', 'arg2']);
71+
checkCounts();
72+
t.same(statuses, [
73+
'Building daemons',
74+
'Starting daemons',
75+
integration.startedMessage
76+
]);
77+
statuses.length = 0;
78+
79+
await integration.stop();
80+
counts.stop++;
81+
checkCounts();
82+
t.same(statuses, [
83+
'Stopping daemons',
84+
'Stopped daemons',
85+
'resolved stopped'
86+
]);
87+
statuses.length = 0;
88+
89+
await integration.checkStopped();
90+
counts.checkStopped++;
91+
checkCounts();
92+
t.same(statuses, []);
93+
});

0 commit comments

Comments
 (0)