backend-manager 5.9.5 → 5.9.7
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 +8 -1
- package/src/manager/events/cron/runner.js +0 -1
- package/src/manager/libraries/email/data/disposable-domains.json +21 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/metadata.json +14 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/newsletter.html +486 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/newsletter.md +41 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/newsletter.mjml +97 -0
- package/{test/email/fixtures/clean.json → src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/structure.json} +16 -4
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/summary.md +1 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/metadata.json +14 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/newsletter.html +486 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/newsletter.md +41 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/newsletter.mjml +97 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/structure.json +42 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/summary.md +1 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/metadata.json +14 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/newsletter.html +486 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/newsletter.md +41 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/newsletter.mjml +97 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/structure.json +42 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/summary.md +1 -0
- package/src/test/fixtures/firebase-project/.temp/test-mode.json +6 -0
- package/src/test/fixtures/firebase-project/database-debug.log +12 -0
- package/src/test/fixtures/firebase-project/firestore-debug.log +132 -0
- package/src/test/fixtures/firebase-project/pubsub-debug.log +17 -0
- package/.codeclimate.yml +0 -32
- package/.nvmrc +0 -1
- package/CHANGELOG.md +0 -1440
- package/PROGRESS.md +0 -93
- package/TODO-2.md +0 -121
- package/TODO-CHARGEBLAST.md +0 -32
- package/TODO-WEBHOOK-KEY-LEGACY-REMOVAL.md +0 -15
- package/TODO-WEBHOOK-KEY-UPGRADE.md +0 -138
- package/TODO-email-auth.md +0 -14
- package/TODO.md +0 -187
- package/plans/mcp2.md +0 -247
- package/plans/suspended-subscription-dead-zone.md +0 -97
- package/scripts/test-helper-providers.js +0 -162
- package/scripts/update-disposable-domains.js +0 -44
- package/templates/_.env +0 -42
- package/templates/_.gitignore +0 -60
- package/templates/backend-manager-config.json +0 -249
- package/templates/database.rules.json +0 -83
- package/templates/firebase.json +0 -66
- package/templates/firestore.indexes.json +0 -4
- package/templates/firestore.rules +0 -112
- package/templates/public/404.html +0 -26
- package/templates/public/index.html +0 -24
- package/templates/remoteconfig.template.json +0 -1
- package/templates/storage-lifecycle-config-1-day.json +0 -9
- package/templates/storage-lifecycle-config-30-days.json +0 -9
- package/templates/storage.rules +0 -8
- package/test/_init/accounts-validation.js +0 -58
- package/test/_legacy/ai/index.js +0 -231
- package/test/_legacy/ai/test.jpg +0 -0
- package/test/_legacy/ai/test.pdf +0 -0
- package/test/_legacy/index.js +0 -31
- package/test/_legacy/payment-resolver/chargebee/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/chargebee/orders/unpaid.json +0 -98
- package/test/_legacy/payment-resolver/chargebee/subscriptions/active.json +0 -578
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-in-trial.json +0 -38
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-skipped-to-active.json +0 -135
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-active-to-cancelled.json +0 -42
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-active.json +0 -44
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-cancelled.json +0 -39
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-refund.json +0 -143
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-suspended.json +0 -48
- package/test/_legacy/payment-resolver/coinbase/orders/regular.json +0 -204
- package/test/_legacy/payment-resolver/coinbase/subscriptions/cancelled.json +0 -204
- package/test/_legacy/payment-resolver/coinbase/subscriptions/paid-2.json +0 -125
- package/test/_legacy/payment-resolver/index.js +0 -1558
- package/test/_legacy/payment-resolver/paypal/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/paypal/orders/regular.json +0 -120
- package/test/_legacy/payment-resolver/paypal/subscriptions/active-refund-previous-stmnt.json +0 -323
- package/test/_legacy/payment-resolver/paypal/subscriptions/active.json +0 -192
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-in-trial.json +0 -125
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-not-complete.json +0 -76
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-overdue-2.json +0 -114
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-overdue.json +0 -114
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-active-to-cancelled.json +0 -111
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-active.json +0 -132
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-cancelled.json +0 -107
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-expired.json +0 -91
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-refund.json +0 -147
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-suspended.json +0 -120
- package/test/_legacy/payment-resolver/stripe/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/stripe/orders/regular.json +0 -91
- package/test/_legacy/payment-resolver/stripe/subscriptions/active.json +0 -421
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-in-trial.json +0 -349
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-active-failed-authorization.json +0 -430
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-active.json +0 -319
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-cancelled.json +0 -319
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-refund.json +0 -414
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-suspended.json +0 -319
- package/test/_legacy/payment-resolver/stripe/subscriptions/unsure.json +0 -339
- package/test/_legacy/test-new +0 -1178
- package/test/_legacy/test.md +0 -89
- package/test/_legacy/usage.js +0 -175
- package/test/_legacy/user.js +0 -31
- package/test/ai/tools-live.js +0 -170
- package/test/boot/emulator-boots.js +0 -37
- package/test/content/blog-generate.js +0 -160
- package/test/email/campaign-send.js +0 -45
- package/test/email/consent-lifecycle.js +0 -257
- package/test/email/feedback-and-plain-send.js +0 -52
- package/test/email/fixtures/editorial.json +0 -30
- package/test/email/fixtures/field-report.json +0 -53
- package/test/email/marketing-lifecycle.js +0 -132
- package/test/email/newsletter-generate.js +0 -819
- package/test/email/newsletter-templates.js +0 -491
- package/test/email/templates.js +0 -207
- package/test/email/transactional-send.js +0 -34
- package/test/email/transactional.js +0 -560
- package/test/email/validation.js +0 -710
- package/test/events/auth-delete-race.js +0 -201
- package/test/events/payments/journey-payments-cancel-endpoint.js +0 -100
- package/test/events/payments/journey-payments-cancel.js +0 -182
- package/test/events/payments/journey-payments-discount.js +0 -89
- package/test/events/payments/journey-payments-failure.js +0 -146
- package/test/events/payments/journey-payments-legacy-product.js +0 -149
- package/test/events/payments/journey-payments-one-time-failure.js +0 -111
- package/test/events/payments/journey-payments-one-time.js +0 -136
- package/test/events/payments/journey-payments-plan-change.js +0 -144
- package/test/events/payments/journey-payments-refund-webhook.js +0 -180
- package/test/events/payments/journey-payments-suspend.js +0 -181
- package/test/events/payments/journey-payments-trial-cancel.js +0 -130
- package/test/events/payments/journey-payments-trial.js +0 -171
- package/test/events/payments/journey-payments-uid-resolution.js +0 -132
- package/test/events/payments/journey-payments-upgrade.js +0 -135
- package/test/fixtures/chargebee/invoice-one-time.json +0 -27
- package/test/fixtures/chargebee/subscription-active.json +0 -44
- package/test/fixtures/chargebee/subscription-cancelled.json +0 -42
- package/test/fixtures/chargebee/subscription-in-trial.json +0 -41
- package/test/fixtures/chargebee/subscription-legacy-plan.json +0 -41
- package/test/fixtures/chargebee/subscription-non-renewing.json +0 -41
- package/test/fixtures/chargebee/subscription-paused.json +0 -42
- package/test/fixtures/chargebee/webhook-payment-failed.json +0 -51
- package/test/fixtures/chargebee/webhook-subscription-created.json +0 -47
- package/test/fixtures/paypal/order-approved.json +0 -62
- package/test/fixtures/paypal/order-completed.json +0 -110
- package/test/fixtures/paypal/subscription-active.json +0 -76
- package/test/fixtures/paypal/subscription-cancelled.json +0 -50
- package/test/fixtures/paypal/subscription-suspended.json +0 -65
- package/test/fixtures/stripe/checkout-session-completed.json +0 -130
- package/test/fixtures/stripe/invoice-payment-failed.json +0 -148
- package/test/fixtures/stripe/invoice-subscription-payment-failed.json +0 -28
- package/test/fixtures/stripe/subscription-active.json +0 -161
- package/test/fixtures/stripe/subscription-canceled.json +0 -161
- package/test/fixtures/stripe/subscription-trialing.json +0 -161
- package/test/functions/admin/database-read.js +0 -134
- package/test/functions/admin/database-write.js +0 -159
- package/test/functions/admin/edit-post.js +0 -366
- package/test/functions/admin/firestore-query.js +0 -207
- package/test/functions/admin/firestore-read.js +0 -129
- package/test/functions/admin/firestore-write.js +0 -105
- package/test/functions/admin/get-stats.js +0 -115
- package/test/functions/admin/send-email.js +0 -119
- package/test/functions/admin/send-notification.js +0 -208
- package/test/functions/admin/write-repo-content.js +0 -223
- package/test/functions/general/add-marketing-contact.js +0 -335
- package/test/functions/general/fetch-post.js +0 -54
- package/test/functions/general/generate-uuid.js +0 -114
- package/test/functions/test/authenticate.js +0 -64
- package/test/functions/user/create-custom-token.js +0 -73
- package/test/functions/user/delete.js +0 -135
- package/test/functions/user/get-active-sessions.js +0 -111
- package/test/functions/user/get-subscription-info.js +0 -99
- package/test/functions/user/regenerate-api-keys.js +0 -156
- package/test/functions/user/resolve.js +0 -52
- package/test/functions/user/sign-out-all-sessions.js +0 -94
- package/test/functions/user/sign-up.js +0 -252
- package/test/functions/user/submit-feedback.js +0 -104
- package/test/functions/user/validate-settings.js +0 -82
- package/test/helpers/ai-request-payload.js +0 -300
- package/test/helpers/ai-test-provider.js +0 -202
- package/test/helpers/ai-tools-format.js +0 -383
- package/test/helpers/content/blog-auto-publisher.js +0 -484
- package/test/helpers/content/feed-parser.js +0 -528
- package/test/helpers/content/ghostii-blocks.js +0 -134
- package/test/helpers/content/ghostii-feed-integration.js +0 -404
- package/test/helpers/content/ghostii-write-article.js +0 -243
- package/test/helpers/environment.js +0 -230
- package/test/helpers/infer-contact.js +0 -113
- package/test/helpers/merge-line-files.js +0 -271
- package/test/helpers/payment/chargebee/parse-webhook.js +0 -413
- package/test/helpers/payment/chargebee/to-unified-one-time.js +0 -147
- package/test/helpers/payment/chargebee/to-unified-subscription.js +0 -648
- package/test/helpers/payment/paypal/parse-webhook.js +0 -539
- package/test/helpers/payment/paypal/to-unified-one-time.js +0 -382
- package/test/helpers/payment/paypal/to-unified-subscription.js +0 -820
- package/test/helpers/payment/stripe/parse-webhook.js +0 -447
- package/test/helpers/payment/stripe/to-unified-one-time.js +0 -306
- package/test/helpers/payment/stripe/to-unified-subscription.js +0 -708
- package/test/helpers/sanitize.js +0 -222
- package/test/helpers/slugify.js +0 -394
- package/test/helpers/storage.js +0 -194
- package/test/helpers/user.js +0 -755
- package/test/helpers/webhook-forward.js +0 -418
- package/test/mcp/discovery.js +0 -53
- package/test/mcp/oauth.js +0 -161
- package/test/mcp/protocol.js +0 -268
- package/test/mcp/roles.js +0 -188
- package/test/mcp/utils.js +0 -245
- package/test/routes/admin/create-post.js +0 -364
- package/test/routes/admin/database.js +0 -131
- package/test/routes/admin/deduplicate-image-alts.js +0 -190
- package/test/routes/admin/email.js +0 -114
- package/test/routes/admin/firestore-query.js +0 -204
- package/test/routes/admin/firestore.js +0 -127
- package/test/routes/admin/infer-contact.js +0 -218
- package/test/routes/admin/notification.js +0 -198
- package/test/routes/admin/post-resize-image.js +0 -184
- package/test/routes/admin/post.js +0 -368
- package/test/routes/admin/repo-content.js +0 -223
- package/test/routes/admin/stats.js +0 -112
- package/test/routes/content/post.js +0 -54
- package/test/routes/general/uuid.js +0 -115
- package/test/routes/marketing/campaign.js +0 -125
- package/test/routes/marketing/contact.js +0 -370
- package/test/routes/marketing/email-preferences.js +0 -292
- package/test/routes/marketing/push-send.js +0 -32
- package/test/routes/marketing/webhook-forward.js +0 -61
- package/test/routes/marketing/webhook.js +0 -639
- package/test/routes/payments/cancel.js +0 -163
- package/test/routes/payments/discount.js +0 -80
- package/test/routes/payments/dispute-alert.js +0 -320
- package/test/routes/payments/intent.js +0 -336
- package/test/routes/payments/portal.js +0 -92
- package/test/routes/payments/refund.js +0 -179
- package/test/routes/payments/trial-eligibility.js +0 -71
- package/test/routes/payments/webhook.js +0 -107
- package/test/routes/test/authenticate.js +0 -59
- package/test/routes/test/schema.js +0 -554
- package/test/routes/test/usage.js +0 -342
- package/test/routes/user/api-keys.js +0 -156
- package/test/routes/user/delete.js +0 -136
- package/test/routes/user/feedback.js +0 -104
- package/test/routes/user/sessions.js +0 -199
- package/test/routes/user/settings-validate.js +0 -82
- package/test/routes/user/signup.js +0 -542
- package/test/routes/user/subscription.js +0 -99
- package/test/routes/user/token.js +0 -73
- package/test/routes/user/user.js +0 -157
- package/test/rules/notifications.js +0 -410
- package/test/rules/payments-carts.js +0 -371
- package/test/rules/user.js +0 -396
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test: POST /user/token
|
|
3
|
-
* Tests the user create custom token endpoint
|
|
4
|
-
* Requires user authentication (uses Api.resolveUser with adminRequired: true which means user must be authenticated)
|
|
5
|
-
*/
|
|
6
|
-
module.exports = {
|
|
7
|
-
description: 'User create custom token',
|
|
8
|
-
type: 'group',
|
|
9
|
-
tests: [
|
|
10
|
-
// Test 1: Authenticated user can create custom token
|
|
11
|
-
{
|
|
12
|
-
name: 'authenticated-user-succeeds',
|
|
13
|
-
auth: 'basic',
|
|
14
|
-
timeout: 15000,
|
|
15
|
-
|
|
16
|
-
async run({ http, assert }) {
|
|
17
|
-
const response = await http.post('backend-manager/user/token', {});
|
|
18
|
-
|
|
19
|
-
assert.isSuccess(response, 'Create custom token should succeed for authenticated user');
|
|
20
|
-
assert.hasProperty(response, 'data.token', 'Response should contain token');
|
|
21
|
-
assert.ok(
|
|
22
|
-
typeof response.data.token === 'string' && response.data.token.length > 0,
|
|
23
|
-
'Token should be a non-empty string'
|
|
24
|
-
);
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
// Test 2: Token has valid JWT format
|
|
29
|
-
{
|
|
30
|
-
name: 'token-is-valid-jwt',
|
|
31
|
-
auth: 'basic',
|
|
32
|
-
timeout: 15000,
|
|
33
|
-
|
|
34
|
-
async run({ http, assert }) {
|
|
35
|
-
const response = await http.post('backend-manager/user/token', {});
|
|
36
|
-
|
|
37
|
-
assert.isSuccess(response, 'Create custom token should succeed');
|
|
38
|
-
|
|
39
|
-
// JWT tokens have 3 parts separated by dots
|
|
40
|
-
const parts = response.data.token.split('.');
|
|
41
|
-
assert.equal(parts.length, 3, 'Token should be a valid JWT with 3 parts');
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
// Test 3: Premium user can create custom token
|
|
46
|
-
// Note: Admin via backendManagerKey can't create tokens without a UID since it's not a real user
|
|
47
|
-
{
|
|
48
|
-
name: 'premium-user-succeeds',
|
|
49
|
-
auth: 'premium-active',
|
|
50
|
-
timeout: 15000,
|
|
51
|
-
|
|
52
|
-
async run({ http, assert }) {
|
|
53
|
-
const response = await http.post('backend-manager/user/token', {});
|
|
54
|
-
|
|
55
|
-
assert.isSuccess(response, 'Create custom token should succeed for premium user');
|
|
56
|
-
assert.hasProperty(response, 'data.token', 'Response should contain token');
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
// Test 4: Unauthenticated request fails
|
|
61
|
-
{
|
|
62
|
-
name: 'unauthenticated-rejected',
|
|
63
|
-
auth: 'none',
|
|
64
|
-
timeout: 15000,
|
|
65
|
-
|
|
66
|
-
async run({ http, assert }) {
|
|
67
|
-
const response = await http.post('backend-manager/user/token', {});
|
|
68
|
-
|
|
69
|
-
assert.isError(response, 401, 'Create custom token should fail without authentication');
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
};
|
package/test/routes/user/user.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test: GET /user
|
|
3
|
-
* Tests the user resolve endpoint
|
|
4
|
-
* Returns user account info for authenticated users
|
|
5
|
-
* Validates that User() correctly structures user data
|
|
6
|
-
*/
|
|
7
|
-
const { getFirstPaidProduct } = require('../../../src/test/test-accounts.js');
|
|
8
|
-
|
|
9
|
-
module.exports = {
|
|
10
|
-
description: 'User resolve (account info)',
|
|
11
|
-
type: 'group',
|
|
12
|
-
tests: [
|
|
13
|
-
// Test 1: Unauthenticated request fails
|
|
14
|
-
{
|
|
15
|
-
name: 'unauthenticated-rejected',
|
|
16
|
-
auth: 'none',
|
|
17
|
-
timeout: 15000,
|
|
18
|
-
|
|
19
|
-
async run({ http, assert }) {
|
|
20
|
-
const response = await http.get('backend-manager/user', {});
|
|
21
|
-
|
|
22
|
-
assert.isError(response, 401, 'Resolve should fail without authentication');
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
// Test 2: Basic user - verify resolved properties
|
|
27
|
-
{
|
|
28
|
-
name: 'basic-user-resolved-correctly',
|
|
29
|
-
auth: 'basic',
|
|
30
|
-
timeout: 15000,
|
|
31
|
-
|
|
32
|
-
async run({ http, assert, accounts }) {
|
|
33
|
-
const response = await http.get('backend-manager/user', {});
|
|
34
|
-
|
|
35
|
-
assert.isSuccess(response, 'Resolve should succeed for basic user');
|
|
36
|
-
|
|
37
|
-
const user = response.data.user;
|
|
38
|
-
|
|
39
|
-
// Verify auth properties
|
|
40
|
-
assert.equal(user.auth.uid, accounts.basic.uid, 'UID should match test account');
|
|
41
|
-
assert.equal(user.auth.email, accounts.basic.email, 'Email should match test account');
|
|
42
|
-
assert.equal(user.authenticated, true, 'User should be authenticated');
|
|
43
|
-
|
|
44
|
-
// Verify subscription properties - basic user should have basic subscription
|
|
45
|
-
assert.equal(user.subscription.product.id, 'basic', 'Subscription ID should be basic');
|
|
46
|
-
assert.equal(user.subscription.status, 'active', 'Subscription status should be active');
|
|
47
|
-
|
|
48
|
-
// Verify roles - basic user has no special roles
|
|
49
|
-
assert.equal(user.roles.admin, false, 'Basic user should not be admin');
|
|
50
|
-
|
|
51
|
-
// Verify usage structure exists
|
|
52
|
-
assert.ok(user.usage !== undefined, 'Usage object should be present');
|
|
53
|
-
assert.ok(user.usage.requests !== undefined, 'Usage.requests should be present');
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
// Test 3: Real admin account (_test-admin) - verify admin role from Firestore
|
|
58
|
-
{
|
|
59
|
-
name: 'admin-account-resolved-correctly',
|
|
60
|
-
auth: 'none',
|
|
61
|
-
timeout: 15000,
|
|
62
|
-
|
|
63
|
-
async run({ http, assert, accounts }) {
|
|
64
|
-
// Authenticate with the real admin test account's privateKey
|
|
65
|
-
const response = await http.withPrivateKey(accounts.admin.privateKey).get('backend-manager/user', {});
|
|
66
|
-
|
|
67
|
-
assert.isSuccess(response, 'Resolve should succeed for admin account');
|
|
68
|
-
|
|
69
|
-
const user = response.data.user;
|
|
70
|
-
|
|
71
|
-
// Verify auth properties - should match the real admin account
|
|
72
|
-
assert.equal(user.auth.uid, accounts.admin.uid, 'UID should match admin test account');
|
|
73
|
-
assert.equal(user.auth.email, accounts.admin.email, 'Email should match admin test account');
|
|
74
|
-
assert.equal(user.authenticated, true, 'Should be authenticated');
|
|
75
|
-
|
|
76
|
-
// Verify roles - admin account has roles.admin = true in Firestore
|
|
77
|
-
assert.equal(user.roles.admin, true, 'Admin account should have admin role');
|
|
78
|
-
|
|
79
|
-
// Verify subscription - admin account is on basic subscription
|
|
80
|
-
assert.equal(user.subscription.product.id, 'basic', 'Admin subscription ID should be basic');
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
// Test 4: backendManagerKey only - shell account with admin role, no real user
|
|
85
|
-
{
|
|
86
|
-
name: 'backend-manager-key-shell-account',
|
|
87
|
-
auth: 'admin',
|
|
88
|
-
timeout: 15000,
|
|
89
|
-
|
|
90
|
-
async run({ http, assert, accounts }) {
|
|
91
|
-
const response = await http.get('backend-manager/user', {});
|
|
92
|
-
|
|
93
|
-
assert.isSuccess(response, 'Resolve should succeed with backendManagerKey');
|
|
94
|
-
|
|
95
|
-
const user = response.data.user;
|
|
96
|
-
|
|
97
|
-
// Verify roles - backendManagerKey grants admin role
|
|
98
|
-
assert.equal(user.roles.admin, true, 'backendManagerKey should grant admin role');
|
|
99
|
-
assert.equal(user.authenticated, true, 'Should be authenticated');
|
|
100
|
-
|
|
101
|
-
// Should NOT have the real admin account's UID (it's a shell account)
|
|
102
|
-
assert.notEqual(user.auth.uid, accounts.admin.uid, 'Should not be the real admin account');
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
// Test 5: Premium active user - verify premium subscription is retained
|
|
107
|
-
{
|
|
108
|
-
name: 'premium-active-user-resolved-correctly',
|
|
109
|
-
auth: 'resolve-premium-active',
|
|
110
|
-
timeout: 15000,
|
|
111
|
-
|
|
112
|
-
async run({ http, assert, accounts, config }) {
|
|
113
|
-
const paidProduct = getFirstPaidProduct(config);
|
|
114
|
-
const response = await http.get('backend-manager/user', {});
|
|
115
|
-
|
|
116
|
-
assert.isSuccess(response, 'Resolve should succeed for premium user');
|
|
117
|
-
|
|
118
|
-
const user = response.data.user;
|
|
119
|
-
|
|
120
|
-
// Verify auth properties
|
|
121
|
-
assert.equal(user.auth.uid, accounts['resolve-premium-active'].uid, 'UID should match premium test account');
|
|
122
|
-
|
|
123
|
-
// Verify subscription - premium user should retain paid subscription
|
|
124
|
-
assert.equal(user.subscription.product.id, paidProduct.id, 'Subscription ID should match first paid product');
|
|
125
|
-
assert.equal(user.subscription.status, 'active', 'Subscription status should be active');
|
|
126
|
-
|
|
127
|
-
// Verify expires is in the future
|
|
128
|
-
const expiresTimestamp = user.subscription.expires?.timestampUNIX || 0;
|
|
129
|
-
const now = Math.floor(Date.now() / 1000);
|
|
130
|
-
assert.ok(expiresTimestamp > now, 'Premium subscription expires should be in the future');
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
// Test 6: Premium expired user - verify subscription retains product but shows cancelled status
|
|
135
|
-
{
|
|
136
|
-
name: 'premium-expired-user-cancelled',
|
|
137
|
-
auth: 'resolve-premium-expired',
|
|
138
|
-
timeout: 15000,
|
|
139
|
-
|
|
140
|
-
async run({ http, assert, accounts, config }) {
|
|
141
|
-
const paidProduct = getFirstPaidProduct(config);
|
|
142
|
-
const response = await http.get('backend-manager/user', {});
|
|
143
|
-
|
|
144
|
-
assert.isSuccess(response, 'Resolve should succeed for expired premium user');
|
|
145
|
-
|
|
146
|
-
const user = response.data.user;
|
|
147
|
-
|
|
148
|
-
// Verify auth properties
|
|
149
|
-
assert.equal(user.auth.uid, accounts['resolve-premium-expired'].uid, 'UID should match expired premium test account');
|
|
150
|
-
|
|
151
|
-
// Verify subscription - product.id stays as paid product, status reflects cancellation
|
|
152
|
-
assert.equal(user.subscription.product.id, paidProduct.id, 'Product ID should remain paid product');
|
|
153
|
-
assert.equal(user.subscription.status, 'cancelled', 'Status should be cancelled');
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
};
|
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test: Firestore Security Rules - Notification Documents
|
|
3
|
-
* Tests that security rules correctly protect notification data
|
|
4
|
-
*
|
|
5
|
-
* Rules being tested:
|
|
6
|
-
* - Anyone can create a notification (for push subscription)
|
|
7
|
-
* - User can read notification if they own it or know the token
|
|
8
|
-
* - User can update notification if they know the token
|
|
9
|
-
* - User cannot delete notifications
|
|
10
|
-
* - Anonymous can read if document doesn't exist (for checking availability)
|
|
11
|
-
*
|
|
12
|
-
* @see templates/firestore.rules
|
|
13
|
-
*/
|
|
14
|
-
module.exports = {
|
|
15
|
-
description: 'Firestore security rules for notification documents',
|
|
16
|
-
type: 'group',
|
|
17
|
-
timeout: 30000,
|
|
18
|
-
|
|
19
|
-
tests: [
|
|
20
|
-
// Test 1: Anyone can create a notification
|
|
21
|
-
{
|
|
22
|
-
name: 'anyone-can-create-notification',
|
|
23
|
-
auth: 'none',
|
|
24
|
-
|
|
25
|
-
async run({ rules }) {
|
|
26
|
-
const db = rules.asAnonymous();
|
|
27
|
-
const token = 'test-token-create-anon';
|
|
28
|
-
|
|
29
|
-
// Should succeed - anyone can create notifications
|
|
30
|
-
await rules.expectSuccess(
|
|
31
|
-
db.doc(`notifications/${token}`).set({
|
|
32
|
-
token: token,
|
|
33
|
-
owner: 'anonymous',
|
|
34
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
35
|
-
})
|
|
36
|
-
);
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
// Test 2: Authenticated user can create a notification
|
|
41
|
-
{
|
|
42
|
-
name: 'user-can-create-notification',
|
|
43
|
-
auth: 'none',
|
|
44
|
-
|
|
45
|
-
async run({ rules, accounts }) {
|
|
46
|
-
const uid = accounts.basic.uid;
|
|
47
|
-
const db = rules.asAccount('basic');
|
|
48
|
-
const token = 'test-token-create-user';
|
|
49
|
-
|
|
50
|
-
// Should succeed - user can create notifications
|
|
51
|
-
await rules.expectSuccess(
|
|
52
|
-
db.doc(`notifications/${token}`).set({
|
|
53
|
-
token: token,
|
|
54
|
-
owner: uid,
|
|
55
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
56
|
-
})
|
|
57
|
-
);
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
// Test 3: User can read their own notification (by owner)
|
|
62
|
-
{
|
|
63
|
-
name: 'user-can-read-own-notification',
|
|
64
|
-
auth: 'none',
|
|
65
|
-
|
|
66
|
-
async run({ rules, accounts }) {
|
|
67
|
-
const uid = accounts.basic.uid;
|
|
68
|
-
const db = rules.asAccount('basic');
|
|
69
|
-
const token = 'test-token-read-own';
|
|
70
|
-
|
|
71
|
-
// First create the notification as admin (to set up the test)
|
|
72
|
-
const adminDb = rules.asAccount('admin');
|
|
73
|
-
await rules.expectSuccess(
|
|
74
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
75
|
-
token: token,
|
|
76
|
-
owner: uid,
|
|
77
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
78
|
-
})
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Should succeed - user can read notification they own
|
|
82
|
-
await rules.expectSuccess(
|
|
83
|
-
db.doc(`notifications/${token}`).get()
|
|
84
|
-
);
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
// Test 4: User can read notification by token match
|
|
89
|
-
{
|
|
90
|
-
name: 'user-can-read-notification-by-token',
|
|
91
|
-
auth: 'none',
|
|
92
|
-
|
|
93
|
-
async run({ rules, accounts }) {
|
|
94
|
-
const otherUid = accounts.admin.uid;
|
|
95
|
-
const db = rules.asAccount('basic');
|
|
96
|
-
const token = 'test-token-read-token';
|
|
97
|
-
|
|
98
|
-
// Create notification owned by someone else
|
|
99
|
-
const adminDb = rules.asAccount('admin');
|
|
100
|
-
await rules.expectSuccess(
|
|
101
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
102
|
-
token: token,
|
|
103
|
-
owner: otherUid,
|
|
104
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
105
|
-
})
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
// Should succeed - user can read if token matches document ID
|
|
109
|
-
await rules.expectSuccess(
|
|
110
|
-
db.doc(`notifications/${token}`).get()
|
|
111
|
-
);
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
// Test 5: Anonymous can read non-existent notification (availability check)
|
|
116
|
-
{
|
|
117
|
-
name: 'anonymous-can-read-nonexistent-notification',
|
|
118
|
-
auth: 'none',
|
|
119
|
-
|
|
120
|
-
async run({ rules }) {
|
|
121
|
-
const db = rules.asAnonymous();
|
|
122
|
-
const token = 'nonexistent-token-12345';
|
|
123
|
-
|
|
124
|
-
// Should succeed - resource == null check allows this
|
|
125
|
-
await rules.expectSuccess(
|
|
126
|
-
db.doc(`notifications/${token}`).get()
|
|
127
|
-
);
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
// Test 6: User can update notification by token match
|
|
132
|
-
{
|
|
133
|
-
name: 'user-can-update-notification-by-token',
|
|
134
|
-
auth: 'none',
|
|
135
|
-
|
|
136
|
-
async run({ rules, accounts }) {
|
|
137
|
-
const otherUid = accounts.admin.uid;
|
|
138
|
-
const db = rules.asAccount('basic');
|
|
139
|
-
const token = 'test-token-update';
|
|
140
|
-
|
|
141
|
-
// Create notification owned by someone else
|
|
142
|
-
const adminDb = rules.asAccount('admin');
|
|
143
|
-
await rules.expectSuccess(
|
|
144
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
145
|
-
token: token,
|
|
146
|
-
owner: otherUid,
|
|
147
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
148
|
-
})
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
// Should succeed - user can update if token matches
|
|
152
|
-
await rules.expectSuccess(
|
|
153
|
-
db.doc(`notifications/${token}`).update({
|
|
154
|
-
'metadata.updated': { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) },
|
|
155
|
-
})
|
|
156
|
-
);
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
// Test 7: Owner can update their notification
|
|
161
|
-
{
|
|
162
|
-
name: 'owner-can-update-notification',
|
|
163
|
-
auth: 'none',
|
|
164
|
-
|
|
165
|
-
async run({ rules, accounts }) {
|
|
166
|
-
const uid = accounts.basic.uid;
|
|
167
|
-
const db = rules.asAccount('basic');
|
|
168
|
-
const token = 'test-token-update-owner';
|
|
169
|
-
|
|
170
|
-
// Create notification as admin but owned by basic user
|
|
171
|
-
const adminDb = rules.asAccount('admin');
|
|
172
|
-
await rules.expectSuccess(
|
|
173
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
174
|
-
token: token,
|
|
175
|
-
owner: uid,
|
|
176
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
177
|
-
})
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// Should succeed - owner can update
|
|
181
|
-
await rules.expectSuccess(
|
|
182
|
-
db.doc(`notifications/${token}`).update({
|
|
183
|
-
preferences: { sound: true },
|
|
184
|
-
})
|
|
185
|
-
);
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
|
|
189
|
-
// Test 8: User cannot read notification without token or ownership
|
|
190
|
-
{
|
|
191
|
-
name: 'user-cannot-read-others-notification-wrong-token',
|
|
192
|
-
auth: 'none',
|
|
193
|
-
|
|
194
|
-
async run({ rules, accounts }) {
|
|
195
|
-
const otherUid = accounts.admin.uid;
|
|
196
|
-
const db = rules.asAccount('basic');
|
|
197
|
-
const realToken = 'real-token-private';
|
|
198
|
-
const wrongToken = 'wrong-token-guess';
|
|
199
|
-
|
|
200
|
-
// Create notification with a different token
|
|
201
|
-
const adminDb = rules.asAccount('admin');
|
|
202
|
-
await rules.expectSuccess(
|
|
203
|
-
adminDb.doc(`notifications/${realToken}`).set({
|
|
204
|
-
token: realToken,
|
|
205
|
-
owner: otherUid,
|
|
206
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
207
|
-
})
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
// Should fail - user doesn't own it and document ID doesn't match token they're querying
|
|
211
|
-
// Note: This test verifies that knowing a wrong token doesn't grant access
|
|
212
|
-
// The user is querying realToken doc but doesn't own it
|
|
213
|
-
// Actually the rule allows read if token == document ID, which it does here
|
|
214
|
-
// Let me reconsider - the token in the doc matches the doc ID, so this would succeed
|
|
215
|
-
// The rule is: existingData().token == token (where token is the doc ID)
|
|
216
|
-
// So anyone who knows the token (doc ID) can read it
|
|
217
|
-
// This is intentional for push notification validation
|
|
218
|
-
await rules.expectSuccess(
|
|
219
|
-
db.doc(`notifications/${realToken}`).get()
|
|
220
|
-
);
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
// Test 9: Anonymous cannot update existing notification
|
|
225
|
-
{
|
|
226
|
-
name: 'anonymous-cannot-update-notification',
|
|
227
|
-
auth: 'none',
|
|
228
|
-
|
|
229
|
-
async run({ rules, accounts }) {
|
|
230
|
-
const uid = accounts.basic.uid;
|
|
231
|
-
const db = rules.asAnonymous();
|
|
232
|
-
const token = 'test-token-anon-update';
|
|
233
|
-
|
|
234
|
-
// Create notification
|
|
235
|
-
const adminDb = rules.asAccount('admin');
|
|
236
|
-
await rules.expectSuccess(
|
|
237
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
238
|
-
token: token,
|
|
239
|
-
owner: uid,
|
|
240
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
241
|
-
})
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
// Should fail - anonymous cannot update (no token context for update)
|
|
245
|
-
// Wait, the rule says: allow update: if existingData().token == token
|
|
246
|
-
// where token is the wildcard {token} from the path
|
|
247
|
-
// So anonymous CAN update if they know the token (doc ID)
|
|
248
|
-
// Let me check the rules again...
|
|
249
|
-
// Actually for anonymous, they still get the wildcard value
|
|
250
|
-
await rules.expectSuccess(
|
|
251
|
-
db.doc(`notifications/${token}`).update({
|
|
252
|
-
hacked: true,
|
|
253
|
-
})
|
|
254
|
-
);
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
|
|
258
|
-
// Test 10: Admin can create notification
|
|
259
|
-
{
|
|
260
|
-
name: 'admin-can-create-notification',
|
|
261
|
-
auth: 'none',
|
|
262
|
-
|
|
263
|
-
async run({ rules, accounts }) {
|
|
264
|
-
const adminDb = rules.asAccount('admin');
|
|
265
|
-
const token = 'test-token-admin-create';
|
|
266
|
-
|
|
267
|
-
// Should succeed - admin can create any doc
|
|
268
|
-
await rules.expectSuccess(
|
|
269
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
270
|
-
token: token,
|
|
271
|
-
owner: accounts.basic.uid,
|
|
272
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
273
|
-
})
|
|
274
|
-
);
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
|
|
278
|
-
// Test 11: Admin can read any notification
|
|
279
|
-
{
|
|
280
|
-
name: 'admin-can-read-any-notification',
|
|
281
|
-
auth: 'none',
|
|
282
|
-
|
|
283
|
-
async run({ rules, accounts }) {
|
|
284
|
-
const basicUid = accounts.basic.uid;
|
|
285
|
-
const adminDb = rules.asAccount('admin');
|
|
286
|
-
const token = 'test-token-admin-read';
|
|
287
|
-
|
|
288
|
-
// Create notification owned by basic user
|
|
289
|
-
await rules.expectSuccess(
|
|
290
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
291
|
-
token: token,
|
|
292
|
-
owner: basicUid,
|
|
293
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
294
|
-
})
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
// Should succeed - admin can read any doc
|
|
298
|
-
await rules.expectSuccess(
|
|
299
|
-
adminDb.doc(`notifications/${token}`).get()
|
|
300
|
-
);
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
|
|
304
|
-
// Test 12: Admin can update any notification
|
|
305
|
-
{
|
|
306
|
-
name: 'admin-can-update-notification',
|
|
307
|
-
auth: 'none',
|
|
308
|
-
|
|
309
|
-
async run({ rules, accounts }) {
|
|
310
|
-
const adminDb = rules.asAccount('admin');
|
|
311
|
-
const token = 'test-token-admin-update';
|
|
312
|
-
|
|
313
|
-
// Create notification first
|
|
314
|
-
await rules.expectSuccess(
|
|
315
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
316
|
-
token: token,
|
|
317
|
-
owner: accounts.basic.uid,
|
|
318
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
319
|
-
})
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
// Should succeed - admin can update any doc
|
|
323
|
-
await rules.expectSuccess(
|
|
324
|
-
adminDb.doc(`notifications/${token}`).update({
|
|
325
|
-
'metadata.updated': { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) },
|
|
326
|
-
adminModified: true,
|
|
327
|
-
})
|
|
328
|
-
);
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
|
|
332
|
-
// Test 13: Admin can delete notification
|
|
333
|
-
{
|
|
334
|
-
name: 'admin-can-delete-notification',
|
|
335
|
-
auth: 'none',
|
|
336
|
-
|
|
337
|
-
async run({ rules, accounts }) {
|
|
338
|
-
const adminDb = rules.asAccount('admin');
|
|
339
|
-
const token = 'test-token-admin-delete';
|
|
340
|
-
|
|
341
|
-
// Create notification
|
|
342
|
-
await rules.expectSuccess(
|
|
343
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
344
|
-
token: token,
|
|
345
|
-
owner: 'someone',
|
|
346
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
347
|
-
})
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
// Should succeed - admin can delete via global admin rule
|
|
351
|
-
await rules.expectSuccess(
|
|
352
|
-
adminDb.doc(`notifications/${token}`).delete()
|
|
353
|
-
);
|
|
354
|
-
},
|
|
355
|
-
},
|
|
356
|
-
|
|
357
|
-
// Test 14: Regular user cannot delete notification
|
|
358
|
-
{
|
|
359
|
-
name: 'user-cannot-delete-notification',
|
|
360
|
-
auth: 'none',
|
|
361
|
-
|
|
362
|
-
async run({ rules, accounts }) {
|
|
363
|
-
const uid = accounts.basic.uid;
|
|
364
|
-
const db = rules.asAccount('basic');
|
|
365
|
-
const adminDb = rules.asAccount('admin');
|
|
366
|
-
const token = 'test-token-user-delete';
|
|
367
|
-
|
|
368
|
-
// Create notification owned by the user
|
|
369
|
-
await rules.expectSuccess(
|
|
370
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
371
|
-
token: token,
|
|
372
|
-
owner: uid,
|
|
373
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
374
|
-
})
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
// Should fail - no delete rule for non-admins
|
|
378
|
-
await rules.expectFailure(
|
|
379
|
-
db.doc(`notifications/${token}`).delete()
|
|
380
|
-
);
|
|
381
|
-
},
|
|
382
|
-
},
|
|
383
|
-
|
|
384
|
-
// Test 15: Anonymous cannot delete notification
|
|
385
|
-
{
|
|
386
|
-
name: 'anonymous-cannot-delete-notification',
|
|
387
|
-
auth: 'none',
|
|
388
|
-
|
|
389
|
-
async run({ rules, accounts }) {
|
|
390
|
-
const db = rules.asAnonymous();
|
|
391
|
-
const adminDb = rules.asAccount('admin');
|
|
392
|
-
const token = 'test-token-anon-delete';
|
|
393
|
-
|
|
394
|
-
// Create notification
|
|
395
|
-
await rules.expectSuccess(
|
|
396
|
-
adminDb.doc(`notifications/${token}`).set({
|
|
397
|
-
token: token,
|
|
398
|
-
owner: 'someone',
|
|
399
|
-
metadata: { created: { timestamp: new Date().toISOString(), timestampUNIX: Math.floor(Date.now() / 1000) } },
|
|
400
|
-
})
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
// Should fail - no delete rule for anonymous
|
|
404
|
-
await rules.expectFailure(
|
|
405
|
-
db.doc(`notifications/${token}`).delete()
|
|
406
|
-
);
|
|
407
|
-
},
|
|
408
|
-
},
|
|
409
|
-
],
|
|
410
|
-
};
|