@cosmicdrift/kumiko-bundled-features 0.14.0 → 0.16.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 +2 -2
- package/src/__tests__/env-schemas.test.ts +1 -1
- package/src/__tests__/es-ops-e2e.integration.ts +10 -9
- package/src/audit/__tests__/audit.integration.ts +3 -3
- package/src/audit/handlers/list.query.ts +39 -51
- package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.ts +4 -3
- package/src/auth-email-password/__tests__/account-lockout.integration.ts +4 -3
- package/src/auth-email-password/__tests__/auth-claims.integration.ts +5 -4
- package/src/auth-email-password/__tests__/auth.integration.ts +4 -3
- package/src/auth-email-password/__tests__/confirm-token-flow.test.ts +1 -1
- package/src/auth-email-password/__tests__/email-templates.test.ts +1 -1
- package/src/auth-email-password/__tests__/email-verification.integration.ts +7 -10
- package/src/auth-email-password/__tests__/identity-v3-hash.test.ts +1 -1
- package/src/auth-email-password/__tests__/identity-v3-login.integration.ts +4 -3
- package/src/auth-email-password/__tests__/invite-flow.integration.ts +16 -43
- package/src/auth-email-password/__tests__/multi-roles.integration.ts +6 -9
- package/src/auth-email-password/__tests__/password-reset.integration.ts +8 -7
- package/src/auth-email-password/__tests__/public-routes-rate-limit.integration.ts +4 -3
- package/src/auth-email-password/__tests__/seed-admin.integration.ts +19 -32
- package/src/auth-email-password/__tests__/session-callbacks.integration.ts +6 -5
- package/src/auth-email-password/__tests__/session-strict-mode.integration.ts +1 -1
- package/src/auth-email-password/__tests__/signed-token.test.ts +1 -1
- package/src/auth-email-password/__tests__/signup-flow.integration.ts +11 -15
- package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +26 -26
- package/src/auth-email-password/handlers/invite-accept.write.ts +24 -21
- package/src/auth-email-password/handlers/invite-create.write.ts +3 -8
- package/src/auth-email-password/handlers/invite-signup-complete.write.ts +20 -17
- package/src/auth-email-password/handlers/signup-confirm.write.ts +3 -7
- package/src/auth-email-password/seeding.ts +1 -1
- package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +1 -2
- package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +10 -19
- package/src/auth-email-password/web/__tests__/login-screen.test.tsx +12 -18
- package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +12 -17
- package/src/auth-email-password/web/__tests__/session-roles.test.ts +1 -1
- package/src/auth-email-password/web/__tests__/tenant-switcher.test.tsx +1 -8
- package/src/auth-email-password/web/__tests__/test-utils.tsx +4 -8
- package/src/auth-email-password/web/__tests__/user-menu.test.tsx +2 -8
- package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +10 -15
- package/src/billing-foundation/__tests__/billing-foundation.integration.ts +1 -1
- package/src/billing-foundation/__tests__/feature.test.ts +1 -1
- package/src/billing-foundation/__tests__/webhook-handler.test.ts +6 -5
- package/src/billing-foundation/db/queries/subscription-projection.ts +15 -0
- package/src/billing-foundation/get-subscription-for-tenant.ts +2 -6
- package/src/billing-foundation/handlers/create-portal-session.write.ts +2 -2
- package/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -1
- package/src/billing-foundation/projection.ts +32 -13
- package/src/cap-counter/__tests__/cap-counter.integration.ts +1 -1
- package/src/cap-counter/__tests__/enforce-cap.test.ts +37 -32
- package/src/cap-counter/__tests__/with-cap-enforcement.integration.ts +1 -1
- package/src/cap-counter/enforce-cap.ts +14 -20
- package/src/cap-counter/handlers/get-counter.query.ts +7 -13
- package/src/cap-counter/handlers/increment.write.ts +2 -2
- package/src/cap-counter/handlers/mark-soft-warned.write.ts +2 -2
- package/src/channel-in-app/handlers/inbox.query.ts +7 -13
- package/src/channel-in-app/handlers/mark-all-read.write.ts +7 -9
- package/src/channel-in-app/handlers/mark-read.write.ts +8 -14
- package/src/channel-in-app/handlers/unread-count.query.ts +10 -9
- package/src/channel-in-app/in-app-channel.ts +10 -12
- package/src/channel-in-app/tables.ts +1 -1
- package/src/compliance-profiles/__tests__/compliance-profiles.integration.ts +1 -1
- package/src/compliance-profiles/__tests__/seeding.integration.ts +1 -1
- package/src/compliance-profiles/_internal/parse-override.ts +19 -0
- package/src/compliance-profiles/handlers/for-tenant.query.ts +10 -32
- package/src/compliance-profiles/handlers/needs-profile.query.ts +4 -7
- package/src/compliance-profiles/handlers/set-profile.write.ts +5 -7
- package/src/compliance-profiles/resolve-for-tenant.ts +11 -27
- package/src/compliance-profiles/schema/profile-selection.ts +2 -2
- package/src/compliance-profiles/seeding.ts +4 -7
- package/src/config/__tests__/app-overrides.test.ts +1 -1
- package/src/config/__tests__/cascade.integration.ts +1 -1
- package/src/config/__tests__/config.integration.ts +8 -27
- package/src/config/db/queries/resolver.ts +47 -0
- package/src/config/handlers/__tests__/prepare-config-write.test.ts +1 -1
- package/src/config/resolver.ts +14 -62
- package/src/config/table.ts +4 -4
- package/src/config/write-helpers.ts +7 -11
- package/src/custom-fields/__tests__/audit-integration.integration.ts +6 -6
- package/src/custom-fields/__tests__/custom-fields.integration.ts +7 -7
- package/src/custom-fields/__tests__/feature.test.ts +1 -1
- package/src/custom-fields/__tests__/field-access.integration.ts +6 -6
- package/src/custom-fields/__tests__/quota.integration.ts +6 -6
- package/src/custom-fields/__tests__/retention.integration.ts +12 -10
- package/src/custom-fields/__tests__/user-data-rights.integration.ts +27 -17
- package/src/custom-fields/__tests__/wire-for-entity.test.ts +5 -5
- package/src/custom-fields/db/queries/field-access.ts +16 -0
- package/src/custom-fields/db/queries/projection.ts +43 -0
- package/src/custom-fields/db/queries/quota.ts +14 -0
- package/src/custom-fields/db/queries/retention.ts +39 -0
- package/src/custom-fields/db/queries/user-data-rights.ts +54 -0
- package/src/custom-fields/lib/field-access.ts +2 -41
- package/src/custom-fields/lib/quota.ts +2 -25
- package/src/custom-fields/run-retention.ts +19 -21
- package/src/custom-fields/wire-for-entity.ts +30 -23
- package/src/custom-fields/wire-user-data-rights.ts +33 -85
- package/src/data-retention/__tests__/data-retention.integration.ts +1 -1
- package/src/data-retention/__tests__/keep-for.test.ts +1 -1
- package/src/data-retention/__tests__/override-schema.test.ts +1 -1
- package/src/data-retention/__tests__/policy-for.integration.ts +1 -1
- package/src/data-retention/__tests__/resolver.test.ts +1 -1
- package/src/data-retention/handlers/policy-for.query.ts +5 -8
- package/src/data-retention/resolve-for-tenant.ts +6 -8
- package/src/data-retention/schema/tenant-retention-override.ts +2 -2
- package/src/delivery/__tests__/delivery-events.integration.ts +8 -21
- package/src/delivery/__tests__/delivery.integration.ts +100 -190
- package/src/delivery/db/queries/preferences.ts +30 -0
- package/src/delivery/delivery-service.ts +8 -36
- package/src/delivery/feature.ts +10 -2
- package/src/delivery/handlers/log.query.ts +5 -7
- package/src/delivery/handlers/preferences.query.ts +2 -5
- package/src/delivery/tables.ts +26 -1
- package/src/delivery/upsert-preference.ts +8 -14
- package/src/feature-toggles/__tests__/feature-toggles.integration.ts +30 -30
- package/src/feature-toggles/__tests__/registered-system-tenant.test.ts +7 -6
- package/src/feature-toggles/db/queries/toggle-state.ts +25 -0
- package/src/feature-toggles/feature.ts +16 -2
- package/src/feature-toggles/global-feature-state-table.ts +1 -1
- package/src/feature-toggles/handlers/list.query.ts +9 -2
- package/src/feature-toggles/handlers/registered.query.ts +3 -7
- package/src/feature-toggles/handlers/set.write.ts +37 -25
- package/src/feature-toggles/toggle-runtime.ts +3 -6
- package/src/file-foundation/__tests__/feature.test.ts +1 -1
- package/src/file-foundation/__tests__/file-foundation.integration.ts +1 -1
- package/src/file-provider-inmemory/__tests__/feature.test.ts +1 -1
- package/src/file-provider-s3/__tests__/feature.test.ts +1 -1
- package/src/files/__tests__/files.integration.ts +18 -7
- package/src/files/schema/file-ref.ts +1 -1
- package/src/files-provider-s3/__tests__/env-helper.test.ts +1 -1
- package/src/files-provider-s3/__tests__/s3-provider.integration.ts +1 -1
- package/src/files-provider-s3/__tests__/s3-provider.test.ts +1 -1
- package/src/jobs/__tests__/job-system-user.integration.ts +1 -1
- package/src/jobs/__tests__/jobs-events.integration.ts +8 -21
- package/src/jobs/__tests__/jobs-feature.integration.ts +1 -1
- package/src/jobs/feature.ts +26 -15
- package/src/jobs/handlers/detail.query.ts +10 -8
- package/src/jobs/handlers/list.query.ts +9 -21
- package/src/jobs/handlers/retry.write.ts +2 -7
- package/src/jobs/job-run-logger.ts +3 -9
- package/src/jobs/job-run-table.ts +49 -17
- package/src/legal-pages/__tests__/legal-pages.integration.ts +1 -1
- package/src/mail-foundation/__tests__/feature.test.ts +1 -1
- package/src/mail-foundation/__tests__/mail-foundation.integration.ts +1 -1
- package/src/mail-transport-inmemory/__tests__/feature.test.ts +1 -1
- package/src/mail-transport-smtp/__tests__/feature.test.ts +1 -1
- package/src/rate-limiting/__tests__/rate-limiting.integration.ts +1 -1
- package/src/renderer-foundation/__tests__/api.test.ts +2 -2
- package/src/renderer-foundation/__tests__/collect-plugins.integration.ts +1 -1
- package/src/renderer-simple/__tests__/adapter.test.ts +2 -2
- package/src/renderer-simple/__tests__/simple-renderer.test.ts +1 -1
- package/src/secrets/__tests__/require-secrets-context.test.ts +6 -5
- package/src/secrets/__tests__/rotate.integration.ts +6 -9
- package/src/secrets/__tests__/secrets-events.integration.ts +6 -12
- package/src/secrets/__tests__/secrets.integration.ts +6 -11
- package/src/secrets/db/queries/read.ts +16 -0
- package/src/secrets/handlers/list.query.ts +16 -17
- package/src/secrets/handlers/rotate.job.ts +8 -12
- package/src/secrets/secrets-context.ts +9 -21
- package/src/secrets/table.ts +1 -1
- package/src/sessions/__tests__/cleanup.integration.ts +8 -6
- package/src/sessions/__tests__/password-auto-revoke.integration.ts +7 -6
- package/src/sessions/__tests__/sessions.integration.ts +23 -38
- package/src/sessions/__tests__/test-helpers.ts +1 -1
- package/src/sessions/db/queries/cleanup.ts +21 -0
- package/src/sessions/handlers/cleanup.job.ts +6 -29
- package/src/sessions/handlers/list.query.ts +24 -24
- package/src/sessions/handlers/mine.query.ts +24 -23
- package/src/sessions/handlers/revoke-all-for-user.write.ts +7 -11
- package/src/sessions/handlers/revoke-all-others.write.ts +7 -12
- package/src/sessions/handlers/revoke.write.ts +11 -18
- package/src/sessions/schema/user-session.ts +2 -2
- package/src/sessions/session-callbacks.ts +19 -21
- package/src/subscription-mollie/__tests__/feature.test.ts +1 -1
- package/src/subscription-mollie/__tests__/mollie-foundation.integration.ts +1 -1
- package/src/subscription-mollie/__tests__/verify-webhook.test.ts +8 -7
- package/src/subscription-stripe/__tests__/feature.test.ts +1 -1
- package/src/subscription-stripe/__tests__/plugin-methods.test.ts +14 -15
- package/src/subscription-stripe/__tests__/stripe-foundation.integration.ts +1 -1
- package/src/subscription-stripe/__tests__/verify-webhook.test.ts +14 -14
- package/src/subscription-stripe/verify-webhook.ts +1 -1
- package/src/template-resolver/__tests__/handlers.integration.ts +1 -1
- package/src/template-resolver/__tests__/template-resolver.integration.ts +3 -2
- package/src/template-resolver/api.ts +7 -13
- package/src/template-resolver/handlers/archive.write.ts +4 -7
- package/src/template-resolver/handlers/find-by-id.query.ts +4 -7
- package/src/template-resolver/handlers/list.query.ts +13 -21
- package/src/template-resolver/handlers/publish.write.ts +4 -7
- package/src/template-resolver/handlers/upsert-system.write.ts +7 -10
- package/src/template-resolver/handlers/upsert-tenant.write.ts +7 -10
- package/src/template-resolver/table.ts +2 -5
- package/src/tenant/__tests__/multi-tenant.integration.ts +1 -1
- package/src/tenant/__tests__/seed-testing.integration.ts +19 -45
- package/src/tenant/__tests__/tenant.integration.ts +1 -1
- package/src/tenant/handlers/active-tenant-ids.query.ts +3 -8
- package/src/tenant/handlers/add-member.write.ts +6 -8
- package/src/tenant/handlers/cancel-invitation.write.ts +5 -7
- package/src/tenant/handlers/invitations.query.ts +5 -10
- package/src/tenant/handlers/me.query.ts +2 -3
- package/src/tenant/handlers/members.query.ts +4 -5
- package/src/tenant/handlers/memberships.query.ts +2 -5
- package/src/tenant/handlers/remove-member.write.ts +6 -8
- package/src/tenant/handlers/resolve-user-ids.query.ts +6 -16
- package/src/tenant/handlers/update-member-roles.write.ts +6 -8
- package/src/tenant/invitation-table.ts +2 -5
- package/src/tenant/membership-table.ts +3 -6
- package/src/tenant/schema/tenant.ts +2 -2
- package/src/tenant/seeding.ts +12 -18
- package/src/text-content/README.md +1 -1
- package/src/text-content/__tests__/text-content.integration.ts +2 -2
- package/src/text-content/api.ts +2 -9
- package/src/text-content/handlers/by-slug.query.ts +6 -9
- package/src/text-content/handlers/by-tenant.query.ts +2 -2
- package/src/text-content/handlers/set.write.ts +7 -9
- package/src/text-content/seeding.ts +6 -9
- package/src/text-content/table.ts +2 -2
- package/src/text-content/web/__tests__/editor-read-only.test.tsx +31 -45
- package/src/text-content/web/__tests__/group-blocks.test.ts +1 -18
- package/src/text-content/web/client-plugin.tsx +11 -23
- package/src/tier-engine/__tests__/auto-default-tier.integration.ts +10 -16
- package/src/tier-engine/__tests__/compose-app.test.ts +1 -1
- package/src/tier-engine/__tests__/drift.test.ts +1 -1
- package/src/tier-engine/__tests__/resolver.integration.ts +6 -6
- package/src/tier-engine/__tests__/tier-engine.integration.ts +1 -1
- package/src/tier-engine/feature.ts +9 -16
- package/src/user/__tests__/seed-testing.integration.ts +10 -22
- package/src/user/__tests__/user-status.test.ts +1 -1
- package/src/user/__tests__/user.integration.ts +6 -5
- package/src/user/handlers/create.write.ts +5 -7
- package/src/user/handlers/find-for-auth.query.ts +5 -7
- package/src/user/schema/user.ts +2 -2
- package/src/user/seeding.ts +2 -3
- package/src/user-data-rights/__tests__/audit-log.integration.ts +24 -12
- package/src/user-data-rights/__tests__/cross-data-matrix.integration.ts +64 -37
- package/src/user-data-rights/__tests__/download.integration.ts +29 -46
- package/src/user-data-rights/__tests__/export-job-idempotency.integration.ts +35 -28
- package/src/user-data-rights/__tests__/export-job-schema.test.ts +2 -2
- package/src/user-data-rights/__tests__/policy-to-strategy.test.ts +1 -1
- package/src/user-data-rights/__tests__/request-cancel-deletion.integration.ts +11 -15
- package/src/user-data-rights/__tests__/request-deletion-callback.integration.ts +10 -12
- package/src/user-data-rights/__tests__/request-export.integration.ts +23 -16
- package/src/user-data-rights/__tests__/restriction-flow.integration.ts +24 -32
- package/src/user-data-rights/__tests__/run-export-jobs.integration.ts +142 -137
- package/src/user-data-rights/__tests__/run-forget-cleanup.integration.ts +46 -28
- package/src/user-data-rights/__tests__/run-user-export.integration.ts +20 -14
- package/src/user-data-rights/__tests__/token-helpers.test.ts +1 -1
- package/src/user-data-rights/__tests__/user-data-rights.integration.ts +1 -1
- package/src/user-data-rights/__tests__/zip-path.test.ts +1 -1
- package/src/user-data-rights/audit-download.ts +3 -3
- package/src/user-data-rights/db/queries/export-jobs.ts +23 -0
- package/src/user-data-rights/db/queries/forget-cleanup.ts +13 -0
- package/src/user-data-rights/handlers/cancel-deletion.write.ts +28 -22
- package/src/user-data-rights/handlers/download-by-job.query.ts +11 -21
- package/src/user-data-rights/handlers/download-by-token.query.ts +20 -35
- package/src/user-data-rights/handlers/export-status.query.ts +19 -33
- package/src/user-data-rights/handlers/lift-restriction.write.ts +7 -12
- package/src/user-data-rights/handlers/list-download-attempts.query.ts +14 -23
- package/src/user-data-rights/handlers/my-audit-log.query.ts +33 -23
- package/src/user-data-rights/handlers/request-deletion.write.ts +15 -15
- package/src/user-data-rights/handlers/request-export.write.ts +7 -11
- package/src/user-data-rights/handlers/restrict-account.write.ts +12 -12
- package/src/user-data-rights/run-export-jobs.ts +20 -60
- package/src/user-data-rights/run-forget-cleanup.ts +19 -33
- package/src/user-data-rights/run-user-export.ts +4 -6
- package/src/user-data-rights/schema/download-attempt.ts +2 -2
- package/src/user-data-rights/schema/download-token.ts +2 -2
- package/src/user-data-rights/schema/export-job.ts +2 -3
- package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.ts +37 -30
- package/src/user-data-rights-defaults/db/queries/user-hook.ts +17 -0
- package/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +12 -27
- package/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +16 -18
- package/CHANGELOG.md +0 -689
|
@@ -12,10 +12,17 @@
|
|
|
12
12
|
// downloadStorageKey wird genullt + storage-key geloescht
|
|
13
13
|
// 6. Idempotency: 2× run → kein Re-Processing von done/failed-Jobs
|
|
14
14
|
|
|
15
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
15
16
|
import { spawn } from "node:child_process";
|
|
16
17
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
17
18
|
import { tmpdir } from "node:os";
|
|
18
19
|
import { join } from "node:path";
|
|
20
|
+
import {
|
|
21
|
+
asRawClient,
|
|
22
|
+
insertOne,
|
|
23
|
+
selectMany,
|
|
24
|
+
updateMany,
|
|
25
|
+
} from "@cosmicdrift/kumiko-framework/bun-db";
|
|
19
26
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
20
27
|
import {
|
|
21
28
|
createInMemoryFileProvider,
|
|
@@ -29,8 +36,6 @@ import {
|
|
|
29
36
|
unsafeCreateEntityTable,
|
|
30
37
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
31
38
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
32
|
-
import { sql } from "drizzle-orm";
|
|
33
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
34
39
|
import {
|
|
35
40
|
createComplianceProfilesFeature,
|
|
36
41
|
tenantComplianceProfileEntity,
|
|
@@ -65,7 +70,7 @@ beforeAll(async () => {
|
|
|
65
70
|
await createEventsTable(stack.db);
|
|
66
71
|
// tenant-membership-table fuer runUserExport's Cross-Tenant-Iteration.
|
|
67
72
|
// Pattern matched user-data-rights-defaults integration-test.
|
|
68
|
-
await stack.db.
|
|
73
|
+
await asRawClient(stack.db).unsafe(`
|
|
69
74
|
CREATE TABLE IF NOT EXISTS read_tenant_memberships (
|
|
70
75
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
71
76
|
tenant_id UUID NOT NULL,
|
|
@@ -89,30 +94,27 @@ afterAll(async () => {
|
|
|
89
94
|
});
|
|
90
95
|
|
|
91
96
|
beforeEach(async () => {
|
|
92
|
-
await stack.db.
|
|
93
|
-
await stack.db.
|
|
94
|
-
await stack.db.
|
|
95
|
-
await stack.db.
|
|
96
|
-
await stack.db.
|
|
97
|
-
await stack.db.
|
|
97
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${exportDownloadTokensTable.tableName}"`);
|
|
98
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${exportJobsTable.tableName}"`);
|
|
99
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${userTable.tableName}"`);
|
|
100
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM kumiko_events`);
|
|
101
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_compliance_profiles`);
|
|
102
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_memberships`);
|
|
98
103
|
providerPerTenant = new Map();
|
|
99
104
|
|
|
100
105
|
// Atom 5: aliceUser-Row mit email seeden — Worker-Notification-Callback
|
|
101
106
|
// schaut email via lookupUserEmail an.
|
|
102
|
-
await stack.db
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
status: USER_STATUS.Active,
|
|
114
|
-
})
|
|
115
|
-
.onConflictDoNothing();
|
|
107
|
+
await insertOne(stack.db, userTable, {
|
|
108
|
+
id: String(aliceUser.id),
|
|
109
|
+
tenantId: tenantA,
|
|
110
|
+
email: "alice@example.com",
|
|
111
|
+
passwordHash: "hashed",
|
|
112
|
+
displayName: "Alice",
|
|
113
|
+
locale: "de",
|
|
114
|
+
emailVerified: true,
|
|
115
|
+
roles: '["Member"]',
|
|
116
|
+
status: USER_STATUS.Active,
|
|
117
|
+
});
|
|
116
118
|
});
|
|
117
119
|
|
|
118
120
|
const NOW = () => getTemporal().Now.instant();
|
|
@@ -154,10 +156,7 @@ describe("runExportJobs :: happy path", () => {
|
|
|
154
156
|
expect(result.errors).toEqual([]);
|
|
155
157
|
|
|
156
158
|
// Job-Row ist done
|
|
157
|
-
const [row] = (await stack.db
|
|
158
|
-
.select()
|
|
159
|
-
.from(exportJobsTable)
|
|
160
|
-
.where(sql`id = ${jobId}`)) as Array<{
|
|
159
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
161
160
|
status: string;
|
|
162
161
|
downloadStorageKey: string | null;
|
|
163
162
|
expiresAt: { toString(): string } | null;
|
|
@@ -215,10 +214,12 @@ describe("runExportJobs :: stale-detection", () => {
|
|
|
215
214
|
const jobId = await seedPendingJob();
|
|
216
215
|
const T = getTemporal();
|
|
217
216
|
const twoHoursAgo = T.Instant.fromEpochMilliseconds(Date.now() - 2 * 60 * 60 * 1000);
|
|
218
|
-
await
|
|
219
|
-
.
|
|
220
|
-
|
|
221
|
-
.
|
|
217
|
+
await updateMany(
|
|
218
|
+
stack.db,
|
|
219
|
+
exportJobsTable,
|
|
220
|
+
{ status: EXPORT_JOB_STATUS.Running, startedAt: twoHoursAgo },
|
|
221
|
+
{ id: jobId },
|
|
222
|
+
);
|
|
222
223
|
|
|
223
224
|
const result = await runExportJobs({
|
|
224
225
|
db: stack.db,
|
|
@@ -229,10 +230,7 @@ describe("runExportJobs :: stale-detection", () => {
|
|
|
229
230
|
|
|
230
231
|
expect(result.staleFailedJobIds).toContain(jobId);
|
|
231
232
|
|
|
232
|
-
const [row] = (await stack.db
|
|
233
|
-
.select()
|
|
234
|
-
.from(exportJobsTable)
|
|
235
|
-
.where(sql`id = ${jobId}`)) as Array<{
|
|
233
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
236
234
|
status: string;
|
|
237
235
|
errorMessage: string | null;
|
|
238
236
|
}>;
|
|
@@ -244,10 +242,12 @@ describe("runExportJobs :: stale-detection", () => {
|
|
|
244
242
|
const jobId = await seedPendingJob();
|
|
245
243
|
const T = getTemporal();
|
|
246
244
|
const justNow = T.Instant.fromEpochMilliseconds(Date.now() - 60 * 1000); // 1min ago
|
|
247
|
-
await
|
|
248
|
-
.
|
|
249
|
-
|
|
250
|
-
.
|
|
245
|
+
await updateMany(
|
|
246
|
+
stack.db,
|
|
247
|
+
exportJobsTable,
|
|
248
|
+
{ status: EXPORT_JOB_STATUS.Running, startedAt: justNow },
|
|
249
|
+
{ id: jobId },
|
|
250
|
+
);
|
|
251
251
|
|
|
252
252
|
const result = await runExportJobs({
|
|
253
253
|
db: stack.db,
|
|
@@ -257,10 +257,9 @@ describe("runExportJobs :: stale-detection", () => {
|
|
|
257
257
|
});
|
|
258
258
|
|
|
259
259
|
expect(result.staleFailedJobIds).not.toContain(jobId);
|
|
260
|
-
const [row] = (await stack.db
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
.where(sql`id = ${jobId}`)) as Array<{ status: string }>;
|
|
260
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
261
|
+
status: string;
|
|
262
|
+
}>;
|
|
264
263
|
expect(row?.status).toBe(EXPORT_JOB_STATUS.Running);
|
|
265
264
|
});
|
|
266
265
|
|
|
@@ -278,14 +277,16 @@ describe("runExportJobs :: stale-detection", () => {
|
|
|
278
277
|
// gesetzt + ZIP geschrieben. Worker dann gecrashed (kein done-flip).
|
|
279
278
|
const provider = await buildProvider(tenantA);
|
|
280
279
|
await provider.write(storageKey, new Uint8Array([1, 2, 3]));
|
|
281
|
-
await
|
|
282
|
-
.
|
|
283
|
-
|
|
280
|
+
await updateMany(
|
|
281
|
+
stack.db,
|
|
282
|
+
exportJobsTable,
|
|
283
|
+
{
|
|
284
284
|
status: EXPORT_JOB_STATUS.Running,
|
|
285
285
|
startedAt: twoHoursAgo,
|
|
286
286
|
downloadStorageKey: storageKey,
|
|
287
|
-
}
|
|
288
|
-
|
|
287
|
+
},
|
|
288
|
+
{ id: jobId },
|
|
289
|
+
);
|
|
289
290
|
|
|
290
291
|
const result = await runExportJobs({
|
|
291
292
|
db: stack.db,
|
|
@@ -298,10 +299,10 @@ describe("runExportJobs :: stale-detection", () => {
|
|
|
298
299
|
expect(result.cleanedJobIds).toContain(jobId);
|
|
299
300
|
expect(await provider.exists(storageKey)).toBe(false);
|
|
300
301
|
|
|
301
|
-
const [row] = (await stack.db
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
302
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
303
|
+
status: string;
|
|
304
|
+
downloadStorageKey: string | null;
|
|
305
|
+
}>;
|
|
305
306
|
expect(row?.status).toBe(EXPORT_JOB_STATUS.Failed);
|
|
306
307
|
expect(row?.downloadStorageKey).toBeNull();
|
|
307
308
|
});
|
|
@@ -318,16 +319,18 @@ describe("runExportJobs :: storage-cleanup", () => {
|
|
|
318
319
|
const provider = await buildProvider(tenantA);
|
|
319
320
|
await provider.write(storageKey, new Uint8Array([1, 2, 3]));
|
|
320
321
|
|
|
321
|
-
await
|
|
322
|
-
.
|
|
323
|
-
|
|
322
|
+
await updateMany(
|
|
323
|
+
stack.db,
|
|
324
|
+
exportJobsTable,
|
|
325
|
+
{
|
|
324
326
|
status: EXPORT_JOB_STATUS.Done,
|
|
325
327
|
startedAt: longAgo,
|
|
326
328
|
completedAt: longAgo,
|
|
327
329
|
downloadStorageKey: storageKey,
|
|
328
330
|
expiresAt: longAgo,
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
+
},
|
|
332
|
+
{ id: jobId },
|
|
333
|
+
);
|
|
331
334
|
|
|
332
335
|
const result = await runExportJobs({
|
|
333
336
|
db: stack.db,
|
|
@@ -339,10 +342,7 @@ describe("runExportJobs :: storage-cleanup", () => {
|
|
|
339
342
|
expect(result.cleanedJobIds).toContain(jobId);
|
|
340
343
|
expect(await provider.exists(storageKey)).toBe(false);
|
|
341
344
|
|
|
342
|
-
const [row] = (await stack.db
|
|
343
|
-
.select()
|
|
344
|
-
.from(exportJobsTable)
|
|
345
|
-
.where(sql`id = ${jobId}`)) as Array<{
|
|
345
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
346
346
|
status: string;
|
|
347
347
|
downloadStorageKey: string | null;
|
|
348
348
|
}>;
|
|
@@ -358,16 +358,18 @@ describe("runExportJobs :: storage-cleanup", () => {
|
|
|
358
358
|
const provider = await buildProvider(tenantA);
|
|
359
359
|
await provider.write(storageKey, new Uint8Array([4, 5, 6]));
|
|
360
360
|
|
|
361
|
-
await
|
|
362
|
-
.
|
|
363
|
-
|
|
361
|
+
await updateMany(
|
|
362
|
+
stack.db,
|
|
363
|
+
exportJobsTable,
|
|
364
|
+
{
|
|
364
365
|
status: EXPORT_JOB_STATUS.Done,
|
|
365
366
|
startedAt: oneHourAgo,
|
|
366
367
|
completedAt: oneHourAgo,
|
|
367
368
|
downloadStorageKey: storageKey,
|
|
368
369
|
expiresAt: oneHourAgo,
|
|
369
|
-
}
|
|
370
|
-
|
|
370
|
+
},
|
|
371
|
+
{ id: jobId },
|
|
372
|
+
);
|
|
371
373
|
|
|
372
374
|
const result = await runExportJobs({
|
|
373
375
|
db: stack.db,
|
|
@@ -436,10 +438,10 @@ describe("runExportJobs :: concurrency", () => {
|
|
|
436
438
|
expect([...resultA.failedJobIds, ...resultB.failedJobIds]).toEqual([]);
|
|
437
439
|
|
|
438
440
|
// DB hat genau eine done-Row
|
|
439
|
-
const rows = (await stack.db
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
const rows = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
442
|
+
status: string;
|
|
443
|
+
downloadStorageKey: string | null;
|
|
444
|
+
}>;
|
|
443
445
|
expect(rows).toHaveLength(1);
|
|
444
446
|
expect(rows[0]?.status).toBe(EXPORT_JOB_STATUS.Done);
|
|
445
447
|
expect(rows[0]?.downloadStorageKey).toBe(`${tenantA}/exports/${jobId}.zip`);
|
|
@@ -460,10 +462,12 @@ describe("runExportJobs :: stale-detection profile-driven cutoff", () => {
|
|
|
460
462
|
const jobId = await seedPendingJob();
|
|
461
463
|
const T = getTemporal();
|
|
462
464
|
const fortyFiveMinAgo = T.Instant.fromEpochMilliseconds(Date.now() - 45 * 60 * 1000);
|
|
463
|
-
await
|
|
464
|
-
.
|
|
465
|
-
|
|
466
|
-
.
|
|
465
|
+
await updateMany(
|
|
466
|
+
stack.db,
|
|
467
|
+
exportJobsTable,
|
|
468
|
+
{ status: EXPORT_JOB_STATUS.Running, startedAt: fortyFiveMinAgo },
|
|
469
|
+
{ id: jobId },
|
|
470
|
+
);
|
|
467
471
|
|
|
468
472
|
const result = await runExportJobs({
|
|
469
473
|
db: stack.db,
|
|
@@ -473,10 +477,9 @@ describe("runExportJobs :: stale-detection profile-driven cutoff", () => {
|
|
|
473
477
|
});
|
|
474
478
|
|
|
475
479
|
expect(result.staleFailedJobIds).toContain(jobId);
|
|
476
|
-
const [row] = (await stack.db
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
.where(sql`id = ${jobId}`)) as Array<{ status: string }>;
|
|
480
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
481
|
+
status: string;
|
|
482
|
+
}>;
|
|
480
483
|
expect(row?.status).toBe(EXPORT_JOB_STATUS.Failed);
|
|
481
484
|
});
|
|
482
485
|
});
|
|
@@ -500,10 +503,7 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
500
503
|
expect(plainToken).toMatch(/^[A-Za-z0-9_-]{43}$/);
|
|
501
504
|
|
|
502
505
|
// Hash in DB matched plain via hashDownloadToken roundtrip
|
|
503
|
-
const tokenRows = (await stack.db
|
|
504
|
-
.select()
|
|
505
|
-
.from(exportDownloadTokensTable)
|
|
506
|
-
.where(sql`job_id = ${jobId}`)) as Array<{
|
|
506
|
+
const tokenRows = (await selectMany(stack.db, exportDownloadTokensTable, { jobId })) as Array<{
|
|
507
507
|
jobId: string;
|
|
508
508
|
tokenHash: string;
|
|
509
509
|
issuedAt: { toString(): string };
|
|
@@ -519,10 +519,9 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
519
519
|
expect(tokenRows[0]?.useCount).toBe(0);
|
|
520
520
|
|
|
521
521
|
// expiresAt im Token = job.expiresAt (denormalized)
|
|
522
|
-
const [jobRow] = (await stack.db
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
.where(sql`id = ${jobId}`)) as Array<{ expiresAt: { toString(): string } | null }>;
|
|
522
|
+
const [jobRow] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
523
|
+
expiresAt: { toString(): string } | null;
|
|
524
|
+
}>;
|
|
526
525
|
expect(tokenRows[0]?.expiresAt.toString()).toBe(jobRow?.expiresAt?.toString());
|
|
527
526
|
});
|
|
528
527
|
|
|
@@ -539,8 +538,8 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
539
538
|
now: NOW(),
|
|
540
539
|
});
|
|
541
540
|
|
|
542
|
-
const events = (await stack.db.
|
|
543
|
-
|
|
541
|
+
const events = (await asRawClient(stack.db).unsafe(
|
|
542
|
+
`SELECT type FROM kumiko_events WHERE type LIKE 'export-download-token.%'`,
|
|
544
543
|
)) as unknown as Array<{ type: string }>;
|
|
545
544
|
// Mindestens 1 created-Event fuer den Token
|
|
546
545
|
expect(events.some((e) => e.type === "export-download-token.created")).toBe(true);
|
|
@@ -565,10 +564,9 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
565
564
|
expect(second.completedJobIds).toEqual([]);
|
|
566
565
|
expect(second.tokenByJobId.size).toBe(0);
|
|
567
566
|
|
|
568
|
-
const tokenRows = (await stack.db
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
.where(sql`job_id = ${jobId}`)) as Array<unknown>;
|
|
567
|
+
const tokenRows = (await selectMany(stack.db, exportDownloadTokensTable, {
|
|
568
|
+
jobId,
|
|
569
|
+
})) as Array<unknown>;
|
|
572
570
|
expect(tokenRows).toHaveLength(1);
|
|
573
571
|
});
|
|
574
572
|
|
|
@@ -585,7 +583,7 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
585
583
|
expect(result.completedJobIds).toEqual([]);
|
|
586
584
|
expect(result.tokenByJobId.size).toBe(0);
|
|
587
585
|
|
|
588
|
-
const allTokens = (await stack.db
|
|
586
|
+
const allTokens = (await selectMany(stack.db, exportDownloadTokensTable)) as Array<unknown>;
|
|
589
587
|
expect(allTokens).toHaveLength(0);
|
|
590
588
|
});
|
|
591
589
|
|
|
@@ -605,21 +603,24 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
605
603
|
// direct-INSERT (Test-Exemption). Worker's tokenCrud.create wird
|
|
606
604
|
// dann mit constraintName "read_export_download_tokens_one_per_job"
|
|
607
605
|
// failen.
|
|
608
|
-
await stack.db.
|
|
606
|
+
await asRawClient(stack.db).unsafe(
|
|
607
|
+
`
|
|
609
608
|
INSERT INTO read_export_download_tokens
|
|
610
609
|
(id, tenant_id, job_id, token_hash, issued_at, expires_at, version, inserted_at, modified_at)
|
|
611
610
|
VALUES (
|
|
612
611
|
gen_random_uuid(),
|
|
613
|
-
$
|
|
614
|
-
$
|
|
615
|
-
$
|
|
612
|
+
$1,
|
|
613
|
+
$2,
|
|
614
|
+
$3,
|
|
616
615
|
now(),
|
|
617
616
|
now() + interval '7 days',
|
|
618
617
|
1,
|
|
619
618
|
now(),
|
|
620
619
|
now()
|
|
621
620
|
)
|
|
622
|
-
|
|
621
|
+
`,
|
|
622
|
+
[tenantA, jobId, "existing-hash"],
|
|
623
|
+
);
|
|
623
624
|
|
|
624
625
|
const result = await runExportJobs({
|
|
625
626
|
db: stack.db,
|
|
@@ -632,10 +633,10 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
632
633
|
expect(result.completedJobIds).not.toContain(jobId);
|
|
633
634
|
expect(result.failedJobIds).toContain(jobId);
|
|
634
635
|
|
|
635
|
-
const [row] = (await stack.db
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
636
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
637
|
+
status: string;
|
|
638
|
+
errorMessage: string | null;
|
|
639
|
+
}>;
|
|
639
640
|
expect(row?.status).toBe(EXPORT_JOB_STATUS.Failed);
|
|
640
641
|
expect(row?.errorMessage).toMatch(/Token-Creation failed/);
|
|
641
642
|
});
|
|
@@ -652,14 +653,16 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
652
653
|
// (simuliert orphan-state nach Worker-crash).
|
|
653
654
|
const provider = await buildProvider(tenantA);
|
|
654
655
|
await provider.write(storageKey, new Uint8Array([99, 99, 99]));
|
|
655
|
-
await
|
|
656
|
-
.
|
|
657
|
-
|
|
656
|
+
await updateMany(
|
|
657
|
+
stack.db,
|
|
658
|
+
exportJobsTable,
|
|
659
|
+
{
|
|
658
660
|
status: EXPORT_JOB_STATUS.Failed,
|
|
659
661
|
downloadStorageKey: storageKey,
|
|
660
662
|
errorMessage: "synthetic crash mid-run",
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
+
},
|
|
664
|
+
{ id: jobId },
|
|
665
|
+
);
|
|
663
666
|
|
|
664
667
|
const result = await runExportJobs({
|
|
665
668
|
db: stack.db,
|
|
@@ -671,10 +674,9 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
671
674
|
expect(result.cleanedJobIds).toContain(jobId);
|
|
672
675
|
expect(await provider.exists(storageKey)).toBe(false);
|
|
673
676
|
|
|
674
|
-
const [row] = (await stack.db
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
.where(sql`id = ${jobId}`)) as Array<{ downloadStorageKey: string | null }>;
|
|
677
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
678
|
+
downloadStorageKey: string | null;
|
|
679
|
+
}>;
|
|
678
680
|
expect(row?.downloadStorageKey).toBeNull();
|
|
679
681
|
});
|
|
680
682
|
});
|
|
@@ -740,7 +742,7 @@ describe("runExportJobs :: Atom 3c file-binaries", () => {
|
|
|
740
742
|
await unsafeCreateEntityTable(localStack.db, exportDownloadTokenEntity);
|
|
741
743
|
await unsafeCreateEntityTable(localStack.db, tenantComplianceProfileEntity);
|
|
742
744
|
await createEventsTable(localStack.db);
|
|
743
|
-
await localStack.db.
|
|
745
|
+
await asRawClient(localStack.db).unsafe(`
|
|
744
746
|
CREATE TABLE IF NOT EXISTS read_tenant_memberships (
|
|
745
747
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
746
748
|
tenant_id UUID NOT NULL,
|
|
@@ -765,21 +767,24 @@ describe("runExportJobs :: Atom 3c file-binaries", () => {
|
|
|
765
767
|
|
|
766
768
|
beforeEach(async () => {
|
|
767
769
|
if (!localStack) return;
|
|
768
|
-
await localStack.db.
|
|
769
|
-
await localStack.db.
|
|
770
|
-
await localStack.db.
|
|
771
|
-
await localStack.db.
|
|
772
|
-
await localStack.db.
|
|
770
|
+
await asRawClient(localStack.db).unsafe(`DELETE FROM "${exportDownloadTokensTable.tableName}"`);
|
|
771
|
+
await asRawClient(localStack.db).unsafe(`DELETE FROM "${exportJobsTable.tableName}"`);
|
|
772
|
+
await asRawClient(localStack.db).unsafe(`DELETE FROM kumiko_events`);
|
|
773
|
+
await asRawClient(localStack.db).unsafe(`DELETE FROM read_tenant_compliance_profiles`);
|
|
774
|
+
await asRawClient(localStack.db).unsafe(`DELETE FROM read_tenant_memberships`);
|
|
773
775
|
// Reset zu safe Default damit kein Test den State an den naechsten leakt.
|
|
774
776
|
currentTestFileName = "report.pdf";
|
|
775
777
|
});
|
|
776
778
|
|
|
777
779
|
async function seedMembership(tenantId: string, userId: string | number) {
|
|
778
|
-
await localStack.db.
|
|
780
|
+
await asRawClient(localStack.db).unsafe(
|
|
781
|
+
`
|
|
779
782
|
INSERT INTO read_tenant_memberships (tenant_id, user_id)
|
|
780
|
-
VALUES ($
|
|
783
|
+
VALUES ($1, $2)
|
|
781
784
|
ON CONFLICT (user_id, tenant_id) DO NOTHING
|
|
782
|
-
|
|
785
|
+
`,
|
|
786
|
+
[tenantId, String(userId)],
|
|
787
|
+
);
|
|
783
788
|
}
|
|
784
789
|
|
|
785
790
|
async function seedPendingJobLocal(user: typeof aliceUser): Promise<string> {
|
|
@@ -879,10 +884,10 @@ describe("runExportJobs :: Atom 3c file-binaries", () => {
|
|
|
879
884
|
expect(result.failedJobIds).toContain(jobId);
|
|
880
885
|
expect(result.errors[0]?.message).toMatch(/in-memory file not found/);
|
|
881
886
|
|
|
882
|
-
const [row] = (await localStack.db
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
887
|
+
const [row] = (await selectMany(localStack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
888
|
+
status: string;
|
|
889
|
+
errorMessage: string | null;
|
|
890
|
+
}>;
|
|
886
891
|
expect(row?.status).toBe(EXPORT_JOB_STATUS.Failed);
|
|
887
892
|
expect(row?.errorMessage).toMatch(/in-memory file not found/);
|
|
888
893
|
});
|
|
@@ -1028,8 +1033,8 @@ describe("runExportJobs :: Atom 5 notification-callbacks", () => {
|
|
|
1028
1033
|
test("user ohne email → Callback skipped + console.warn (kein Throw)", async () => {
|
|
1029
1034
|
// User-Row mit email=null seeden (override). Worker logged warn,
|
|
1030
1035
|
// Callback wird NICHT gerufen, Worker-Run bleibt successful.
|
|
1031
|
-
await stack.db.
|
|
1032
|
-
await stack.db
|
|
1036
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${userTable.tableName}"`);
|
|
1037
|
+
await insertOne(stack.db, userTable, {
|
|
1033
1038
|
id: String(aliceUser.id),
|
|
1034
1039
|
tenantId: tenantA,
|
|
1035
1040
|
email: "" as string, // empty string — lookupUserEmail returnt null
|
|
@@ -1056,10 +1061,9 @@ describe("runExportJobs :: Atom 5 notification-callbacks", () => {
|
|
|
1056
1061
|
expect(callbackInvoked).toBe(false);
|
|
1057
1062
|
// Job ist trotzdem done (Notification ist best-effort, Audit-Trail
|
|
1058
1063
|
// existiert via Token-DB-row)
|
|
1059
|
-
const [row] = (await stack.db
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
.where(sql`id = ${jobId}`)) as Array<{ status: string }>;
|
|
1064
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
1065
|
+
status: string;
|
|
1066
|
+
}>;
|
|
1063
1067
|
expect(row?.status).toBe("done");
|
|
1064
1068
|
});
|
|
1065
1069
|
|
|
@@ -1076,7 +1080,7 @@ describe("runExportJobs :: Atom 5 notification-callbacks", () => {
|
|
|
1076
1080
|
tenantId: tenantA,
|
|
1077
1081
|
roles: ["Member"],
|
|
1078
1082
|
});
|
|
1079
|
-
await stack.db
|
|
1083
|
+
await insertOne(stack.db, userTable, {
|
|
1080
1084
|
id: String(bobUser.id),
|
|
1081
1085
|
tenantId: tenantA,
|
|
1082
1086
|
email: "bob@example.com",
|
|
@@ -1114,11 +1118,12 @@ describe("runExportJobs :: Atom 5 notification-callbacks", () => {
|
|
|
1114
1118
|
|
|
1115
1119
|
// Beide DB-Rows tatsaechlich done (Job A's Status wurde VOR dem
|
|
1116
1120
|
// Email-Versand committed — der Throw aenderte daran nichts).
|
|
1117
|
-
const rows = (await stack.db
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
expect(
|
|
1121
|
+
const rows = (await selectMany(stack.db, exportJobsTable, {})) as Array<{
|
|
1122
|
+
id: string;
|
|
1123
|
+
status: string;
|
|
1124
|
+
}>;
|
|
1125
|
+
const filteredRows = rows.filter((r) => r.id === jobAId || r.id === jobBId);
|
|
1126
|
+
expect(filteredRows).toHaveLength(2);
|
|
1127
|
+
expect(filteredRows.every((r) => r.status === "done")).toBe(true);
|
|
1123
1128
|
});
|
|
1124
1129
|
});
|