mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-06 08:15:45 +00:00
fix: keep the stored heartbeat schedule when the setup wizard sets telemetry consent #1066
This commit is contained in:
@@ -19,6 +19,7 @@ vi.mock('@/lib/telemetry', () => ({
|
||||
effectiveConsent: async () => ({ source: 'env' }),
|
||||
loadState: async () => ({}),
|
||||
saveState: async () => {},
|
||||
updateState: async () => ({}),
|
||||
reschedule: async () => {},
|
||||
}));
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
// The wizard's telemetry choice used to load the whole state, change it and
|
||||
// save it back, putting its copy over anything written meanwhile (the
|
||||
// scheduler leader storing the next heartbeat, on this or another replica).
|
||||
// It changes only the consent fields of what is stored now.
|
||||
|
||||
vi.mock('@/lib/setup/state', () => ({ detectSetupState: () => 'bootstrap' }));
|
||||
vi.mock('@/lib/setup/session', () => ({ authenticateWizardRequest: async () => true }));
|
||||
vi.mock('@/lib/admin/config-manager', () => ({
|
||||
configManager: { ensureLoaded: async () => {}, setAdminConfig: async () => {} },
|
||||
}));
|
||||
const stored = vi.hoisted(() => ({ consent: 'off', consentedAt: null as string | null, nextScheduledAt: 'kept' }));
|
||||
vi.mock('@/lib/telemetry', () => ({
|
||||
effectiveConsent: async () => ({ source: 'file' }),
|
||||
loadState: async () => { throw new Error('read-modify-write of the whole state'); },
|
||||
saveState: async () => { throw new Error('read-modify-write of the whole state'); },
|
||||
updateState: async (change: (s: typeof stored) => void) => { change(stored); return stored; },
|
||||
reschedule: async () => {},
|
||||
}));
|
||||
|
||||
import { POST } from '../step/route';
|
||||
|
||||
describe('POST /api/setup/step (security, telemetry)', () => {
|
||||
it('turns consent on against the stored state', async () => {
|
||||
const res = await POST(new NextRequest('http://localhost/api/setup/step', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ step: 'security', values: { telemetryConsent: 'on' } }),
|
||||
}));
|
||||
expect(res.status).toBe(200);
|
||||
expect(stored).toMatchObject({ consent: 'on', nextScheduledAt: 'kept' });
|
||||
expect(stored.consentedAt).not.toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import { authenticateWizardRequest } from '@/lib/setup/session';
|
||||
import { configManager } from '@/lib/admin/config-manager';
|
||||
import { CONFIG_ENV_MAP } from '@/lib/admin/types';
|
||||
import { parseJmapServers } from '@/lib/admin/jmap-servers';
|
||||
import { effectiveConsent, loadState, saveState, reschedule } from '@/lib/telemetry';
|
||||
import { effectiveConsent, updateState, reschedule } from '@/lib/telemetry';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
@@ -93,12 +93,12 @@ export async function POST(request: NextRequest) {
|
||||
// A BULWARK_TELEMETRY env var hard-locks the choice; don't fight it.
|
||||
const { source } = await effectiveConsent();
|
||||
if (source !== 'env') {
|
||||
const tstate = await loadState();
|
||||
tstate.consent = consent;
|
||||
if (consent === 'on' && !tstate.consentedAt) {
|
||||
tstate.consentedAt = new Date().toISOString();
|
||||
}
|
||||
await saveState(tstate);
|
||||
const now = new Date().toISOString();
|
||||
// Against the stored state, so a schedule written meanwhile stays.
|
||||
await updateState((tstate) => {
|
||||
tstate.consent = consent;
|
||||
if (consent === 'on' && !tstate.consentedAt) tstate.consentedAt = now;
|
||||
});
|
||||
await reschedule();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user