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/); });