@serfab/cadre-host 0.12.0 → 1.0.0
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/README.md +6 -4
- package/dist/bin/host.js +9 -1
- package/dist/bin/host.js.map +1 -1
- package/dist/donation/__tests__/fake-orchestrator.js +1 -1
- package/dist/donation/__tests__/fake-orchestrator.js.map +1 -1
- package/dist/donation/donation-service.d.ts +7 -2
- package/dist/donation/donation-service.js +16 -2
- package/dist/donation/donation-service.js.map +1 -1
- package/dist/donation/donation-supervisor.d.ts +12 -2
- package/dist/donation/donation-supervisor.js +25 -2
- package/dist/donation/donation-supervisor.js.map +1 -1
- package/dist/donation/types.d.ts +2 -1
- package/dist/donation/types.js.map +1 -1
- package/dist/installer/index.d.ts +9 -1
- package/dist/installer/index.js +5 -1
- package/dist/installer/index.js.map +1 -1
- package/dist/orchestrator/host-process-orchestrator.d.ts +71 -11
- package/dist/orchestrator/host-process-orchestrator.js +173 -75
- package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
- package/dist/orchestrator/port-allocator.d.ts +44 -2
- package/dist/orchestrator/port-allocator.js +63 -5
- package/dist/orchestrator/port-allocator.js.map +1 -1
- package/dist/orchestrator/port-probe.d.ts +15 -0
- package/dist/orchestrator/port-probe.js +22 -0
- package/dist/orchestrator/port-probe.js.map +1 -0
- package/dist/orchestrator/types.d.ts +13 -1
- package/dist/orchestrator/types.js.map +1 -1
- package/dist/server/routes/bootstrap-node-validation.d.ts +115 -0
- package/dist/server/routes/bootstrap-node-validation.js +197 -0
- package/dist/server/routes/bootstrap-node-validation.js.map +1 -0
- package/dist/server/routes/grants.js +8 -25
- package/dist/server/routes/grants.js.map +1 -1
- package/dist/server/routes/provision-request-validation.d.ts +45 -0
- package/dist/server/routes/provision-request-validation.js +97 -0
- package/dist/server/routes/provision-request-validation.js.map +1 -0
- package/dist/server/server.d.ts +4 -1
- package/dist/server/server.js +36 -2
- package/dist/server/server.js.map +1 -1
- package/dist/ui/assets/{index-C-iks41M.js → index-CEoNRwM_.js} +3 -3
- package/dist/ui/assets/{index-C-iks41M.js.map → index-CEoNRwM_.js.map} +1 -1
- package/dist/ui/index.html +1 -1
- package/package.json +5 -4
- package/src/__tests__/orchestrator-env-scrub.test.ts +22 -0
- package/src/__tests__/orchestrator-node-identity.test.ts +4 -4
- package/src/__tests__/orchestrator-owner.test.ts +45 -2
- package/src/__tests__/orchestrator-ports.test.ts +172 -0
- package/src/__tests__/orchestrator.test.ts +1062 -951
- package/src/bin/host.ts +9 -1
- package/src/donation/__tests__/donation-service.test.ts +94 -7
- package/src/donation/__tests__/donation-supervisor.test.ts +50 -12
- package/src/donation/__tests__/fake-orchestrator.test.ts +168 -168
- package/src/donation/__tests__/fake-orchestrator.ts +1 -1
- package/src/donation/donation-service.ts +22 -3
- package/src/donation/donation-supervisor.ts +25 -2
- package/src/donation/types.ts +2 -1
- package/src/installer/__tests__/installer.smoke.test.ts +263 -218
- package/src/installer/index.ts +327 -314
- package/src/orchestrator/host-process-orchestrator.ts +1250 -1138
- package/src/orchestrator/port-allocator.ts +148 -91
- package/src/orchestrator/port-probe.ts +31 -0
- package/src/orchestrator/types.ts +13 -1
- package/src/server/__tests__/bootstrap-node-validation.test.ts +138 -0
- package/src/server/__tests__/grants-route.test.ts +394 -263
- package/src/server/__tests__/nodes-route.test.ts +2 -2
- package/src/server/__tests__/publishers.test.ts +1 -1
- package/src/server/__tests__/status-route.test.ts +1 -1
- package/src/server/routes/bootstrap-node-validation.ts +206 -0
- package/src/server/routes/grants.ts +9 -26
- package/src/server/routes/provision-request-validation.ts +105 -0
- package/src/server/server.ts +157 -122
|
@@ -1,218 +1,263 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
|
|
6
|
-
import { Installer } from '../index.js';
|
|
7
|
-
import type { ServiceHost, ServiceHostContext, ServiceHostStatus } from '../service-host/types.js';
|
|
8
|
-
|
|
9
|
-
class FakeServiceHost implements ServiceHost {
|
|
10
|
-
readonly name = 'cadre-host';
|
|
11
|
-
installCalls: ServiceHostContext[] = [];
|
|
12
|
-
uninstallCalls: ServiceHostContext[] = [];
|
|
13
|
-
installed = false;
|
|
14
|
-
running = false;
|
|
15
|
-
|
|
16
|
-
async install(ctx: ServiceHostContext): Promise<void> {
|
|
17
|
-
this.installCalls.push(ctx);
|
|
18
|
-
this.installed = true;
|
|
19
|
-
this.running = true;
|
|
20
|
-
}
|
|
21
|
-
async uninstall(ctx: ServiceHostContext): Promise<void> {
|
|
22
|
-
this.uninstallCalls.push(ctx);
|
|
23
|
-
this.installed = false;
|
|
24
|
-
this.running = false;
|
|
25
|
-
}
|
|
26
|
-
async restart(_ctx: ServiceHostContext): Promise<void> {
|
|
27
|
-
this.running = true;
|
|
28
|
-
}
|
|
29
|
-
async status(_ctx: ServiceHostContext): Promise<ServiceHostStatus> {
|
|
30
|
-
return { installed: this.installed, running: this.running };
|
|
31
|
-
}
|
|
32
|
-
renderUnit(_ctx: ServiceHostContext): string | null {
|
|
33
|
-
return 'rendered-unit';
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
describe('Installer smoke', () => {
|
|
38
|
-
let tmp: string;
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
tmp = mkdtempSync(join(tmpdir(), 'cadre-host-installer-smoke-'));
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
rmSync(tmp, { recursive: true, force: true });
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('install creates config + identity + invokes service-host', async () => {
|
|
49
|
-
const fake = new FakeServiceHost();
|
|
50
|
-
const installer = new Installer({ platform: 'linux', installerVersion: 'test-1.0.0' });
|
|
51
|
-
const result = await installer.install({
|
|
52
|
-
nonInteractive: true,
|
|
53
|
-
dataDir: tmp,
|
|
54
|
-
uiPort: 19999,
|
|
55
|
-
libp2pPort: 14001,
|
|
56
|
-
openBrowser: false,
|
|
57
|
-
noInvite: true,
|
|
58
|
-
serviceHost: fake,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
expect(result.dataDir).toBe(tmp);
|
|
62
|
-
expect(result.uiUrl).toBe('http://127.0.0.1:19999/');
|
|
63
|
-
expect(result.serviceName).toBe('cadre-host');
|
|
64
|
-
expect(result.configPath).toBe(join(tmp, 'host.config.json'));
|
|
65
|
-
|
|
66
|
-
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
67
|
-
expect(existsSync(join(tmp, 'identity.key'))).toBe(true);
|
|
68
|
-
expect(existsSync(join(tmp, 'nat.json'))).toBe(true);
|
|
69
|
-
expect(existsSync(join(tmp, 'logs'))).toBe(true);
|
|
70
|
-
|
|
71
|
-
expect(fake.installCalls).toHaveLength(1);
|
|
72
|
-
expect(fake.installCalls[0]!.dataDir).toBe(tmp);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('
|
|
76
|
-
const fake = new FakeServiceHost();
|
|
77
|
-
const installer = new Installer({ platform: 'linux' });
|
|
78
|
-
await installer.install({
|
|
79
|
-
nonInteractive: true,
|
|
80
|
-
dataDir: tmp,
|
|
81
|
-
uiPort:
|
|
82
|
-
libp2pPort:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
expect(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
expect(
|
|
91
|
-
expect(existsSync(join(tmp, '
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
});
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { Installer } from '../index.js';
|
|
7
|
+
import type { ServiceHost, ServiceHostContext, ServiceHostStatus } from '../service-host/types.js';
|
|
8
|
+
|
|
9
|
+
class FakeServiceHost implements ServiceHost {
|
|
10
|
+
readonly name = 'cadre-host';
|
|
11
|
+
installCalls: ServiceHostContext[] = [];
|
|
12
|
+
uninstallCalls: ServiceHostContext[] = [];
|
|
13
|
+
installed = false;
|
|
14
|
+
running = false;
|
|
15
|
+
|
|
16
|
+
async install(ctx: ServiceHostContext): Promise<void> {
|
|
17
|
+
this.installCalls.push(ctx);
|
|
18
|
+
this.installed = true;
|
|
19
|
+
this.running = true;
|
|
20
|
+
}
|
|
21
|
+
async uninstall(ctx: ServiceHostContext): Promise<void> {
|
|
22
|
+
this.uninstallCalls.push(ctx);
|
|
23
|
+
this.installed = false;
|
|
24
|
+
this.running = false;
|
|
25
|
+
}
|
|
26
|
+
async restart(_ctx: ServiceHostContext): Promise<void> {
|
|
27
|
+
this.running = true;
|
|
28
|
+
}
|
|
29
|
+
async status(_ctx: ServiceHostContext): Promise<ServiceHostStatus> {
|
|
30
|
+
return { installed: this.installed, running: this.running };
|
|
31
|
+
}
|
|
32
|
+
renderUnit(_ctx: ServiceHostContext): string | null {
|
|
33
|
+
return 'rendered-unit';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('Installer smoke', () => {
|
|
38
|
+
let tmp: string;
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
tmp = mkdtempSync(join(tmpdir(), 'cadre-host-installer-smoke-'));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('install creates config + identity + invokes service-host', async () => {
|
|
49
|
+
const fake = new FakeServiceHost();
|
|
50
|
+
const installer = new Installer({ platform: 'linux', installerVersion: 'test-1.0.0' });
|
|
51
|
+
const result = await installer.install({
|
|
52
|
+
nonInteractive: true,
|
|
53
|
+
dataDir: tmp,
|
|
54
|
+
uiPort: 19999,
|
|
55
|
+
libp2pPort: 14001,
|
|
56
|
+
openBrowser: false,
|
|
57
|
+
noInvite: true,
|
|
58
|
+
serviceHost: fake,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(result.dataDir).toBe(tmp);
|
|
62
|
+
expect(result.uiUrl).toBe('http://127.0.0.1:19999/');
|
|
63
|
+
expect(result.serviceName).toBe('cadre-host');
|
|
64
|
+
expect(result.configPath).toBe(join(tmp, 'host.config.json'));
|
|
65
|
+
|
|
66
|
+
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
67
|
+
expect(existsSync(join(tmp, 'identity.key'))).toBe(true);
|
|
68
|
+
expect(existsSync(join(tmp, 'nat.json'))).toBe(true);
|
|
69
|
+
expect(existsSync(join(tmp, 'logs'))).toBe(true);
|
|
70
|
+
|
|
71
|
+
expect(fake.installCalls).toHaveLength(1);
|
|
72
|
+
expect(fake.installCalls[0]!.dataDir).toBe(tmp);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('noService writes the data dir without registering a service', async () => {
|
|
76
|
+
const fake = new FakeServiceHost();
|
|
77
|
+
const installer = new Installer({ platform: 'linux', installerVersion: 'test-1.0.0' });
|
|
78
|
+
const result = await installer.install({
|
|
79
|
+
nonInteractive: true,
|
|
80
|
+
dataDir: tmp,
|
|
81
|
+
uiPort: 19995,
|
|
82
|
+
libp2pPort: 14005,
|
|
83
|
+
noService: true,
|
|
84
|
+
serviceHost: fake,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(fake.installCalls).toHaveLength(0);
|
|
88
|
+
expect(result.serviceName).toBeUndefined();
|
|
89
|
+
expect(result.uiUrl).toBe('http://127.0.0.1:19995/');
|
|
90
|
+
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
91
|
+
expect(existsSync(join(tmp, 'identity.key'))).toBe(true);
|
|
92
|
+
expect(existsSync(join(tmp, 'nat.json'))).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('noService on an interactive install skips the enrollment-invite fetch', async () => {
|
|
96
|
+
const fetchSpy = vi.spyOn(globalThis, 'fetch');
|
|
97
|
+
try {
|
|
98
|
+
const installer = new Installer({ platform: 'linux', installerVersion: 'test-1.0.0' });
|
|
99
|
+
const result = await installer.install({
|
|
100
|
+
nonInteractive: false,
|
|
101
|
+
noService: true,
|
|
102
|
+
wizard: async () => ({
|
|
103
|
+
dataDir: tmp,
|
|
104
|
+
uiPort: 19994,
|
|
105
|
+
libp2pPort: 14006,
|
|
106
|
+
upnpEnabled: false,
|
|
107
|
+
configureDdns: false,
|
|
108
|
+
ownCadre: false,
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
113
|
+
expect(result.enrollmentInvite).toBeUndefined();
|
|
114
|
+
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
115
|
+
} finally {
|
|
116
|
+
fetchSpy.mockRestore();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('uninstall removes data only when --remove-data is set', async () => {
|
|
121
|
+
const fake = new FakeServiceHost();
|
|
122
|
+
const installer = new Installer({ platform: 'linux' });
|
|
123
|
+
await installer.install({
|
|
124
|
+
nonInteractive: true,
|
|
125
|
+
dataDir: tmp,
|
|
126
|
+
uiPort: 19998,
|
|
127
|
+
libp2pPort: 14002,
|
|
128
|
+
openBrowser: false,
|
|
129
|
+
noInvite: true,
|
|
130
|
+
serviceHost: fake,
|
|
131
|
+
});
|
|
132
|
+
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
133
|
+
|
|
134
|
+
await installer.uninstall({ yes: true, removeData: false, dataDir: tmp, serviceHost: fake });
|
|
135
|
+
expect(fake.uninstallCalls).toHaveLength(1);
|
|
136
|
+
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
137
|
+
|
|
138
|
+
await installer.uninstall({ yes: true, removeData: true, dataDir: tmp, serviceHost: fake });
|
|
139
|
+
expect(existsSync(tmp)).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('rejects --system in v1', async () => {
|
|
143
|
+
const fake = new FakeServiceHost();
|
|
144
|
+
const installer = new Installer({ platform: 'linux' });
|
|
145
|
+
await expect(installer.install({
|
|
146
|
+
nonInteractive: true,
|
|
147
|
+
dataDir: tmp,
|
|
148
|
+
system: true,
|
|
149
|
+
serviceHost: fake,
|
|
150
|
+
openBrowser: false,
|
|
151
|
+
noInvite: true,
|
|
152
|
+
})).rejects.toThrow(/system.*not yet supported/);
|
|
153
|
+
expect(fake.installCalls).toHaveLength(0);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('passes CLI overrides to the wizard as suggested defaults', async () => {
|
|
157
|
+
// Regression: the interactive wizard previously showed the platform
|
|
158
|
+
// default (`~/.local/share/cadre-host`) in the prompt even when the
|
|
159
|
+
// caller passed `--data-dir <path>`. Whatever the user typed was then
|
|
160
|
+
// silently discarded because the CLI flag won post-hoc. The fix: CLI
|
|
161
|
+
// overrides become the wizard's suggested defaults, and the wizard's
|
|
162
|
+
// returned answers stand on their own.
|
|
163
|
+
const fake = new FakeServiceHost();
|
|
164
|
+
const installer = new Installer({ platform: 'linux' });
|
|
165
|
+
let seenDefaults: { dataDir: string; uiPort: number; libp2pPort: number; upnpEnabled: boolean; ownCadre: boolean } | undefined;
|
|
166
|
+
await installer.install({
|
|
167
|
+
nonInteractive: false,
|
|
168
|
+
dataDir: tmp,
|
|
169
|
+
uiPort: 19997,
|
|
170
|
+
libp2pPort: 14003,
|
|
171
|
+
noUpnp: true,
|
|
172
|
+
openBrowser: false,
|
|
173
|
+
noInvite: true,
|
|
174
|
+
serviceHost: fake,
|
|
175
|
+
wizard: async (defaults) => {
|
|
176
|
+
seenDefaults = { ...defaults };
|
|
177
|
+
// Simulate the user pressing Enter at every prompt.
|
|
178
|
+
return {
|
|
179
|
+
dataDir: defaults.dataDir,
|
|
180
|
+
uiPort: defaults.uiPort,
|
|
181
|
+
libp2pPort: defaults.libp2pPort,
|
|
182
|
+
upnpEnabled: defaults.upnpEnabled,
|
|
183
|
+
configureDdns: false,
|
|
184
|
+
ownCadre: defaults.ownCadre,
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
expect(seenDefaults).toEqual({
|
|
190
|
+
dataDir: tmp,
|
|
191
|
+
uiPort: 19997,
|
|
192
|
+
libp2pPort: 14003,
|
|
193
|
+
upnpEnabled: false,
|
|
194
|
+
ownCadre: false,
|
|
195
|
+
});
|
|
196
|
+
expect(fake.installCalls).toHaveLength(1);
|
|
197
|
+
expect(fake.installCalls[0]!.dataDir).toBe(tmp);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('honors wizard-typed input when no CLI override is passed', async () => {
|
|
201
|
+
// Mirror of the previous test: with no `--data-dir`, the wizard's
|
|
202
|
+
// returned value is what the installer uses.
|
|
203
|
+
const fake = new FakeServiceHost();
|
|
204
|
+
const installer = new Installer({ platform: 'linux' });
|
|
205
|
+
await installer.install({
|
|
206
|
+
nonInteractive: false,
|
|
207
|
+
openBrowser: false,
|
|
208
|
+
noInvite: true,
|
|
209
|
+
serviceHost: fake,
|
|
210
|
+
wizard: async () => ({
|
|
211
|
+
dataDir: tmp,
|
|
212
|
+
uiPort: 19996,
|
|
213
|
+
libp2pPort: 14004,
|
|
214
|
+
upnpEnabled: true,
|
|
215
|
+
configureDdns: false,
|
|
216
|
+
ownCadre: false,
|
|
217
|
+
}),
|
|
218
|
+
});
|
|
219
|
+
expect(fake.installCalls).toHaveLength(1);
|
|
220
|
+
expect(fake.installCalls[0]!.dataDir).toBe(tmp);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('writes ownCadre.enabled=false by default and true when opted in', async () => {
|
|
224
|
+
const { readHostConfig } = await import('../config.js');
|
|
225
|
+
|
|
226
|
+
// Default (donor-only): --own-cadre absent → ownCadre.enabled false.
|
|
227
|
+
const fakeA = new FakeServiceHost();
|
|
228
|
+
const installerA = new Installer({ platform: 'linux' });
|
|
229
|
+
await installerA.install({
|
|
230
|
+
nonInteractive: true, dataDir: tmp, openBrowser: false, noInvite: true, serviceHost: fakeA,
|
|
231
|
+
});
|
|
232
|
+
const donorCfg = readHostConfig(join(tmp, 'host.config.json'));
|
|
233
|
+
expect(donorCfg.ownCadre).toEqual({ enabled: false });
|
|
234
|
+
|
|
235
|
+
// Opt-in: ownCadre:true (the --own-cadre flag) → ownCadre.enabled true.
|
|
236
|
+
const tmp2 = mkdtempSync(join(tmpdir(), 'cadre-host-installer-owncadre-'));
|
|
237
|
+
try {
|
|
238
|
+
const fakeB = new FakeServiceHost();
|
|
239
|
+
const installerB = new Installer({ platform: 'linux' });
|
|
240
|
+
await installerB.install({
|
|
241
|
+
nonInteractive: true, dataDir: tmp2, ownCadre: true, openBrowser: false, noInvite: true, serviceHost: fakeB,
|
|
242
|
+
});
|
|
243
|
+
const founderCfg = readHostConfig(join(tmp2, 'host.config.json'));
|
|
244
|
+
expect(founderCfg.ownCadre).toEqual({ enabled: true });
|
|
245
|
+
} finally {
|
|
246
|
+
rmSync(tmp2, { recursive: true, force: true });
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('re-running install preserves the existing identity', async () => {
|
|
251
|
+
const fake = new FakeServiceHost();
|
|
252
|
+
const installer = new Installer({ platform: 'linux' });
|
|
253
|
+
await installer.install({
|
|
254
|
+
nonInteractive: true, dataDir: tmp, openBrowser: false, noInvite: true, serviceHost: fake,
|
|
255
|
+
});
|
|
256
|
+
const idBefore = (await import('node:fs')).readFileSync(join(tmp, 'identity.key'));
|
|
257
|
+
await installer.install({
|
|
258
|
+
nonInteractive: true, dataDir: tmp, openBrowser: false, noInvite: true, serviceHost: fake,
|
|
259
|
+
});
|
|
260
|
+
const idAfter = (await import('node:fs')).readFileSync(join(tmp, 'identity.key'));
|
|
261
|
+
expect(Buffer.from(idBefore).equals(Buffer.from(idAfter))).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
});
|