balanceofsatoshis 23.1.7 → 23.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/bos +2 -1
- package/package.json +1 -1
- package/swaps/get_avoid_list.js +53 -0
- package/swaps/manage_rebalance.js +71 -37
- package/swaps/write_avoid_list.js +68 -0
- package/test/swaps/test_get_avoid_list.js +62 -0
- package/test/swaps/test_write_avoid_list.js +83 -0
package/CHANGELOG.md
CHANGED
package/bos
CHANGED
|
@@ -4,6 +4,7 @@ const {lstat} = require('fs');
|
|
|
4
4
|
const {mkdir} = require('fs');
|
|
5
5
|
const {readdir} = require('fs');
|
|
6
6
|
const {readFile} = require('fs');
|
|
7
|
+
const {rename} = require('fs');
|
|
7
8
|
const {rmdir} = require('fs');
|
|
8
9
|
const {spawn} = require('child_process');
|
|
9
10
|
const {statSync} = require('fs');
|
|
@@ -1752,7 +1753,7 @@ prog
|
|
|
1752
1753
|
avoid: flatten([options.avoid].filter(n => !!n)),
|
|
1753
1754
|
avoid_append: options.avoidAppend || undefined,
|
|
1754
1755
|
avoid_list: options.avoidList || undefined,
|
|
1755
|
-
fs: {appendFile, writeFile, getFile: readFile},
|
|
1756
|
+
fs: {appendFile, writeFile, getFile: readFile, renameFile: rename},
|
|
1756
1757
|
in_filters: flatten([options.inFilter].filter(n => !!n)),
|
|
1757
1758
|
in_outbound: options.inTargetOutbound || undefined,
|
|
1758
1759
|
in_through: options.in || undefined,
|
package/package.json
CHANGED
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"postpublish": "docker buildx build --platform linux/amd64,linux/arm64 -t alexbosworth/balanceofsatoshis -t alexbosworth/balanceofsatoshis:$npm_package_version --push .",
|
|
84
84
|
"test": "node --experimental-test-coverage --test test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js"
|
|
85
85
|
},
|
|
86
|
-
"version": "23.1.
|
|
86
|
+
"version": "23.1.8"
|
|
87
87
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const matchNewLines = /\r?\n/;
|
|
5
|
+
const trim = n => n.trim();
|
|
6
|
+
const uniq = arr => Array.from(new Set(arr));
|
|
7
|
+
|
|
8
|
+
/** Get the list of avoid directives from an avoid list file
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
fs: {
|
|
12
|
+
getFile: <Read File Contents Function> (path, cbk) => {}
|
|
13
|
+
}
|
|
14
|
+
path: <Path To Avoid List File String>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@returns via cbk or Promise
|
|
18
|
+
{
|
|
19
|
+
lines: [<Avoid Directive Line String>]
|
|
20
|
+
}
|
|
21
|
+
*/
|
|
22
|
+
module.exports = ({fs, path}, cbk) => {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
return asyncAuto({
|
|
25
|
+
// Check arguments
|
|
26
|
+
validate: cbk => {
|
|
27
|
+
if (!fs) {
|
|
28
|
+
return cbk([400, 'ExpectedFilesystemMethodsToGetAvoidList']);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!path) {
|
|
32
|
+
return cbk([400, 'ExpectedPathToAvoidListFileToGetAvoidList']);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return cbk();
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// Get the lines of the avoid list file
|
|
39
|
+
getList: ['validate', ({}, cbk) => {
|
|
40
|
+
return fs.getFile(path, (err, res) => {
|
|
41
|
+
if (!!err) {
|
|
42
|
+
return cbk([503, 'UnexpectedErrorGettingAvoidListFile', {err}]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const lines = res.toString().split(matchNewLines).map(trim);
|
|
46
|
+
|
|
47
|
+
return cbk(null, {lines: uniq(lines.filter(n => !!n))});
|
|
48
|
+
});
|
|
49
|
+
}],
|
|
50
|
+
},
|
|
51
|
+
returnResult({reject, resolve, of: 'getList'}, cbk));
|
|
52
|
+
});
|
|
53
|
+
};
|
|
@@ -2,18 +2,18 @@ const asyncAuto = require('async/auto');
|
|
|
2
2
|
const asyncFilter = require('async/filter');
|
|
3
3
|
const asyncRetry = require('async/retry');
|
|
4
4
|
const {getChannel} = require('ln-service');
|
|
5
|
+
const {getWalletInfo} = require('ln-service');
|
|
5
6
|
const {returnResult} = require('asyncjs-util');
|
|
6
7
|
|
|
7
8
|
const appendFailingEdge = require('./append_failing_edge');
|
|
9
|
+
const getAvoidList = require('./get_avoid_list');
|
|
8
10
|
const rebalance = require('./rebalance');
|
|
11
|
+
const writeAvoidList = require('./write_avoid_list');
|
|
9
12
|
|
|
10
13
|
const channelFromEdge = edge => edge.slice(0, -2);
|
|
11
14
|
const codeMissingChannel = 404;
|
|
12
15
|
const {isArray} = Array;
|
|
13
16
|
const isEdge = n => /^\d*x\d*x\d*x(0|1)*$/.test(n);
|
|
14
|
-
const joinWithNewLines = lines => lines.join('\n');
|
|
15
|
-
const matchNewLines = /\r?\n/;
|
|
16
|
-
const uniq = arr => Array.from(new Set(arr));
|
|
17
17
|
|
|
18
18
|
/** Manage rebalance attempts
|
|
19
19
|
|
|
@@ -24,6 +24,7 @@ const uniq = arr => Array.from(new Set(arr));
|
|
|
24
24
|
fs: {
|
|
25
25
|
appendFile: <Append to File Function> (path, content, cbk) => {}
|
|
26
26
|
getFile: <Read File Contents Function> (path, cbk) => {}
|
|
27
|
+
renameFile: <Rename File Function> (from, to, cbk) => {}
|
|
27
28
|
writeFile: <Write File Contents Function> (path, contents, cbk) => {}
|
|
28
29
|
}
|
|
29
30
|
[in_filters]: [<Inbound Filter Formula String>]
|
|
@@ -76,31 +77,28 @@ module.exports = (args, cbk) => {
|
|
|
76
77
|
return cbk();
|
|
77
78
|
},
|
|
78
79
|
|
|
79
|
-
// Get the avoid
|
|
80
|
+
// Get the avoid directives from the avoid list file
|
|
80
81
|
getAvoids: ['validate', ({}, cbk) => {
|
|
81
82
|
// Exit early when there is no ignore list
|
|
82
83
|
if (!args.avoid_list) {
|
|
83
|
-
return cbk(null, {
|
|
84
|
+
return cbk(null, {lines: []});
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
return getAvoidList({
|
|
88
|
+
fs: {getFile: args.fs.getFile},
|
|
89
|
+
path: args.avoid_list,
|
|
90
|
+
},
|
|
91
|
+
cbk);
|
|
92
|
+
}],
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
// Get the graph sync status to know if missing channels are reliable
|
|
95
|
+
getGraphSyncStatus: ['validate', ({}, cbk) => {
|
|
96
|
+
// Exit early when there is no ignore list to clean
|
|
97
|
+
if (!args.avoid_list) {
|
|
98
|
+
return cbk();
|
|
99
|
+
}
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
file,
|
|
100
|
-
original,
|
|
101
|
-
avoid: file.concat(args.avoid || []),
|
|
102
|
-
});
|
|
103
|
-
});
|
|
101
|
+
return getWalletInfo({lnd: args.lnd}, cbk);
|
|
104
102
|
}],
|
|
105
103
|
|
|
106
104
|
// Create failing edge logger for avoid appending
|
|
@@ -133,8 +131,17 @@ module.exports = (args, cbk) => {
|
|
|
133
131
|
}],
|
|
134
132
|
|
|
135
133
|
// Look at all of the lines in the file and clean them up
|
|
136
|
-
getCleanAvoids: [
|
|
137
|
-
|
|
134
|
+
getCleanAvoids: [
|
|
135
|
+
'getAvoids',
|
|
136
|
+
'getGraphSyncStatus',
|
|
137
|
+
({getAvoids, getGraphSyncStatus}, cbk) =>
|
|
138
|
+
{
|
|
139
|
+
// Exit early with no info when missing channels may be resurrected
|
|
140
|
+
if (!!getGraphSyncStatus && !getGraphSyncStatus.is_synced_to_graph) {
|
|
141
|
+
return cbk();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return asyncFilter(getAvoids.lines, (line, cbk) => {
|
|
138
145
|
// Exit early when not looking at an edge
|
|
139
146
|
if (!isEdge(line)) {
|
|
140
147
|
return cbk(null, true);
|
|
@@ -186,7 +193,7 @@ module.exports = (args, cbk) => {
|
|
|
186
193
|
cbk => {
|
|
187
194
|
return rebalance({
|
|
188
195
|
start,
|
|
189
|
-
avoid: getAvoids.avoid,
|
|
196
|
+
avoid: getAvoids.lines.concat(args.avoid || []),
|
|
190
197
|
fs: args.fs,
|
|
191
198
|
in_filters: args.in_filters,
|
|
192
199
|
in_outbound: args.in_outbound,
|
|
@@ -208,31 +215,58 @@ module.exports = (args, cbk) => {
|
|
|
208
215
|
cbk);
|
|
209
216
|
}],
|
|
210
217
|
|
|
211
|
-
//
|
|
212
|
-
|
|
218
|
+
// Determine which lines should be removed from the avoid list
|
|
219
|
+
removals: [
|
|
213
220
|
'getAvoids',
|
|
214
221
|
'getCleanAvoids',
|
|
215
222
|
({getAvoids, getCleanAvoids}, cbk) =>
|
|
216
223
|
{
|
|
217
|
-
// Exit early when
|
|
218
|
-
if (!
|
|
219
|
-
return cbk(null,
|
|
224
|
+
// Exit early with no removals when the clean check had no answer
|
|
225
|
+
if (!getCleanAvoids) {
|
|
226
|
+
return cbk(null, []);
|
|
220
227
|
}
|
|
221
228
|
|
|
222
|
-
|
|
229
|
+
// The clean avoids are the original lines minus missing channels
|
|
230
|
+
const keeping = new Set(getCleanAvoids);
|
|
223
231
|
|
|
224
|
-
//
|
|
225
|
-
|
|
232
|
+
// A line that was not kept refers to a channel absent from the graph
|
|
233
|
+
return cbk(null, getAvoids.lines.filter(n => !keeping.has(n)));
|
|
234
|
+
}],
|
|
235
|
+
|
|
236
|
+
// Get the avoid list again to preserve concurrently appended lines
|
|
237
|
+
getCurrentAvoids: ['removals', ({removals}, cbk) => {
|
|
238
|
+
// Exit early when no lines were cleaned out of the avoid list
|
|
239
|
+
if (!removals.length) {
|
|
226
240
|
return cbk();
|
|
227
241
|
}
|
|
228
242
|
|
|
229
|
-
return
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
243
|
+
return getAvoidList({
|
|
244
|
+
fs: {getFile: args.fs.getFile},
|
|
245
|
+
path: args.avoid_list,
|
|
246
|
+
},
|
|
247
|
+
cbk);
|
|
248
|
+
}],
|
|
233
249
|
|
|
250
|
+
// Write a cleaned up avoid list
|
|
251
|
+
writeCleanAvoidList: [
|
|
252
|
+
'getCurrentAvoids',
|
|
253
|
+
'removals',
|
|
254
|
+
({getCurrentAvoids, removals}, cbk) =>
|
|
255
|
+
{
|
|
256
|
+
// Exit early when there is no cleaned avoid list to write out
|
|
257
|
+
if (!getCurrentAvoids) {
|
|
234
258
|
return cbk();
|
|
235
|
-
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const removing = new Set(removals);
|
|
262
|
+
|
|
263
|
+
// Atomically write the avoid list without the cleaned out lines
|
|
264
|
+
return writeAvoidList({
|
|
265
|
+
fs: {renameFile: args.fs.renameFile, writeFile: args.fs.writeFile},
|
|
266
|
+
lines: getCurrentAvoids.lines.filter(n => !removing.has(n)),
|
|
267
|
+
path: args.avoid_list,
|
|
268
|
+
},
|
|
269
|
+
cbk);
|
|
236
270
|
}],
|
|
237
271
|
},
|
|
238
272
|
returnResult({reject, resolve, of: 'rebalance'}, cbk));
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const {isArray} = Array;
|
|
5
|
+
const joinWithNewLines = lines => lines.join('\n');
|
|
6
|
+
const tempFile = path => `${path}.${process.pid}`;
|
|
7
|
+
|
|
8
|
+
/** Atomically write the lines of an avoid list file
|
|
9
|
+
|
|
10
|
+
The list is written to a temporary file that is swapped into place so that
|
|
11
|
+
concurrent readers of the list never see a partially written file.
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
fs: {
|
|
15
|
+
renameFile: <Rename File Function> (from, to, cbk) => {}
|
|
16
|
+
writeFile: <Write File Contents Function> (path, contents, cbk) => {}
|
|
17
|
+
}
|
|
18
|
+
lines: [<Avoid Directive Line String>]
|
|
19
|
+
path: <Path To Avoid List File String>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@returns via cbk or Promise
|
|
23
|
+
*/
|
|
24
|
+
module.exports = ({fs, lines, path}, cbk) => {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
return asyncAuto({
|
|
27
|
+
// Check arguments
|
|
28
|
+
validate: cbk => {
|
|
29
|
+
if (!fs) {
|
|
30
|
+
return cbk([400, 'ExpectedFilesystemMethodsToWriteAvoidList']);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!isArray(lines)) {
|
|
34
|
+
return cbk([400, 'ExpectedAvoidLinesToWriteAvoidList']);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!path) {
|
|
38
|
+
return cbk([400, 'ExpectedPathToAvoidListFileToWriteAvoidList']);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return cbk();
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Write the list to a temporary file next to the avoid list
|
|
45
|
+
write: ['validate', ({}, cbk) => {
|
|
46
|
+
return fs.writeFile(tempFile(path), joinWithNewLines(lines), err => {
|
|
47
|
+
if (!!err) {
|
|
48
|
+
return cbk([503, 'UnexpectedErrorWritingAvoidList', {err}]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return cbk();
|
|
52
|
+
});
|
|
53
|
+
}],
|
|
54
|
+
|
|
55
|
+
// Atomically swap the temporary file in place of the avoid list
|
|
56
|
+
swap: ['write', ({}, cbk) => {
|
|
57
|
+
return fs.renameFile(tempFile(path), path, err => {
|
|
58
|
+
if (!!err) {
|
|
59
|
+
return cbk([503, 'UnexpectedErrorReplacingAvoidList', {err}]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return cbk();
|
|
63
|
+
});
|
|
64
|
+
}],
|
|
65
|
+
},
|
|
66
|
+
returnResult({reject, resolve}, cbk));
|
|
67
|
+
});
|
|
68
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const {deepEqual} = require('node:assert').strict;
|
|
2
|
+
const {rejects} = require('node:assert').strict;
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
|
|
5
|
+
const getAvoidList = require('./../../swaps/get_avoid_list.js');
|
|
6
|
+
|
|
7
|
+
const makeFs = ({content, err} = {}) => {
|
|
8
|
+
return {getFile: (path, cbk) => cbk(err, Buffer.from(content || ''))};
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const makeArgs = override => {
|
|
12
|
+
const args = {
|
|
13
|
+
fs: makeFs({content: '\n1x1x1x0\n b \n\na\r\n1x1x1x0\nc'}),
|
|
14
|
+
path: '/tmp/avoid',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
Object.keys(override).forEach(key => args[key] = override[key]);
|
|
18
|
+
|
|
19
|
+
return args;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const tests = [
|
|
23
|
+
{
|
|
24
|
+
args: makeArgs({fs: undefined}),
|
|
25
|
+
description: 'Filesystem methods are required',
|
|
26
|
+
error: [400, 'ExpectedFilesystemMethodsToGetAvoidList'],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
args: makeArgs({path: undefined}),
|
|
30
|
+
description: 'An avoid list path is required',
|
|
31
|
+
error: [400, 'ExpectedPathToAvoidListFileToGetAvoidList'],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
args: makeArgs({fs: makeFs({err: new Error('Failed to read')})}),
|
|
35
|
+
description: 'A read error is returned',
|
|
36
|
+
error: [503, 'UnexpectedErrorGettingAvoidListFile'],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
args: makeArgs({fs: makeFs({})}),
|
|
40
|
+
description: 'An empty file has no lines',
|
|
41
|
+
expected: {lines: []},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
args: makeArgs({}),
|
|
45
|
+
description: 'Lines are trimmed, deduplicated, and blanks are removed',
|
|
46
|
+
expected: {lines: ['1x1x1x0', 'b', 'a', 'c']},
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
tests.forEach(({args, description, error, expected}) => {
|
|
51
|
+
return test(description, async () => {
|
|
52
|
+
if (!!error) {
|
|
53
|
+
await rejects(getAvoidList(args), error, 'Got expected error');
|
|
54
|
+
} else {
|
|
55
|
+
const result = await getAvoidList(args);
|
|
56
|
+
|
|
57
|
+
deepEqual(result, expected, 'Got expected result');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return;
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const {deepEqual} = require('node:assert').strict;
|
|
2
|
+
const {rejects} = require('node:assert').strict;
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
|
|
5
|
+
const writeAvoidList = require('./../../swaps/write_avoid_list.js');
|
|
6
|
+
|
|
7
|
+
const temp = `/tmp/avoid.${process.pid}`;
|
|
8
|
+
|
|
9
|
+
const makeFs = ({renameErr, writeErr} = {}) => {
|
|
10
|
+
const fs = {
|
|
11
|
+
calls: [],
|
|
12
|
+
renameFile: (from, to, cbk) => {
|
|
13
|
+
fs.calls.push({from, to});
|
|
14
|
+
|
|
15
|
+
return cbk(renameErr);
|
|
16
|
+
},
|
|
17
|
+
writeFile: (path, contents, cbk) => {
|
|
18
|
+
fs.calls.push({contents, path});
|
|
19
|
+
|
|
20
|
+
return cbk(writeErr);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return fs;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const makeArgs = override => {
|
|
28
|
+
const args = {fs: makeFs({}), lines: ['a', 'b'], path: '/tmp/avoid'};
|
|
29
|
+
|
|
30
|
+
Object.keys(override).forEach(key => args[key] = override[key]);
|
|
31
|
+
|
|
32
|
+
return args;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const tests = [
|
|
36
|
+
{
|
|
37
|
+
args: makeArgs({fs: undefined}),
|
|
38
|
+
description: 'Filesystem methods are required',
|
|
39
|
+
error: [400, 'ExpectedFilesystemMethodsToWriteAvoidList'],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
args: makeArgs({lines: undefined}),
|
|
43
|
+
description: 'Avoid lines are required',
|
|
44
|
+
error: [400, 'ExpectedAvoidLinesToWriteAvoidList'],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
args: makeArgs({path: undefined}),
|
|
48
|
+
description: 'An avoid list path is required',
|
|
49
|
+
error: [400, 'ExpectedPathToAvoidListFileToWriteAvoidList'],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
args: makeArgs({fs: makeFs({writeErr: new Error('WriteFailed')})}),
|
|
53
|
+
description: 'A write error is returned',
|
|
54
|
+
error: [503, 'UnexpectedErrorWritingAvoidList'],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
args: makeArgs({fs: makeFs({renameErr: new Error('RenameFailed')})}),
|
|
58
|
+
description: 'A rename error is returned',
|
|
59
|
+
error: [503, 'UnexpectedErrorReplacingAvoidList'],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
args: makeArgs({}),
|
|
63
|
+
calls: [
|
|
64
|
+
{contents: 'a\nb', path: temp},
|
|
65
|
+
{from: temp, to: '/tmp/avoid'},
|
|
66
|
+
],
|
|
67
|
+
description: 'The list is written to a temp file and swapped in place',
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
tests.forEach(({args, calls, description, error}) => {
|
|
72
|
+
return test(description, async () => {
|
|
73
|
+
if (!!error) {
|
|
74
|
+
await rejects(writeAvoidList(args), error, 'Got expected error');
|
|
75
|
+
} else {
|
|
76
|
+
await writeAvoidList(args);
|
|
77
|
+
|
|
78
|
+
deepEqual(args.fs.calls, calls, 'Got expected filesystem calls');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return;
|
|
82
|
+
});
|
|
83
|
+
});
|