1- import type { RecoveryCode } from '@/keys/types' ;
21import os from 'os' ;
32import path from 'path' ;
43import fs from 'fs' ;
54import readline from 'readline' ;
65import Logger , { LogLevel , StreamHandler } from '@matrixai/logger' ;
7- import { Status , errors as statusErrors } from '@/status' ;
6+ import { errors as statusErrors } from '@/status' ;
87import { errors as bootstrapErrors } from '@/bootstrap' ;
98import * as binUtils from '@/bin/utils' ;
10- import config from '@/config' ;
119import * as testBinUtils from './utils' ;
1210
1311describe ( '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 ( ) => {
0 commit comments