chahinebrini cddc4d0f26 feat(profile): DiGA-Demographics + Pro-Trial-Reward + 7 Profile-Endpoints
Schema:
- 8 neue Profile-Felder fuer DiGA-Demographics (birthYear/gender/maritalStatus/
  profession/bundesland/city + 2 consent-stamps demographicsConsentAt/
  demographicsWithdrawnAt)
- 4 Pro-Trial-Felder (proTrialStartedAt/ExpiresAt/Source/UsedAt) — Free-User
  bekommen 1 Woche Pro als Reward fuer DiGA-Daten-Pflege (siehe
  project_demographic_pro_trial_reward.md)
- lyra_voice_id (Legend-only Voice-Picker)
- diga_banner_dismissed_at (server-side persistence ueber Re-Install)
- last_install_at (Streak-Logic survives Re-Install)
- Migration 20260507_profile_demographics_and_trial: alle Felder optional,
  keine Backfill-Logik notwendig

Endpoints (alle auth-protected, scope=me):
- GET /api/profile/me/sos-insights
- GET /api/profile/me/cooldown-history
- GET /api/profile/me/approved-domains
- POST /api/profile/me/install-event (track app re-installs)
- POST /api/profile/me/diga-banner-dismiss
- PATCH /api/profile/me/demographics (consent-stamp + re-grant-after-withdrawal in tx)
- DELETE /api/profile/me/demographics (DSGVO right-to-be-forgotten)

Plugin:
- pro-trial-expiry-cron: 6h-Interval, conservative-fallback (revoke nur wenn
  kein stripeSubId), 60s initial-delay damit Server-boot nicht blockiert wird

Tests:
- vitest config + erste Test-Files (test-infrastructure setup)

Memory:
- feedback_demographics_user_initiated.md (Lyra darf NIE extrahieren)
- project_demographic_pro_trial_reward.md (Pro-Trial-Reward-Mechanik)
- project_profile_page_design.md (UI-Showpiece, eigene/fremde-Ansicht streng getrennt)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:14:06 +02:00

75 lines
2.5 KiB
TypeScript

/**
* Vitest test setup.
*
* Backend uses Nitro auto-imports (defineEventHandler, requireUser, etc.) which
* are injected at build time via unimport. In unit tests we don't actually
* boot Nitro; we test pure logic + DB-layer functions in isolation.
*
* For endpoint-handler tests we either:
* 1. Test the underlying db/* function directly (preferred — easy to mock)
* 2. Stub Nitro globals (defineEventHandler etc.) — see below
*
* Stubs intentionally minimal — when we need full request-cycle tests we'll
* add supertest + nitro `.output/server` boot (see ops/TESTING_STATE.md §4).
*/
import { vi } from "vitest";
// ─── Nitro / h3 globals ─────────────────────────────────────────────────────
// These are injected at build time via Nitro's unimport. In tests we stub
// the most-used ones so endpoint files can be imported (or referenced) without
// crashing. Add more as test surface grows.
const g = globalThis as Record<string, unknown>;
if (typeof g.defineEventHandler === "undefined") {
g.defineEventHandler = (handler: unknown) => handler;
}
if (typeof g.defineNitroPlugin === "undefined") {
g.defineNitroPlugin = (handler: unknown) => handler;
}
if (typeof g.createError === "undefined") {
g.createError = ({
statusCode,
message,
data,
}: {
statusCode?: number;
message?: string;
data?: unknown;
}) => {
const err = new Error(message ?? "error") as Error & {
statusCode?: number;
data?: unknown;
};
err.statusCode = statusCode;
err.data = data;
return err;
};
}
if (typeof g.readBody === "undefined") {
g.readBody = vi.fn(async (event: { body?: unknown }) => event.body);
}
if (typeof g.getQuery === "undefined") {
g.getQuery = vi.fn((event: { query?: unknown }) => event.query ?? {});
}
if (typeof g.getHeader === "undefined") {
g.getHeader = vi.fn(() => undefined);
}
if (typeof g.setResponseStatus === "undefined") {
g.setResponseStatus = vi.fn();
}
if (typeof g.setHeader === "undefined") {
g.setHeader = vi.fn();
}
if (typeof g.useRuntimeConfig === "undefined") {
g.useRuntimeConfig = vi.fn(() => ({
public: { supabase: { url: "", key: "" } },
supabase: { url: "", key: "" },
}));
}
if (typeof g.usePrisma === "undefined") {
// Tests should override per-test via vi.mock("../../server/utils/prisma")
g.usePrisma = vi.fn(() => {
throw new Error("usePrisma not mocked in this test");
});
}