@onesub/server 0.12.1 → 0.13.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.
Files changed (129) hide show
  1. package/dist/__tests__/admin-customer-detail.test.js +146 -0
  2. package/dist/__tests__/admin-customer-detail.test.js.map +1 -0
  3. package/dist/__tests__/admin-list-subscriptions.test.js +192 -0
  4. package/dist/__tests__/admin-list-subscriptions.test.js.map +1 -0
  5. package/dist/__tests__/admin-subscription-detail.test.js +112 -0
  6. package/dist/__tests__/admin-subscription-detail.test.js.map +1 -0
  7. package/dist/__tests__/apple-root-ca.test.js +41 -0
  8. package/dist/__tests__/apple-root-ca.test.js.map +1 -0
  9. package/dist/__tests__/apple-status-api.test.js +345 -0
  10. package/dist/__tests__/apple-status-api.test.js.map +1 -0
  11. package/dist/__tests__/cache.test.js +34 -0
  12. package/dist/__tests__/cache.test.js.map +1 -0
  13. package/dist/__tests__/entitlements.test.js +268 -0
  14. package/dist/__tests__/entitlements.test.js.map +1 -0
  15. package/dist/__tests__/error-codes.test.js +173 -0
  16. package/dist/__tests__/error-codes.test.js.map +1 -0
  17. package/dist/__tests__/fetch-hardening.test.js +192 -0
  18. package/dist/__tests__/fetch-hardening.test.js.map +1 -0
  19. package/dist/__tests__/google-paused.test.js +126 -0
  20. package/dist/__tests__/google-paused.test.js.map +1 -0
  21. package/dist/__tests__/google-price-change.test.js +186 -0
  22. package/dist/__tests__/google-price-change.test.js.map +1 -0
  23. package/dist/__tests__/google-subscriptions-v2.test.js +206 -0
  24. package/dist/__tests__/google-subscriptions-v2.test.js.map +1 -0
  25. package/dist/__tests__/lifecycle-scenarios.test.js +579 -0
  26. package/dist/__tests__/lifecycle-scenarios.test.js.map +1 -0
  27. package/dist/__tests__/lifecycle-states.test.js +423 -0
  28. package/dist/__tests__/lifecycle-states.test.js.map +1 -0
  29. package/dist/__tests__/linked-purchase-token.test.js +227 -0
  30. package/dist/__tests__/linked-purchase-token.test.js.map +1 -0
  31. package/dist/__tests__/metrics.test.js +340 -0
  32. package/dist/__tests__/metrics.test.js.map +1 -0
  33. package/dist/__tests__/mock-provider.test.js +188 -0
  34. package/dist/__tests__/mock-provider.test.js.map +1 -0
  35. package/dist/__tests__/openapi.test.js +32 -0
  36. package/dist/__tests__/openapi.test.js.map +1 -0
  37. package/dist/__tests__/paused-auto-resume.test.js +162 -0
  38. package/dist/__tests__/paused-auto-resume.test.js.map +1 -0
  39. package/dist/__tests__/providers.test.js +321 -0
  40. package/dist/__tests__/providers.test.js.map +1 -0
  41. package/dist/__tests__/redis-store.test.js +166 -0
  42. package/dist/__tests__/redis-store.test.js.map +1 -0
  43. package/dist/__tests__/refund-policy.test.js +317 -0
  44. package/dist/__tests__/refund-policy.test.js.map +1 -0
  45. package/dist/__tests__/schema.test.js +46 -0
  46. package/dist/__tests__/schema.test.js.map +1 -0
  47. package/dist/__tests__/store.test.js +172 -0
  48. package/dist/__tests__/store.test.js.map +1 -0
  49. package/dist/__tests__/test-utils.js +30 -0
  50. package/dist/__tests__/test-utils.js.map +1 -0
  51. package/dist/__tests__/webhook-apple.test.js +247 -0
  52. package/dist/__tests__/webhook-apple.test.js.map +1 -0
  53. package/dist/__tests__/webhook-events.test.js +36 -0
  54. package/dist/__tests__/webhook-events.test.js.map +1 -0
  55. package/dist/__tests__/webhook-google.test.js +255 -0
  56. package/dist/__tests__/webhook-google.test.js.map +1 -0
  57. package/dist/__tests__/webhook-queue.test.js +26 -0
  58. package/dist/__tests__/webhook-queue.test.js.map +1 -0
  59. package/dist/__tests__/webhook-refund.test.js +313 -0
  60. package/dist/__tests__/webhook-refund.test.js.map +1 -0
  61. package/dist/cache.js +39 -0
  62. package/dist/cache.js.map +1 -0
  63. package/dist/errors.js +19 -0
  64. package/dist/errors.js.map +1 -0
  65. package/dist/http.js +36 -0
  66. package/dist/http.js.map +1 -0
  67. package/dist/index.cjs +241 -9
  68. package/dist/index.cjs.map +1 -1
  69. package/dist/index.d.ts +1 -1
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +241 -11
  72. package/dist/index.js.map +1 -1
  73. package/dist/logger.js +20 -0
  74. package/dist/logger.js.map +1 -0
  75. package/dist/openapi.js +196 -0
  76. package/dist/openapi.js.map +1 -0
  77. package/dist/providers/apple-root-ca.js +38 -0
  78. package/dist/providers/apple-root-ca.js.map +1 -0
  79. package/dist/providers/apple.d.ts +76 -0
  80. package/dist/providers/apple.d.ts.map +1 -1
  81. package/dist/providers/apple.js +630 -0
  82. package/dist/providers/apple.js.map +1 -0
  83. package/dist/providers/google.d.ts +25 -0
  84. package/dist/providers/google.d.ts.map +1 -1
  85. package/dist/providers/google.js +564 -0
  86. package/dist/providers/google.js.map +1 -0
  87. package/dist/providers/mock.js +93 -0
  88. package/dist/providers/mock.js.map +1 -0
  89. package/dist/routes/admin.d.ts.map +1 -1
  90. package/dist/routes/admin.js +327 -0
  91. package/dist/routes/admin.js.map +1 -0
  92. package/dist/routes/apple-offer.d.ts +21 -0
  93. package/dist/routes/apple-offer.d.ts.map +1 -0
  94. package/dist/routes/apple-offer.js +72 -0
  95. package/dist/routes/apple-offer.js.map +1 -0
  96. package/dist/routes/entitlements.js +130 -0
  97. package/dist/routes/entitlements.js.map +1 -0
  98. package/dist/routes/metrics.js +281 -0
  99. package/dist/routes/metrics.js.map +1 -0
  100. package/dist/routes/purchase.d.ts.map +1 -1
  101. package/dist/routes/purchase.js +204 -0
  102. package/dist/routes/purchase.js.map +1 -0
  103. package/dist/routes/status.js +55 -0
  104. package/dist/routes/status.js.map +1 -0
  105. package/dist/routes/validate.js +70 -0
  106. package/dist/routes/validate.js.map +1 -0
  107. package/dist/routes/webhook-apple.d.ts.map +1 -1
  108. package/dist/routes/webhook-apple.js +145 -0
  109. package/dist/routes/webhook-apple.js.map +1 -0
  110. package/dist/routes/webhook-google.d.ts.map +1 -1
  111. package/dist/routes/webhook-google.js +204 -0
  112. package/dist/routes/webhook-google.js.map +1 -0
  113. package/dist/routes/webhook.js +19 -0
  114. package/dist/routes/webhook.js.map +1 -0
  115. package/dist/store.js +145 -0
  116. package/dist/store.js.map +1 -0
  117. package/dist/stores/postgres.js +329 -0
  118. package/dist/stores/postgres.js.map +1 -0
  119. package/dist/stores/redis.js +297 -0
  120. package/dist/stores/redis.js.map +1 -0
  121. package/dist/stores/schema.js +67 -0
  122. package/dist/stores/schema.js.map +1 -0
  123. package/dist/tracing.js +74 -0
  124. package/dist/tracing.js.map +1 -0
  125. package/dist/webhook-events.js +58 -0
  126. package/dist/webhook-events.js.map +1 -0
  127. package/dist/webhook-queue.js +108 -0
  128. package/dist/webhook-queue.js.map +1 -0
  129. package/package.json +4 -4
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Entitlements abstraction tests.
3
+ *
4
+ * Covers:
5
+ * - evaluateEntitlement helper (subscription / purchase / mixed / none /
6
+ * expired sub / wrong status / consumable excluded)
7
+ * - GET /onesub/entitlement single-check route (404 unknown id, 400 bad input)
8
+ * - GET /onesub/entitlements bulk route
9
+ * - Router not mounted when config.entitlements is absent (404)
10
+ * - getAllByUserId returns multi-product subscriptions (regression for the
11
+ * InMemoryStore change that backs entitlements)
12
+ */
13
+ import { describe, it, expect, beforeEach } from 'vitest';
14
+ import express from 'express';
15
+ import { SUBSCRIPTION_STATUS, PURCHASE_TYPE } from '@onesub/shared';
16
+ import { createOneSubMiddleware } from '../index.js';
17
+ import { evaluateEntitlement } from '../routes/entitlements.js';
18
+ import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
19
+ // ── helpers ─────────────────────────────────────────────────────────────────
20
+ const futureExpiry = '2099-01-01T00:00:00.000Z';
21
+ const pastExpiry = '2024-01-01T00:00:00.000Z';
22
+ function sub(overrides) {
23
+ return {
24
+ userId: 'u',
25
+ productId: 'pro_monthly',
26
+ platform: 'apple',
27
+ status: 'active',
28
+ expiresAt: futureExpiry,
29
+ originalTransactionId: `orig_${Math.random()}`,
30
+ purchasedAt: '2026-01-01T00:00:00.000Z',
31
+ willRenew: true,
32
+ ...overrides,
33
+ };
34
+ }
35
+ function purchase(overrides) {
36
+ return {
37
+ userId: 'u',
38
+ productId: 'lifetime_pass',
39
+ platform: 'apple',
40
+ type: 'non_consumable',
41
+ transactionId: `tx_${Math.random()}`,
42
+ purchasedAt: '2026-01-01T00:00:00.000Z',
43
+ quantity: 1,
44
+ ...overrides,
45
+ };
46
+ }
47
+ function spinUp(handler) {
48
+ return {
49
+ async get(path) {
50
+ const httpServer = handler.listen(0);
51
+ const port = httpServer.address().port;
52
+ try {
53
+ const resp = await fetch(`http://127.0.0.1:${port}${path}`);
54
+ const text = await resp.text();
55
+ let parsed = text;
56
+ try {
57
+ parsed = JSON.parse(text);
58
+ }
59
+ catch { /* keep as text */ }
60
+ return { status: resp.status, body: parsed };
61
+ }
62
+ finally {
63
+ await new Promise((r) => httpServer.close(() => r()));
64
+ }
65
+ },
66
+ };
67
+ }
68
+ function buildServer(opts) {
69
+ const store = new InMemorySubscriptionStore();
70
+ const purchaseStore = new InMemoryPurchaseStore();
71
+ const config = {
72
+ apple: { bundleId: 'com.example.app', skipJwsVerification: true },
73
+ database: { url: '' },
74
+ entitlements: opts.entitlements,
75
+ };
76
+ const app = express();
77
+ // createOneSubMiddleware mounts express.json itself, but the entitlement
78
+ // routes are GET-only so it's fine either way.
79
+ const middleware = createOneSubMiddleware({ ...config, store, purchaseStore });
80
+ app.use(middleware);
81
+ return { store, purchaseStore, server: spinUp(app) };
82
+ }
83
+ const PREMIUM = {
84
+ premium: { productIds: ['pro_monthly', 'pro_yearly', 'lifetime_pass'] },
85
+ };
86
+ // ─────────────────────────────────────────────────────────────────────────────
87
+ // evaluateEntitlement (unit, no HTTP)
88
+ // ─────────────────────────────────────────────────────────────────────────────
89
+ describe('evaluateEntitlement', () => {
90
+ let store;
91
+ let purchaseStore;
92
+ beforeEach(() => {
93
+ store = new InMemorySubscriptionStore();
94
+ purchaseStore = new InMemoryPurchaseStore();
95
+ });
96
+ it('active subscription matching one of the productIds → entitled', async () => {
97
+ await store.save(sub({ userId: 'u1', productId: 'pro_yearly' }));
98
+ const result = await evaluateEntitlement('u1', PREMIUM.premium, store, purchaseStore);
99
+ expect(result.active).toBe(true);
100
+ expect(result.source).toBe('subscription');
101
+ expect(result.productId).toBe('pro_yearly');
102
+ expect(result.expiresAt).toBe(futureExpiry);
103
+ });
104
+ it('non-consumable purchase matching one of the productIds → entitled', async () => {
105
+ await purchaseStore.savePurchase(purchase({ userId: 'u2', productId: 'lifetime_pass' }));
106
+ const result = await evaluateEntitlement('u2', PREMIUM.premium, store, purchaseStore);
107
+ expect(result.active).toBe(true);
108
+ expect(result.source).toBe('purchase');
109
+ expect(result.productId).toBe('lifetime_pass');
110
+ expect(result.expiresAt).toBeUndefined();
111
+ });
112
+ it('no matching record → not entitled', async () => {
113
+ const result = await evaluateEntitlement('u_none', PREMIUM.premium, store, purchaseStore);
114
+ expect(result).toEqual({ active: false, source: null });
115
+ });
116
+ it('subscription preferred over purchase when both match', async () => {
117
+ await store.save(sub({ userId: 'u3', productId: 'pro_monthly' }));
118
+ await purchaseStore.savePurchase(purchase({ userId: 'u3', productId: 'lifetime_pass' }));
119
+ const result = await evaluateEntitlement('u3', PREMIUM.premium, store, purchaseStore);
120
+ expect(result.source).toBe('subscription');
121
+ expect(result.productId).toBe('pro_monthly');
122
+ });
123
+ it('expired subscription (expiresAt < now) → not entitled even if status=active', async () => {
124
+ await store.save(sub({ userId: 'u4', productId: 'pro_monthly', expiresAt: pastExpiry }));
125
+ const result = await evaluateEntitlement('u4', PREMIUM.premium, store, purchaseStore);
126
+ expect(result.active).toBe(false);
127
+ });
128
+ it('grace_period subscription → entitled (still has valid expiresAt)', async () => {
129
+ await store.save(sub({ userId: 'u5', productId: 'pro_monthly', status: 'grace_period' }));
130
+ const result = await evaluateEntitlement('u5', PREMIUM.premium, store, purchaseStore);
131
+ expect(result.active).toBe(true);
132
+ expect(result.source).toBe('subscription');
133
+ });
134
+ it('on_hold / paused / canceled / expired status → not entitled', async () => {
135
+ for (const status of ['on_hold', 'paused', 'canceled', 'expired']) {
136
+ const localStore = new InMemorySubscriptionStore();
137
+ await localStore.save(sub({ userId: 'u', productId: 'pro_monthly', status }));
138
+ const result = await evaluateEntitlement('u', PREMIUM.premium, localStore, purchaseStore);
139
+ expect(result.active).toBe(false);
140
+ }
141
+ });
142
+ it('consumable purchase is NOT considered for entitlement', async () => {
143
+ await purchaseStore.savePurchase(purchase({
144
+ userId: 'u6',
145
+ productId: 'pro_monthly', // even matching productId
146
+ type: 'consumable',
147
+ }));
148
+ const result = await evaluateEntitlement('u6', PREMIUM.premium, store, purchaseStore);
149
+ expect(result.active).toBe(false);
150
+ });
151
+ it('multi-product subscriptions: returns the first matching active sub', async () => {
152
+ // User has two subs — one for pro_yearly (matches), one for off-premium product (no match)
153
+ await store.save(sub({ userId: 'u7', productId: 'unrelated_product' }));
154
+ await store.save(sub({ userId: 'u7', productId: 'pro_yearly' }));
155
+ const result = await evaluateEntitlement('u7', PREMIUM.premium, store, purchaseStore);
156
+ expect(result.active).toBe(true);
157
+ expect(result.productId).toBe('pro_yearly');
158
+ });
159
+ });
160
+ // ─────────────────────────────────────────────────────────────────────────────
161
+ // HTTP routes
162
+ // ─────────────────────────────────────────────────────────────────────────────
163
+ describe('GET /onesub/entitlement', () => {
164
+ it('returns active=true when user has matching active sub', async () => {
165
+ const { store, server } = buildServer({ entitlements: PREMIUM });
166
+ await store.save(sub({ userId: 'u_yes', productId: 'pro_monthly' }));
167
+ const resp = await server.get('/onesub/entitlement?userId=u_yes&id=premium');
168
+ expect(resp.status).toBe(200);
169
+ expect(resp.body.id).toBe('premium');
170
+ expect(resp.body.active).toBe(true);
171
+ expect(resp.body.source).toBe('subscription');
172
+ });
173
+ it('returns active=false when no matching record', async () => {
174
+ const { server } = buildServer({ entitlements: PREMIUM });
175
+ const resp = await server.get('/onesub/entitlement?userId=u_none&id=premium');
176
+ expect(resp.status).toBe(200);
177
+ expect(resp.body.active).toBe(false);
178
+ expect(resp.body.source).toBeNull();
179
+ });
180
+ it('404 + ENTITLEMENT_NOT_FOUND for unknown id', async () => {
181
+ const { server } = buildServer({ entitlements: PREMIUM });
182
+ const resp = await server.get('/onesub/entitlement?userId=u&id=enterprise');
183
+ expect(resp.status).toBe(404);
184
+ expect(resp.body.errorCode).toBe('ENTITLEMENT_NOT_FOUND');
185
+ });
186
+ it('400 INVALID_INPUT when userId or id is missing', async () => {
187
+ const { server } = buildServer({ entitlements: PREMIUM });
188
+ const resp = await server.get('/onesub/entitlement?userId=u');
189
+ expect(resp.status).toBe(400);
190
+ expect(resp.body.errorCode).toBe('INVALID_INPUT');
191
+ });
192
+ });
193
+ describe('GET /onesub/entitlements (bulk)', () => {
194
+ it('evaluates every configured entitlement in one round-trip', async () => {
195
+ const config = {
196
+ premium: { productIds: ['pro_monthly', 'pro_yearly'] },
197
+ promode: { productIds: ['dev_tools_addon'] },
198
+ };
199
+ const { store, server } = buildServer({ entitlements: config });
200
+ await store.save(sub({ userId: 'u_bulk', productId: 'pro_yearly' }));
201
+ const resp = await server.get('/onesub/entitlements?userId=u_bulk');
202
+ expect(resp.status).toBe(200);
203
+ expect(resp.body.entitlements.premium.active).toBe(true);
204
+ expect(resp.body.entitlements.premium.productId).toBe('pro_yearly');
205
+ expect(resp.body.entitlements.promode.active).toBe(false);
206
+ });
207
+ it('returns empty entitlement statuses for a user with nothing', async () => {
208
+ const config = {
209
+ premium: { productIds: ['pro_monthly'] },
210
+ addon: { productIds: ['some_addon'] },
211
+ };
212
+ const { server } = buildServer({ entitlements: config });
213
+ const resp = await server.get('/onesub/entitlements?userId=u_empty');
214
+ expect(resp.body.entitlements.premium.active).toBe(false);
215
+ expect(resp.body.entitlements.addon.active).toBe(false);
216
+ });
217
+ });
218
+ describe('Entitlement routes are NOT mounted without config', () => {
219
+ it('returns 404 when config.entitlements is absent', async () => {
220
+ const { server } = buildServer({}); // no entitlements
221
+ const resp = await server.get('/onesub/entitlement?userId=u&id=premium');
222
+ expect(resp.status).toBe(404);
223
+ const respBulk = await server.get('/onesub/entitlements?userId=u');
224
+ expect(respBulk.status).toBe(404);
225
+ });
226
+ it('returns 404 when config.entitlements is empty {}', async () => {
227
+ const { server } = buildServer({ entitlements: {} });
228
+ const resp = await server.get('/onesub/entitlement?userId=u&id=premium');
229
+ expect(resp.status).toBe(404);
230
+ });
231
+ });
232
+ // ─────────────────────────────────────────────────────────────────────────────
233
+ // Store regression: getAllByUserId returns multi-product subs
234
+ // ─────────────────────────────────────────────────────────────────────────────
235
+ describe('InMemorySubscriptionStore — multi-sub per user (entitlement support)', () => {
236
+ it('getAllByUserId returns all subs across productIds, latest first', async () => {
237
+ const store = new InMemorySubscriptionStore();
238
+ await store.save(sub({ userId: 'u', productId: 'pro_monthly', originalTransactionId: 'orig_a' }));
239
+ await store.save(sub({ userId: 'u', productId: 'pro_yearly', originalTransactionId: 'orig_b' }));
240
+ await store.save(sub({ userId: 'u', productId: 'addon', originalTransactionId: 'orig_c' }));
241
+ const all = await store.getAllByUserId('u');
242
+ expect(all.map((s) => s.productId)).toEqual(['addon', 'pro_yearly', 'pro_monthly']);
243
+ });
244
+ it('getByUserId still returns the most recent (legacy contract preserved)', async () => {
245
+ const store = new InMemorySubscriptionStore();
246
+ await store.save(sub({ userId: 'u', productId: 'pro_monthly', originalTransactionId: 'orig_a' }));
247
+ await store.save(sub({ userId: 'u', productId: 'pro_yearly', originalTransactionId: 'orig_b' }));
248
+ const latest = await store.getByUserId('u');
249
+ expect(latest?.productId).toBe('pro_yearly');
250
+ });
251
+ it('save() with same originalTransactionId replaces prior record (no duplicates)', async () => {
252
+ const store = new InMemorySubscriptionStore();
253
+ await store.save(sub({
254
+ userId: 'u', productId: 'pro_monthly', originalTransactionId: 'orig_x',
255
+ status: 'active',
256
+ }));
257
+ await store.save(sub({
258
+ userId: 'u', productId: 'pro_monthly', originalTransactionId: 'orig_x',
259
+ status: 'canceled',
260
+ }));
261
+ const all = await store.getAllByUserId('u');
262
+ expect(all).toHaveLength(1);
263
+ expect(all[0].status).toBe(SUBSCRIPTION_STATUS.CANCELED);
264
+ });
265
+ });
266
+ // keep the import honest
267
+ PURCHASE_TYPE;
268
+ //# sourceMappingURL=entitlements.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlements.test.js","sourceRoot":"","sources":["../../src/__tests__/entitlements.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,173 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import request from 'supertest';
3
+ import express from 'express';
4
+ import { ONESUB_ERROR_CODE } from '@onesub/shared';
5
+ import { createOneSubMiddleware } from '../index.js';
6
+ import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
7
+ /**
8
+ * HTTP-level tests: the errorCode field is present on every 4xx/5xx response,
9
+ * and the value matches the canonical `ONESUB_ERROR_CODE` enum. Clients can
10
+ * rely on `errorCode` to drive programmatic branching regardless of the
11
+ * human-readable `error` string.
12
+ */
13
+ describe('server error codes', () => {
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ let app;
16
+ beforeEach(() => {
17
+ const config = {
18
+ database: { url: '' },
19
+ adminSecret: 'test-admin-secret',
20
+ };
21
+ app = express();
22
+ app.use(createOneSubMiddleware({
23
+ ...config,
24
+ store: new InMemorySubscriptionStore(),
25
+ purchaseStore: new InMemoryPurchaseStore(),
26
+ }));
27
+ });
28
+ // ── /onesub/validate ──────────────────────────────────────────
29
+ describe('POST /onesub/validate', () => {
30
+ it('invalid body → errorCode: INVALID_INPUT', async () => {
31
+ const res = await request(app).post('/onesub/validate').send({ platform: 'not-valid' });
32
+ expect(res.status).toBe(400);
33
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.INVALID_INPUT);
34
+ });
35
+ it('apple config missing → errorCode: APPLE_CONFIG_MISSING', async () => {
36
+ const res = await request(app).post('/onesub/validate').send({
37
+ platform: 'apple',
38
+ receipt: 'dummy',
39
+ userId: 'u1',
40
+ productId: 'p1',
41
+ });
42
+ expect(res.status).toBe(500);
43
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING);
44
+ });
45
+ it('google config missing → errorCode: GOOGLE_CONFIG_MISSING', async () => {
46
+ const res = await request(app).post('/onesub/validate').send({
47
+ platform: 'google',
48
+ receipt: 'dummy',
49
+ userId: 'u1',
50
+ productId: 'p1',
51
+ });
52
+ expect(res.status).toBe(500);
53
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.GOOGLE_CONFIG_MISSING);
54
+ });
55
+ });
56
+ // ── /onesub/status ────────────────────────────────────────────
57
+ describe('GET /onesub/status', () => {
58
+ it('missing userId → errorCode: INVALID_INPUT', async () => {
59
+ const res = await request(app).get('/onesub/status');
60
+ expect(res.status).toBe(400);
61
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.INVALID_INPUT);
62
+ });
63
+ it('oversize userId → errorCode: USER_ID_TOO_LONG', async () => {
64
+ const res = await request(app).get('/onesub/status').query({ userId: 'x'.repeat(300) });
65
+ expect(res.status).toBe(400);
66
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.USER_ID_TOO_LONG);
67
+ });
68
+ });
69
+ // ── /onesub/purchase/validate ────────────────────────────────
70
+ describe('POST /onesub/purchase/validate', () => {
71
+ it('invalid body → errorCode: INVALID_INPUT', async () => {
72
+ const res = await request(app).post('/onesub/purchase/validate').send({});
73
+ expect(res.status).toBe(400);
74
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.INVALID_INPUT);
75
+ });
76
+ it('non-consumable already owned → 200 idempotent restore with recorded transactionId', async () => {
77
+ // Pre-seed the store with a purchase
78
+ const purchaseStore = new InMemoryPurchaseStore();
79
+ await purchaseStore.savePurchase({
80
+ transactionId: 'tx_1',
81
+ userId: 'u1',
82
+ productId: 'premium',
83
+ platform: 'apple',
84
+ type: 'non_consumable',
85
+ quantity: 1,
86
+ purchasedAt: new Date().toISOString(),
87
+ });
88
+ const app2 = express();
89
+ app2.use(createOneSubMiddleware({
90
+ database: { url: '' },
91
+ apple: { bundleId: 'com.test' },
92
+ store: new InMemorySubscriptionStore(),
93
+ purchaseStore,
94
+ }));
95
+ const res = await request(app2).post('/onesub/purchase/validate').send({
96
+ platform: 'apple',
97
+ receipt: 'dummy',
98
+ userId: 'u1',
99
+ productId: 'premium',
100
+ type: 'non_consumable',
101
+ });
102
+ // Owning a non-consumable IS success. The server returns the recorded
103
+ // purchase as a `restored` result — crucially WITH its transactionId — so
104
+ // downstream consumers can re-entitle instead of receiving a 409 + null.
105
+ expect(res.status).toBe(200);
106
+ expect(res.body.valid).toBe(true);
107
+ expect(res.body.action).toBe('restored');
108
+ expect(res.body.purchase.transactionId).toBe('tx_1');
109
+ });
110
+ });
111
+ // ── /onesub/webhook/apple ────────────────────────────────────
112
+ describe('POST /onesub/webhook/apple', () => {
113
+ it('missing signedPayload → errorCode: MISSING_SIGNED_PAYLOAD', async () => {
114
+ const res = await request(app).post('/onesub/webhook/apple').send({});
115
+ expect(res.status).toBe(400);
116
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.MISSING_SIGNED_PAYLOAD);
117
+ });
118
+ it('invalid signedPayload → errorCode: INVALID_SIGNED_PAYLOAD', async () => {
119
+ const res = await request(app).post('/onesub/webhook/apple').send({ signedPayload: 'not-a-jws' });
120
+ expect(res.status).toBe(400);
121
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.INVALID_SIGNED_PAYLOAD);
122
+ });
123
+ });
124
+ // ── /onesub/webhook/google ───────────────────────────────────
125
+ describe('POST /onesub/webhook/google', () => {
126
+ it('missing message.data → errorCode: MISSING_MESSAGE_DATA', async () => {
127
+ const res = await request(app).post('/onesub/webhook/google').send({});
128
+ expect(res.status).toBe(400);
129
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.MISSING_MESSAGE_DATA);
130
+ });
131
+ });
132
+ // ── /onesub/purchase/admin/* ─────────────────────────────────
133
+ describe('admin routes', () => {
134
+ it('missing admin secret → errorCode: INVALID_ADMIN_SECRET', async () => {
135
+ const res = await request(app).delete('/onesub/purchase/admin/u1/premium');
136
+ expect(res.status).toBe(401);
137
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.INVALID_ADMIN_SECRET);
138
+ });
139
+ it('admin auth middleware does NOT intercept non-admin routes on the host app', async () => {
140
+ // Regression: before the fix, `router.use(authMiddleware)` without a path
141
+ // prefix caught every request routed through createOneSubMiddleware,
142
+ // so host-app routes like /health returned 401.
143
+ const appWithHealth = express();
144
+ appWithHealth.use(createOneSubMiddleware({
145
+ database: { url: '' },
146
+ adminSecret: 'test-admin-secret',
147
+ store: new InMemorySubscriptionStore(),
148
+ purchaseStore: new InMemoryPurchaseStore(),
149
+ }));
150
+ appWithHealth.get('/health', (_req, res) => res.json({ ok: true }));
151
+ const res = await request(appWithHealth).get('/health');
152
+ expect(res.status).toBe(200);
153
+ expect(res.body.ok).toBe(true);
154
+ });
155
+ it('transfer with unknown tx → errorCode: TRANSACTION_NOT_FOUND', async () => {
156
+ const res = await request(app)
157
+ .post('/onesub/purchase/admin/transfer')
158
+ .set('x-admin-secret', 'test-admin-secret')
159
+ .send({ transactionId: 'ghost', newUserId: 'new_user' });
160
+ expect(res.status).toBe(404);
161
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND);
162
+ });
163
+ it('grant with bad body → errorCode: INVALID_INPUT', async () => {
164
+ const res = await request(app)
165
+ .post('/onesub/purchase/admin/grant')
166
+ .set('x-admin-secret', 'test-admin-secret')
167
+ .send({});
168
+ expect(res.status).toBe(400);
169
+ expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.INVALID_INPUT);
170
+ });
171
+ });
172
+ });
173
+ //# sourceMappingURL=error-codes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-codes.test.js","sourceRoot":"","sources":["../../src/__tests__/error-codes.test.ts"],"names":[],"mappings":"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"}