onbuzz 3.6.1 → 3.6.3

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.
Files changed (84) hide show
  1. package/package.json +1 -1
  2. package/src/__test-utils__/fixtures/malformedJson.js +31 -0
  3. package/src/__test-utils__/globalSetup.js +9 -0
  4. package/src/__test-utils__/globalTeardown.js +12 -0
  5. package/src/__test-utils__/mockFactories.js +101 -0
  6. package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -0
  7. package/src/analyzers/__tests__/ConfigValidator.test.js +362 -0
  8. package/src/analyzers/__tests__/ESLintAnalyzer.test.js +271 -0
  9. package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -0
  10. package/src/analyzers/__tests__/PrettierFormatter.test.js +197 -0
  11. package/src/analyzers/__tests__/PythonAnalyzer.test.js +208 -0
  12. package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -0
  13. package/src/analyzers/__tests__/SparrowAnalyzer.test.js +270 -0
  14. package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -0
  15. package/src/core/__tests__/agentPool.test.js +601 -0
  16. package/src/core/__tests__/agentScheduler.test.js +576 -0
  17. package/src/core/__tests__/contextManager.test.js +252 -0
  18. package/src/core/__tests__/flowExecutor.test.js +262 -0
  19. package/src/core/__tests__/messageProcessor.test.js +627 -0
  20. package/src/core/__tests__/orchestrator.test.js +257 -0
  21. package/src/core/__tests__/stateManager.test.js +375 -0
  22. package/src/core/agentPool.js +11 -1
  23. package/src/index.js +25 -9
  24. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +3 -5
  25. package/src/services/__tests__/agentActivityService.test.js +319 -0
  26. package/src/services/__tests__/apiKeyManager.test.js +206 -0
  27. package/src/services/__tests__/benchmarkService.test.js +184 -0
  28. package/src/services/__tests__/budgetService.test.js +211 -0
  29. package/src/services/__tests__/contextInjectionService.test.js +205 -0
  30. package/src/services/__tests__/conversationCompactionService.test.js +280 -0
  31. package/src/services/__tests__/credentialVault.test.js +469 -0
  32. package/src/services/__tests__/errorHandler.test.js +314 -0
  33. package/src/services/__tests__/fileAttachmentService.test.js +278 -0
  34. package/src/services/__tests__/flowContextService.test.js +199 -0
  35. package/src/services/__tests__/memoryService.test.js +450 -0
  36. package/src/services/__tests__/modelRouterService.test.js +388 -0
  37. package/src/services/__tests__/modelsService.test.js +261 -0
  38. package/src/services/__tests__/portRegistry.test.js +123 -0
  39. package/src/services/__tests__/projectDetector.test.js +34 -0
  40. package/src/services/__tests__/promptService.test.js +242 -0
  41. package/src/services/__tests__/qualityInspector.test.js +97 -0
  42. package/src/services/__tests__/scheduleService.test.js +308 -0
  43. package/src/services/__tests__/serviceRegistry.test.js +74 -0
  44. package/src/services/__tests__/skillsService.test.js +402 -0
  45. package/src/services/__tests__/tokenCountingService.test.js +48 -0
  46. package/src/tools/__tests__/agentCommunicationTool.test.js +500 -0
  47. package/src/tools/__tests__/agentDelayTool.test.js +342 -0
  48. package/src/tools/__tests__/asyncToolManager.test.js +344 -0
  49. package/src/tools/__tests__/baseTool.test.js +420 -0
  50. package/src/tools/__tests__/codeMapTool.test.js +348 -0
  51. package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -0
  52. package/src/tools/__tests__/fileSystemTool.test.js +717 -0
  53. package/src/tools/__tests__/fileTreeTool.test.js +274 -0
  54. package/src/tools/__tests__/helpTool.test.js +204 -0
  55. package/src/tools/__tests__/jobDoneTool.test.js +296 -0
  56. package/src/tools/__tests__/memoryTool.test.js +297 -0
  57. package/src/tools/__tests__/seekTool.test.js +282 -0
  58. package/src/tools/__tests__/skillsTool.test.js +226 -0
  59. package/src/tools/__tests__/staticAnalysisTool.test.js +509 -0
  60. package/src/tools/__tests__/taskManagerTool.test.js +725 -0
  61. package/src/tools/__tests__/terminalTool.test.js +384 -0
  62. package/src/tools/__tests__/userPromptTool.test.js +297 -0
  63. package/src/tools/__tests__/webTool.e2e.test.js +25 -11
  64. package/src/tools/webTool.js +6 -12
  65. package/src/types/__tests__/agent.test.js +499 -0
  66. package/src/types/__tests__/contextReference.test.js +606 -0
  67. package/src/types/__tests__/conversation.test.js +555 -0
  68. package/src/types/__tests__/toolCommand.test.js +584 -0
  69. package/src/types/contextReference.js +1 -1
  70. package/src/utilities/__tests__/attachmentValidator.test.js +80 -0
  71. package/src/utilities/__tests__/configManager.test.js +397 -0
  72. package/src/utilities/__tests__/constants.test.js +49 -0
  73. package/src/utilities/__tests__/directoryAccessManager.test.js +388 -0
  74. package/src/utilities/__tests__/fileProcessor.test.js +104 -0
  75. package/src/utilities/__tests__/jsonRepair.test.js +104 -0
  76. package/src/utilities/__tests__/logger.test.js +129 -0
  77. package/src/utilities/__tests__/platformUtils.test.js +87 -0
  78. package/src/utilities/__tests__/structuredFileValidator.test.js +263 -0
  79. package/src/utilities/__tests__/tagParser.test.js +887 -0
  80. package/src/utilities/__tests__/toolConstants.test.js +94 -0
  81. package/src/utilities/tagParser.js +2 -2
  82. package/src/tools/browserTool.js +0 -897
  83. package/src/utilities/platformUtils.test.js +0 -98
  84. /package/src/tools/{filesystemTool.js → fileSystemTool.js} +0 -0
@@ -0,0 +1,469 @@
1
+ import { jest, describe, test, expect, beforeEach } from '@jest/globals';
2
+ import { createMockLogger } from '../../__test-utils__/mockFactories.js';
3
+
4
+ // Mock fs
5
+ const mockReadFile = jest.fn();
6
+ const mockWriteFile = jest.fn().mockResolvedValue(undefined);
7
+ jest.unstable_mockModule('fs', () => ({
8
+ promises: {
9
+ readFile: mockReadFile,
10
+ writeFile: mockWriteFile
11
+ }
12
+ }));
13
+
14
+ // Mock crypto with real-enough implementations
15
+ const mockRandomBytes = jest.fn().mockReturnValue(Buffer.from('deadbeef', 'hex'));
16
+ const mockPbkdf2Sync = jest.fn();
17
+ const mockCreateCipheriv = jest.fn();
18
+ const mockCreateDecipheriv = jest.fn();
19
+ jest.unstable_mockModule('crypto', () => ({
20
+ default: {
21
+ randomBytes: mockRandomBytes,
22
+ pbkdf2Sync: mockPbkdf2Sync,
23
+ createCipheriv: mockCreateCipheriv,
24
+ createDecipheriv: mockCreateDecipheriv
25
+ },
26
+ randomBytes: mockRandomBytes,
27
+ pbkdf2Sync: mockPbkdf2Sync,
28
+ createCipheriv: mockCreateCipheriv,
29
+ createDecipheriv: mockCreateDecipheriv
30
+ }));
31
+
32
+ // Mock os
33
+ jest.unstable_mockModule('os', () => ({
34
+ default: {
35
+ hostname: () => 'test-host',
36
+ homedir: () => '/home/test',
37
+ userInfo: () => ({ username: 'testuser' })
38
+ }
39
+ }));
40
+
41
+ // Mock userDataDir
42
+ const mockGetUserDataPaths = jest.fn().mockReturnValue({
43
+ agents: '/tmp/test-agents',
44
+ settings: '/tmp/test-settings'
45
+ });
46
+ const mockEnsureUserDataDirs = jest.fn().mockResolvedValue(undefined);
47
+ jest.unstable_mockModule('../../utilities/userDataDir.js', () => ({
48
+ getUserDataPaths: mockGetUserDataPaths,
49
+ ensureUserDataDirs: mockEnsureUserDataDirs
50
+ }));
51
+
52
+ // Mock stealthConstants
53
+ jest.unstable_mockModule('../../utilities/stealthConstants.js', () => ({
54
+ CREDENTIAL_CONFIG: {
55
+ ENCRYPTION_ALGORITHM: 'aes-256-gcm',
56
+ KEY_DERIVATION_ITERATIONS: 1000, // Low for testing
57
+ SALT_LENGTH: 32,
58
+ IV_LENGTH: 16,
59
+ AUTH_TAG_LENGTH: 16,
60
+ SESSION_EXPIRY_MS: 7 * 24 * 60 * 60 * 1000,
61
+ REQUEST_TIMEOUT_MS: 5 * 60 * 1000,
62
+ STORAGE: {
63
+ CREDENTIALS_FILE: 'credentials.enc',
64
+ SESSIONS_FILE: 'sessions.enc',
65
+ SETTINGS_DIR: 'settings'
66
+ }
67
+ },
68
+ KNOWN_SITES: {
69
+ linkedin: {
70
+ name: 'LinkedIn',
71
+ loginUrl: 'https://www.linkedin.com/login',
72
+ selectors: { username: '#username', password: '#password' },
73
+ usernameType: 'email',
74
+ multiStep: false
75
+ },
76
+ github: {
77
+ name: 'GitHub',
78
+ loginUrl: 'https://github.com/login',
79
+ selectors: { username: '#login_field', password: '#password' },
80
+ usernameType: 'username',
81
+ multiStep: false
82
+ }
83
+ },
84
+ CREDENTIAL_EVENTS: {
85
+ REQUEST: 'credential_request',
86
+ RESPONSE: 'credential_response',
87
+ CANCEL: 'credential_cancel',
88
+ STATUS: 'credential_status'
89
+ }
90
+ }));
91
+
92
+ const { default: CredentialVault, getCredentialVault } = await import('../../services/credentialVault.js');
93
+
94
+ describe('CredentialVault', () => {
95
+ let vault;
96
+ let logger;
97
+
98
+ beforeEach(() => {
99
+ logger = createMockLogger();
100
+ vault = new CredentialVault(logger);
101
+ jest.clearAllMocks();
102
+ });
103
+
104
+ // ── Constructor ──
105
+ test('constructor initializes with defaults', () => {
106
+ expect(vault.logger).toBe(logger);
107
+ expect(vault.credentials).toBeInstanceOf(Map);
108
+ expect(vault.sessions).toBeInstanceOf(Map);
109
+ expect(vault.pendingRequests).toBeInstanceOf(Map);
110
+ expect(vault.initialized).toBe(false);
111
+ expect(vault.encryptionKey).toBeNull();
112
+ });
113
+
114
+ test('constructor works without logger', () => {
115
+ const v = new CredentialVault();
116
+ expect(v.logger).toBeNull();
117
+ });
118
+
119
+ // ── _getMachineIdentifier ──
120
+ test('_getMachineIdentifier returns consistent string', () => {
121
+ const id = vault._getMachineIdentifier();
122
+ expect(typeof id).toBe('string');
123
+ expect(id).toContain('loxia-credential-vault-v1');
124
+ });
125
+
126
+ // ── _maskUsername ──
127
+ test('_maskUsername masks usernames', () => {
128
+ expect(vault._maskUsername('john@example.com')).toBe('j***om');
129
+ expect(vault._maskUsername('ab')).toBe('***');
130
+ expect(vault._maskUsername(null)).toBe('***');
131
+ expect(vault._maskUsername('')).toBe('***');
132
+ });
133
+
134
+ // ── Credential CRUD (without encryption) ──
135
+ test('saveCredentials stores credential entry', async () => {
136
+ // Don't persist - just test in-memory
137
+ vault.credentialsFile = null;
138
+ vault.encryptionKey = null;
139
+
140
+ await vault.saveCredentials('LinkedIn', {
141
+ username: 'user@test.com',
142
+ password: 'pass123'
143
+ });
144
+
145
+ expect(vault.credentials.has('linkedin')).toBe(true);
146
+ const entry = vault.credentials.get('linkedin');
147
+ expect(entry.username).toBe('user@test.com');
148
+ expect(entry.name).toBe('LinkedIn');
149
+ expect(entry.siteId).toBe('linkedin');
150
+ });
151
+
152
+ test('saveCredentials throws for missing fields', async () => {
153
+ await expect(vault.saveCredentials('test', { username: 'u' }))
154
+ .rejects.toThrow('siteId, username, and password are required');
155
+ await expect(vault.saveCredentials('', { username: 'u', password: 'p' }))
156
+ .rejects.toThrow();
157
+ });
158
+
159
+ test('saveCredentials uses known site info', async () => {
160
+ vault.credentialsFile = null;
161
+ vault.encryptionKey = null;
162
+
163
+ await vault.saveCredentials('github', {
164
+ username: 'octocat',
165
+ password: 'pass'
166
+ });
167
+
168
+ const entry = vault.credentials.get('github');
169
+ expect(entry.name).toBe('GitHub');
170
+ expect(entry.loginUrl).toBe('https://github.com/login');
171
+ expect(entry.usernameType).toBe('username');
172
+ });
173
+
174
+ test('saveCredentials uses custom values over known site', async () => {
175
+ vault.credentialsFile = null;
176
+ vault.encryptionKey = null;
177
+
178
+ await vault.saveCredentials('github', {
179
+ username: 'octocat',
180
+ password: 'pass',
181
+ loginUrl: 'https://custom.github.com/login'
182
+ });
183
+
184
+ const entry = vault.credentials.get('github');
185
+ expect(entry.loginUrl).toBe('https://custom.github.com/login');
186
+ });
187
+
188
+ test('getCredentials returns credential and updates lastUsed', () => {
189
+ vault.credentialsFile = null;
190
+ vault.encryptionKey = null;
191
+ vault.credentials.set('github', {
192
+ siteId: 'github',
193
+ username: 'user',
194
+ password: 'pass',
195
+ lastUsed: null
196
+ });
197
+
198
+ const result = vault.getCredentials('GitHub');
199
+ expect(result.username).toBe('user');
200
+ expect(result.lastUsed).toBeDefined();
201
+ });
202
+
203
+ test('getCredentials returns null for unknown site', () => {
204
+ expect(vault.getCredentials('nonexistent')).toBeNull();
205
+ });
206
+
207
+ test('hasCredentials checks existence', () => {
208
+ vault.credentials.set('github', { siteId: 'github' });
209
+ expect(vault.hasCredentials('GitHub')).toBe(true);
210
+ expect(vault.hasCredentials('unknown')).toBe(false);
211
+ });
212
+
213
+ test('deleteCredentials removes credential', async () => {
214
+ vault.credentialsFile = null;
215
+ vault.encryptionKey = null;
216
+ vault.sessionsFile = null;
217
+ vault.credentials.set('github', { siteId: 'github' });
218
+
219
+ const deleted = await vault.deleteCredentials('GitHub');
220
+ expect(deleted).toBe(true);
221
+ expect(vault.credentials.has('github')).toBe(false);
222
+ });
223
+
224
+ test('deleteCredentials returns false for non-existent', async () => {
225
+ vault.credentialsFile = null;
226
+ vault.encryptionKey = null;
227
+
228
+ const deleted = await vault.deleteCredentials('nonexistent');
229
+ expect(deleted).toBe(false);
230
+ });
231
+
232
+ test('listCredentials returns masked summaries', () => {
233
+ vault.credentials.set('github', {
234
+ siteId: 'github',
235
+ name: 'GitHub',
236
+ username: 'octocat@github.com',
237
+ loginUrl: 'https://github.com/login',
238
+ createdAt: Date.now(),
239
+ lastUsed: null
240
+ });
241
+
242
+ const list = vault.listCredentials();
243
+ expect(list.length).toBe(1);
244
+ expect(list[0].username).not.toBe('octocat@github.com');
245
+ expect(list[0].siteId).toBe('github');
246
+ });
247
+
248
+ // ── Session Management ──
249
+ test('saveSession stores session cookies', async () => {
250
+ vault.sessionsFile = null;
251
+ vault.encryptionKey = null;
252
+
253
+ await vault.saveSession('github', [{ name: 'session', value: 'abc123' }]);
254
+
255
+ const session = vault.sessions.get('github');
256
+ expect(session.cookies.length).toBe(1);
257
+ expect(session.expiresAt).toBeGreaterThan(Date.now());
258
+ });
259
+
260
+ test('getSession returns session if not expired', () => {
261
+ vault.sessions.set('github', {
262
+ siteId: 'github',
263
+ cookies: [{ name: 'session' }],
264
+ savedAt: Date.now(),
265
+ expiresAt: Date.now() + 86400000
266
+ });
267
+
268
+ const session = vault.getSession('GitHub');
269
+ expect(session).not.toBeNull();
270
+ expect(session.cookies.length).toBe(1);
271
+ });
272
+
273
+ test('getSession returns null for expired session', () => {
274
+ vault.sessions.set('github', {
275
+ siteId: 'github',
276
+ cookies: [],
277
+ savedAt: Date.now() - 86400000,
278
+ expiresAt: Date.now() - 1000 // expired
279
+ });
280
+
281
+ const session = vault.getSession('GitHub');
282
+ expect(session).toBeNull();
283
+ expect(vault.sessions.has('github')).toBe(false);
284
+ });
285
+
286
+ test('getSession returns null for non-existent', () => {
287
+ expect(vault.getSession('unknown')).toBeNull();
288
+ });
289
+
290
+ test('getAllSessions skips expired sessions', () => {
291
+ vault.sessions.set('github', {
292
+ siteId: 'github',
293
+ cookies: [],
294
+ expiresAt: Date.now() + 86400000
295
+ });
296
+ vault.sessions.set('linkedin', {
297
+ siteId: 'linkedin',
298
+ cookies: [],
299
+ expiresAt: Date.now() - 1000 // expired
300
+ });
301
+
302
+ const all = vault.getAllSessions();
303
+ expect(Object.keys(all).length).toBe(1);
304
+ expect(all.github).toBeDefined();
305
+ });
306
+
307
+ test('deleteSession removes session', async () => {
308
+ vault.sessionsFile = null;
309
+ vault.encryptionKey = null;
310
+ vault.sessions.set('github', { siteId: 'github' });
311
+
312
+ const deleted = await vault.deleteSession('GitHub');
313
+ expect(deleted).toBe(true);
314
+ expect(vault.sessions.has('github')).toBe(false);
315
+ });
316
+
317
+ test('deleteSession returns false for non-existent', async () => {
318
+ vault.sessionsFile = null;
319
+ vault.encryptionKey = null;
320
+
321
+ const deleted = await vault.deleteSession('unknown');
322
+ expect(deleted).toBe(false);
323
+ });
324
+
325
+ // ── _cleanupExpiredSessions ──
326
+ test('_cleanupExpiredSessions removes expired sessions', () => {
327
+ vault.sessionsFile = null;
328
+ vault.encryptionKey = null;
329
+ vault.sessions.set('expired', { expiresAt: Date.now() - 1000 });
330
+ vault.sessions.set('active', { expiresAt: Date.now() + 86400000 });
331
+
332
+ vault._cleanupExpiredSessions();
333
+ expect(vault.sessions.has('expired')).toBe(false);
334
+ expect(vault.sessions.has('active')).toBe(true);
335
+ });
336
+
337
+ test('_cleanupExpiredSessions does nothing when none expired', () => {
338
+ vault.sessions.set('active', { expiresAt: Date.now() + 86400000 });
339
+
340
+ vault._cleanupExpiredSessions();
341
+ expect(vault.sessions.has('active')).toBe(true);
342
+ });
343
+
344
+ // ── Credential Request Flow ──
345
+ test('createCredentialRequest returns requestInfo and promise', async () => {
346
+ const { requestInfo, promise } = vault.createCredentialRequest('github');
347
+ expect(requestInfo.requestId).toMatch(/^cred_/);
348
+ expect(requestInfo.siteId).toBe('github');
349
+ expect(requestInfo.siteName).toBe('GitHub');
350
+ expect(promise).toBeInstanceOf(Promise);
351
+
352
+ // Clean up — catch the rejection from cancel
353
+ vault.cancelCredentialRequest(requestInfo.requestId);
354
+ await expect(promise).rejects.toThrow();
355
+ });
356
+
357
+ test('submitCredentials resolves pending request', async () => {
358
+ const { requestInfo, promise } = vault.createCredentialRequest('github');
359
+
360
+ vault.credentialsFile = null;
361
+ vault.encryptionKey = null;
362
+
363
+ // Submit credentials
364
+ await vault.submitCredentials(requestInfo.requestId, {
365
+ username: 'user',
366
+ password: 'pass'
367
+ }, false);
368
+
369
+ const result = await promise;
370
+ expect(result.credentials.username).toBe('user');
371
+ expect(result.siteId).toBe('github');
372
+ expect(result.saved).toBe(false);
373
+ });
374
+
375
+ test('submitCredentials throws for unknown request', async () => {
376
+ await expect(vault.submitCredentials('nonexistent', {}))
377
+ .rejects.toThrow('No pending credential request found');
378
+ });
379
+
380
+ test('cancelCredentialRequest rejects pending promise', async () => {
381
+ const { requestInfo, promise } = vault.createCredentialRequest('github');
382
+
383
+ vault.cancelCredentialRequest(requestInfo.requestId);
384
+
385
+ await expect(promise).rejects.toThrow('cancelled by user');
386
+ });
387
+
388
+ test('cancelCredentialRequest does nothing for unknown request', () => {
389
+ // Should not throw
390
+ vault.cancelCredentialRequest('nonexistent');
391
+ });
392
+
393
+ test('getPendingRequest returns request info', async () => {
394
+ const { requestInfo, promise } = vault.createCredentialRequest('github');
395
+
396
+ const pending = vault.getPendingRequest(requestInfo.requestId);
397
+ expect(pending.siteId).toBe('github');
398
+
399
+ // Cleanup — catch rejection
400
+ vault.cancelCredentialRequest(requestInfo.requestId);
401
+ await expect(promise).rejects.toThrow();
402
+ });
403
+
404
+ test('getPendingRequest returns null for unknown', () => {
405
+ expect(vault.getPendingRequest('nonexistent')).toBeNull();
406
+ });
407
+
408
+ // ── Known Sites ──
409
+ test('getKnownSite returns site config for known sites', () => {
410
+ const site = vault.getKnownSite('LinkedIn');
411
+ expect(site).not.toBeNull();
412
+ expect(site.name).toBe('LinkedIn');
413
+ });
414
+
415
+ test('getKnownSite returns null for unknown sites', () => {
416
+ expect(vault.getKnownSite('fakebook')).toBeNull();
417
+ });
418
+
419
+ test('listKnownSites returns all known sites', () => {
420
+ const sites = vault.listKnownSites();
421
+ expect(sites.length).toBeGreaterThan(0);
422
+ expect(sites[0]).toHaveProperty('siteId');
423
+ expect(sites[0]).toHaveProperty('name');
424
+ expect(sites[0]).toHaveProperty('hasCredentials');
425
+ });
426
+
427
+ // ── Static methods ──
428
+ test('getEventTypes returns credential events', () => {
429
+ const events = CredentialVault.getEventTypes();
430
+ expect(events).toHaveProperty('REQUEST');
431
+ expect(events).toHaveProperty('RESPONSE');
432
+ });
433
+
434
+ // ── Encryption ──
435
+ test('_encrypt throws when key not initialized', () => {
436
+ vault.encryptionKey = null;
437
+ expect(() => vault._encrypt('test')).toThrow('Encryption key not initialized');
438
+ });
439
+
440
+ test('_decrypt throws when key not initialized', () => {
441
+ vault.encryptionKey = null;
442
+ expect(() => vault._decrypt('test')).toThrow('Encryption key not initialized');
443
+ });
444
+
445
+ // ── Persistence (edge cases) ──
446
+ test('_persistCredentials does nothing without file/key', async () => {
447
+ vault.credentialsFile = null;
448
+ await vault._persistCredentials();
449
+ expect(mockWriteFile).not.toHaveBeenCalled();
450
+ });
451
+
452
+ test('_persistSessions does nothing without file/key', async () => {
453
+ vault.sessionsFile = null;
454
+ await vault._persistSessions();
455
+ expect(mockWriteFile).not.toHaveBeenCalled();
456
+ });
457
+
458
+ test('_loadCredentials does nothing without file/key', async () => {
459
+ vault.credentialsFile = null;
460
+ await vault._loadCredentials();
461
+ expect(mockReadFile).not.toHaveBeenCalled();
462
+ });
463
+
464
+ test('_loadSessions does nothing without file/key', async () => {
465
+ vault.sessionsFile = null;
466
+ await vault._loadSessions();
467
+ expect(mockReadFile).not.toHaveBeenCalled();
468
+ });
469
+ });