@actuate-media/cms-core 0.1.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/dist/__tests__/actions/document-crud.test.d.ts +2 -0
- package/dist/__tests__/actions/document-crud.test.d.ts.map +1 -0
- package/dist/__tests__/actions/document-crud.test.js +156 -0
- package/dist/__tests__/actions/document-crud.test.js.map +1 -0
- package/dist/__tests__/auth/password.test.d.ts +2 -0
- package/dist/__tests__/auth/password.test.d.ts.map +1 -0
- package/dist/__tests__/auth/password.test.js +102 -0
- package/dist/__tests__/auth/password.test.js.map +1 -0
- package/dist/__tests__/auth/session.test.d.ts +2 -0
- package/dist/__tests__/auth/session.test.d.ts.map +1 -0
- package/dist/__tests__/auth/session.test.js +66 -0
- package/dist/__tests__/auth/session.test.js.map +1 -0
- package/dist/__tests__/codegen/generate-types.test.d.ts +2 -0
- package/dist/__tests__/codegen/generate-types.test.d.ts.map +1 -0
- package/dist/__tests__/codegen/generate-types.test.js +173 -0
- package/dist/__tests__/codegen/generate-types.test.js.map +1 -0
- package/dist/__tests__/scheduling/scheduling.test.d.ts +2 -0
- package/dist/__tests__/scheduling/scheduling.test.d.ts.map +1 -0
- package/dist/__tests__/scheduling/scheduling.test.js +84 -0
- package/dist/__tests__/scheduling/scheduling.test.js.map +1 -0
- package/dist/__tests__/security/access.test.d.ts +2 -0
- package/dist/__tests__/security/access.test.d.ts.map +1 -0
- package/dist/__tests__/security/access.test.js +181 -0
- package/dist/__tests__/security/access.test.js.map +1 -0
- package/dist/__tests__/security/csrf.test.d.ts +2 -0
- package/dist/__tests__/security/csrf.test.d.ts.map +1 -0
- package/dist/__tests__/security/csrf.test.js +40 -0
- package/dist/__tests__/security/csrf.test.js.map +1 -0
- package/dist/__tests__/security/rate-limit.test.d.ts +2 -0
- package/dist/__tests__/security/rate-limit.test.d.ts.map +1 -0
- package/dist/__tests__/security/rate-limit.test.js +62 -0
- package/dist/__tests__/security/rate-limit.test.js.map +1 -0
- package/dist/__tests__/security/reauth.test.d.ts +2 -0
- package/dist/__tests__/security/reauth.test.d.ts.map +1 -0
- package/dist/__tests__/security/reauth.test.js +30 -0
- package/dist/__tests__/security/reauth.test.js.map +1 -0
- package/dist/__tests__/security/sanitize.test.d.ts +2 -0
- package/dist/__tests__/security/sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/security/sanitize.test.js +75 -0
- package/dist/__tests__/security/sanitize.test.js.map +1 -0
- package/dist/__tests__/webhooks/webhooks.test.d.ts +2 -0
- package/dist/__tests__/webhooks/webhooks.test.d.ts.map +1 -0
- package/dist/__tests__/webhooks/webhooks.test.js +96 -0
- package/dist/__tests__/webhooks/webhooks.test.js.map +1 -0
- package/dist/a11y/index.d.ts +25 -0
- package/dist/a11y/index.d.ts.map +1 -0
- package/dist/a11y/index.js +88 -0
- package/dist/a11y/index.js.map +1 -0
- package/dist/actions.d.ts +42 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +391 -0
- package/dist/actions.js.map +1 -0
- package/dist/api/handler-factory.d.ts +7 -0
- package/dist/api/handler-factory.d.ts.map +1 -0
- package/dist/api/handler-factory.js +120 -0
- package/dist/api/handler-factory.js.map +1 -0
- package/dist/api/handlers.d.ts +4 -0
- package/dist/api/handlers.d.ts.map +1 -0
- package/dist/api/handlers.js +2119 -0
- package/dist/api/handlers.js.map +1 -0
- package/dist/api/index.d.ts +23 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +57 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/openapi.d.ts +3 -0
- package/dist/api/openapi.d.ts.map +1 -0
- package/dist/api/openapi.js +348 -0
- package/dist/api/openapi.js.map +1 -0
- package/dist/auth/index.d.ts +11 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +9 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth.d.ts +84 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +201 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/password.d.ts +13 -0
- package/dist/auth/password.d.ts.map +1 -0
- package/dist/auth/password.js +47 -0
- package/dist/auth/password.js.map +1 -0
- package/dist/auth/providers/github.d.ts +9 -0
- package/dist/auth/providers/github.d.ts.map +1 -0
- package/dist/auth/providers/github.js +10 -0
- package/dist/auth/providers/github.js.map +1 -0
- package/dist/auth/providers/google.d.ts +9 -0
- package/dist/auth/providers/google.d.ts.map +1 -0
- package/dist/auth/providers/google.js +10 -0
- package/dist/auth/providers/google.js.map +1 -0
- package/dist/auth/providers/microsoft.d.ts +9 -0
- package/dist/auth/providers/microsoft.d.ts.map +1 -0
- package/dist/auth/providers/microsoft.js +11 -0
- package/dist/auth/providers/microsoft.js.map +1 -0
- package/dist/auth/session.d.ts +21 -0
- package/dist/auth/session.d.ts.map +1 -0
- package/dist/auth/session.js +35 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/totp.d.ts +5 -0
- package/dist/auth/totp.d.ts.map +1 -0
- package/dist/auth/totp.js +86 -0
- package/dist/auth/totp.js.map +1 -0
- package/dist/backup/index.d.ts +19 -0
- package/dist/backup/index.d.ts.map +1 -0
- package/dist/backup/index.js +22 -0
- package/dist/backup/index.js.map +1 -0
- package/dist/cache/index.d.ts +15 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +32 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/client.d.ts +30 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +50 -0
- package/dist/client.js.map +1 -0
- package/dist/codegen/index.d.ts +4 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +370 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/collections/index.d.ts +17 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/collections/index.js +29 -0
- package/dist/collections/index.js.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +74 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +307 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +3 -0
- package/dist/config/types.js.map +1 -0
- package/dist/content/ai-api.d.ts +21 -0
- package/dist/content/ai-api.d.ts.map +1 -0
- package/dist/content/ai-api.js +19 -0
- package/dist/content/ai-api.js.map +1 -0
- package/dist/content/content-graph.d.ts +25 -0
- package/dist/content/content-graph.d.ts.map +1 -0
- package/dist/content/content-graph.js +40 -0
- package/dist/content/content-graph.js.map +1 -0
- package/dist/content/extract.d.ts +7 -0
- package/dist/content/extract.d.ts.map +1 -0
- package/dist/content/extract.js +33 -0
- package/dist/content/extract.js.map +1 -0
- package/dist/content/index.d.ts +8 -0
- package/dist/content/index.d.ts.map +1 -0
- package/dist/content/index.js +5 -0
- package/dist/content/index.js.map +1 -0
- package/dist/content/structured-data.d.ts +80 -0
- package/dist/content/structured-data.d.ts.map +1 -0
- package/dist/content/structured-data.js +295 -0
- package/dist/content/structured-data.js.map +1 -0
- package/dist/db/adapters/mysql.d.ts +5 -0
- package/dist/db/adapters/mysql.d.ts.map +1 -0
- package/dist/db/adapters/mysql.js +18 -0
- package/dist/db/adapters/mysql.js.map +1 -0
- package/dist/db/adapters/postgres.d.ts +7 -0
- package/dist/db/adapters/postgres.d.ts.map +1 -0
- package/dist/db/adapters/postgres.js +20 -0
- package/dist/db/adapters/postgres.js.map +1 -0
- package/dist/db/adapters/sqlite.d.ts +5 -0
- package/dist/db/adapters/sqlite.d.ts.map +1 -0
- package/dist/db/adapters/sqlite.js +19 -0
- package/dist/db/adapters/sqlite.js.map +1 -0
- package/dist/db/create-adapter.d.ts +11 -0
- package/dist/db/create-adapter.d.ts.map +1 -0
- package/dist/db/create-adapter.js +43 -0
- package/dist/db/create-adapter.js.map +1 -0
- package/dist/db/index.d.ts +9 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +5 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db.d.ts +20 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +35 -0
- package/dist/db.js.map +1 -0
- package/dist/fields/index.d.ts +15 -0
- package/dist/fields/index.d.ts.map +1 -0
- package/dist/fields/index.js +87 -0
- package/dist/fields/index.js.map +1 -0
- package/dist/forms/analytics.d.ts +62 -0
- package/dist/forms/analytics.d.ts.map +1 -0
- package/dist/forms/analytics.js +95 -0
- package/dist/forms/analytics.js.map +1 -0
- package/dist/forms/attribution.d.ts +29 -0
- package/dist/forms/attribution.d.ts.map +1 -0
- package/dist/forms/attribution.js +216 -0
- package/dist/forms/attribution.js.map +1 -0
- package/dist/forms/index.d.ts +5 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/index.js +3 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/graphql/index.d.ts +11 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +58 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/resolvers.d.ts +8 -0
- package/dist/graphql/resolvers.d.ts.map +1 -0
- package/dist/graphql/resolvers.js +93 -0
- package/dist/graphql/resolvers.js.map +1 -0
- package/dist/graphql/schema-builder.d.ts +3 -0
- package/dist/graphql/schema-builder.d.ts.map +1 -0
- package/dist/graphql/schema-builder.js +103 -0
- package/dist/graphql/schema-builder.js.map +1 -0
- package/dist/health/index.d.ts +27 -0
- package/dist/health/index.d.ts.map +1 -0
- package/dist/health/index.js +43 -0
- package/dist/health/index.js.map +1 -0
- package/dist/i18n/index.d.ts +22 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +37 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/media/index.d.ts +3 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +2 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/optimize.d.ts +40 -0
- package/dist/media/optimize.d.ts.map +1 -0
- package/dist/media/optimize.js +137 -0
- package/dist/media/optimize.js.map +1 -0
- package/dist/middleware.d.ts +7 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +86 -0
- package/dist/middleware.js.map +1 -0
- package/dist/multisite/index.d.ts +20 -0
- package/dist/multisite/index.d.ts.map +1 -0
- package/dist/multisite/index.js +26 -0
- package/dist/multisite/index.js.map +1 -0
- package/dist/next/preview.d.ts +10 -0
- package/dist/next/preview.d.ts.map +1 -0
- package/dist/next/preview.js +17 -0
- package/dist/next/preview.js.map +1 -0
- package/dist/next.d.ts +9 -0
- package/dist/next.d.ts.map +1 -0
- package/dist/next.js +35 -0
- package/dist/next.js.map +1 -0
- package/dist/notifications/index.d.ts +20 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +22 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/presence/index.d.ts +24 -0
- package/dist/presence/index.d.ts.map +1 -0
- package/dist/presence/index.js +99 -0
- package/dist/presence/index.js.map +1 -0
- package/dist/preview/index.d.ts +14 -0
- package/dist/preview/index.d.ts.map +1 -0
- package/dist/preview/index.js +45 -0
- package/dist/preview/index.js.map +1 -0
- package/dist/privacy/index.d.ts +33 -0
- package/dist/privacy/index.d.ts.map +1 -0
- package/dist/privacy/index.js +15 -0
- package/dist/privacy/index.js.map +1 -0
- package/dist/relationships/index.d.ts +13 -0
- package/dist/relationships/index.d.ts.map +1 -0
- package/dist/relationships/index.js +12 -0
- package/dist/relationships/index.js.map +1 -0
- package/dist/scheduling/index.d.ts +44 -0
- package/dist/scheduling/index.d.ts.map +1 -0
- package/dist/scheduling/index.js +119 -0
- package/dist/scheduling/index.js.map +1 -0
- package/dist/search/index.d.ts +25 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +168 -0
- package/dist/search/index.js.map +1 -0
- package/dist/security/access.d.ts +26 -0
- package/dist/security/access.d.ts.map +1 -0
- package/dist/security/access.js +92 -0
- package/dist/security/access.js.map +1 -0
- package/dist/security/anomaly-detection.d.ts +17 -0
- package/dist/security/anomaly-detection.d.ts.map +1 -0
- package/dist/security/anomaly-detection.js +17 -0
- package/dist/security/anomaly-detection.js.map +1 -0
- package/dist/security/api-key-enhanced.d.ts +25 -0
- package/dist/security/api-key-enhanced.d.ts.map +1 -0
- package/dist/security/api-key-enhanced.js +25 -0
- package/dist/security/api-key-enhanced.js.map +1 -0
- package/dist/security/audit.d.ts +39 -0
- package/dist/security/audit.d.ts.map +1 -0
- package/dist/security/audit.js +40 -0
- package/dist/security/audit.js.map +1 -0
- package/dist/security/breach-check.d.ts +3 -0
- package/dist/security/breach-check.d.ts.map +1 -0
- package/dist/security/breach-check.js +27 -0
- package/dist/security/breach-check.js.map +1 -0
- package/dist/security/cors.d.ts +11 -0
- package/dist/security/cors.d.ts.map +1 -0
- package/dist/security/cors.js +33 -0
- package/dist/security/cors.js.map +1 -0
- package/dist/security/csp-nonces.d.ts +5 -0
- package/dist/security/csp-nonces.d.ts.map +1 -0
- package/dist/security/csp-nonces.js +24 -0
- package/dist/security/csp-nonces.js.map +1 -0
- package/dist/security/csrf.d.ts +5 -0
- package/dist/security/csrf.d.ts.map +1 -0
- package/dist/security/csrf.js +20 -0
- package/dist/security/csrf.js.map +1 -0
- package/dist/security/encrypted-fields.d.ts +5 -0
- package/dist/security/encrypted-fields.d.ts.map +1 -0
- package/dist/security/encrypted-fields.js +40 -0
- package/dist/security/encrypted-fields.js.map +1 -0
- package/dist/security/headers.d.ts +11 -0
- package/dist/security/headers.d.ts.map +1 -0
- package/dist/security/headers.js +32 -0
- package/dist/security/headers.js.map +1 -0
- package/dist/security/index.d.ts +31 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +20 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/ip-allowlist.d.ts +3 -0
- package/dist/security/ip-allowlist.d.ts.map +1 -0
- package/dist/security/ip-allowlist.js +35 -0
- package/dist/security/ip-allowlist.js.map +1 -0
- package/dist/security/middleware.d.ts +20 -0
- package/dist/security/middleware.d.ts.map +1 -0
- package/dist/security/middleware.js +45 -0
- package/dist/security/middleware.js.map +1 -0
- package/dist/security/rate-limit.d.ts +24 -0
- package/dist/security/rate-limit.d.ts.map +1 -0
- package/dist/security/rate-limit.js +84 -0
- package/dist/security/rate-limit.js.map +1 -0
- package/dist/security/reauth.d.ts +15 -0
- package/dist/security/reauth.d.ts.map +1 -0
- package/dist/security/reauth.js +38 -0
- package/dist/security/reauth.js.map +1 -0
- package/dist/security/sanitize.d.ts +13 -0
- package/dist/security/sanitize.d.ts.map +1 -0
- package/dist/security/sanitize.js +34 -0
- package/dist/security/sanitize.js.map +1 -0
- package/dist/security/security-txt.d.ts +12 -0
- package/dist/security/security-txt.d.ts.map +1 -0
- package/dist/security/security-txt.js +19 -0
- package/dist/security/security-txt.js.map +1 -0
- package/dist/security/session-limits.d.ts +17 -0
- package/dist/security/session-limits.d.ts.map +1 -0
- package/dist/security/session-limits.js +14 -0
- package/dist/security/session-limits.js.map +1 -0
- package/dist/security/upload.d.ts +13 -0
- package/dist/security/upload.d.ts.map +1 -0
- package/dist/security/upload.js +34 -0
- package/dist/security/upload.js.map +1 -0
- package/dist/security/webhook.d.ts +12 -0
- package/dist/security/webhook.d.ts.map +1 -0
- package/dist/security/webhook.js +38 -0
- package/dist/security/webhook.js.map +1 -0
- package/dist/seo/analysis.d.ts +66 -0
- package/dist/seo/analysis.d.ts.map +1 -0
- package/dist/seo/analysis.js +594 -0
- package/dist/seo/analysis.js.map +1 -0
- package/dist/seo/index.d.ts +9 -0
- package/dist/seo/index.d.ts.map +1 -0
- package/dist/seo/index.js +5 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/llms-txt.d.ts +16 -0
- package/dist/seo/llms-txt.d.ts.map +1 -0
- package/dist/seo/llms-txt.js +70 -0
- package/dist/seo/llms-txt.js.map +1 -0
- package/dist/seo/meta-tags.d.ts +33 -0
- package/dist/seo/meta-tags.d.ts.map +1 -0
- package/dist/seo/meta-tags.js +159 -0
- package/dist/seo/meta-tags.js.map +1 -0
- package/dist/seo/title-templates.d.ts +17 -0
- package/dist/seo/title-templates.d.ts.map +1 -0
- package/dist/seo/title-templates.js +28 -0
- package/dist/seo/title-templates.js.map +1 -0
- package/dist/setup/index.d.ts +38 -0
- package/dist/setup/index.d.ts.map +1 -0
- package/dist/setup/index.js +77 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/templates/index.d.ts +16 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +23 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/upgrade/changelog.d.ts +13 -0
- package/dist/upgrade/changelog.d.ts.map +1 -0
- package/dist/upgrade/changelog.js +54 -0
- package/dist/upgrade/changelog.js.map +1 -0
- package/dist/upgrade/index.d.ts +7 -0
- package/dist/upgrade/index.d.ts.map +1 -0
- package/dist/upgrade/index.js +4 -0
- package/dist/upgrade/index.js.map +1 -0
- package/dist/upgrade/upgrade-pr.d.ts +16 -0
- package/dist/upgrade/upgrade-pr.d.ts.map +1 -0
- package/dist/upgrade/upgrade-pr.js +38 -0
- package/dist/upgrade/upgrade-pr.js.map +1 -0
- package/dist/upgrade/version-check.d.ts +17 -0
- package/dist/upgrade/version-check.d.ts.map +1 -0
- package/dist/upgrade/version-check.js +30 -0
- package/dist/upgrade/version-check.js.map +1 -0
- package/dist/webhooks/index.d.ts +46 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +245 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/workflow/index.d.ts +8 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +56 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflows/index.d.ts +30 -0
- package/dist/workflows/index.d.ts.map +1 -0
- package/dist/workflows/index.js +14 -0
- package/dist/workflows/index.js.map +1 -0
- package/generated/browser.ts +109 -0
- package/generated/client.ts +133 -0
- package/generated/commonInputTypes.ts +709 -0
- package/generated/enums.ts +125 -0
- package/generated/internal/class.ts +376 -0
- package/generated/internal/prismaNamespace.ts +2617 -0
- package/generated/internal/prismaNamespaceBrowser.ts +611 -0
- package/generated/models/ApiKey.ts +1550 -0
- package/generated/models/AuditLog.ts +1206 -0
- package/generated/models/BackupRecord.ts +1250 -0
- package/generated/models/ContentLock.ts +1472 -0
- package/generated/models/ContentTemplate.ts +1416 -0
- package/generated/models/Document.ts +3005 -0
- package/generated/models/Folder.ts +1904 -0
- package/generated/models/FormSubmission.ts +1200 -0
- package/generated/models/InAppNotification.ts +1457 -0
- package/generated/models/Media.ts +2340 -0
- package/generated/models/MediaUsage.ts +1472 -0
- package/generated/models/OAuthAccount.ts +1463 -0
- package/generated/models/Redirect.ts +1284 -0
- package/generated/models/Session.ts +1492 -0
- package/generated/models/Site.ts +1206 -0
- package/generated/models/User.ts +3513 -0
- package/generated/models/Version.ts +1511 -0
- package/generated/models/WorkflowState.ts +1514 -0
- package/generated/models.ts +29 -0
- package/package.json +83 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { processScheduledPublish, processScheduledUnpublish, getScheduleCalendar, } from '../../scheduling/index';
|
|
3
|
+
function createMockDB(docs = []) {
|
|
4
|
+
return {
|
|
5
|
+
document: {
|
|
6
|
+
findMany: vi.fn(async () => docs),
|
|
7
|
+
update: vi.fn(async ({ where, data }) => {
|
|
8
|
+
const doc = docs.find((d) => d.id === where.id);
|
|
9
|
+
if (doc)
|
|
10
|
+
Object.assign(doc, data);
|
|
11
|
+
return doc;
|
|
12
|
+
}),
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
describe('processScheduledPublish', () => {
|
|
17
|
+
it('publishes documents with scheduledAt in the past', async () => {
|
|
18
|
+
const docs = [
|
|
19
|
+
{ id: 'doc-1', status: 'SCHEDULED', scheduledAt: new Date(Date.now() - 10000) },
|
|
20
|
+
{ id: 'doc-2', status: 'SCHEDULED', scheduledAt: new Date(Date.now() - 5000) },
|
|
21
|
+
];
|
|
22
|
+
const db = createMockDB(docs);
|
|
23
|
+
const result = await processScheduledPublish(db);
|
|
24
|
+
expect(result.published).toBe(2);
|
|
25
|
+
expect(result.errors).toHaveLength(0);
|
|
26
|
+
expect(db.document.update).toHaveBeenCalledTimes(2);
|
|
27
|
+
});
|
|
28
|
+
it('returns empty when no documents are due', async () => {
|
|
29
|
+
const db = createMockDB([]);
|
|
30
|
+
const result = await processScheduledPublish(db);
|
|
31
|
+
expect(result.published).toBe(0);
|
|
32
|
+
expect(result.errors).toHaveLength(0);
|
|
33
|
+
});
|
|
34
|
+
it('captures errors for individual documents', async () => {
|
|
35
|
+
const docs = [{ id: 'doc-1', status: 'SCHEDULED', scheduledAt: new Date(Date.now() - 10000) }];
|
|
36
|
+
const db = createMockDB(docs);
|
|
37
|
+
db.document.update.mockRejectedValueOnce(new Error('DB error'));
|
|
38
|
+
const result = await processScheduledPublish(db);
|
|
39
|
+
expect(result.published).toBe(0);
|
|
40
|
+
expect(result.errors).toHaveLength(1);
|
|
41
|
+
expect(result.errors[0].documentId).toBe('doc-1');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('processScheduledUnpublish', () => {
|
|
45
|
+
it('unpublishes documents with scheduledUnpublishAt in the past', async () => {
|
|
46
|
+
const docs = [
|
|
47
|
+
{ id: 'doc-1', status: 'PUBLISHED', scheduledUnpublishAt: new Date(Date.now() - 10000) },
|
|
48
|
+
];
|
|
49
|
+
const db = createMockDB(docs);
|
|
50
|
+
const result = await processScheduledUnpublish(db);
|
|
51
|
+
expect(result.unpublished).toBe(1);
|
|
52
|
+
expect(result.errors).toHaveLength(0);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('getScheduleCalendar', () => {
|
|
56
|
+
it('returns entries for scheduled publish and unpublish', async () => {
|
|
57
|
+
const now = new Date();
|
|
58
|
+
const future = new Date(Date.now() + 86_400_000);
|
|
59
|
+
const docs = [
|
|
60
|
+
{ id: 'doc-1', collection: 'pages', title: 'Page 1', scheduledAt: future, scheduledUnpublishAt: null },
|
|
61
|
+
{ id: 'doc-2', collection: 'posts', title: 'Post 1', scheduledAt: null, scheduledUnpublishAt: future },
|
|
62
|
+
];
|
|
63
|
+
const db = createMockDB(docs);
|
|
64
|
+
const from = new Date(Date.now() - 86_400_000);
|
|
65
|
+
const to = new Date(Date.now() + 86_400_000 * 2);
|
|
66
|
+
const entries = await getScheduleCalendar(from, to, db);
|
|
67
|
+
expect(entries).toHaveLength(2);
|
|
68
|
+
expect(entries[0].action).toBe('publish');
|
|
69
|
+
expect(entries[1].action).toBe('unpublish');
|
|
70
|
+
});
|
|
71
|
+
it('returns sorted entries', async () => {
|
|
72
|
+
const earlier = new Date(Date.now() + 10_000);
|
|
73
|
+
const later = new Date(Date.now() + 86_400_000);
|
|
74
|
+
const docs = [
|
|
75
|
+
{ id: 'doc-1', collection: 'pages', title: 'Later', scheduledAt: later, scheduledUnpublishAt: null },
|
|
76
|
+
{ id: 'doc-2', collection: 'pages', title: 'Earlier', scheduledAt: earlier, scheduledUnpublishAt: null },
|
|
77
|
+
];
|
|
78
|
+
const db = createMockDB(docs);
|
|
79
|
+
const entries = await getScheduleCalendar(new Date(), new Date(Date.now() + 86_400_000 * 2), db);
|
|
80
|
+
expect(entries[0].title).toBe('Earlier');
|
|
81
|
+
expect(entries[1].title).toBe('Later');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=scheduling.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduling.test.js","sourceRoot":"","sources":["../../../src/__tests__/scheduling/scheduling.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/security/access.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { checkAccess, getPermissionsForRole, filterFieldsByRole, applyFieldAccess } from '../../security/access';
|
|
3
|
+
describe('checkAccess', () => {
|
|
4
|
+
it('ADMIN can access everything', () => {
|
|
5
|
+
expect(checkAccess('ADMIN', 'ADMIN')).toBe(true);
|
|
6
|
+
expect(checkAccess('ADMIN', 'EDITOR')).toBe(true);
|
|
7
|
+
expect(checkAccess('ADMIN', 'AUTHOR')).toBe(true);
|
|
8
|
+
expect(checkAccess('ADMIN', 'CLIENT')).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
it('EDITOR can access AUTHOR level', () => {
|
|
11
|
+
expect(checkAccess('EDITOR', 'AUTHOR')).toBe(true);
|
|
12
|
+
expect(checkAccess('EDITOR', 'CLIENT')).toBe(true);
|
|
13
|
+
expect(checkAccess('EDITOR', 'EDITOR')).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it('EDITOR cannot access ADMIN level', () => {
|
|
16
|
+
expect(checkAccess('EDITOR', 'ADMIN')).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it('AUTHOR cannot access EDITOR level', () => {
|
|
19
|
+
expect(checkAccess('AUTHOR', 'EDITOR')).toBe(false);
|
|
20
|
+
expect(checkAccess('AUTHOR', 'ADMIN')).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
it('AUTHOR can access AUTHOR and CLIENT levels', () => {
|
|
23
|
+
expect(checkAccess('AUTHOR', 'AUTHOR')).toBe(true);
|
|
24
|
+
expect(checkAccess('AUTHOR', 'CLIENT')).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
it('CLIENT cannot access AUTHOR level', () => {
|
|
27
|
+
expect(checkAccess('CLIENT', 'AUTHOR')).toBe(false);
|
|
28
|
+
expect(checkAccess('CLIENT', 'EDITOR')).toBe(false);
|
|
29
|
+
expect(checkAccess('CLIENT', 'ADMIN')).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it('CLIENT can access CLIENT level', () => {
|
|
32
|
+
expect(checkAccess('CLIENT', 'CLIENT')).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('getPermissionsForRole', () => {
|
|
36
|
+
it('ADMIN gets delete permission', () => {
|
|
37
|
+
const perms = getPermissionsForRole('ADMIN');
|
|
38
|
+
const actions = perms.map((p) => p.action);
|
|
39
|
+
expect(actions).toContain('delete');
|
|
40
|
+
expect(actions).toContain('create');
|
|
41
|
+
expect(actions).toContain('read');
|
|
42
|
+
expect(actions).toContain('update');
|
|
43
|
+
expect(actions).toContain('publish');
|
|
44
|
+
});
|
|
45
|
+
it('EDITOR gets publish but not delete', () => {
|
|
46
|
+
const perms = getPermissionsForRole('EDITOR');
|
|
47
|
+
const actions = perms.map((p) => p.action);
|
|
48
|
+
expect(actions).toContain('publish');
|
|
49
|
+
expect(actions).toContain('read');
|
|
50
|
+
expect(actions).toContain('create');
|
|
51
|
+
expect(actions).not.toContain('delete');
|
|
52
|
+
});
|
|
53
|
+
it('AUTHOR gets create but not delete', () => {
|
|
54
|
+
const perms = getPermissionsForRole('AUTHOR');
|
|
55
|
+
const actions = perms.map((p) => p.action);
|
|
56
|
+
expect(actions).toContain('read');
|
|
57
|
+
expect(actions).toContain('create');
|
|
58
|
+
expect(actions).not.toContain('delete');
|
|
59
|
+
expect(actions).not.toContain('publish');
|
|
60
|
+
});
|
|
61
|
+
it('CLIENT only gets read permission', () => {
|
|
62
|
+
const perms = getPermissionsForRole('CLIENT');
|
|
63
|
+
const actions = perms.map((p) => p.action);
|
|
64
|
+
expect(actions).toEqual(['read']);
|
|
65
|
+
});
|
|
66
|
+
it('AUTHOR has "update own" but not "update *"', () => {
|
|
67
|
+
const perms = getPermissionsForRole('AUTHOR');
|
|
68
|
+
const updatePerms = perms.filter((p) => p.action === 'update');
|
|
69
|
+
expect(updatePerms).toHaveLength(1);
|
|
70
|
+
expect(updatePerms[0].resource).toBe('own');
|
|
71
|
+
});
|
|
72
|
+
it('EDITOR has "update *" (all resources)', () => {
|
|
73
|
+
const perms = getPermissionsForRole('EDITOR');
|
|
74
|
+
const updatePerms = perms.filter((p) => p.action === 'update');
|
|
75
|
+
const resources = updatePerms.map((p) => p.resource);
|
|
76
|
+
expect(resources).toContain('*');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('filterFieldsByRole', () => {
|
|
80
|
+
const adminUser = { id: 'admin-1', role: 'ADMIN' };
|
|
81
|
+
const clientUser = { id: 'client-1', role: 'CLIENT' };
|
|
82
|
+
it('returns all fields when no access control is defined', async () => {
|
|
83
|
+
const fields = {
|
|
84
|
+
title: { type: 'text', label: 'Title', required: true },
|
|
85
|
+
body: { type: 'richText', label: 'Body' },
|
|
86
|
+
};
|
|
87
|
+
const filtered = await filterFieldsByRole(fields, clientUser);
|
|
88
|
+
expect(Object.keys(filtered)).toEqual(['title', 'body']);
|
|
89
|
+
});
|
|
90
|
+
it('returns a shallow copy, not the same reference', async () => {
|
|
91
|
+
const fields = {
|
|
92
|
+
name: { type: 'text', label: 'Name' },
|
|
93
|
+
};
|
|
94
|
+
const filtered = await filterFieldsByRole(fields, adminUser);
|
|
95
|
+
expect(filtered).not.toBe(fields);
|
|
96
|
+
expect(filtered).toEqual(fields);
|
|
97
|
+
});
|
|
98
|
+
it('filters out fields the user cannot read', async () => {
|
|
99
|
+
const fields = {
|
|
100
|
+
title: { type: 'text', label: 'Title' },
|
|
101
|
+
secret: {
|
|
102
|
+
type: 'text',
|
|
103
|
+
label: 'Secret',
|
|
104
|
+
access: {
|
|
105
|
+
read: async ({ user }) => user?.role === 'ADMIN',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const clientFiltered = await filterFieldsByRole(fields, clientUser);
|
|
110
|
+
expect(Object.keys(clientFiltered)).toEqual(['title']);
|
|
111
|
+
const adminFiltered = await filterFieldsByRole(fields, adminUser);
|
|
112
|
+
expect(Object.keys(adminFiltered)).toEqual(['title', 'secret']);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe('applyFieldAccess', () => {
|
|
116
|
+
const adminUser = { id: 'admin-1', role: 'ADMIN' };
|
|
117
|
+
const clientUser = { id: 'client-1', role: 'CLIENT' };
|
|
118
|
+
it('read mode: strips fields the user cannot read', async () => {
|
|
119
|
+
const fields = {
|
|
120
|
+
title: { type: 'text', label: 'Title' },
|
|
121
|
+
internal: {
|
|
122
|
+
type: 'text',
|
|
123
|
+
label: 'Internal',
|
|
124
|
+
access: {
|
|
125
|
+
read: async ({ user }) => user?.role === 'ADMIN',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
const data = { title: 'Hello', internal: 'secret-value', extra: 'kept' };
|
|
130
|
+
const clientResult = await applyFieldAccess('read', fields, data, clientUser);
|
|
131
|
+
expect(clientResult).toEqual({ title: 'Hello', extra: 'kept' });
|
|
132
|
+
const adminResult = await applyFieldAccess('read', fields, data, adminUser);
|
|
133
|
+
expect(adminResult).toEqual({ title: 'Hello', internal: 'secret-value', extra: 'kept' });
|
|
134
|
+
});
|
|
135
|
+
it('write mode: strips fields the user cannot update', async () => {
|
|
136
|
+
const fields = {
|
|
137
|
+
title: { type: 'text', label: 'Title' },
|
|
138
|
+
locked: {
|
|
139
|
+
type: 'text',
|
|
140
|
+
label: 'Locked',
|
|
141
|
+
access: {
|
|
142
|
+
update: async ({ user }) => user?.role === 'ADMIN',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const data = { title: 'New Title', locked: 'overwrite-attempt' };
|
|
147
|
+
const clientResult = await applyFieldAccess('write', fields, data, clientUser);
|
|
148
|
+
expect(clientResult).toEqual({ title: 'New Title' });
|
|
149
|
+
const adminResult = await applyFieldAccess('write', fields, data, adminUser);
|
|
150
|
+
expect(adminResult).toEqual({ title: 'New Title', locked: 'overwrite-attempt' });
|
|
151
|
+
});
|
|
152
|
+
it('passes through unknown keys on read', async () => {
|
|
153
|
+
const fields = {
|
|
154
|
+
title: { type: 'text', label: 'Title' },
|
|
155
|
+
};
|
|
156
|
+
const data = { title: 'Hello', unknownField: 'value' };
|
|
157
|
+
const result = await applyFieldAccess('read', fields, data, clientUser);
|
|
158
|
+
expect(result).toEqual({ title: 'Hello', unknownField: 'value' });
|
|
159
|
+
});
|
|
160
|
+
it('strips unknown keys on write (mass assignment prevention)', async () => {
|
|
161
|
+
const fields = {
|
|
162
|
+
title: { type: 'text', label: 'Title' },
|
|
163
|
+
};
|
|
164
|
+
const data = { title: 'Hello', isAdmin: true, role: 'ADMIN' };
|
|
165
|
+
const result = await applyFieldAccess('write', fields, data, clientUser);
|
|
166
|
+
expect(result).toEqual({ title: 'Hello' });
|
|
167
|
+
expect(result).not.toHaveProperty('isAdmin');
|
|
168
|
+
expect(result).not.toHaveProperty('role');
|
|
169
|
+
});
|
|
170
|
+
it('allows system keys on write', async () => {
|
|
171
|
+
const fields = {
|
|
172
|
+
body: { type: 'richText', label: 'Body' },
|
|
173
|
+
};
|
|
174
|
+
const data = { body: '<p>Hi</p>', title: 'My Page', slug: 'my-page', status: 'PUBLISHED' };
|
|
175
|
+
const result = await applyFieldAccess('write', fields, data, clientUser);
|
|
176
|
+
expect(result).toHaveProperty('title');
|
|
177
|
+
expect(result).toHaveProperty('slug');
|
|
178
|
+
expect(result).toHaveProperty('status');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=access.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access.test.js","sourceRoot":"","sources":["../../../src/__tests__/security/access.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/security/csrf.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generateToken, validateToken } from '../../security/csrf.js';
|
|
3
|
+
describe('generateToken', () => {
|
|
4
|
+
it('returns a 64-char hex string', async () => {
|
|
5
|
+
const token = await generateToken();
|
|
6
|
+
expect(token).toHaveLength(64);
|
|
7
|
+
expect(token).toMatch(/^[0-9a-f]{64}$/);
|
|
8
|
+
});
|
|
9
|
+
it('generates unique tokens on each call', async () => {
|
|
10
|
+
const token1 = await generateToken();
|
|
11
|
+
const token2 = await generateToken();
|
|
12
|
+
expect(token1).not.toBe(token2);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe('validateToken', () => {
|
|
16
|
+
it('returns true for matching tokens', async () => {
|
|
17
|
+
const token = await generateToken();
|
|
18
|
+
expect(validateToken(token, token)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it('returns true for identical manually-constructed tokens', () => {
|
|
21
|
+
const token = 'a'.repeat(64);
|
|
22
|
+
expect(validateToken(token, token)).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
it('returns false for different tokens of the same length', async () => {
|
|
25
|
+
const token1 = await generateToken();
|
|
26
|
+
const token2 = await generateToken();
|
|
27
|
+
expect(validateToken(token1, token2)).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
it('returns false for tokens of different lengths', () => {
|
|
30
|
+
const short = 'abcdef';
|
|
31
|
+
const long = 'abcdefabcdef';
|
|
32
|
+
expect(validateToken(short, long)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it('returns false when one token is empty', async () => {
|
|
35
|
+
const token = await generateToken();
|
|
36
|
+
expect(validateToken(token, '')).toBe(false);
|
|
37
|
+
expect(validateToken('', token)).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=csrf.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf.test.js","sourceRoot":"","sources":["../../../src/__tests__/security/csrf.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEtE,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,KAAK,GAAG,MAAM,aAAa,EAAE,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,KAAK,GAAG,MAAM,aAAa,EAAE,CAAC;QACpC,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC;QACvB,MAAM,IAAI,GAAG,cAAc,CAAC;QAC5B,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,KAAK,GAAG,MAAM,aAAa,EAAE,CAAC;QACpC,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/security/rate-limit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { createRateLimiter } from '../../security/rate-limit.js';
|
|
3
|
+
describe('createRateLimiter', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.useFakeTimers();
|
|
6
|
+
});
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
vi.useRealTimers();
|
|
9
|
+
});
|
|
10
|
+
it('allows requests under the limit', async () => {
|
|
11
|
+
const limiter = createRateLimiter({ windowMs: 60_000, maxRequests: 5 });
|
|
12
|
+
const r1 = await limiter.check('ip:127.0.0.1');
|
|
13
|
+
expect(r1.allowed).toBe(true);
|
|
14
|
+
expect(r1.remaining).toBe(4);
|
|
15
|
+
const r2 = await limiter.check('ip:127.0.0.1');
|
|
16
|
+
expect(r2.allowed).toBe(true);
|
|
17
|
+
expect(r2.remaining).toBe(3);
|
|
18
|
+
});
|
|
19
|
+
it('blocks requests over the limit', async () => {
|
|
20
|
+
const limiter = createRateLimiter({ windowMs: 60_000, maxRequests: 3 });
|
|
21
|
+
await limiter.check('ip:test');
|
|
22
|
+
await limiter.check('ip:test');
|
|
23
|
+
await limiter.check('ip:test');
|
|
24
|
+
const blocked = await limiter.check('ip:test');
|
|
25
|
+
expect(blocked.allowed).toBe(false);
|
|
26
|
+
expect(blocked.remaining).toBe(0);
|
|
27
|
+
expect(blocked.retryAfter).toBeDefined();
|
|
28
|
+
expect(blocked.retryAfter).toBeGreaterThan(0);
|
|
29
|
+
});
|
|
30
|
+
it('resets after window expires', async () => {
|
|
31
|
+
const limiter = createRateLimiter({ windowMs: 10_000, maxRequests: 2 });
|
|
32
|
+
await limiter.check('ip:expire');
|
|
33
|
+
await limiter.check('ip:expire');
|
|
34
|
+
const blocked = await limiter.check('ip:expire');
|
|
35
|
+
expect(blocked.allowed).toBe(false);
|
|
36
|
+
vi.advanceTimersByTime(11_000);
|
|
37
|
+
const afterReset = await limiter.check('ip:expire');
|
|
38
|
+
expect(afterReset.allowed).toBe(true);
|
|
39
|
+
expect(afterReset.remaining).toBe(1);
|
|
40
|
+
});
|
|
41
|
+
it('reset() clears the counter', async () => {
|
|
42
|
+
const limiter = createRateLimiter({ windowMs: 60_000, maxRequests: 2 });
|
|
43
|
+
await limiter.check('ip:reset');
|
|
44
|
+
await limiter.check('ip:reset');
|
|
45
|
+
const blocked = await limiter.check('ip:reset');
|
|
46
|
+
expect(blocked.allowed).toBe(false);
|
|
47
|
+
await limiter.reset('ip:reset');
|
|
48
|
+
const afterReset = await limiter.check('ip:reset');
|
|
49
|
+
expect(afterReset.allowed).toBe(true);
|
|
50
|
+
expect(afterReset.remaining).toBe(1);
|
|
51
|
+
});
|
|
52
|
+
it('tracks different keys independently', async () => {
|
|
53
|
+
const limiter = createRateLimiter({ windowMs: 60_000, maxRequests: 1 });
|
|
54
|
+
const r1 = await limiter.check('key-a');
|
|
55
|
+
expect(r1.allowed).toBe(true);
|
|
56
|
+
const r2 = await limiter.check('key-b');
|
|
57
|
+
expect(r2.allowed).toBe(true);
|
|
58
|
+
const r3 = await limiter.check('key-a');
|
|
59
|
+
expect(r3.allowed).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
//# sourceMappingURL=rate-limit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.test.js","sourceRoot":"","sources":["../../../src/__tests__/security/rate-limit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reauth.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/security/reauth.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { requiresReauth, DEFAULT_REAUTH_CONFIG } from '../../security/reauth';
|
|
3
|
+
describe('requiresReauth', () => {
|
|
4
|
+
it('returns false for actions not in the required list', () => {
|
|
5
|
+
const result = requiresReauth({ lastAuthAt: new Date(0), action: 'view_dashboard' }, DEFAULT_REAUTH_CONFIG);
|
|
6
|
+
expect(result).toBe(false);
|
|
7
|
+
});
|
|
8
|
+
it('returns false when recently authenticated', () => {
|
|
9
|
+
const result = requiresReauth({ lastAuthAt: new Date(), action: 'delete_user' }, DEFAULT_REAUTH_CONFIG);
|
|
10
|
+
expect(result).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
it('returns true when authentication is stale', () => {
|
|
13
|
+
const staleDate = new Date(Date.now() - 600_000);
|
|
14
|
+
const result = requiresReauth({ lastAuthAt: staleDate, action: 'delete_user' }, DEFAULT_REAUTH_CONFIG);
|
|
15
|
+
expect(result).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it('returns true for change_settings when stale', () => {
|
|
18
|
+
const staleDate = new Date(Date.now() - 600_000);
|
|
19
|
+
const result = requiresReauth({ lastAuthAt: staleDate, action: 'change_settings' }, DEFAULT_REAUTH_CONFIG);
|
|
20
|
+
expect(result).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
it('respects custom maxAgeSeconds', () => {
|
|
23
|
+
const config = { maxAgeSeconds: 10, requiredForActions: ['test_action'] };
|
|
24
|
+
const recentEnough = new Date(Date.now() - 5_000);
|
|
25
|
+
expect(requiresReauth({ lastAuthAt: recentEnough, action: 'test_action' }, config)).toBe(false);
|
|
26
|
+
const tooOld = new Date(Date.now() - 15_000);
|
|
27
|
+
expect(requiresReauth({ lastAuthAt: tooOld, action: 'test_action' }, config)).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=reauth.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reauth.test.js","sourceRoot":"","sources":["../../../src/__tests__/security/reauth.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE9E,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,MAAM,GAAG,cAAc,CAC3B,EAAE,UAAU,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,EACrD,qBAAqB,CACtB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,MAAM,GAAG,cAAc,CAC3B,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,EACjD,qBAAqB,CACtB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,cAAc,CAC3B,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,EAChD,qBAAqB,CACtB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,cAAc,CAC3B,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,EACpD,qBAAqB,CACtB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,MAAM,GAAG,EAAE,aAAa,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1E,MAAM,YAAY,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;QAClD,MAAM,CAAC,cAAc,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEhG,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,cAAc,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/security/sanitize.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { sanitizeHtml, stripHtml } from '../../security/sanitize';
|
|
3
|
+
describe('sanitizeHtml', () => {
|
|
4
|
+
it('strips script tags', () => {
|
|
5
|
+
const input = '<p>Hello</p><script>alert("xss")</script>';
|
|
6
|
+
const result = sanitizeHtml(input);
|
|
7
|
+
expect(result).not.toContain('<script');
|
|
8
|
+
expect(result).toContain('<p>Hello</p>');
|
|
9
|
+
});
|
|
10
|
+
it('strips onerror attributes', () => {
|
|
11
|
+
const input = '<img src="x" onerror="alert(1)" alt="test" />';
|
|
12
|
+
const result = sanitizeHtml(input);
|
|
13
|
+
expect(result).not.toContain('onerror');
|
|
14
|
+
expect(result).toContain('alt="test"');
|
|
15
|
+
});
|
|
16
|
+
it('strips javascript: URLs from hrefs', () => {
|
|
17
|
+
const input = '<a href="javascript:alert(1)">click</a>';
|
|
18
|
+
const result = sanitizeHtml(input);
|
|
19
|
+
expect(result).not.toContain('javascript:');
|
|
20
|
+
});
|
|
21
|
+
it('preserves safe HTML', () => {
|
|
22
|
+
const input = '<h1>Title</h1><p>Text with <strong>bold</strong> and <a href="https://example.com">link</a></p>';
|
|
23
|
+
const result = sanitizeHtml(input);
|
|
24
|
+
expect(result).toContain('<h1>');
|
|
25
|
+
expect(result).toContain('<strong>bold</strong>');
|
|
26
|
+
expect(result).toContain('href="https://example.com"');
|
|
27
|
+
});
|
|
28
|
+
it('preserves image tags with allowed attributes', () => {
|
|
29
|
+
const input = '<img src="https://example.com/img.jpg" alt="photo" width="100" />';
|
|
30
|
+
const result = sanitizeHtml(input);
|
|
31
|
+
expect(result).toContain('src="https://example.com/img.jpg"');
|
|
32
|
+
expect(result).toContain('alt="photo"');
|
|
33
|
+
});
|
|
34
|
+
it('preserves tables', () => {
|
|
35
|
+
const input = '<table><thead><tr><th>Header</th></tr></thead><tbody><tr><td>Cell</td></tr></tbody></table>';
|
|
36
|
+
const result = sanitizeHtml(input);
|
|
37
|
+
expect(result).toContain('<table>');
|
|
38
|
+
expect(result).toContain('<th>Header</th>');
|
|
39
|
+
expect(result).toContain('<td>Cell</td>');
|
|
40
|
+
});
|
|
41
|
+
it('strips everything with stripAll option', () => {
|
|
42
|
+
const input = '<p>Hello <strong>world</strong></p>';
|
|
43
|
+
const result = sanitizeHtml(input, { stripAll: true });
|
|
44
|
+
expect(result).toBe('Hello world');
|
|
45
|
+
});
|
|
46
|
+
it('strips style tags', () => {
|
|
47
|
+
const input = '<style>body{display:none}</style><p>visible</p>';
|
|
48
|
+
const result = sanitizeHtml(input);
|
|
49
|
+
expect(result).not.toContain('<style');
|
|
50
|
+
expect(result).toContain('<p>visible</p>');
|
|
51
|
+
});
|
|
52
|
+
it('strips iframe tags', () => {
|
|
53
|
+
const input = '<iframe src="https://evil.com"></iframe><p>safe</p>';
|
|
54
|
+
const result = sanitizeHtml(input);
|
|
55
|
+
expect(result).not.toContain('<iframe');
|
|
56
|
+
expect(result).toContain('<p>safe</p>');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('stripHtml', () => {
|
|
60
|
+
it('returns plain text from HTML', () => {
|
|
61
|
+
const input = '<p>Hello <strong>world</strong></p>';
|
|
62
|
+
expect(stripHtml(input)).toBe('Hello world');
|
|
63
|
+
});
|
|
64
|
+
it('handles empty string', () => {
|
|
65
|
+
expect(stripHtml('')).toBe('');
|
|
66
|
+
});
|
|
67
|
+
it('strips nested tags', () => {
|
|
68
|
+
const input = '<div><ul><li>Item 1</li><li>Item 2</li></ul></div>';
|
|
69
|
+
const result = stripHtml(input);
|
|
70
|
+
expect(result).toContain('Item 1');
|
|
71
|
+
expect(result).toContain('Item 2');
|
|
72
|
+
expect(result).not.toContain('<');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=sanitize.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.test.js","sourceRoot":"","sources":["../../../src/__tests__/security/sanitize.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/webhooks/webhooks.test.ts"],"names":[],"mappings":""}
|