@cosmicdrift/kumiko-bundled-features 1.0.0 → 2.0.0
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.
- package/package.json +41 -7
- package/src/__tests__/access-invalidation-consumer.integration.test.ts +279 -0
- package/src/__tests__/access-invalidation-stream.integration.test.ts +217 -0
- package/src/admin-shell/__tests__/admin-shell-security.integration.test.ts +200 -0
- package/src/admin-shell/__tests__/admin-shell.boot.test.ts +147 -0
- package/src/admin-shell/__tests__/overview-allowlist.test.ts +43 -0
- package/src/admin-shell/__tests__/overview-screens.boot.test.ts +35 -0
- package/src/admin-shell/constants.ts +11 -0
- package/src/admin-shell/feature.ts +135 -0
- package/src/admin-shell/i18n.ts +36 -0
- package/src/admin-shell/index.ts +17 -0
- package/src/admin-shell/overview-allowlist.ts +37 -0
- package/src/admin-shell/web/client-plugin.tsx +26 -0
- package/src/admin-shell/web/i18n.ts +16 -0
- package/src/admin-shell/web/index.ts +2 -0
- package/src/admin-shell/web/overview-layout.tsx +69 -0
- package/src/admin-shell/web/overview-query.ts +15 -0
- package/src/admin-shell/web/platform-overview-screen.tsx +90 -0
- package/src/admin-shell/web/tenant-overview-screen.tsx +90 -0
- package/src/agent-tools/__tests__/tool-catalog.test.ts +179 -0
- package/src/agent-tools/__tests__/tool-dispatch.integration.test.ts +100 -0
- package/src/agent-tools/__tests__/tool-dispatch.test.ts +126 -0
- package/src/agent-tools/index.ts +9 -0
- package/src/agent-tools/tool-catalog.ts +130 -0
- package/src/agent-tools/tool-dispatch.ts +82 -0
- package/src/agent-tools/types.ts +38 -0
- package/src/audit/__tests__/audit-screens.boot.test.ts +51 -0
- package/src/audit/__tests__/audit-security.integration.test.ts +201 -0
- package/src/audit/__tests__/audit.integration.test.ts +50 -25
- package/src/audit/constants.ts +9 -2
- package/src/audit/feature.ts +33 -1
- package/src/audit/handlers/details.query.ts +47 -0
- package/src/audit/handlers/list.query.ts +3 -2
- package/src/audit/i18n.ts +31 -0
- package/src/audit/web/audit-log-detail-screen.tsx +131 -0
- package/src/audit/web/audit-log-screen.tsx +250 -0
- package/src/audit/web/client-plugin.tsx +22 -0
- package/src/audit/web/i18n.ts +12 -0
- package/src/audit/web/index.ts +5 -0
- package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.test.ts +4 -4
- package/src/auth-email-password/__tests__/account-lockout.integration.test.ts +4 -4
- package/src/auth-email-password/__tests__/account-unlock.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/auth-claims.integration.test.ts +10 -11
- package/src/auth-email-password/__tests__/auth-user-row.test.ts +36 -0
- package/src/auth-email-password/__tests__/auth.integration.test.ts +33 -6
- package/src/auth-email-password/__tests__/email-verification.integration.test.ts +9 -6
- package/src/auth-email-password/__tests__/feature-options.test.ts +33 -0
- package/src/auth-email-password/__tests__/identity-v3-login.integration.test.ts +3 -3
- package/src/auth-email-password/__tests__/invite-flow-kms.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/invite-flow.integration.test.ts +35 -1
- package/src/auth-email-password/__tests__/login-mfa-setup-required.integration.test.ts +129 -0
- package/src/auth-email-password/__tests__/multi-roles.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/password-policy.test.ts +15 -0
- package/src/auth-email-password/__tests__/password-reset.integration.test.ts +19 -6
- package/src/auth-email-password/__tests__/public-routes-rate-limit.integration.test.ts +3 -3
- package/src/auth-email-password/__tests__/seed-admin.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/self-registration-toggle.integration.test.ts +264 -0
- package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +20 -4
- package/src/auth-email-password/__tests__/session-strict-mode.integration.test.ts +7 -14
- package/src/auth-email-password/__tests__/signup-flow.integration.test.ts +91 -0
- package/src/auth-email-password/constants.ts +25 -0
- package/src/auth-email-password/email-templates.ts +32 -0
- package/src/auth-email-password/errors.ts +9 -0
- package/src/auth-email-password/feature.ts +59 -11
- package/src/auth-email-password/handlers/change-password.write.ts +14 -13
- package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +59 -0
- package/src/auth-email-password/handlers/confirm-token-flow.ts +34 -105
- package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +12 -5
- package/src/auth-email-password/handlers/invite-accept.write.ts +9 -2
- package/src/auth-email-password/handlers/invite-create.write.ts +2 -2
- package/src/auth-email-password/handlers/invite-signup-complete.write.ts +12 -4
- package/src/auth-email-password/handlers/login.write.ts +86 -16
- package/src/auth-email-password/handlers/logout.write.ts +4 -2
- package/src/auth-email-password/handlers/request-account-unlock.write.ts +38 -0
- package/src/auth-email-password/handlers/reset-password.write.ts +3 -2
- package/src/auth-email-password/handlers/self-registration-status.query.ts +23 -0
- package/src/auth-email-password/handlers/signup-confirm.write.ts +19 -11
- package/src/auth-email-password/handlers/signup-request.write.ts +22 -0
- package/src/auth-email-password/handlers/token-request-handler.ts +4 -2
- package/src/auth-email-password/i18n.ts +52 -0
- package/src/auth-email-password/index.ts +13 -2
- package/src/auth-email-password/lockout-store.ts +10 -5
- package/src/auth-email-password/password-policy.ts +2108 -0
- package/src/auth-email-password/seeding.ts +55 -27
- package/src/auth-email-password/self-registration-toggle.ts +25 -0
- package/src/auth-email-password/signed-token.ts +1 -0
- package/src/auth-email-password/unlock-token.ts +28 -0
- package/src/auth-email-password/web/__tests__/auth-client.test.ts +542 -0
- package/src/auth-email-password/web/__tests__/auth-form-logic.test.ts +44 -0
- package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +194 -6
- package/src/auth-email-password/web/__tests__/client-plugin.test.tsx +34 -0
- package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +64 -0
- package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +5 -1
- package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +129 -2
- package/src/auth-email-password/web/__tests__/login-screen.test.tsx +149 -5
- package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +137 -0
- package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +5 -1
- package/src/auth-email-password/web/__tests__/signup-complete-screen.test.tsx +134 -0
- package/src/auth-email-password/web/__tests__/signup-screen.test.tsx +93 -0
- package/src/auth-email-password/web/__tests__/test-utils.tsx +12 -2
- package/src/auth-email-password/web/__tests__/use-url-token.test.tsx +29 -0
- package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +5 -1
- package/src/auth-email-password/web/auth-client.ts +96 -18
- package/src/auth-email-password/web/auth-form-logic.ts +30 -0
- package/src/auth-email-password/web/auth-form-primitives.tsx +22 -20
- package/src/auth-email-password/web/auth-gate.tsx +140 -19
- package/src/auth-email-password/web/client-plugin.ts +24 -2
- package/src/auth-email-password/web/confirm-account-unlock-screen.tsx +95 -0
- package/src/auth-email-password/web/forgot-password-screen.tsx +6 -6
- package/src/auth-email-password/web/index.ts +16 -4
- package/src/auth-email-password/web/invite-accept-screen.tsx +5 -5
- package/src/auth-email-password/web/login-screen.tsx +78 -22
- package/src/auth-email-password/web/request-account-unlock-screen.tsx +112 -0
- package/src/auth-email-password/web/reset-password-screen.tsx +15 -18
- package/src/auth-email-password/web/session.tsx +11 -6
- package/src/auth-email-password/web/signup-complete-screen.tsx +13 -15
- package/src/auth-email-password/web/signup-screen.tsx +8 -10
- package/src/auth-email-password/web/use-shell-user.ts +5 -2
- package/src/auth-email-password/web/user-menu.tsx +3 -1
- package/src/auth-email-password/web/verify-email-screen.tsx +10 -14
- package/src/auth-foundation/__tests__/feature.test.ts +46 -0
- package/src/auth-foundation/__tests__/session-store.test.ts +77 -0
- package/src/auth-foundation/__tests__/tenant-providers.test.ts +160 -0
- package/src/auth-foundation/__tests__/token-verifier.test.ts +150 -0
- package/src/auth-foundation/boot-checks.ts +150 -0
- package/src/auth-foundation/feature.ts +157 -0
- package/src/auth-foundation/index.ts +37 -0
- package/src/auth-foundation/resolve-anonymous-access.ts +62 -0
- package/src/auth-foundation/types.ts +170 -0
- package/src/auth-mfa/__tests__/base32.test.ts +28 -0
- package/src/auth-mfa/__tests__/disable-and-regenerate.integration.test.ts +265 -0
- package/src/auth-mfa/__tests__/enable-confirm-preauth.integration.test.ts +321 -0
- package/src/auth-mfa/__tests__/enable-flow.integration.test.ts +322 -0
- package/src/auth-mfa/__tests__/enable-start-preauth.integration.test.ts +237 -0
- package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +39 -0
- package/src/auth-mfa/__tests__/mfa-preauth-setup-token.test.ts +113 -0
- package/src/auth-mfa/__tests__/no-redis.integration.test.ts +209 -0
- package/src/auth-mfa/__tests__/pii-subject-encryption.integration.test.ts +139 -0
- package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +247 -0
- package/src/auth-mfa/__tests__/session-auto-revoke.integration.test.ts +169 -0
- package/src/auth-mfa/__tests__/totp.test.ts +131 -0
- package/src/auth-mfa/__tests__/verify.integration.test.ts +319 -0
- package/src/auth-mfa/base32.ts +51 -0
- package/src/auth-mfa/config.ts +26 -0
- package/src/auth-mfa/constants.ts +45 -0
- package/src/auth-mfa/db/queries.ts +45 -0
- package/src/auth-mfa/errors.ts +66 -0
- package/src/auth-mfa/feature.ts +170 -0
- package/src/auth-mfa/handlers/disable.write.ts +67 -0
- package/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +200 -0
- package/src/auth-mfa/handlers/enable-confirm.write.ts +106 -0
- package/src/auth-mfa/handlers/enable-start-preauth.write.ts +87 -0
- package/src/auth-mfa/handlers/enable-start.write.ts +66 -0
- package/src/auth-mfa/handlers/reencrypt.job.ts +234 -0
- package/src/auth-mfa/handlers/regenerate-recovery.write.ts +77 -0
- package/src/auth-mfa/handlers/status.query.ts +23 -0
- package/src/auth-mfa/handlers/verify.write.ts +179 -0
- package/src/auth-mfa/i18n.ts +12 -0
- package/src/auth-mfa/index.ts +19 -0
- package/src/auth-mfa/mfa-challenge-token.ts +90 -0
- package/src/auth-mfa/mfa-preauth-setup-token.ts +86 -0
- package/src/auth-mfa/mfa-setup-token.ts +96 -0
- package/src/auth-mfa/mfa-status-checker.ts +84 -0
- package/src/auth-mfa/mfa-verify-attempts.ts +90 -0
- package/src/auth-mfa/otpauth-uri.ts +21 -0
- package/src/auth-mfa/recovery-codes.ts +54 -0
- package/src/auth-mfa/schema/user-mfa.ts +60 -0
- package/src/auth-mfa/testing.ts +5 -0
- package/src/auth-mfa/totp.ts +67 -0
- package/src/auth-mfa/verify-factor.ts +58 -0
- package/src/auth-mfa/web/__tests__/mfa-client.test.ts +265 -0
- package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +182 -0
- package/src/auth-mfa/web/__tests__/mfa-setup-preauth-screen.test.tsx +379 -0
- package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +104 -0
- package/src/auth-mfa/web/client-plugin.ts +37 -0
- package/src/auth-mfa/web/i18n.ts +136 -0
- package/src/auth-mfa/web/index.ts +27 -0
- package/src/auth-mfa/web/mfa-client.ts +184 -0
- package/src/auth-mfa/web/mfa-disable-dialog.tsx +69 -0
- package/src/auth-mfa/web/mfa-enable-screen.tsx +228 -0
- package/src/auth-mfa/web/mfa-recovery-codes-reveal.tsx +53 -0
- package/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +68 -0
- package/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +267 -0
- package/src/auth-mfa/web/mfa-verify-screen.tsx +117 -0
- package/src/auth-mfa/web/qrcode-browser.d.ts +12 -0
- package/src/auth-mfa-user-data/__tests__/hooks.integration.test.ts +134 -0
- package/src/auth-mfa-user-data/hooks.ts +53 -0
- package/src/auth-mfa-user-data/index.ts +21 -0
- package/src/billing-foundation/__tests__/billing-foundation.integration.test.ts +206 -2
- package/src/billing-foundation/__tests__/subscription-provider-contract.ts +93 -0
- package/src/billing-foundation/entities.ts +28 -4
- package/src/billing-foundation/events.ts +6 -2
- package/src/billing-foundation/feature.ts +9 -1
- package/src/billing-foundation/get-subscription-for-tenant.ts +16 -5
- package/src/billing-foundation/handlers/create-portal-session.write.ts +17 -1
- package/src/billing-foundation/handlers/list-subscriptions.query.ts +15 -1
- package/src/billing-foundation/handlers/process-event.write.ts +39 -3
- package/src/billing-foundation/tenant-destroy-hook.ts +46 -0
- package/src/cap-counter/__tests__/cap-list.boot.test.ts +27 -0
- package/src/cap-counter/__tests__/cap-list.security.integration.test.ts +53 -0
- package/src/cap-counter/__tests__/entity-ddl.test.ts +15 -0
- package/src/cap-counter/__tests__/with-cap-enforcement.integration.test.ts +8 -5
- package/src/cap-counter/constants.ts +2 -0
- package/src/cap-counter/enforce-cap.ts +15 -18
- package/src/cap-counter/entity.ts +3 -3
- package/src/cap-counter/feature.ts +28 -1
- package/src/cap-counter/i18n.ts +12 -0
- package/src/cap-counter/with-cap-enforcement.ts +2 -2
- package/src/channel-email/__tests__/pii-guard.test.ts +56 -0
- package/src/channel-email/email-channel.ts +2 -1
- package/src/channel-email/index.ts +1 -0
- package/src/channel-email/pii-guard.ts +39 -0
- package/src/compliance-profiles/__tests__/profile-picker.boot.test.ts +28 -0
- package/src/compliance-profiles/__tests__/profile-picker.security.integration.test.ts +91 -0
- package/src/compliance-profiles/constants.ts +15 -0
- package/src/compliance-profiles/feature.ts +22 -1
- package/src/compliance-profiles/handlers/set-profile.write.ts +2 -2
- package/src/compliance-profiles/i18n.ts +6 -0
- package/src/compliance-profiles/index.ts +6 -0
- package/src/compliance-profiles/web/client-plugin.tsx +22 -0
- package/src/compliance-profiles/web/compliance-profile-screen.tsx +152 -0
- package/src/compliance-profiles/web/i18n.ts +25 -0
- package/src/compliance-profiles/web/index.ts +6 -0
- package/src/config/__tests__/app-overrides.test.ts +11 -0
- package/src/config/__tests__/backing-secrets.integration.test.ts +22 -0
- package/src/config/__tests__/cascade.integration.test.ts +35 -0
- package/src/config/__tests__/config.integration.test.ts +71 -10
- package/src/config/__tests__/deserialize-value.test.ts +5 -8
- package/src/config/__tests__/inherited-redaction.integration.test.ts +4 -3
- package/src/config/__tests__/pii-encrypted.integration.test.ts +179 -0
- package/src/config/__tests__/reencrypt-job-concurrent-write-race.integration.test.ts +234 -0
- package/src/config/__tests__/reencrypt-job-kek-rotation.integration.test.ts +243 -0
- package/src/config/__tests__/write-helpers.test.ts +23 -0
- package/src/config/db/queries/resolver.ts +3 -3
- package/src/config/feature.ts +15 -8
- package/src/config/handlers/cascade.query.ts +2 -1
- package/src/config/handlers/reencrypt.job.ts +209 -0
- package/src/config/handlers/set.write.ts +55 -3
- package/src/config/handlers/values.query.ts +2 -1
- package/src/config/i18n.ts +9 -0
- package/src/config/index.ts +5 -4
- package/src/config/resolver.ts +75 -61
- package/src/config/table.ts +1 -1
- package/src/config/write-helpers.ts +22 -0
- package/src/crypto-shredding/__tests__/forget-subject.integration.test.ts +148 -0
- package/src/crypto-shredding/constants.ts +5 -0
- package/src/crypto-shredding/feature.ts +18 -0
- package/src/crypto-shredding/handlers/forget-subject.write.ts +91 -0
- package/src/crypto-shredding/index.ts +11 -0
- package/src/custom-fields/__tests__/audit-integration.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/custom-fields.integration.test.ts +92 -53
- package/src/custom-fields/__tests__/field-access.integration.test.ts +25 -0
- package/src/custom-fields/__tests__/parse-serialized-field.test.ts +11 -3
- package/src/custom-fields/__tests__/retention.integration.test.ts +40 -0
- package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +26 -79
- package/src/custom-fields/constants.ts +1 -0
- package/src/custom-fields/db/queries/user-data-rights.ts +0 -20
- package/src/custom-fields/entity.ts +1 -1
- package/src/custom-fields/events.ts +4 -3
- package/src/custom-fields/feature.ts +8 -0
- package/src/custom-fields/handlers/delete-system-field.write.ts +2 -1
- package/src/custom-fields/handlers/delete-tenant-field.write.ts +2 -1
- package/src/custom-fields/handlers/set-custom-field.write.ts +3 -44
- package/src/custom-fields/handlers/update-tenant-field.write.ts +0 -17
- package/src/custom-fields/lib/field-access.ts +8 -1
- package/src/custom-fields/lib/parse-serialized-field.ts +12 -4
- package/src/custom-fields/lib/value-schema.ts +1 -1
- package/src/custom-fields/run-retention.ts +9 -1
- package/src/custom-fields/schemas.ts +9 -9
- package/src/custom-fields/wire-for-entity.ts +110 -78
- package/src/custom-fields/wire-user-data-rights.ts +5 -54
- package/src/data-retention/__tests__/parse-override.test.ts +14 -13
- package/src/data-retention/__tests__/retention-cleanup.integration.test.ts +145 -8
- package/src/data-retention/feature.ts +3 -3
- package/src/data-retention/run-retention-cleanup.ts +102 -19
- package/src/delivery/__tests__/delivery-pii-kms.integration.test.ts +171 -0
- package/src/delivery/__tests__/delivery-screens.boot.test.ts +27 -0
- package/src/delivery/__tests__/delivery-security.integration.test.ts +106 -0
- package/src/delivery/constants.ts +13 -51
- package/src/delivery/feature.ts +29 -5
- package/src/delivery/handlers/log.query.ts +28 -7
- package/src/delivery/i18n.ts +6 -0
- package/src/delivery/index.ts +6 -1
- package/src/delivery/public-names.ts +36 -0
- package/src/delivery/tables.ts +3 -3
- package/src/delivery/web/client-plugin.tsx +20 -0
- package/src/delivery/web/delivery-log-screen.tsx +103 -0
- package/src/delivery/web/i18n.ts +25 -0
- package/src/delivery/web/index.ts +3 -0
- package/src/document-ingest-foundation/__tests__/feature.integration.test.ts +147 -0
- package/src/document-ingest-foundation/__tests__/feature.test.ts +105 -0
- package/src/document-ingest-foundation/__tests__/pages.test.ts +37 -0
- package/src/document-ingest-foundation/entity.ts +43 -0
- package/src/document-ingest-foundation/events.ts +25 -0
- package/src/document-ingest-foundation/feature.ts +126 -0
- package/src/document-ingest-foundation/index.ts +12 -0
- package/src/document-ingest-foundation/pages.ts +34 -0
- package/src/feature-toggles/__tests__/compose-tier-resolver.test.ts +304 -0
- package/src/feature-toggles/__tests__/feature-toggles.integration.test.ts +7 -6
- package/src/feature-toggles/__tests__/toggle-admin.boot.test.ts +31 -0
- package/src/feature-toggles/__tests__/toggle-admin.security.integration.test.ts +69 -0
- package/src/feature-toggles/compose-tier-resolver.ts +134 -0
- package/src/feature-toggles/constants.ts +14 -0
- package/src/feature-toggles/feature.ts +23 -3
- package/src/feature-toggles/global-feature-state-table.ts +20 -1
- package/src/feature-toggles/handlers/list.query.ts +1 -1
- package/src/feature-toggles/handlers/registered.query.ts +1 -1
- package/src/feature-toggles/i18n.ts +6 -0
- package/src/feature-toggles/index.ts +8 -0
- package/src/feature-toggles/toggle-runtime.ts +14 -0
- package/src/feature-toggles/web/client-plugin.tsx +22 -0
- package/src/feature-toggles/web/i18n.ts +29 -0
- package/src/feature-toggles/web/index.ts +6 -0
- package/src/feature-toggles/web/toggle-admin-screen.tsx +136 -0
- package/src/file-foundation/__tests__/feature.test.ts +5 -5
- package/src/file-foundation/__tests__/file-foundation.integration.test.ts +4 -3
- package/src/file-foundation/feature.ts +10 -11
- package/src/files-provider-s3/__tests__/s3-provider.integration.test.ts +27 -2
- package/src/files-provider-s3/s3-provider.ts +12 -1
- package/src/folders/__tests__/folders.integration.test.ts +3 -0
- package/src/folders/web/__tests__/folder-manager.test.tsx +43 -2
- package/src/folders/web/__tests__/tree.test.ts +8 -0
- package/src/folders/web/folder-manager.tsx +2 -3
- package/src/folders/web/i18n.ts +0 -2
- package/src/folders/web/tree.ts +13 -2
- package/src/folders-user-data/__tests__/hooks.integration.test.ts +1 -0
- package/src/folders-user-data/index.ts +4 -4
- package/src/inbound-mail-foundation/__tests__/account-state.test.ts +30 -0
- package/src/inbound-mail-foundation/__tests__/connect-routes.integration.test.ts +307 -0
- package/src/inbound-mail-foundation/__tests__/feature.test.ts +169 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-foundation.integration.test.ts +644 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-provider-contract.ts +111 -0
- package/src/inbound-mail-foundation/__tests__/retention.integration.test.ts +237 -0
- package/src/inbound-mail-foundation/__tests__/watch-supervisor.integration.test.ts +433 -0
- package/src/inbound-mail-foundation/aggregate-id.ts +40 -0
- package/src/inbound-mail-foundation/connect-routes.ts +230 -0
- package/src/inbound-mail-foundation/constants.ts +80 -0
- package/src/inbound-mail-foundation/db/queries/inbound-projections.ts +38 -0
- package/src/inbound-mail-foundation/entities.ts +172 -0
- package/src/inbound-mail-foundation/events.ts +147 -0
- package/src/inbound-mail-foundation/feature.ts +243 -0
- package/src/inbound-mail-foundation/handlers/account-state.ts +23 -0
- package/src/inbound-mail-foundation/handlers/connect-account.write.ts +112 -0
- package/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +69 -0
- package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +333 -0
- package/src/inbound-mail-foundation/handlers/list-accounts.query.ts +44 -0
- package/src/inbound-mail-foundation/handlers/list-messages.query.ts +58 -0
- package/src/inbound-mail-foundation/handlers/scope-visibility.ts +14 -0
- package/src/inbound-mail-foundation/handlers/update-account.write.ts +78 -0
- package/src/inbound-mail-foundation/index.ts +97 -0
- package/src/inbound-mail-foundation/oauth-state.ts +103 -0
- package/src/inbound-mail-foundation/projection.ts +152 -0
- package/src/inbound-mail-foundation/provider-factory.ts +53 -0
- package/src/inbound-mail-foundation/retention-sweep.ts +168 -0
- package/src/inbound-mail-foundation/tenant-destroy-hook.ts +93 -0
- package/src/inbound-mail-foundation/types.ts +250 -0
- package/src/inbound-mail-foundation/watch-supervisor.ts +482 -0
- package/src/inbound-provider-imap/__tests__/feature.test.ts +115 -0
- package/src/inbound-provider-imap/__tests__/imap-foundation.integration.test.ts +302 -0
- package/src/inbound-provider-imap/__tests__/imap-live.integration.test.ts +206 -0
- package/src/inbound-provider-imap/__tests__/plugin-mocked.test.ts +358 -0
- package/src/inbound-provider-imap/credential-document.ts +51 -0
- package/src/inbound-provider-imap/feature.ts +283 -0
- package/src/inbound-provider-imap/imap-client.ts +158 -0
- package/src/inbound-provider-imap/index.ts +17 -0
- package/src/inbound-provider-inmemory/__tests__/feature.test.ts +52 -0
- package/src/inbound-provider-inmemory/feature.ts +154 -0
- package/src/inbound-provider-inmemory/index.ts +12 -0
- package/src/jobs/__tests__/entity-ddl.test.ts +15 -0
- package/src/jobs/__tests__/job-instant.test.ts +15 -0
- package/src/jobs/__tests__/job-system-user.integration.test.ts +4 -8
- package/src/jobs/__tests__/jobs-events.integration.test.ts +37 -1
- package/src/jobs/__tests__/jobs-pii-kms.integration.test.ts +120 -0
- package/src/jobs/__tests__/jobs-screens.boot.test.ts +36 -0
- package/src/jobs/__tests__/jobs-security.integration.test.ts +108 -0
- package/src/jobs/__tests__/reindex-entity-job.integration.test.ts +169 -0
- package/src/jobs/constants.ts +8 -1
- package/src/jobs/feature.ts +52 -8
- package/src/jobs/handlers/detail.query.ts +6 -0
- package/src/jobs/handlers/list.query.ts +8 -1
- package/src/jobs/handlers/projection-rebuild.job.ts +14 -4
- package/src/jobs/handlers/reindex-entity.job.ts +49 -0
- package/src/jobs/i18n.ts +37 -0
- package/src/jobs/job-instant.ts +9 -0
- package/src/jobs/job-run-table.ts +4 -4
- package/src/jobs/web/client-plugin.tsx +22 -0
- package/src/jobs/web/i18n.ts +12 -0
- package/src/jobs/web/index.ts +4 -0
- package/src/jobs/web/job-run-detail-screen.tsx +180 -0
- package/src/jobs/web/job-runs-screen.tsx +146 -0
- package/src/ledger/__tests__/ledger.integration.test.ts +8 -3
- package/src/ledger/handlers/confirm-schedule-period.write.ts +17 -4
- package/src/ledger/recurring.ts +17 -8
- package/src/ledger/web/index.ts +3 -2
- package/src/legal-pages/__tests__/legal-pages.integration.test.ts +55 -0
- package/src/legal-pages/feature.ts +24 -31
- package/src/legal-pages/index.ts +1 -0
- package/src/mail-foundation/__tests__/feature.test.ts +5 -6
- package/src/mail-foundation/__tests__/mail-foundation.integration.test.ts +4 -3
- package/src/mail-foundation/__tests__/mail-transport-contract.ts +58 -0
- package/src/mail-foundation/feature.ts +26 -24
- package/src/mail-transport-inmemory/__tests__/feature.test.ts +23 -0
- package/src/mail-transport-smtp/__tests__/feature.test.ts +65 -0
- package/src/managed-pages/__tests__/branding-coerce.test.ts +2 -6
- package/src/managed-pages/__tests__/i18n-surface.boot.test.ts +30 -0
- package/src/managed-pages/__tests__/managed-pages.integration.test.ts +137 -0
- package/src/managed-pages/feature.ts +45 -47
- package/src/managed-pages/handlers/branding.query.ts +5 -1
- package/src/managed-pages/handlers/by-tenant-published.query.ts +52 -0
- package/src/managed-pages/i18n.ts +66 -0
- package/src/managed-pages/screens/page-screens.ts +5 -1
- package/src/managed-pages/web/client-plugin.tsx +22 -0
- package/src/managed-pages/web/i18n.ts +24 -0
- package/src/managed-pages/web/index.ts +3 -0
- package/src/page-render/__tests__/layout.test.ts +63 -0
- package/src/page-render/__tests__/markdown.test.ts +29 -0
- package/src/page-render/branding.ts +4 -3
- package/src/page-render/index.ts +1 -1
- package/src/page-render/layout.ts +18 -3
- package/src/page-render/markdown.ts +1 -9
- package/src/page-render/web.ts +13 -0
- package/src/personal-access-tokens/__tests__/feature.test.ts +42 -0
- package/src/personal-access-tokens/__tests__/pat.integration.test.ts +239 -0
- package/src/personal-access-tokens/__tests__/scopes.test.ts +42 -0
- package/src/personal-access-tokens/constants.ts +33 -0
- package/src/personal-access-tokens/feature.ts +105 -0
- package/src/personal-access-tokens/handlers/available-scopes.query.ts +21 -0
- package/src/personal-access-tokens/handlers/create.write.ts +47 -0
- package/src/personal-access-tokens/handlers/list.query.ts +50 -0
- package/src/personal-access-tokens/handlers/revoke.write.ts +37 -0
- package/src/personal-access-tokens/hash.ts +17 -0
- package/src/personal-access-tokens/i18n.ts +5 -0
- package/src/personal-access-tokens/index.ts +31 -0
- package/src/personal-access-tokens/resolver.ts +58 -0
- package/src/personal-access-tokens/roles.ts +29 -0
- package/src/personal-access-tokens/schema/api-token.ts +63 -0
- package/src/personal-access-tokens/scopes.ts +43 -0
- package/src/personal-access-tokens/web/client-plugin.tsx +29 -0
- package/src/personal-access-tokens/web/i18n.ts +69 -0
- package/src/personal-access-tokens/web/index.ts +7 -0
- package/src/personal-access-tokens/web/pat-tokens-screen.tsx +265 -0
- package/src/readiness/__tests__/readiness.integration.test.ts +4 -3
- package/src/renderer-simple/__tests__/resolve-variables.test.ts +37 -0
- package/src/renderer-simple/resolve-variables.ts +3 -1
- package/src/secrets/__tests__/rotate.integration.test.ts +115 -18
- package/src/secrets/handlers/rotate.job.ts +77 -98
- package/src/secrets/secrets-context.ts +7 -38
- package/src/secrets/table.ts +7 -8
- package/src/seo/__tests__/llms-txt.test.ts +38 -0
- package/src/seo/__tests__/robots-txt.test.ts +18 -0
- package/src/seo/__tests__/schema-builders.test.ts +57 -0
- package/src/seo/__tests__/seo.integration.test.ts +350 -0
- package/src/seo/__tests__/sitemap.test.ts +38 -0
- package/src/seo/constants.ts +60 -0
- package/src/seo/feature.ts +316 -0
- package/src/seo/handlers/seo-config.query.ts +35 -0
- package/src/seo/index.ts +19 -0
- package/src/seo/llms-txt.ts +33 -0
- package/src/seo/robots-txt.ts +13 -0
- package/src/seo/schema-builders.ts +57 -0
- package/src/seo/sitemap.ts +33 -0
- package/src/sessions/__tests__/auto-revoke-binding.test.ts +71 -0
- package/src/sessions/__tests__/cleanup.integration.test.ts +2 -1
- package/src/sessions/__tests__/password-auto-revoke.integration.test.ts +11 -7
- package/src/sessions/__tests__/rebuild-survival.integration.test.ts +18 -7
- package/src/sessions/__tests__/session-store-registration.test.ts +21 -0
- package/src/sessions/__tests__/sessions-screens.boot.test.ts +55 -0
- package/src/sessions/__tests__/sessions.integration.test.ts +272 -20
- package/src/sessions/__tests__/test-helpers.ts +16 -3
- package/src/sessions/constants.ts +5 -0
- package/src/sessions/db/queries/cleanup.ts +2 -2
- package/src/sessions/feature.ts +175 -26
- package/src/sessions/handlers/detail.query.ts +36 -0
- package/src/sessions/handlers/list.query.ts +14 -9
- package/src/sessions/handlers/mine.query.ts +13 -8
- package/src/sessions/handlers/revoke-all-for-user.write.ts +51 -12
- package/src/sessions/handlers/revoke-all-others.write.ts +19 -1
- package/src/sessions/handlers/revoke.write.ts +13 -0
- package/src/sessions/i18n.ts +23 -0
- package/src/sessions/index.ts +9 -2
- package/src/sessions/schema/user-session.ts +8 -2
- package/src/sessions/session-callbacks.ts +99 -14
- package/src/sessions/session-checker-fail-open.test.ts +77 -0
- package/src/sessions/session-revoked-event.ts +29 -0
- package/src/sessions/testing.ts +14 -1
- package/src/shared/__tests__/chunked-entity-migration.test.ts +145 -0
- package/src/shared/chunked-entity-migration.ts +89 -0
- package/src/shared/decrypt-stored-pii.ts +26 -0
- package/src/shared/encrypt-for-direct-write.ts +23 -0
- package/src/shared/entities-of.ts +17 -0
- package/src/shared/grace-period.test.ts +26 -0
- package/src/shared/grace-period.ts +20 -0
- package/src/{auth-email-password/__tests__ → shared}/identity-v3-hash.test.ts +2 -2
- package/src/shared/index.ts +16 -0
- package/src/shared/map-with-concurrency.test.ts +104 -0
- package/src/shared/map-with-concurrency.ts +38 -0
- package/src/shared/password-hashing.test.ts +35 -0
- package/src/{auth-email-password → shared}/password-hashing.ts +11 -0
- package/src/shared/system-query.ts +5 -0
- package/src/{auth-email-password → shared}/token-burn-store.ts +1 -1
- package/src/subscription-mollie/__tests__/feature.test.ts +97 -0
- package/src/subscription-mollie/__tests__/mollie-foundation.integration.test.ts +12 -1
- package/src/subscription-mollie/__tests__/verify-webhook.test.ts +21 -0
- package/src/subscription-mollie/verify-webhook.ts +1 -0
- package/src/subscription-stripe/__tests__/feature.test.ts +127 -1
- package/src/subscription-stripe/__tests__/runtime.test.ts +14 -24
- package/src/subscription-stripe/__tests__/stripe-foundation.integration.test.ts +15 -5
- package/src/subscription-stripe/__tests__/verify-webhook.test.ts +20 -0
- package/src/subscription-stripe/feature.ts +2 -0
- package/src/subscription-stripe/verify-webhook.ts +3 -2
- package/src/tags/__tests__/schemas.test.ts +34 -0
- package/src/tags/__tests__/tags.integration.test.ts +12 -1
- package/src/tags/entity.ts +6 -1
- package/src/tags/feature.ts +2 -0
- package/src/tags/i18n.ts +5 -0
- package/src/tags/schemas.ts +9 -2
- package/src/tags/web/__tests__/tag-manager.test.tsx +333 -0
- package/src/tags/web/__tests__/tag-section.test.tsx +47 -1
- package/src/tags/web/tag-manager.tsx +1 -1
- package/src/tags/web/tag-section.tsx +6 -3
- package/src/template-resolver/testing.ts +2 -1
- package/src/tenant/__tests__/members-query-kms.integration.test.ts +98 -0
- package/src/tenant/__tests__/members-screens.boot.test.ts +51 -0
- package/src/tenant/__tests__/multi-tenant.integration.test.ts +1 -1
- package/src/tenant/__tests__/tenant-security.integration.test.ts +310 -0
- package/src/tenant/__tests__/tenant.integration.test.ts +8 -4
- package/src/tenant/constants.ts +5 -0
- package/src/tenant/feature.ts +19 -0
- package/src/tenant/handlers/active-tenant-ids.query.ts +8 -2
- package/src/tenant/handlers/cancel-invitation.write.ts +2 -2
- package/src/tenant/handlers/invitations.query.ts +21 -4
- package/src/tenant/handlers/members.query.ts +41 -6
- package/src/tenant/i18n.ts +28 -0
- package/src/tenant/index.ts +7 -2
- package/src/tenant/invitation-table.ts +3 -2
- package/src/tenant/schema/tenant.ts +27 -2
- package/src/tenant/screens.ts +3 -2
- package/src/tenant/seeding.ts +136 -3
- package/src/tenant/web/client-plugin.tsx +20 -0
- package/src/tenant/web/i18n.ts +43 -0
- package/src/tenant/web/index.ts +4 -0
- package/src/tenant/web/members-screen.tsx +270 -0
- package/src/tenant-lifecycle/__tests__/stages.test.ts +22 -0
- package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +471 -0
- package/src/tenant-lifecycle/boot-checks.ts +45 -0
- package/src/tenant-lifecycle/constants.ts +56 -0
- package/src/tenant-lifecycle/events.ts +46 -0
- package/src/tenant-lifecycle/feature.ts +112 -0
- package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +79 -0
- package/src/tenant-lifecycle/handlers/request-destruction.write.ts +83 -0
- package/src/tenant-lifecycle/index.ts +7 -0
- package/src/tenant-lifecycle/lib/revoke-tenant-sessions.ts +16 -0
- package/src/tenant-lifecycle/lifecycle-gate.ts +65 -0
- package/src/tenant-lifecycle/run-tenant-destroy.ts +368 -0
- package/src/tenant-lifecycle/stages.ts +206 -0
- package/src/tenant-settings/__tests__/tenant-settings.integration.test.ts +115 -0
- package/src/tenant-settings/config.ts +44 -0
- package/src/tenant-settings/constants.ts +16 -0
- package/src/tenant-settings/feature.ts +49 -0
- package/src/tenant-settings/index.ts +8 -0
- package/src/tenant-settings/tenant-defaults.ts +67 -0
- package/src/text-content/web/__tests__/editor-read-only.test.tsx +33 -6
- package/src/text-content/web/__tests__/group-blocks.test.ts +28 -3
- package/src/text-content/web/client-plugin.tsx +126 -104
- package/src/tier-engine/__tests__/auto-default-tier.integration.test.ts +1 -1
- package/src/tier-engine/__tests__/resolver.integration.test.ts +1 -1
- package/src/tier-engine/__tests__/tier-engine.integration.test.ts +7 -4
- package/src/tier-engine/__tests__/trial-config-validation.test.ts +30 -0
- package/src/tier-engine/feature.ts +13 -6
- package/src/tier-engine/i18n.ts +8 -0
- package/src/tier-engine/web/tier-admin-screen.tsx +26 -19
- package/src/user/__tests__/stream-tenant-backfill.integration.test.ts +186 -0
- package/src/user/__tests__/user.integration.test.ts +26 -0
- package/src/user/constants.ts +1 -0
- package/src/user/db/queries/stream-tenant-backfill.ts +112 -0
- package/src/user/feature.ts +3 -0
- package/src/user/handlers/find-for-auth.query.ts +8 -6
- package/src/user/i18n.ts +20 -0
- package/src/user/index.ts +4 -0
- package/src/user/schema/user.ts +6 -2
- package/src/user/screens.ts +3 -15
- package/src/user/seeding.ts +8 -0
- package/src/user-data-rights/COMPLIANCE.md +21 -1
- package/src/user-data-rights/__tests__/anonymous-deletion-kms.integration.test.ts +145 -0
- package/src/user-data-rights/__tests__/anonymous-deletion.integration.test.ts +3 -0
- package/src/user-data-rights/__tests__/audit-log.integration.test.ts +67 -0
- package/src/user-data-rights/__tests__/boot-checks.test.ts +170 -0
- package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/download-by-token-resolver-trust.integration.test.ts +169 -0
- package/src/user-data-rights/__tests__/download.integration.test.ts +101 -33
- package/src/user-data-rights/__tests__/email-templates.test.ts +15 -0
- package/src/user-data-rights/__tests__/export-encrypted-fields.integration.test.ts +154 -0
- package/src/user-data-rights/__tests__/export-job-idempotency.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/export-job-list-cross-tenant.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/file-binary-forget-cleanup.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-binary-forget-failure.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-retention.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-storage-unification.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/forget-cleanup-hook-ordering.integration.test.ts +41 -62
- package/src/user-data-rights/__tests__/forget-hook-registry.integration.test.ts +110 -0
- package/src/user-data-rights/__tests__/inspector-screens.boot.test.ts +14 -0
- package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +19 -2
- package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +25 -14
- package/src/user-data-rights/__tests__/request-cancel-deletion.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/request-deletion-callback.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/request-export.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +172 -19
- package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/run-export-jobs.integration.test.ts +257 -26
- package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +339 -5
- package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +77 -1
- package/src/user-data-rights/__tests__/tenant-model-erasure.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/user-data-rights.integration.test.ts +9 -1
- package/src/user-data-rights/boot-checks.ts +70 -0
- package/src/user-data-rights/db/queries/__tests__/forget-cleanup.integration.test.ts +69 -0
- package/src/user-data-rights/db/queries/forget-cleanup.ts +9 -2
- package/src/user-data-rights/email-templates.ts +6 -6
- package/src/user-data-rights/feature.ts +162 -43
- package/src/user-data-rights/handlers/cancel-deletion.write.ts +8 -11
- package/src/user-data-rights/handlers/deletion-grace-period.ts +4 -1
- package/src/user-data-rights/handlers/download-by-job.query.ts +26 -18
- package/src/user-data-rights/handlers/download-by-token.query.ts +23 -15
- package/src/user-data-rights/handlers/lift-restriction.write.ts +44 -19
- package/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -0
- package/src/user-data-rights/handlers/my-audit-log.query.ts +1 -0
- package/src/user-data-rights/handlers/request-deletion-by-email.write.ts +3 -1
- package/src/user-data-rights/handlers/restrict-account.write.ts +58 -7
- package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +2 -0
- package/src/user-data-rights/i18n.ts +35 -32
- package/src/user-data-rights/lib/is-admin-actor.ts +19 -0
- package/src/user-data-rights/lib/storage-provider-resolver.ts +6 -5
- package/src/user-data-rights/lib/update-user-lifecycle.ts +8 -24
- package/src/user-data-rights/run-export-jobs.ts +55 -19
- package/src/user-data-rights/run-forget-cleanup.ts +112 -7
- package/src/user-data-rights/run-user-export.ts +70 -4
- package/src/user-data-rights/schema/download-token.ts +5 -3
- package/src/user-data-rights/schema/export-job.ts +2 -0
- package/src/user-data-rights/screens.ts +2 -1
- package/src/user-data-rights/web/__tests__/privacy-center-screen.test.tsx +1 -0
- package/src/user-data-rights/web/__tests__/public-deletion-gate.test.tsx +7 -1
- package/src/user-data-rights/web/client-plugin.tsx +6 -1
- package/src/user-data-rights/web/i18n.ts +30 -10
- package/src/user-data-rights/web/privacy-center-screen.tsx +5 -3
- package/src/user-data-rights-defaults/__tests__/bundled-entities.userdata-hooks.integration.test.ts +455 -0
- package/src/user-data-rights-defaults/__tests__/feature-mounted.test.ts +24 -0
- package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.test.ts +33 -9
- package/src/user-data-rights-defaults/feature.ts +78 -1
- package/src/user-data-rights-defaults/hooks/api-token.userdata-hook.ts +37 -0
- package/src/user-data-rights-defaults/hooks/config-value.userdata-hook.ts +51 -0
- package/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +37 -0
- package/src/user-data-rights-defaults/hooks/feature-mounted.ts +10 -0
- package/src/user-data-rights-defaults/hooks/in-app-message.userdata-hook.ts +38 -0
- package/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +35 -0
- package/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +54 -0
- package/src/user-data-rights-defaults/hooks/tenant-invitation.userdata-hook.ts +112 -0
- package/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +42 -0
- package/src/user-data-rights-defaults/index.ts +26 -0
- package/src/user-profile/__tests__/change-email.integration.test.ts +7 -1
- package/src/user-profile/__tests__/profile-screen.test.tsx +5 -0
- package/src/user-profile/feature.ts +4 -1
- package/src/user-profile/handlers/change-email.write.ts +15 -10
- package/src/user-profile/web/profile-screen.tsx +15 -3
- package/src/auth-email-password/stream-tenant.ts +0 -31
- package/src/feature-toggles/db/queries/toggle-state.ts +0 -25
- package/src/legal-pages/security-headers.ts +0 -1
- package/src/sessions/schema/index.ts +0 -5
- package/src/tenant/schema/index.ts +0 -5
- package/src/user/schema/index.ts +0 -5
- /package/src/{auth-email-password → shared}/identity-v3-hash.ts +0 -0
package/src/user-data-rights-defaults/__tests__/bundled-entities.userdata-hooks.integration.test.ts
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
// userData-Hook Integration-Tests für die Bundled-Entity-Hooks (GDPR-Audit
|
|
2
|
+
// 2026-07): user-session, api-token, in-app-message, tenant-invitation,
|
|
3
|
+
// notification-preference, config-value.
|
|
4
|
+
//
|
|
5
|
+
// Zwei Stacks:
|
|
6
|
+
// FULL — alle Source-Features gemountet; testet Export-Inhalt, Forget-
|
|
7
|
+
// Semantik (hard-delete vs. executor-forget vs. anonymize) und
|
|
8
|
+
// Tenant-Isolation.
|
|
9
|
+
// MINIMAL — nur user/files/udr/defaults; testet das Presence-Gating: jeder
|
|
10
|
+
// Hook muss null/no-op liefern statt gegen fehlende Tabellen zu
|
|
11
|
+
// crashen (der Export-Runner hat kein per-Hook try/catch).
|
|
12
|
+
|
|
13
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
14
|
+
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
15
|
+
import {
|
|
16
|
+
setupTestStack,
|
|
17
|
+
type TestStack,
|
|
18
|
+
unsafeCreateEntityTable,
|
|
19
|
+
unsafePushTables,
|
|
20
|
+
} from "@cosmicdrift/kumiko-framework/stack";
|
|
21
|
+
import { seedRow } from "@cosmicdrift/kumiko-framework/testing";
|
|
22
|
+
import { authFoundationFeature } from "../../auth-foundation";
|
|
23
|
+
import { createChannelInAppFeature, inAppMessagesTable } from "../../channel-in-app";
|
|
24
|
+
import { createComplianceProfilesFeature } from "../../compliance-profiles";
|
|
25
|
+
import { configValueEntity, createConfigFeature } from "../../config";
|
|
26
|
+
import { createDataRetentionFeature } from "../../data-retention";
|
|
27
|
+
import { createDeliveryFeature, notificationPreferenceEntity } from "../../delivery";
|
|
28
|
+
import { createFilesFeature } from "../../files";
|
|
29
|
+
import { createJobsFeature, jobRunLogsTable, jobRunsTable } from "../../jobs";
|
|
30
|
+
import {
|
|
31
|
+
apiTokenEntity,
|
|
32
|
+
createPersonalAccessTokensFeature,
|
|
33
|
+
PatQueries,
|
|
34
|
+
type PatScopeConfig,
|
|
35
|
+
} from "../../personal-access-tokens";
|
|
36
|
+
import { createSessionsFeature, userSessionEntity } from "../../sessions";
|
|
37
|
+
import { createTenantFeature, tenantInvitationEntity } from "../../tenant";
|
|
38
|
+
import { createUserFeature, USER_STATUS, userEntity, userTable } from "../../user";
|
|
39
|
+
import { createUserDataRightsFeature } from "../../user-data-rights";
|
|
40
|
+
import { createUserDataRightsDefaultsFeature } from "../feature";
|
|
41
|
+
import {
|
|
42
|
+
apiTokenDeleteHook,
|
|
43
|
+
apiTokenExportHook,
|
|
44
|
+
configValueDeleteHook,
|
|
45
|
+
configValueExportHook,
|
|
46
|
+
deliveryAttemptExportHook,
|
|
47
|
+
inAppMessageDeleteHook,
|
|
48
|
+
inAppMessageExportHook,
|
|
49
|
+
jobRunExportHook,
|
|
50
|
+
notificationPreferenceDeleteHook,
|
|
51
|
+
notificationPreferenceExportHook,
|
|
52
|
+
tenantInvitationDeleteHook,
|
|
53
|
+
tenantInvitationExportHook,
|
|
54
|
+
userSessionDeleteHook,
|
|
55
|
+
userSessionExportHook,
|
|
56
|
+
} from "../index";
|
|
57
|
+
|
|
58
|
+
const PAT_SCOPES: PatScopeConfig = {
|
|
59
|
+
tokens: { label: "Tokens", read: [PatQueries.mine] },
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
let full: TestStack;
|
|
63
|
+
let minimal: TestStack;
|
|
64
|
+
|
|
65
|
+
beforeAll(async () => {
|
|
66
|
+
full = await setupTestStack({
|
|
67
|
+
features: [
|
|
68
|
+
createUserFeature(),
|
|
69
|
+
createFilesFeature(),
|
|
70
|
+
createDataRetentionFeature(),
|
|
71
|
+
createComplianceProfilesFeature(),
|
|
72
|
+
createSessionsFeature(),
|
|
73
|
+
createConfigFeature(),
|
|
74
|
+
createTenantFeature(),
|
|
75
|
+
createPersonalAccessTokensFeature({ scopes: PAT_SCOPES }),
|
|
76
|
+
authFoundationFeature,
|
|
77
|
+
createDeliveryFeature(),
|
|
78
|
+
createChannelInAppFeature(),
|
|
79
|
+
createJobsFeature(),
|
|
80
|
+
createUserDataRightsFeature(),
|
|
81
|
+
createUserDataRightsDefaultsFeature(),
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
await unsafeCreateEntityTable(full.db, userEntity);
|
|
85
|
+
await unsafeCreateEntityTable(full.db, userSessionEntity);
|
|
86
|
+
await unsafeCreateEntityTable(full.db, apiTokenEntity);
|
|
87
|
+
await unsafeCreateEntityTable(full.db, tenantInvitationEntity);
|
|
88
|
+
await unsafeCreateEntityTable(full.db, notificationPreferenceEntity);
|
|
89
|
+
await unsafeCreateEntityTable(full.db, configValueEntity);
|
|
90
|
+
await unsafePushTables(full.db, { inAppMessagesTable, jobRunsTable, jobRunLogsTable });
|
|
91
|
+
|
|
92
|
+
minimal = await setupTestStack({
|
|
93
|
+
features: [
|
|
94
|
+
createUserFeature(),
|
|
95
|
+
createFilesFeature(),
|
|
96
|
+
createDataRetentionFeature(),
|
|
97
|
+
createComplianceProfilesFeature(),
|
|
98
|
+
authFoundationFeature,
|
|
99
|
+
createSessionsFeature(),
|
|
100
|
+
createUserDataRightsFeature(),
|
|
101
|
+
createUserDataRightsDefaultsFeature(),
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
afterAll(async () => {
|
|
107
|
+
await full.cleanup();
|
|
108
|
+
await minimal.cleanup();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const TENANT_A = "00000000-0000-4000-8000-00000000000a";
|
|
112
|
+
const TENANT_B = "00000000-0000-4000-8000-00000000000b";
|
|
113
|
+
|
|
114
|
+
function uuid(suffix: number): string {
|
|
115
|
+
return `bbbbbbbb-bbbb-4bbb-8bbb-${suffix.toString(16).padStart(12, "0")}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function ctx(userId: string, overrides: Record<string, unknown> = {}) {
|
|
119
|
+
return {
|
|
120
|
+
db: full.db,
|
|
121
|
+
registry: full.registry,
|
|
122
|
+
tenantId: TENANT_A,
|
|
123
|
+
userId,
|
|
124
|
+
...overrides,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function rawSelect(sql: string, params: unknown[]) {
|
|
129
|
+
const result = await asRawClient(full.db).unsafe(sql, params);
|
|
130
|
+
// biome-ignore lint/suspicious/noExplicitAny: drizzle execute typing
|
|
131
|
+
return ((result as any).rows ?? result) as Array<Record<string, unknown>>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function seedUser(id: string): Promise<void> {
|
|
135
|
+
const SYSTEM_TENANT = "00000000-0000-4000-8000-000000000001";
|
|
136
|
+
await seedRow(full.db, userTable, {
|
|
137
|
+
id,
|
|
138
|
+
tenantId: SYSTEM_TENANT,
|
|
139
|
+
email: `user-${id}@example.com`,
|
|
140
|
+
passwordHash: "hashed-password",
|
|
141
|
+
displayName: `User ${id}`,
|
|
142
|
+
locale: "de",
|
|
143
|
+
emailVerified: true,
|
|
144
|
+
roles: '["Member"]',
|
|
145
|
+
status: USER_STATUS.Active,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
describe("user-session userData-hooks", () => {
|
|
150
|
+
async function seedSession(id: string, userId: string, tenantId: string): Promise<void> {
|
|
151
|
+
await asRawClient(full.db).unsafe(
|
|
152
|
+
`INSERT INTO store_user_sessions (id, user_id, tenant_id, created_at, expires_at, ip, user_agent)
|
|
153
|
+
VALUES ($1, $2, $3, now(), now() + interval '1 day', '203.0.113.7', 'TestAgent/1.0')`,
|
|
154
|
+
[id, userId, tenantId],
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
test("export liefert ip/userAgent, aber keine Session-Id (jti)", async () => {
|
|
159
|
+
await seedSession(uuid(1), "sess-user-1", TENANT_A);
|
|
160
|
+
|
|
161
|
+
const result = await userSessionExportHook(ctx("sess-user-1"));
|
|
162
|
+
expect(result?.entity).toBe("user-session");
|
|
163
|
+
expect(result?.rows).toHaveLength(1);
|
|
164
|
+
expect(result?.rows[0]?.["ip"]).toBe("203.0.113.7");
|
|
165
|
+
expect(result?.rows[0]?.["userAgent"]).toBe("TestAgent/1.0");
|
|
166
|
+
expect(result?.rows[0]?.["id"]).toBeUndefined();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("delete entfernt Sessions des Users, Cross-Tenant bleibt", async () => {
|
|
170
|
+
await seedSession(uuid(2), "sess-user-2", TENANT_A);
|
|
171
|
+
await seedSession(uuid(3), "sess-user-2", TENANT_B);
|
|
172
|
+
|
|
173
|
+
await userSessionDeleteHook(ctx("sess-user-2"), "delete");
|
|
174
|
+
|
|
175
|
+
const a = await rawSelect(
|
|
176
|
+
"SELECT * FROM store_user_sessions WHERE user_id = $1 AND tenant_id = $2",
|
|
177
|
+
["sess-user-2", TENANT_A],
|
|
178
|
+
);
|
|
179
|
+
const b = await rawSelect(
|
|
180
|
+
"SELECT * FROM store_user_sessions WHERE user_id = $1 AND tenant_id = $2",
|
|
181
|
+
["sess-user-2", TENANT_B],
|
|
182
|
+
);
|
|
183
|
+
expect(a).toHaveLength(0);
|
|
184
|
+
expect(b).toHaveLength(1);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe("api-token userData-hooks", () => {
|
|
189
|
+
async function seedToken(id: string, userId: string): Promise<void> {
|
|
190
|
+
await asRawClient(full.db).unsafe(
|
|
191
|
+
`INSERT INTO store_api_tokens (id, user_id, tenant_id, name, token_hash, prefix, scopes, created_at)
|
|
192
|
+
VALUES ($1, $2, $3, 'My MacBook', $4, 'kum_ab12', '["read"]', now())`,
|
|
193
|
+
[id, userId, TENANT_A, `hash-${id}`],
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
test("export liefert Token-Metadata ohne tokenHash", async () => {
|
|
198
|
+
await seedToken(uuid(10), "pat-user-1");
|
|
199
|
+
|
|
200
|
+
const result = await apiTokenExportHook(ctx("pat-user-1"));
|
|
201
|
+
expect(result?.entity).toBe("api-token");
|
|
202
|
+
expect(result?.rows).toHaveLength(1);
|
|
203
|
+
expect(result?.rows[0]?.["name"]).toBe("My MacBook");
|
|
204
|
+
expect(result?.rows[0]?.["prefix"]).toBe("kum_ab12");
|
|
205
|
+
expect(result?.rows[0]?.["tokenHash"]).toBeUndefined();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("delete entfernt Tokens (= Revoke)", async () => {
|
|
209
|
+
await seedToken(uuid(11), "pat-user-2");
|
|
210
|
+
|
|
211
|
+
await apiTokenDeleteHook(ctx("pat-user-2"), "delete");
|
|
212
|
+
|
|
213
|
+
const rows = await rawSelect("SELECT * FROM store_api_tokens WHERE user_id = $1", [
|
|
214
|
+
"pat-user-2",
|
|
215
|
+
]);
|
|
216
|
+
expect(rows).toHaveLength(0);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("in-app-message userData-hooks", () => {
|
|
221
|
+
async function seedMessage(userId: string, title: string): Promise<void> {
|
|
222
|
+
await asRawClient(full.db).unsafe(
|
|
223
|
+
`INSERT INTO in_app_messages (tenant_id, user_id, notification_type, title, body)
|
|
224
|
+
VALUES ($1, $2, 'test:notify', $3, 'Hallo Marc, dein Export ist da')`,
|
|
225
|
+
[TENANT_A, userId, title],
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
test("export liefert Messages, delete entfernt sie", async () => {
|
|
230
|
+
await seedMessage("inapp-user-1", "Export fertig");
|
|
231
|
+
|
|
232
|
+
const result = await inAppMessageExportHook(ctx("inapp-user-1"));
|
|
233
|
+
expect(result?.entity).toBe("in-app-message");
|
|
234
|
+
expect(result?.rows[0]?.["title"]).toBe("Export fertig");
|
|
235
|
+
|
|
236
|
+
await inAppMessageDeleteHook(ctx("inapp-user-1"), "delete");
|
|
237
|
+
const rows = await rawSelect("SELECT * FROM in_app_messages WHERE user_id = $1", [
|
|
238
|
+
"inapp-user-1",
|
|
239
|
+
]);
|
|
240
|
+
expect(rows).toHaveLength(0);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe("tenant-invitation userData-hooks", () => {
|
|
245
|
+
async function seedInvitation(
|
|
246
|
+
id: string,
|
|
247
|
+
email: string,
|
|
248
|
+
invitedBy: string,
|
|
249
|
+
tenantId = TENANT_A,
|
|
250
|
+
): Promise<void> {
|
|
251
|
+
await asRawClient(full.db).unsafe(
|
|
252
|
+
`INSERT INTO read_tenant_invitations (id, tenant_id, email, role, status, invited_by, expires_at)
|
|
253
|
+
VALUES ($1, $2, $3, 'Member', 'pending', $4, now() + interval '7 days')`,
|
|
254
|
+
[id, tenantId, email, invitedBy],
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
test("export matcht Invitations über die User-Email", async () => {
|
|
259
|
+
await seedUser(uuid(20));
|
|
260
|
+
await seedInvitation(uuid(21), `user-${uuid(20)}@example.com`, "admin-1");
|
|
261
|
+
|
|
262
|
+
const result = await tenantInvitationExportHook(ctx(uuid(20)));
|
|
263
|
+
expect(result?.entity).toBe("tenant-invitation");
|
|
264
|
+
expect(result?.rows).toHaveLength(1);
|
|
265
|
+
expect(result?.rows[0]?.["role"]).toBe("Member");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("forget delete purged die Invitee-Row (rebuild-sicher via Executor)", async () => {
|
|
269
|
+
await seedUser(uuid(22));
|
|
270
|
+
await seedInvitation(uuid(23), `user-${uuid(22)}@example.com`, "admin-1");
|
|
271
|
+
|
|
272
|
+
await tenantInvitationDeleteHook(ctx(uuid(22)), "delete");
|
|
273
|
+
|
|
274
|
+
const rows = await rawSelect("SELECT * FROM read_tenant_invitations WHERE id = $1", [uuid(23)]);
|
|
275
|
+
expect(rows).toHaveLength(0);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("anonymize pseudonymisiert die Invitee-Email (unique-index-sicher)", async () => {
|
|
279
|
+
await seedUser(uuid(24));
|
|
280
|
+
await seedInvitation(uuid(25), `user-${uuid(24)}@example.com`, "admin-1");
|
|
281
|
+
|
|
282
|
+
await tenantInvitationDeleteHook(ctx(uuid(24)), "anonymize");
|
|
283
|
+
|
|
284
|
+
const rows = await rawSelect("SELECT email FROM read_tenant_invitations WHERE id = $1", [
|
|
285
|
+
uuid(25),
|
|
286
|
+
]);
|
|
287
|
+
expect(rows).toHaveLength(1);
|
|
288
|
+
expect(String(rows[0]?.["email"])).toBe(`forgotten-${uuid(25)}@anonymized.invalid`);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("userEmailBeforeDelete hat Vorrang (User-Row schon anonymisiert)", async () => {
|
|
292
|
+
await seedInvitation(uuid(26), "vanished@example.com", "admin-1");
|
|
293
|
+
|
|
294
|
+
await tenantInvitationDeleteHook(
|
|
295
|
+
ctx("ghost-user-forgotten", { userEmailBeforeDelete: "Vanished@Example.com" }),
|
|
296
|
+
"delete",
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const rows = await rawSelect("SELECT * FROM read_tenant_invitations WHERE id = $1", [uuid(26)]);
|
|
300
|
+
expect(rows).toHaveLength(0);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("Inviter-Forget severs invitedBy, Row bleibt (gehört dem Invitee)", async () => {
|
|
304
|
+
// userId als echte UUID — der Email-Lookup geht gegen read_users.id (uuid).
|
|
305
|
+
const inviter = uuid(28);
|
|
306
|
+
await seedInvitation(uuid(27), "someone-else@example.com", inviter);
|
|
307
|
+
|
|
308
|
+
await tenantInvitationDeleteHook(ctx(inviter), "delete");
|
|
309
|
+
|
|
310
|
+
const rows = await rawSelect(
|
|
311
|
+
"SELECT email, invited_by FROM read_tenant_invitations WHERE id = $1",
|
|
312
|
+
[uuid(27)],
|
|
313
|
+
);
|
|
314
|
+
expect(rows).toHaveLength(1);
|
|
315
|
+
expect(rows[0]?.["email"]).toBe("someone-else@example.com");
|
|
316
|
+
expect(rows[0]?.["invited_by"]).toBe("anonymized");
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe("notification-preference userData-hooks", () => {
|
|
321
|
+
async function seedPreference(id: string, userId: string): Promise<void> {
|
|
322
|
+
await asRawClient(full.db).unsafe(
|
|
323
|
+
`INSERT INTO read_notification_preferences (id, tenant_id, user_id, notification_type, channel, enabled)
|
|
324
|
+
VALUES ($1, $2, $3, 'test:notify', 'email', false)`,
|
|
325
|
+
[id, TENANT_A, userId],
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
test("export liefert Prefs, forget purged sie via Executor", async () => {
|
|
330
|
+
await seedPreference(uuid(30), "pref-user-1");
|
|
331
|
+
|
|
332
|
+
const result = await notificationPreferenceExportHook(ctx("pref-user-1"));
|
|
333
|
+
expect(result?.entity).toBe("notification-preference");
|
|
334
|
+
expect(result?.rows[0]?.["channel"]).toBe("email");
|
|
335
|
+
expect(result?.rows[0]?.["enabled"]).toBe(false);
|
|
336
|
+
|
|
337
|
+
await notificationPreferenceDeleteHook(ctx("pref-user-1"), "delete");
|
|
338
|
+
const rows = await rawSelect("SELECT * FROM read_notification_preferences WHERE user_id = $1", [
|
|
339
|
+
"pref-user-1",
|
|
340
|
+
]);
|
|
341
|
+
expect(rows).toHaveLength(0);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe("config-value userData-hooks", () => {
|
|
346
|
+
async function seedConfig(id: string, key: string, userId: string | null): Promise<void> {
|
|
347
|
+
await asRawClient(full.db).unsafe(
|
|
348
|
+
`INSERT INTO read_config_values (id, tenant_id, key, value, user_id)
|
|
349
|
+
VALUES ($1, $2, $3, '"dark"', $4)`,
|
|
350
|
+
[id, TENANT_A, key, userId],
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
test("export + forget treffen NUR user-scoped Rows, tenant-scope bleibt", async () => {
|
|
355
|
+
await seedConfig(uuid(40), "ui.theme", "cfg-user-1");
|
|
356
|
+
await seedConfig(uuid(41), "tenant.brand", null);
|
|
357
|
+
|
|
358
|
+
const result = await configValueExportHook(ctx("cfg-user-1"));
|
|
359
|
+
expect(result?.rows).toHaveLength(1);
|
|
360
|
+
expect(result?.rows[0]?.["key"]).toBe("ui.theme");
|
|
361
|
+
|
|
362
|
+
await configValueDeleteHook(ctx("cfg-user-1"), "delete");
|
|
363
|
+
const userRows = await rawSelect("SELECT * FROM read_config_values WHERE user_id = $1", [
|
|
364
|
+
"cfg-user-1",
|
|
365
|
+
]);
|
|
366
|
+
const tenantRows = await rawSelect("SELECT * FROM read_config_values WHERE id = $1", [
|
|
367
|
+
uuid(41),
|
|
368
|
+
]);
|
|
369
|
+
expect(userRows).toHaveLength(0);
|
|
370
|
+
expect(tenantRows).toHaveLength(1);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
describe("delivery-attempt userData-hooks (#799)", () => {
|
|
375
|
+
async function seedAttempt(id: string, recipientId: string, tenantId: string): Promise<void> {
|
|
376
|
+
await asRawClient(full.db).unsafe(
|
|
377
|
+
`INSERT INTO store_delivery_attempts
|
|
378
|
+
(id, tenant_id, notification_type, channel, recipient_id, recipient_address, status, priority)
|
|
379
|
+
VALUES ($1, $2, 'app:notify:ping', 'email', $3, $4, 'sent', 'normal')`,
|
|
380
|
+
[id, tenantId, recipientId, `user-${recipientId}@example.com`],
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
test("export returns only the user's attempts in the export tenant", async () => {
|
|
385
|
+
await seedAttempt(uuid(60), "da-user-1", TENANT_A);
|
|
386
|
+
await seedAttempt(uuid(61), "da-user-1", TENANT_B);
|
|
387
|
+
await seedAttempt(uuid(62), "da-user-2", TENANT_A);
|
|
388
|
+
|
|
389
|
+
const result = await deliveryAttemptExportHook(ctx("da-user-1"));
|
|
390
|
+
expect(result?.entity).toBe("delivery-attempt");
|
|
391
|
+
expect(result?.rows).toHaveLength(1);
|
|
392
|
+
expect(result?.rows[0]?.["recipientAddress"]).toBe("user-da-user-1@example.com");
|
|
393
|
+
expect(result?.rows[0]?.["channel"]).toBe("email");
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("export returns null for a user without attempts", async () => {
|
|
397
|
+
expect(await deliveryAttemptExportHook(ctx("da-nobody"))).toBeNull();
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
describe("job-run userData-hooks (#799)", () => {
|
|
402
|
+
async function seedRun(id: string, triggeredById: string | null): Promise<void> {
|
|
403
|
+
await asRawClient(full.db).unsafe(
|
|
404
|
+
`INSERT INTO read_job_runs
|
|
405
|
+
(id, tenant_id, job_name, bull_job_id, status, payload, attempt, started_at, triggered_by_id)
|
|
406
|
+
VALUES ($1::uuid, $2, 'app:job:export', $3, 'completed', '{"scope":"mine"}', 1, now(), $4)`,
|
|
407
|
+
[id, TENANT_A, `bull-${id}`, triggeredById],
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
test("export filters by triggeredById only (system-tenant rows)", async () => {
|
|
412
|
+
await seedRun(uuid(70), "jr-user-1");
|
|
413
|
+
await seedRun(uuid(71), "jr-user-2");
|
|
414
|
+
await seedRun(uuid(72), null);
|
|
415
|
+
|
|
416
|
+
const result = await jobRunExportHook(ctx("jr-user-1"));
|
|
417
|
+
expect(result?.entity).toBe("job-run");
|
|
418
|
+
expect(result?.rows).toHaveLength(1);
|
|
419
|
+
expect(result?.rows[0]?.["jobName"]).toBe("app:job:export");
|
|
420
|
+
expect(result?.rows[0]?.["payload"]).toBe('{"scope":"mine"}');
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test("export returns null for a user without runs", async () => {
|
|
424
|
+
expect(await jobRunExportHook(ctx("jr-nobody"))).toBeNull();
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
describe("Presence-Gating: Source-Feature nicht gemountet → null/no-op statt Crash", () => {
|
|
429
|
+
test("alle gegateten Hooks no-open auf dem Minimal-Stack", async () => {
|
|
430
|
+
const minCtx = {
|
|
431
|
+
db: minimal.db,
|
|
432
|
+
registry: minimal.registry,
|
|
433
|
+
tenantId: TENANT_A,
|
|
434
|
+
userId: "any-user",
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// sessions ist im Minimal-Stack GEMOUNTET aber die Tabelle existiert
|
|
438
|
+
// nicht — der Gate-Check greift auf Feature-Ebene, deshalb hier nur die
|
|
439
|
+
// fünf wirklich ungemounteten Features. (user-session wird im FULL-Stack
|
|
440
|
+
// getestet.)
|
|
441
|
+
expect(await apiTokenExportHook(minCtx)).toBeNull();
|
|
442
|
+
expect(await inAppMessageExportHook(minCtx)).toBeNull();
|
|
443
|
+
expect(await tenantInvitationExportHook(minCtx)).toBeNull();
|
|
444
|
+
expect(await notificationPreferenceExportHook(minCtx)).toBeNull();
|
|
445
|
+
expect(await configValueExportHook(minCtx)).toBeNull();
|
|
446
|
+
expect(await deliveryAttemptExportHook(minCtx)).toBeNull();
|
|
447
|
+
expect(await jobRunExportHook(minCtx)).toBeNull();
|
|
448
|
+
|
|
449
|
+
await expect(apiTokenDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
|
|
450
|
+
await expect(inAppMessageDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
|
|
451
|
+
await expect(tenantInvitationDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
|
|
452
|
+
await expect(notificationPreferenceDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
|
|
453
|
+
await expect(configValueDeleteHook(minCtx, "delete")).resolves.toBeUndefined();
|
|
454
|
+
});
|
|
455
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { UserDataHookCtx } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { featureMounted } from "../hooks/feature-mounted";
|
|
4
|
+
|
|
5
|
+
function ctxWithFeatures(mounted: readonly string[]): UserDataHookCtx {
|
|
6
|
+
return {
|
|
7
|
+
registry: {
|
|
8
|
+
getFeature: (name: string) => (mounted.includes(name) ? { name } : undefined),
|
|
9
|
+
// @cast-boundary test fixture — featureMounted only reads ctx.registry.getFeature
|
|
10
|
+
} as unknown as UserDataHookCtx["registry"],
|
|
11
|
+
} as unknown as UserDataHookCtx;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe("featureMounted", () => {
|
|
15
|
+
test("returns true when the feature is registered", () => {
|
|
16
|
+
const ctx = ctxWithFeatures(["sessions"]);
|
|
17
|
+
expect(featureMounted(ctx, "sessions")).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("returns false when the feature is not registered", () => {
|
|
21
|
+
const ctx = ctxWithFeatures(["sessions"]);
|
|
22
|
+
expect(featureMounted(ctx, "channel-email")).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
});
|
package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.test.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// fileRef-Forget beruehrt Tenant B's Files nicht)
|
|
12
12
|
|
|
13
13
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
14
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
14
15
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
15
16
|
import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
|
|
16
17
|
import {
|
|
@@ -43,6 +44,7 @@ const features = [
|
|
|
43
44
|
createFilesFeature(),
|
|
44
45
|
createDataRetentionFeature(),
|
|
45
46
|
createComplianceProfilesFeature(),
|
|
47
|
+
authFoundationFeature,
|
|
46
48
|
createSessionsFeature(),
|
|
47
49
|
createUserDataRightsFeature(),
|
|
48
50
|
createUserDataRightsDefaultsFeature(),
|
|
@@ -162,6 +164,7 @@ describe("S2.H1 :: userExportHook", () => {
|
|
|
162
164
|
|
|
163
165
|
const result = await userExportHook({
|
|
164
166
|
db: stack.db,
|
|
167
|
+
registry: stack.registry,
|
|
165
168
|
tenantId: TENANT_A,
|
|
166
169
|
userId: uuid(1001),
|
|
167
170
|
});
|
|
@@ -182,6 +185,7 @@ describe("S2.H1 :: userExportHook", () => {
|
|
|
182
185
|
test("returns null wenn User nicht existiert", async () => {
|
|
183
186
|
const result = await userExportHook({
|
|
184
187
|
db: stack.db,
|
|
188
|
+
registry: stack.registry,
|
|
185
189
|
tenantId: TENANT_A,
|
|
186
190
|
userId: uuid(1002),
|
|
187
191
|
});
|
|
@@ -193,7 +197,10 @@ describe("S2.H1 :: userDeleteHook", () => {
|
|
|
193
197
|
test('strategy="delete" → softDelete + email/displayName anonymisiert + status=deleted', async () => {
|
|
194
198
|
await seedUser(uuid(1003));
|
|
195
199
|
|
|
196
|
-
await userDeleteHook(
|
|
200
|
+
await userDeleteHook(
|
|
201
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: uuid(1003) },
|
|
202
|
+
"delete",
|
|
203
|
+
);
|
|
197
204
|
|
|
198
205
|
const row = await fetchUser(uuid(1003));
|
|
199
206
|
expect(row).not.toBeNull();
|
|
@@ -209,7 +216,10 @@ describe("S2.H1 :: userDeleteHook", () => {
|
|
|
209
216
|
test('strategy="anonymize" → email/displayName anonymisiert aber status bleibt active', async () => {
|
|
210
217
|
await seedUser(uuid(1004));
|
|
211
218
|
|
|
212
|
-
await userDeleteHook(
|
|
219
|
+
await userDeleteHook(
|
|
220
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: uuid(1004) },
|
|
221
|
+
"anonymize",
|
|
222
|
+
);
|
|
213
223
|
|
|
214
224
|
const row = await fetchUser(uuid(1004));
|
|
215
225
|
if (!row) throw new Error("row should exist");
|
|
@@ -222,13 +232,19 @@ describe("S2.H1 :: userDeleteHook", () => {
|
|
|
222
232
|
test("idempotent: zweiter delete-Call crasht nicht UND State bleibt korrekt deleted", async () => {
|
|
223
233
|
await seedUser(uuid(1005));
|
|
224
234
|
|
|
225
|
-
await userDeleteHook(
|
|
235
|
+
await userDeleteHook(
|
|
236
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: uuid(1005) },
|
|
237
|
+
"delete",
|
|
238
|
+
);
|
|
226
239
|
const afterFirst = await fetchUser(uuid(1005));
|
|
227
240
|
if (!afterFirst) throw new Error("user should exist after first delete");
|
|
228
241
|
|
|
229
242
|
// Zweiter Call: kein Crash + State unverändert
|
|
230
243
|
await expect(
|
|
231
|
-
userDeleteHook(
|
|
244
|
+
userDeleteHook(
|
|
245
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: uuid(1005) },
|
|
246
|
+
"delete",
|
|
247
|
+
),
|
|
232
248
|
).resolves.toBeUndefined();
|
|
233
249
|
|
|
234
250
|
// State-Verifikation (S2.H1+H2-Audit N3): Row weiterhin deleted,
|
|
@@ -249,6 +265,7 @@ describe("S2.H2 :: fileRefExportHook", () => {
|
|
|
249
265
|
|
|
250
266
|
const result = await fileRefExportHook({
|
|
251
267
|
db: stack.db,
|
|
268
|
+
registry: stack.registry,
|
|
252
269
|
tenantId: TENANT_A,
|
|
253
270
|
userId: "user-files-1",
|
|
254
271
|
});
|
|
@@ -263,6 +280,7 @@ describe("S2.H2 :: fileRefExportHook", () => {
|
|
|
263
280
|
test("returns null wenn User keine Files hat", async () => {
|
|
264
281
|
const result = await fileRefExportHook({
|
|
265
282
|
db: stack.db,
|
|
283
|
+
registry: stack.registry,
|
|
266
284
|
tenantId: TENANT_A,
|
|
267
285
|
userId: "ghost-user-no-files",
|
|
268
286
|
});
|
|
@@ -276,7 +294,7 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
|
|
|
276
294
|
await seedFileRef(uuid(202), TENANT_A, "user-delete-files", "f2.pdf");
|
|
277
295
|
|
|
278
296
|
await fileRefDeleteHook(
|
|
279
|
-
{ db: stack.db, tenantId: TENANT_A, userId: "user-delete-files" },
|
|
297
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "user-delete-files" },
|
|
280
298
|
"delete",
|
|
281
299
|
);
|
|
282
300
|
|
|
@@ -288,7 +306,7 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
|
|
|
288
306
|
await seedFileRef(uuid(203), TENANT_A, "user-anon-files", "shared.pdf");
|
|
289
307
|
|
|
290
308
|
await fileRefDeleteHook(
|
|
291
|
-
{ db: stack.db, tenantId: TENANT_A, userId: "user-anon-files" },
|
|
309
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "user-anon-files" },
|
|
292
310
|
"anonymize",
|
|
293
311
|
);
|
|
294
312
|
|
|
@@ -305,7 +323,10 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
|
|
|
305
323
|
await seedFileRef(uuid(302), TENANT_B, "shared-user", "tenantB.pdf");
|
|
306
324
|
|
|
307
325
|
// Tenant A loescht alle Files von "shared-user"
|
|
308
|
-
await fileRefDeleteHook(
|
|
326
|
+
await fileRefDeleteHook(
|
|
327
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "shared-user" },
|
|
328
|
+
"delete",
|
|
329
|
+
);
|
|
309
330
|
|
|
310
331
|
const aRemaining = await fetchFileRefs(TENANT_A, "shared-user");
|
|
311
332
|
const bRemaining = await fetchFileRefs(TENANT_B, "shared-user");
|
|
@@ -319,7 +340,7 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
|
|
|
319
340
|
await seedFileRef(uuid(401), TENANT_A, "user-idem-files", "f.pdf");
|
|
320
341
|
|
|
321
342
|
await fileRefDeleteHook(
|
|
322
|
-
{ db: stack.db, tenantId: TENANT_A, userId: "user-idem-files" },
|
|
343
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "user-idem-files" },
|
|
323
344
|
"delete",
|
|
324
345
|
);
|
|
325
346
|
const afterFirst = await fetchFileRefs(TENANT_A, "user-idem-files");
|
|
@@ -327,7 +348,10 @@ describe("S2.H2 :: fileRefDeleteHook", () => {
|
|
|
327
348
|
|
|
328
349
|
// Zweiter Call: kein Crash + State weiter 0 Files
|
|
329
350
|
await expect(
|
|
330
|
-
fileRefDeleteHook(
|
|
351
|
+
fileRefDeleteHook(
|
|
352
|
+
{ db: stack.db, registry: stack.registry, tenantId: TENANT_A, userId: "user-idem-files" },
|
|
353
|
+
"delete",
|
|
354
|
+
),
|
|
331
355
|
).resolves.toBeUndefined();
|
|
332
356
|
const afterSecond = await fetchFileRefs(TENANT_A, "user-idem-files");
|
|
333
357
|
expect(afterSecond).toHaveLength(0);
|