feat: add privacy-aware security manager onboarding
This commit is contained in:
74
test/security-onboarding.test.mjs
Normal file
74
test/security-onboarding.test.mjs
Normal file
@@ -0,0 +1,74 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { SecurityProfileStore } from '../lib/security/security-profile-store.mjs';
|
||||
import { SecurityOnboarding } from '../lib/security/security-onboarding.mjs';
|
||||
|
||||
function setup(secret = 'test-only-security-profile-key-at-least-32-chars') {
|
||||
const sent = [];
|
||||
const alerter = {
|
||||
isConfigured: true,
|
||||
async sendMessage(text, options) {
|
||||
sent.push({ text, options });
|
||||
return { ok: true };
|
||||
},
|
||||
};
|
||||
const path = join(mkdtempSync(join(tmpdir(), 'security-onboarding-')), 'profile.enc');
|
||||
const store = new SecurityProfileStore(path, secret).init();
|
||||
return { sent, store, onboarding: new SecurityOnboarding({ store, alerter, chatId: '42' }) };
|
||||
}
|
||||
|
||||
function query() {
|
||||
return { message: { chat: { id: 42 } } };
|
||||
}
|
||||
|
||||
test('first startup asks only for language before personal information', async () => {
|
||||
const { sent, onboarding } = setup();
|
||||
assert.equal(await onboarding.ensureStarted(), true);
|
||||
assert.equal(sent.length, 1);
|
||||
assert.match(sent[0].text, /language|Sprache/i);
|
||||
assert.deepEqual(sent[0].options.replyMarkup.inline_keyboard[0].map(button => button.callback_data), [
|
||||
'security_language:de',
|
||||
'security_language:en',
|
||||
]);
|
||||
assert.doesNotMatch(sent[0].text, /city|country|address|Stadt|Land|Adresse/i);
|
||||
});
|
||||
|
||||
test('minimal onboarding saves only after review confirmation', async () => {
|
||||
const { store, onboarding } = setup();
|
||||
await onboarding.start(42);
|
||||
let result = await onboarding.handleCallback('security_language:de', query());
|
||||
assert.equal(result.handled, true);
|
||||
assert.match(result.response.text, /verschl/iu);
|
||||
result = await onboarding.handleCallback('security_consent:minimal', query());
|
||||
assert.match(result.response.text, /Land/);
|
||||
|
||||
const answers = ['Deutschland', 'NRW', 'Koeln', 'Europe/Berlin', 'weather,cyber', 'important', '22:00-07:00'];
|
||||
for (const answer of answers) result = onboarding.handleMessage(answer, query().message);
|
||||
assert.equal(store.exists, false);
|
||||
assert.match(result.response.text, /pr.f/iu);
|
||||
|
||||
result = await onboarding.handleCallback('security_review:save', query());
|
||||
assert.equal(store.exists, true);
|
||||
assert.equal(store.getProfile().language, 'de');
|
||||
assert.equal(store.getProfile().location.city, 'Koeln');
|
||||
assert.equal(result.handled, true);
|
||||
});
|
||||
|
||||
test('profile deletion requires explicit callback confirmation', async () => {
|
||||
const { store, onboarding } = setup();
|
||||
store.save({ language: 'en', location: { country: 'DE' }, consentedAt: new Date().toISOString() });
|
||||
assert.match(onboarding.deletePrompt().text, /Delete/);
|
||||
await onboarding.handleCallback('security_delete:cancel', query());
|
||||
assert.equal(store.exists, true);
|
||||
await onboarding.handleCallback('security_delete:confirm', query());
|
||||
assert.equal(store.exists, false);
|
||||
});
|
||||
|
||||
test('setup remains unavailable without a valid encryption key', async () => {
|
||||
const { sent, onboarding } = setup('short');
|
||||
assert.equal(await onboarding.ensureStarted(), false);
|
||||
assert.match(sent[0].text, /SECURITY_PROFILE_ENCRYPTION_KEY/);
|
||||
});
|
||||
Reference in New Issue
Block a user