Skip to content

Commit ec6ad04

Browse files
committed
Added bootstrapping tests including occupied node path and concurrent bootstrapping
1 parent 54f8be2 commit ec6ad04

2 files changed

Lines changed: 154 additions & 3 deletions

File tree

tests/bin/bootstrap.test.ts

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import type { RecoveryCode } from '@/keys/types';
21
import os from 'os';
32
import path from 'path';
43
import fs from 'fs';
54
import readline from 'readline';
65
import Logger, { LogLevel, StreamHandler } from '@matrixai/logger';
7-
import { Status, errors as statusErrors } from '@/status';
6+
import { errors as statusErrors } from '@/status';
87
import { errors as bootstrapErrors } from '@/bootstrap';
98
import * as binUtils from '@/bin/utils';
10-
import config from '@/config';
119
import * as testBinUtils from './utils';
1210

1311
describe('bootstrap', () => {
@@ -26,6 +24,157 @@ describe('bootstrap', () => {
2624
recursive: true,
2725
});
2826
});
27+
test(
28+
'bootstraps node state',
29+
async () => {
30+
const password = 'password';
31+
const passwordPath = path.join(dataDir, 'password');
32+
await fs.promises.writeFile(passwordPath, password);
33+
const { exitCode, stdout } = await testBinUtils.pkStdio(
34+
[
35+
'bootstrap',
36+
'--password-file',
37+
passwordPath,
38+
'--root-key-pair-bits',
39+
'1024',
40+
'--verbose',
41+
],
42+
{
43+
PK_NODE_PATH: path.join(dataDir, 'polykey'),
44+
},
45+
dataDir,
46+
);
47+
expect(exitCode).toBe(0);
48+
const recoveryCode = stdout.trim();
49+
expect(
50+
recoveryCode.split(' ').length === 12 ||
51+
recoveryCode.split(' ').length === 24,
52+
).toBe(true);
53+
},
54+
global.defaultTimeout,
55+
);
56+
test('bootstrapping occupied node state', async () => {
57+
const password = 'password';
58+
await fs.promises.mkdir(path.join(dataDir, 'polykey'));
59+
await fs.promises.writeFile(path.join(dataDir, 'polykey', 'test'), '');
60+
let exitCode, stdout, stderr;
61+
({ exitCode, stdout, stderr } = await testBinUtils.pkStdio(
62+
[
63+
'bootstrap',
64+
'--node-path',
65+
path.join(dataDir, 'polykey'),
66+
'--root-key-pair-bits',
67+
'1024',
68+
'--verbose',
69+
],
70+
{
71+
PK_PASSWORD: password,
72+
},
73+
dataDir,
74+
));
75+
const errorBootstrapExistingState =
76+
new bootstrapErrors.ErrorBootstrapExistingState();
77+
expect(exitCode).toBe(errorBootstrapExistingState.exitCode);
78+
const stdErrLine = stderr.trim().split('\n').pop();
79+
const eOutput = binUtils
80+
.outputFormatter({
81+
type: 'error',
82+
name: errorBootstrapExistingState.name,
83+
description: errorBootstrapExistingState.description,
84+
message: errorBootstrapExistingState.message,
85+
})
86+
.trim();
87+
expect(stdErrLine).toBe(eOutput);
88+
({ exitCode, stdout, stderr } = await testBinUtils.pkStdio(
89+
[
90+
'bootstrap',
91+
'--node-path',
92+
path.join(dataDir, 'polykey'),
93+
'--root-key-pair-bits',
94+
'1024',
95+
'--fresh',
96+
'--verbose',
97+
],
98+
{
99+
PK_PASSWORD: password,
100+
},
101+
dataDir,
102+
));
103+
expect(exitCode).toBe(0);
104+
const recoveryCode = stdout.trim();
105+
expect(
106+
recoveryCode.split(' ').length === 12 ||
107+
recoveryCode.split(' ').length === 24,
108+
).toBe(true);
109+
});
110+
test('concurrent bootstrapping are coalesced', async () => {
111+
const password = 'password';
112+
const [bootstrapProcess1, bootstrapProcess2] = await Promise.all([
113+
testBinUtils.pkSpawn(
114+
['bootstrap', '--root-key-pair-bits', '1024', '--verbose'],
115+
{
116+
PK_NODE_PATH: path.join(dataDir, 'polykey'),
117+
PK_PASSWORD: password,
118+
},
119+
dataDir,
120+
logger.getChild('bootstrapProcess1'),
121+
),
122+
testBinUtils.pkSpawn(
123+
['bootstrap', '--root-key-pair-bits', '1024', '--verbose'],
124+
{
125+
PK_NODE_PATH: path.join(dataDir, 'polykey'),
126+
PK_PASSWORD: password,
127+
},
128+
dataDir,
129+
logger.getChild('bootstrapProcess2'),
130+
),
131+
]);
132+
// These will be the last line of STDERR
133+
// The readline library will automatically trim off newlines
134+
let stdErrLine1;
135+
let stdErrLine2;
136+
const rlErr1 = readline.createInterface(bootstrapProcess1.stderr!);
137+
const rlErr2 = readline.createInterface(bootstrapProcess2.stderr!);
138+
rlErr1.on('line', (l) => {
139+
stdErrLine1 = l;
140+
});
141+
rlErr2.on('line', (l) => {
142+
stdErrLine2 = l;
143+
});
144+
const [index, exitCode, signal] = await new Promise<
145+
[number, number | null, NodeJS.Signals | null]
146+
>((resolve) => {
147+
bootstrapProcess1.once('exit', (code, signal) => {
148+
resolve([0, code, signal]);
149+
});
150+
bootstrapProcess2.once('exit', (code, signal) => {
151+
resolve([1, code, signal]);
152+
});
153+
});
154+
const errorStatusLocked = new statusErrors.ErrorStatusLocked();
155+
expect(exitCode).toBe(errorStatusLocked.exitCode);
156+
expect(signal).toBe(null);
157+
const eOutput = binUtils
158+
.outputFormatter({
159+
type: 'error',
160+
name: errorStatusLocked.name,
161+
description: errorStatusLocked.description,
162+
message: errorStatusLocked.message,
163+
})
164+
.trim();
165+
// It's either the first or second process
166+
if (index === 0) {
167+
expect(stdErrLine1).toBeDefined();
168+
expect(stdErrLine1).toBe(eOutput);
169+
const [exitCode] = await testBinUtils.processExit(bootstrapProcess2);
170+
expect(exitCode).toBe(0);
171+
} else if (index === 1) {
172+
expect(stdErrLine2).toBeDefined();
173+
expect(stdErrLine2).toBe(eOutput);
174+
const [exitCode] = await testBinUtils.processExit(bootstrapProcess1);
175+
expect(exitCode).toBe(0);
176+
}
177+
});
29178
test(
30179
'bootstrap when interrupted, requires fresh on next bootstrap',
31180
async () => {

tests/bin/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ async function pk(args: Array<string>): Promise<any> {
2323
/**
2424
* Runs pk command functionally with mocked STDIO
2525
* Both stdout and stderr are the entire output including newlines
26+
* This can only be used serially, because the mocks it relies on are global singletons
27+
* If it is used concurrently, the mocking side-effects can conflict
2628
* @param env Augments env for command execution
2729
* @param cwd Defaults to temporary directory
2830
*/

0 commit comments

Comments
 (0)