@qwickapps/server 1.4.0 → 1.5.1
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/CHANGELOG.md +507 -0
- package/README.md +9 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/plugins/bans/bans-plugin.d.ts.map +1 -1
- package/dist/plugins/bans/bans-plugin.js +12 -3
- package/dist/plugins/bans/bans-plugin.js.map +1 -1
- package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts +11 -0
- package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts.map +1 -0
- package/dist/plugins/devices/__tests__/devices-plugin.test.js +410 -0
- package/dist/plugins/devices/__tests__/devices-plugin.test.js.map +1 -0
- package/dist/plugins/devices/__tests__/token-utils.test.d.ts +7 -0
- package/dist/plugins/devices/__tests__/token-utils.test.d.ts.map +1 -0
- package/dist/plugins/devices/__tests__/token-utils.test.js +197 -0
- package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -0
- package/dist/plugins/devices/adapters/compute-adapter.d.ts +36 -0
- package/dist/plugins/devices/adapters/compute-adapter.d.ts.map +1 -0
- package/dist/plugins/devices/adapters/compute-adapter.js +100 -0
- package/dist/plugins/devices/adapters/compute-adapter.js.map +1 -0
- package/dist/plugins/devices/adapters/index.d.ts +12 -0
- package/dist/plugins/devices/adapters/index.d.ts.map +1 -0
- package/dist/plugins/devices/adapters/index.js +10 -0
- package/dist/plugins/devices/adapters/index.js.map +1 -0
- package/dist/plugins/devices/adapters/mobile-adapter.d.ts +41 -0
- package/dist/plugins/devices/adapters/mobile-adapter.d.ts.map +1 -0
- package/dist/plugins/devices/adapters/mobile-adapter.js +131 -0
- package/dist/plugins/devices/adapters/mobile-adapter.js.map +1 -0
- package/dist/plugins/devices/devices-plugin.d.ts +70 -0
- package/dist/plugins/devices/devices-plugin.d.ts.map +1 -0
- package/dist/plugins/devices/devices-plugin.js +453 -0
- package/dist/plugins/devices/devices-plugin.js.map +1 -0
- package/dist/plugins/devices/index.d.ts +18 -0
- package/dist/plugins/devices/index.d.ts.map +1 -0
- package/dist/plugins/devices/index.js +18 -0
- package/dist/plugins/devices/index.js.map +1 -0
- package/dist/plugins/devices/stores/index.d.ts +9 -0
- package/dist/plugins/devices/stores/index.d.ts.map +1 -0
- package/dist/plugins/devices/stores/index.js +9 -0
- package/dist/plugins/devices/stores/index.js.map +1 -0
- package/dist/plugins/devices/stores/postgres-store.d.ts +26 -0
- package/dist/plugins/devices/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/devices/stores/postgres-store.js +199 -0
- package/dist/plugins/devices/stores/postgres-store.js.map +1 -0
- package/dist/plugins/devices/token-utils.d.ts +100 -0
- package/dist/plugins/devices/token-utils.d.ts.map +1 -0
- package/dist/plugins/devices/token-utils.js +162 -0
- package/dist/plugins/devices/token-utils.js.map +1 -0
- package/dist/plugins/devices/types.d.ts +307 -0
- package/dist/plugins/devices/types.d.ts.map +1 -0
- package/dist/plugins/devices/types.js +10 -0
- package/dist/plugins/devices/types.js.map +1 -0
- package/dist/plugins/index.d.ts +14 -2
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +13 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts +5 -0
- package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts.map +1 -0
- package/dist/plugins/notifications/__tests__/notifications-manager.test.js +470 -0
- package/dist/plugins/notifications/__tests__/notifications-manager.test.js.map +1 -0
- package/dist/plugins/notifications/index.d.ts +71 -0
- package/dist/plugins/notifications/index.d.ts.map +1 -0
- package/dist/plugins/notifications/index.js +72 -0
- package/dist/plugins/notifications/index.js.map +1 -0
- package/dist/plugins/notifications/notifications-manager.d.ts +182 -0
- package/dist/plugins/notifications/notifications-manager.d.ts.map +1 -0
- package/dist/plugins/notifications/notifications-manager.js +610 -0
- package/dist/plugins/notifications/notifications-manager.js.map +1 -0
- package/dist/plugins/notifications/notifications-plugin.d.ts +83 -0
- package/dist/plugins/notifications/notifications-plugin.d.ts.map +1 -0
- package/dist/plugins/notifications/notifications-plugin.js +337 -0
- package/dist/plugins/notifications/notifications-plugin.js.map +1 -0
- package/dist/plugins/notifications/types.d.ts +164 -0
- package/dist/plugins/notifications/types.d.ts.map +1 -0
- package/dist/plugins/notifications/types.js +9 -0
- package/dist/plugins/notifications/types.js.map +1 -0
- package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts +12 -0
- package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts.map +1 -0
- package/dist/plugins/parental/__tests__/parental-plugin.test.js +349 -0
- package/dist/plugins/parental/__tests__/parental-plugin.test.js.map +1 -0
- package/dist/plugins/parental/adapters/index.d.ts +8 -0
- package/dist/plugins/parental/adapters/index.d.ts.map +1 -0
- package/dist/plugins/parental/adapters/index.js +7 -0
- package/dist/plugins/parental/adapters/index.js.map +1 -0
- package/dist/plugins/parental/adapters/kids-adapter.d.ts +24 -0
- package/dist/plugins/parental/adapters/kids-adapter.d.ts.map +1 -0
- package/dist/plugins/parental/adapters/kids-adapter.js +174 -0
- package/dist/plugins/parental/adapters/kids-adapter.js.map +1 -0
- package/dist/plugins/parental/index.d.ts +14 -0
- package/dist/plugins/parental/index.d.ts.map +1 -0
- package/dist/plugins/parental/index.js +15 -0
- package/dist/plugins/parental/index.js.map +1 -0
- package/dist/plugins/parental/parental-plugin.d.ts +88 -0
- package/dist/plugins/parental/parental-plugin.d.ts.map +1 -0
- package/dist/plugins/parental/parental-plugin.js +666 -0
- package/dist/plugins/parental/parental-plugin.js.map +1 -0
- package/dist/plugins/parental/stores/index.d.ts +7 -0
- package/dist/plugins/parental/stores/index.d.ts.map +1 -0
- package/dist/plugins/parental/stores/index.js +7 -0
- package/dist/plugins/parental/stores/index.js.map +1 -0
- package/dist/plugins/parental/stores/postgres-store.d.ts +10 -0
- package/dist/plugins/parental/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/parental/stores/postgres-store.js +209 -0
- package/dist/plugins/parental/stores/postgres-store.js.map +1 -0
- package/dist/plugins/parental/types.d.ts +154 -0
- package/dist/plugins/parental/types.d.ts.map +1 -0
- package/dist/plugins/parental/types.js +10 -0
- package/dist/plugins/parental/types.js.map +1 -0
- package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts +11 -0
- package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts.map +1 -0
- package/dist/plugins/profiles/__tests__/profiles-plugin.test.js +243 -0
- package/dist/plugins/profiles/__tests__/profiles-plugin.test.js.map +1 -0
- package/dist/plugins/profiles/index.d.ts +12 -0
- package/dist/plugins/profiles/index.d.ts.map +1 -0
- package/dist/plugins/profiles/index.js +13 -0
- package/dist/plugins/profiles/index.js.map +1 -0
- package/dist/plugins/profiles/profiles-plugin.d.ts +71 -0
- package/dist/plugins/profiles/profiles-plugin.d.ts.map +1 -0
- package/dist/plugins/profiles/profiles-plugin.js +481 -0
- package/dist/plugins/profiles/profiles-plugin.js.map +1 -0
- package/dist/plugins/profiles/stores/index.d.ts +9 -0
- package/dist/plugins/profiles/stores/index.d.ts.map +1 -0
- package/dist/plugins/profiles/stores/index.js +9 -0
- package/dist/plugins/profiles/stores/index.js.map +1 -0
- package/dist/plugins/profiles/stores/postgres-store.d.ts +18 -0
- package/dist/plugins/profiles/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/profiles/stores/postgres-store.js +310 -0
- package/dist/plugins/profiles/stores/postgres-store.js.map +1 -0
- package/dist/plugins/profiles/types.d.ts +289 -0
- package/dist/plugins/profiles/types.d.ts.map +1 -0
- package/dist/plugins/profiles/types.js +10 -0
- package/dist/plugins/profiles/types.js.map +1 -0
- package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts +11 -0
- package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts.map +1 -0
- package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js +305 -0
- package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js.map +1 -0
- package/dist/plugins/subscriptions/index.d.ts +12 -0
- package/dist/plugins/subscriptions/index.d.ts.map +1 -0
- package/dist/plugins/subscriptions/index.js +13 -0
- package/dist/plugins/subscriptions/index.js.map +1 -0
- package/dist/plugins/subscriptions/stores/index.d.ts +9 -0
- package/dist/plugins/subscriptions/stores/index.d.ts.map +1 -0
- package/dist/plugins/subscriptions/stores/index.js +9 -0
- package/dist/plugins/subscriptions/stores/index.js.map +1 -0
- package/dist/plugins/subscriptions/stores/postgres-store.d.ts +14 -0
- package/dist/plugins/subscriptions/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/subscriptions/stores/postgres-store.js +359 -0
- package/dist/plugins/subscriptions/stores/postgres-store.js.map +1 -0
- package/dist/plugins/subscriptions/subscriptions-plugin.d.ts +82 -0
- package/dist/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -0
- package/dist/plugins/subscriptions/subscriptions-plugin.js +449 -0
- package/dist/plugins/subscriptions/subscriptions-plugin.js.map +1 -0
- package/dist/plugins/subscriptions/types.d.ts +308 -0
- package/dist/plugins/subscriptions/types.d.ts.map +1 -0
- package/dist/plugins/subscriptions/types.js +10 -0
- package/dist/plugins/subscriptions/types.js.map +1 -0
- package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts +11 -0
- package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts.map +1 -0
- package/dist/plugins/usage/__tests__/usage-plugin.test.js +218 -0
- package/dist/plugins/usage/__tests__/usage-plugin.test.js.map +1 -0
- package/dist/plugins/usage/index.d.ts +12 -0
- package/dist/plugins/usage/index.d.ts.map +1 -0
- package/dist/plugins/usage/index.js +13 -0
- package/dist/plugins/usage/index.js.map +1 -0
- package/dist/plugins/usage/stores/index.d.ts +9 -0
- package/dist/plugins/usage/stores/index.d.ts.map +1 -0
- package/dist/plugins/usage/stores/index.js +9 -0
- package/dist/plugins/usage/stores/index.js.map +1 -0
- package/dist/plugins/usage/stores/postgres-store.d.ts +14 -0
- package/dist/plugins/usage/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/usage/stores/postgres-store.js +146 -0
- package/dist/plugins/usage/stores/postgres-store.js.map +1 -0
- package/dist/plugins/usage/types.d.ts +195 -0
- package/dist/plugins/usage/types.d.ts.map +1 -0
- package/dist/plugins/usage/types.js +10 -0
- package/dist/plugins/usage/types.js.map +1 -0
- package/dist/plugins/usage/usage-plugin.d.ts +51 -0
- package/dist/plugins/usage/usage-plugin.d.ts.map +1 -0
- package/dist/plugins/usage/usage-plugin.js +412 -0
- package/dist/plugins/usage/usage-plugin.js.map +1 -0
- package/dist/plugins/users/__tests__/postgres-store.test.d.ts +10 -0
- package/dist/plugins/users/__tests__/postgres-store.test.d.ts.map +1 -0
- package/dist/plugins/users/__tests__/postgres-store.test.js +229 -0
- package/dist/plugins/users/__tests__/postgres-store.test.js.map +1 -0
- package/dist/plugins/users/__tests__/users-plugin.test.js +3 -0
- package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -1
- package/dist/plugins/users/index.d.ts +2 -2
- package/dist/plugins/users/index.d.ts.map +1 -1
- package/dist/plugins/users/index.js +1 -1
- package/dist/plugins/users/index.js.map +1 -1
- package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -1
- package/dist/plugins/users/stores/postgres-store.js +76 -0
- package/dist/plugins/users/stores/postgres-store.js.map +1 -1
- package/dist/plugins/users/types.d.ts +74 -6
- package/dist/plugins/users/types.d.ts.map +1 -1
- package/dist/plugins/users/users-plugin.d.ts +15 -1
- package/dist/plugins/users/users-plugin.d.ts.map +1 -1
- package/dist/plugins/users/users-plugin.js +29 -0
- package/dist/plugins/users/users-plugin.js.map +1 -1
- package/dist-ui/assets/index-CynOqPkb.js +469 -0
- package/dist-ui/assets/index-CynOqPkb.js.map +1 -0
- package/dist-ui/index.html +1 -1
- package/dist-ui-lib/api/controlPanelApi.d.ts +46 -0
- package/dist-ui-lib/components/StatCard.d.ts +16 -0
- package/dist-ui-lib/dashboard/widgets/NotificationsStatsWidget.d.ts +12 -0
- package/dist-ui-lib/dashboard/widgets/index.d.ts +1 -0
- package/dist-ui-lib/index.js +1822 -1611
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/pages/NotificationsPage.d.ts +9 -0
- package/dist-ui-lib/utils/formatters.d.ts +19 -0
- package/package.json +3 -2
- package/src/index.ts +178 -0
- package/src/plugins/bans/bans-plugin.ts +15 -3
- package/src/plugins/devices/__tests__/devices-plugin.test.ts +551 -0
- package/src/plugins/devices/__tests__/token-utils.test.ts +264 -0
- package/src/plugins/devices/adapters/compute-adapter.ts +139 -0
- package/src/plugins/devices/adapters/index.ts +13 -0
- package/src/plugins/devices/adapters/mobile-adapter.ts +179 -0
- package/src/plugins/devices/devices-plugin.ts +538 -0
- package/src/plugins/devices/index.ts +69 -0
- package/src/plugins/devices/stores/index.ts +9 -0
- package/src/plugins/devices/stores/postgres-store.ts +304 -0
- package/src/plugins/devices/token-utils.ts +213 -0
- package/src/plugins/devices/types.ts +351 -0
- package/src/plugins/index.ts +218 -0
- package/src/plugins/notifications/__tests__/notifications-manager.test.ts +637 -0
- package/src/plugins/notifications/index.ts +91 -0
- package/src/plugins/notifications/notifications-manager.ts +773 -0
- package/src/plugins/notifications/notifications-plugin.ts +398 -0
- package/src/plugins/notifications/types.ts +207 -0
- package/src/plugins/parental/__tests__/parental-plugin.test.ts +465 -0
- package/src/plugins/parental/adapters/index.ts +8 -0
- package/src/plugins/parental/adapters/kids-adapter.ts +206 -0
- package/src/plugins/parental/index.ts +55 -0
- package/src/plugins/parental/parental-plugin.ts +759 -0
- package/src/plugins/parental/stores/index.ts +7 -0
- package/src/plugins/parental/stores/postgres-store.ts +304 -0
- package/src/plugins/parental/types.ts +180 -0
- package/src/plugins/profiles/__tests__/profiles-plugin.test.ts +321 -0
- package/src/plugins/profiles/index.ts +49 -0
- package/src/plugins/profiles/profiles-plugin.ts +546 -0
- package/src/plugins/profiles/stores/index.ts +9 -0
- package/src/plugins/profiles/stores/postgres-store.ts +439 -0
- package/src/plugins/profiles/types.ts +338 -0
- package/src/plugins/subscriptions/__tests__/subscriptions-plugin.test.ts +404 -0
- package/src/plugins/subscriptions/index.ts +51 -0
- package/src/plugins/subscriptions/stores/index.ts +9 -0
- package/src/plugins/subscriptions/stores/postgres-store.ts +482 -0
- package/src/plugins/subscriptions/subscriptions-plugin.ts +530 -0
- package/src/plugins/subscriptions/types.ts +355 -0
- package/src/plugins/usage/__tests__/usage-plugin.test.ts +288 -0
- package/src/plugins/usage/index.ts +39 -0
- package/src/plugins/usage/stores/index.ts +9 -0
- package/src/plugins/usage/stores/postgres-store.ts +213 -0
- package/src/plugins/usage/types.ts +222 -0
- package/src/plugins/usage/usage-plugin.ts +484 -0
- package/src/plugins/users/__tests__/postgres-store.test.ts +326 -0
- package/src/plugins/users/__tests__/users-plugin.test.ts +3 -0
- package/src/plugins/users/index.ts +6 -0
- package/src/plugins/users/stores/postgres-store.ts +104 -0
- package/src/plugins/users/types.ts +82 -6
- package/src/plugins/users/users-plugin.ts +37 -0
- package/ui/src/App.tsx +5 -1
- package/ui/src/api/controlPanelApi.ts +103 -6
- package/ui/src/components/StatCard.tsx +58 -0
- package/ui/src/dashboard/builtInWidgets.tsx +3 -1
- package/ui/src/dashboard/widgets/NotificationsStatsWidget.tsx +167 -0
- package/ui/src/dashboard/widgets/index.ts +1 -0
- package/ui/src/pages/NotificationsPage.tsx +417 -0
- package/ui/src/utils/formatters.ts +33 -0
- package/dist-ui/assets/index-D7DoZ9rL.js +0 -478
- package/dist-ui/assets/index-D7DoZ9rL.js.map +0 -1
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscriptions Plugin Tests
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for the subscriptions plugin including:
|
|
5
|
+
* - Plugin lifecycle
|
|
6
|
+
* - Tier management
|
|
7
|
+
* - Entitlement checks
|
|
8
|
+
* - User subscription management
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
12
|
+
import {
|
|
13
|
+
createSubscriptionsPlugin,
|
|
14
|
+
getSubscriptionsStore,
|
|
15
|
+
getTierById,
|
|
16
|
+
getTierBySlug,
|
|
17
|
+
listTiers,
|
|
18
|
+
createTier,
|
|
19
|
+
getUserSubscription,
|
|
20
|
+
createUserSubscription,
|
|
21
|
+
getFeatureLimit,
|
|
22
|
+
hasFeature,
|
|
23
|
+
} from '../subscriptions-plugin.js';
|
|
24
|
+
import type {
|
|
25
|
+
SubscriptionsStore,
|
|
26
|
+
SubscriptionTier,
|
|
27
|
+
UserSubscription,
|
|
28
|
+
SubscriptionEntitlement,
|
|
29
|
+
} from '../types.js';
|
|
30
|
+
import type { PluginRegistry } from '../../../core/plugin-registry.js';
|
|
31
|
+
|
|
32
|
+
describe('Subscriptions Plugin', () => {
|
|
33
|
+
// Mock tier data
|
|
34
|
+
const mockTier: SubscriptionTier = {
|
|
35
|
+
id: 'tier-id-123',
|
|
36
|
+
slug: 'premium',
|
|
37
|
+
name: 'Premium Plan',
|
|
38
|
+
description: 'Full access to all features',
|
|
39
|
+
price_monthly_cents: 999,
|
|
40
|
+
price_yearly_cents: 9990,
|
|
41
|
+
stripe_price_id_monthly: 'price_monthly_123',
|
|
42
|
+
stripe_price_id_yearly: 'price_yearly_123',
|
|
43
|
+
is_active: true,
|
|
44
|
+
sort_order: 1,
|
|
45
|
+
metadata: {},
|
|
46
|
+
created_at: new Date(),
|
|
47
|
+
updated_at: new Date(),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const mockEntitlement: SubscriptionEntitlement = {
|
|
51
|
+
id: 'entitlement-id-123',
|
|
52
|
+
tier_id: mockTier.id,
|
|
53
|
+
feature_code: 'ai_messages_daily',
|
|
54
|
+
limit_value: -1, // unlimited
|
|
55
|
+
metadata: {},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const mockSubscription: UserSubscription = {
|
|
59
|
+
id: 'subscription-id-123',
|
|
60
|
+
user_id: 'user-123',
|
|
61
|
+
tier_id: mockTier.id,
|
|
62
|
+
stripe_customer_id: 'cus_123',
|
|
63
|
+
stripe_subscription_id: 'sub_123',
|
|
64
|
+
status: 'active',
|
|
65
|
+
current_period_start: new Date(),
|
|
66
|
+
current_period_end: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
|
67
|
+
cancel_at_period_end: false,
|
|
68
|
+
metadata: {},
|
|
69
|
+
created_at: new Date(),
|
|
70
|
+
updated_at: new Date(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Mock store factory
|
|
74
|
+
const createMockStore = (): SubscriptionsStore => ({
|
|
75
|
+
name: 'mock',
|
|
76
|
+
initialize: vi.fn().mockResolvedValue(undefined),
|
|
77
|
+
// Tiers
|
|
78
|
+
createTier: vi.fn().mockResolvedValue(mockTier),
|
|
79
|
+
getTierById: vi.fn().mockResolvedValue(mockTier),
|
|
80
|
+
getTierBySlug: vi.fn().mockResolvedValue(mockTier),
|
|
81
|
+
listTiers: vi.fn().mockResolvedValue([mockTier]),
|
|
82
|
+
updateTier: vi.fn().mockResolvedValue(mockTier),
|
|
83
|
+
deleteTier: vi.fn().mockResolvedValue(true),
|
|
84
|
+
// Entitlements
|
|
85
|
+
createEntitlement: vi.fn().mockResolvedValue(mockEntitlement),
|
|
86
|
+
getEntitlementsByTier: vi.fn().mockResolvedValue([mockEntitlement]),
|
|
87
|
+
updateEntitlement: vi.fn().mockResolvedValue(mockEntitlement),
|
|
88
|
+
deleteEntitlement: vi.fn().mockResolvedValue(true),
|
|
89
|
+
setTierEntitlements: vi.fn().mockResolvedValue(undefined),
|
|
90
|
+
// User subscriptions
|
|
91
|
+
createUserSubscription: vi.fn().mockResolvedValue(mockSubscription),
|
|
92
|
+
getUserSubscriptionById: vi.fn().mockResolvedValue(mockSubscription),
|
|
93
|
+
getActiveSubscription: vi.fn().mockResolvedValue({
|
|
94
|
+
...mockSubscription,
|
|
95
|
+
tier: mockTier,
|
|
96
|
+
}),
|
|
97
|
+
getByStripeSubscriptionId: vi.fn().mockResolvedValue(mockSubscription),
|
|
98
|
+
updateUserSubscription: vi.fn().mockResolvedValue(mockSubscription),
|
|
99
|
+
cancelSubscription: vi.fn().mockResolvedValue(true),
|
|
100
|
+
getFeatureLimit: vi.fn().mockResolvedValue(-1), // unlimited
|
|
101
|
+
hasFeature: vi.fn().mockResolvedValue(true),
|
|
102
|
+
shutdown: vi.fn().mockResolvedValue(undefined),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Mock registry factory
|
|
106
|
+
const createMockRegistry = (): PluginRegistry =>
|
|
107
|
+
({
|
|
108
|
+
hasPlugin: vi.fn().mockReturnValue(false),
|
|
109
|
+
getPlugin: vi.fn().mockReturnValue(null),
|
|
110
|
+
listPlugins: vi.fn().mockReturnValue([]),
|
|
111
|
+
addRoute: vi.fn(),
|
|
112
|
+
addMenuItem: vi.fn(),
|
|
113
|
+
addPage: vi.fn(),
|
|
114
|
+
addWidget: vi.fn(),
|
|
115
|
+
getRoutes: vi.fn().mockReturnValue([]),
|
|
116
|
+
getMenuItems: vi.fn().mockReturnValue([]),
|
|
117
|
+
getPages: vi.fn().mockReturnValue([]),
|
|
118
|
+
getWidgets: vi.fn().mockReturnValue([]),
|
|
119
|
+
getConfig: vi.fn().mockReturnValue({}),
|
|
120
|
+
setConfig: vi.fn().mockResolvedValue(undefined),
|
|
121
|
+
subscribe: vi.fn().mockReturnValue(() => {}),
|
|
122
|
+
emit: vi.fn(),
|
|
123
|
+
registerHealthCheck: vi.fn(),
|
|
124
|
+
getApp: vi.fn().mockReturnValue({} as any),
|
|
125
|
+
getRouter: vi.fn().mockReturnValue({} as any),
|
|
126
|
+
getLogger: vi.fn().mockReturnValue({
|
|
127
|
+
debug: vi.fn(),
|
|
128
|
+
info: vi.fn(),
|
|
129
|
+
warn: vi.fn(),
|
|
130
|
+
error: vi.fn(),
|
|
131
|
+
}),
|
|
132
|
+
}) as unknown as PluginRegistry;
|
|
133
|
+
|
|
134
|
+
let mockStore: SubscriptionsStore;
|
|
135
|
+
let mockRegistry: PluginRegistry;
|
|
136
|
+
|
|
137
|
+
beforeEach(() => {
|
|
138
|
+
vi.clearAllMocks();
|
|
139
|
+
mockStore = createMockStore();
|
|
140
|
+
mockRegistry = createMockRegistry();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
afterEach(async () => {
|
|
144
|
+
// Clean up
|
|
145
|
+
const store = getSubscriptionsStore();
|
|
146
|
+
if (store) {
|
|
147
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
148
|
+
await plugin.onStop?.();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('createSubscriptionsPlugin', () => {
|
|
153
|
+
it('should create a plugin with correct id', () => {
|
|
154
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
155
|
+
expect(plugin.id).toBe('subscriptions');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should create a plugin with correct name', () => {
|
|
159
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
160
|
+
expect(plugin.name).toBe('Subscriptions');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should create a plugin with version', () => {
|
|
164
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
165
|
+
expect(plugin.version).toBe('1.0.0');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('onStart', () => {
|
|
170
|
+
it('should initialize store on start', async () => {
|
|
171
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
172
|
+
await plugin.onStart({}, mockRegistry);
|
|
173
|
+
|
|
174
|
+
expect(mockStore.initialize).toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should register health check', async () => {
|
|
178
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
179
|
+
await plugin.onStart({}, mockRegistry);
|
|
180
|
+
|
|
181
|
+
expect(mockRegistry.registerHealthCheck).toHaveBeenCalledWith(
|
|
182
|
+
expect.objectContaining({
|
|
183
|
+
name: 'subscriptions-store',
|
|
184
|
+
type: 'custom',
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('onStop', () => {
|
|
191
|
+
it('should shutdown store', async () => {
|
|
192
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
193
|
+
await plugin.onStart({}, mockRegistry);
|
|
194
|
+
await plugin.onStop?.();
|
|
195
|
+
|
|
196
|
+
expect(mockStore.shutdown).toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should clear store reference', async () => {
|
|
200
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
201
|
+
await plugin.onStart({}, mockRegistry);
|
|
202
|
+
await plugin.onStop?.();
|
|
203
|
+
|
|
204
|
+
expect(getSubscriptionsStore()).toBeNull();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('helper functions', () => {
|
|
209
|
+
describe('getSubscriptionsStore', () => {
|
|
210
|
+
it('should return null when plugin not started', () => {
|
|
211
|
+
expect(getSubscriptionsStore()).toBeNull();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should return store when plugin started', async () => {
|
|
215
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
216
|
+
await plugin.onStart({}, mockRegistry);
|
|
217
|
+
|
|
218
|
+
expect(getSubscriptionsStore()).toBe(mockStore);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('getTierById', () => {
|
|
223
|
+
it('should throw when plugin not initialized', async () => {
|
|
224
|
+
await expect(getTierById('tier-id')).rejects.toThrow(
|
|
225
|
+
'Subscriptions plugin not initialized'
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should return tier when found', async () => {
|
|
230
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
231
|
+
await plugin.onStart({}, mockRegistry);
|
|
232
|
+
|
|
233
|
+
const result = await getTierById(mockTier.id);
|
|
234
|
+
expect(result).toEqual(mockTier);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('getTierBySlug', () => {
|
|
239
|
+
it('should throw when plugin not initialized', async () => {
|
|
240
|
+
await expect(getTierBySlug('premium')).rejects.toThrow(
|
|
241
|
+
'Subscriptions plugin not initialized'
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should return tier when found', async () => {
|
|
246
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
247
|
+
await plugin.onStart({}, mockRegistry);
|
|
248
|
+
|
|
249
|
+
const result = await getTierBySlug('premium');
|
|
250
|
+
expect(result).toEqual(mockTier);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('listTiers', () => {
|
|
255
|
+
it('should throw when plugin not initialized', async () => {
|
|
256
|
+
await expect(listTiers()).rejects.toThrow(
|
|
257
|
+
'Subscriptions plugin not initialized'
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should return all tiers', async () => {
|
|
262
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
263
|
+
await plugin.onStart({}, mockRegistry);
|
|
264
|
+
|
|
265
|
+
const result = await listTiers();
|
|
266
|
+
|
|
267
|
+
expect(result).toHaveLength(1);
|
|
268
|
+
expect(result[0]).toEqual(mockTier);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('createTier', () => {
|
|
273
|
+
it('should throw when plugin not initialized', async () => {
|
|
274
|
+
await expect(
|
|
275
|
+
createTier({ slug: 'new', name: 'New Tier' })
|
|
276
|
+
).rejects.toThrow('Subscriptions plugin not initialized');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should create tier', async () => {
|
|
280
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
281
|
+
await plugin.onStart({}, mockRegistry);
|
|
282
|
+
|
|
283
|
+
const result = await createTier({ slug: 'new', name: 'New Tier' });
|
|
284
|
+
|
|
285
|
+
expect(result).toEqual(mockTier);
|
|
286
|
+
expect(mockStore.createTier).toHaveBeenCalled();
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe('getUserSubscription', () => {
|
|
291
|
+
it('should throw when plugin not initialized', async () => {
|
|
292
|
+
await expect(getUserSubscription('user-id')).rejects.toThrow(
|
|
293
|
+
'Subscriptions plugin not initialized'
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should return user subscription with tier', async () => {
|
|
298
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
299
|
+
await plugin.onStart({}, mockRegistry);
|
|
300
|
+
|
|
301
|
+
const result = await getUserSubscription('user-123');
|
|
302
|
+
|
|
303
|
+
expect(result?.user_id).toBe('user-123');
|
|
304
|
+
expect(result?.tier).toBeDefined();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should return null when no subscription', async () => {
|
|
308
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
309
|
+
await plugin.onStart({}, mockRegistry);
|
|
310
|
+
|
|
311
|
+
(mockStore.getActiveSubscription as any).mockResolvedValue(null);
|
|
312
|
+
|
|
313
|
+
const result = await getUserSubscription('user-no-sub');
|
|
314
|
+
expect(result).toBeNull();
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('createUserSubscription', () => {
|
|
319
|
+
it('should throw when plugin not initialized', async () => {
|
|
320
|
+
await expect(
|
|
321
|
+
createUserSubscription({
|
|
322
|
+
user_id: 'user-123',
|
|
323
|
+
tier_id: mockTier.id,
|
|
324
|
+
})
|
|
325
|
+
).rejects.toThrow('Subscriptions plugin not initialized');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should create subscription', async () => {
|
|
329
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
330
|
+
await plugin.onStart({}, mockRegistry);
|
|
331
|
+
|
|
332
|
+
const result = await createUserSubscription({
|
|
333
|
+
user_id: 'user-123',
|
|
334
|
+
tier_id: mockTier.id,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
expect(result).toEqual(mockSubscription);
|
|
338
|
+
expect(mockStore.createUserSubscription).toHaveBeenCalled();
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
describe('getFeatureLimit', () => {
|
|
343
|
+
it('should throw when plugin not initialized', async () => {
|
|
344
|
+
await expect(
|
|
345
|
+
getFeatureLimit('user-id', 'ai_messages_daily')
|
|
346
|
+
).rejects.toThrow('Subscriptions plugin not initialized');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('should return -1 for unlimited feature', async () => {
|
|
350
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
351
|
+
await plugin.onStart({}, mockRegistry);
|
|
352
|
+
|
|
353
|
+
const result = await getFeatureLimit('user-123', 'ai_messages_daily');
|
|
354
|
+
expect(result).toBe(-1);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('should return limit value', async () => {
|
|
358
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
359
|
+
await plugin.onStart({}, mockRegistry);
|
|
360
|
+
|
|
361
|
+
(mockStore.getFeatureLimit as any).mockResolvedValue(100);
|
|
362
|
+
|
|
363
|
+
const result = await getFeatureLimit('user-123', 'vision_calls_daily');
|
|
364
|
+
expect(result).toBe(100);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should return null when no subscription', async () => {
|
|
368
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
369
|
+
await plugin.onStart({}, mockRegistry);
|
|
370
|
+
|
|
371
|
+
(mockStore.getFeatureLimit as any).mockResolvedValue(null);
|
|
372
|
+
|
|
373
|
+
const result = await getFeatureLimit('user-no-sub', 'ai_messages_daily');
|
|
374
|
+
expect(result).toBeNull();
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
describe('hasFeature', () => {
|
|
379
|
+
it('should throw when plugin not initialized', async () => {
|
|
380
|
+
await expect(
|
|
381
|
+
hasFeature('user-id', 'ai_messages_daily')
|
|
382
|
+
).rejects.toThrow('Subscriptions plugin not initialized');
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should return true when user has feature', async () => {
|
|
386
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
387
|
+
await plugin.onStart({}, mockRegistry);
|
|
388
|
+
|
|
389
|
+
const result = await hasFeature('user-123', 'ai_messages_daily');
|
|
390
|
+
expect(result).toBe(true);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('should return false when user lacks feature', async () => {
|
|
394
|
+
const plugin = createSubscriptionsPlugin({ store: mockStore });
|
|
395
|
+
await plugin.onStart({}, mockRegistry);
|
|
396
|
+
|
|
397
|
+
(mockStore.hasFeature as any).mockResolvedValue(false);
|
|
398
|
+
|
|
399
|
+
const result = await hasFeature('user-123', 'premium_feature');
|
|
400
|
+
expect(result).toBe(false);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscriptions Plugin
|
|
3
|
+
*
|
|
4
|
+
* Subscription tier and entitlement management.
|
|
5
|
+
* Exports all subscription-related functionality.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Main plugin
|
|
11
|
+
export {
|
|
12
|
+
createSubscriptionsPlugin,
|
|
13
|
+
getSubscriptionsStore,
|
|
14
|
+
createTier,
|
|
15
|
+
getTierBySlug,
|
|
16
|
+
getTierById,
|
|
17
|
+
listTiers,
|
|
18
|
+
getTierEntitlements,
|
|
19
|
+
setTierEntitlements,
|
|
20
|
+
getUserSubscription,
|
|
21
|
+
createUserSubscription,
|
|
22
|
+
updateUserSubscription,
|
|
23
|
+
cancelSubscription,
|
|
24
|
+
getUserTierSlug,
|
|
25
|
+
getFeatureLimit,
|
|
26
|
+
hasFeature,
|
|
27
|
+
checkFeatureLimit,
|
|
28
|
+
ensureUserSubscription,
|
|
29
|
+
} from './subscriptions-plugin.js';
|
|
30
|
+
|
|
31
|
+
// Types
|
|
32
|
+
export type {
|
|
33
|
+
SubscriptionTier,
|
|
34
|
+
SubscriptionEntitlement,
|
|
35
|
+
UserSubscription,
|
|
36
|
+
UserSubscriptionWithTier,
|
|
37
|
+
SubscriptionStatus,
|
|
38
|
+
FeatureLimitResult,
|
|
39
|
+
CreateTierInput,
|
|
40
|
+
UpdateTierInput,
|
|
41
|
+
CreateEntitlementInput,
|
|
42
|
+
CreateUserSubscriptionInput,
|
|
43
|
+
UpdateUserSubscriptionInput,
|
|
44
|
+
SubscriptionsStore,
|
|
45
|
+
SubscriptionsPluginConfig,
|
|
46
|
+
SubscriptionsApiConfig,
|
|
47
|
+
PostgresSubscriptionsStoreConfig,
|
|
48
|
+
} from './types.js';
|
|
49
|
+
|
|
50
|
+
// Stores
|
|
51
|
+
export { postgresSubscriptionsStore } from './stores/index.js';
|