@onesub/server 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/google-subscriptions-v2.test.d.ts +1 -1
- package/dist/__tests__/receipt-age.test.d.ts +7 -0
- package/dist/__tests__/receipt-age.test.d.ts.map +1 -0
- package/dist/__tests__/tracing.test.d.ts +2 -0
- package/dist/__tests__/tracing.test.d.ts.map +1 -0
- package/dist/cache.d.ts +5 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/index.cjs +178 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +178 -55
- package/dist/index.js.map +1 -1
- package/dist/providers/apple.d.ts.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/mock.d.ts.map +1 -1
- package/dist/routes/admin.d.ts.map +1 -1
- package/dist/routes/purchase.d.ts.map +1 -1
- package/dist/routes/validate.d.ts.map +1 -1
- package/dist/routes/webhook-apple.d.ts.map +1 -1
- package/dist/routes/webhook-google.d.ts +1 -1
- package/dist/routes/webhook-google.d.ts.map +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/stores/postgres.d.ts.map +1 -1
- package/dist/stores/redis.d.ts +1 -0
- package/dist/stores/redis.d.ts.map +1 -1
- package/dist/tracing.d.ts.map +1 -1
- package/dist/webhook-events.d.ts +10 -0
- package/dist/webhook-events.d.ts.map +1 -1
- package/package.json +5 -3
- package/dist/__tests__/admin-customer-detail.test.js +0 -146
- package/dist/__tests__/admin-customer-detail.test.js.map +0 -1
- package/dist/__tests__/admin-list-subscriptions.test.js +0 -192
- package/dist/__tests__/admin-list-subscriptions.test.js.map +0 -1
- package/dist/__tests__/admin-subscription-detail.test.js +0 -112
- package/dist/__tests__/admin-subscription-detail.test.js.map +0 -1
- package/dist/__tests__/apple-root-ca.test.js +0 -41
- package/dist/__tests__/apple-root-ca.test.js.map +0 -1
- package/dist/__tests__/apple-status-api.test.js +0 -345
- package/dist/__tests__/apple-status-api.test.js.map +0 -1
- package/dist/__tests__/cache.test.js +0 -34
- package/dist/__tests__/cache.test.js.map +0 -1
- package/dist/__tests__/entitlements.test.js +0 -268
- package/dist/__tests__/entitlements.test.js.map +0 -1
- package/dist/__tests__/error-codes.test.js +0 -173
- package/dist/__tests__/error-codes.test.js.map +0 -1
- package/dist/__tests__/fetch-hardening.test.js +0 -192
- package/dist/__tests__/fetch-hardening.test.js.map +0 -1
- package/dist/__tests__/google-paused.test.js +0 -126
- package/dist/__tests__/google-paused.test.js.map +0 -1
- package/dist/__tests__/google-price-change.test.js +0 -186
- package/dist/__tests__/google-price-change.test.js.map +0 -1
- package/dist/__tests__/google-subscriptions-v2.test.js +0 -206
- package/dist/__tests__/google-subscriptions-v2.test.js.map +0 -1
- package/dist/__tests__/lifecycle-scenarios.test.js +0 -579
- package/dist/__tests__/lifecycle-scenarios.test.js.map +0 -1
- package/dist/__tests__/lifecycle-states.test.js +0 -423
- package/dist/__tests__/lifecycle-states.test.js.map +0 -1
- package/dist/__tests__/linked-purchase-token.test.js +0 -227
- package/dist/__tests__/linked-purchase-token.test.js.map +0 -1
- package/dist/__tests__/metrics.test.js +0 -340
- package/dist/__tests__/metrics.test.js.map +0 -1
- package/dist/__tests__/mock-provider.test.js +0 -188
- package/dist/__tests__/mock-provider.test.js.map +0 -1
- package/dist/__tests__/openapi.test.js +0 -32
- package/dist/__tests__/openapi.test.js.map +0 -1
- package/dist/__tests__/paused-auto-resume.test.js +0 -162
- package/dist/__tests__/paused-auto-resume.test.js.map +0 -1
- package/dist/__tests__/providers.test.js +0 -321
- package/dist/__tests__/providers.test.js.map +0 -1
- package/dist/__tests__/redis-store.test.js +0 -166
- package/dist/__tests__/redis-store.test.js.map +0 -1
- package/dist/__tests__/refund-policy.test.js +0 -317
- package/dist/__tests__/refund-policy.test.js.map +0 -1
- package/dist/__tests__/schema.test.js +0 -46
- package/dist/__tests__/schema.test.js.map +0 -1
- package/dist/__tests__/store.test.js +0 -172
- package/dist/__tests__/store.test.js.map +0 -1
- package/dist/__tests__/test-utils.js +0 -30
- package/dist/__tests__/test-utils.js.map +0 -1
- package/dist/__tests__/webhook-apple.test.js +0 -247
- package/dist/__tests__/webhook-apple.test.js.map +0 -1
- package/dist/__tests__/webhook-events.test.js +0 -36
- package/dist/__tests__/webhook-events.test.js.map +0 -1
- package/dist/__tests__/webhook-google.test.js +0 -255
- package/dist/__tests__/webhook-google.test.js.map +0 -1
- package/dist/__tests__/webhook-queue.test.js +0 -26
- package/dist/__tests__/webhook-queue.test.js.map +0 -1
- package/dist/__tests__/webhook-refund.test.js +0 -313
- package/dist/__tests__/webhook-refund.test.js.map +0 -1
- package/dist/cache.js +0 -39
- package/dist/cache.js.map +0 -1
- package/dist/errors.js +0 -19
- package/dist/errors.js.map +0 -1
- package/dist/http.js +0 -36
- package/dist/http.js.map +0 -1
- package/dist/logger.js +0 -20
- package/dist/logger.js.map +0 -1
- package/dist/openapi.js +0 -196
- package/dist/openapi.js.map +0 -1
- package/dist/providers/apple-root-ca.js +0 -38
- package/dist/providers/apple-root-ca.js.map +0 -1
- package/dist/providers/apple.js +0 -630
- package/dist/providers/apple.js.map +0 -1
- package/dist/providers/google.js +0 -564
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/mock.js +0 -93
- package/dist/providers/mock.js.map +0 -1
- package/dist/routes/admin.js +0 -327
- package/dist/routes/admin.js.map +0 -1
- package/dist/routes/apple-offer.js +0 -72
- package/dist/routes/apple-offer.js.map +0 -1
- package/dist/routes/entitlements.js +0 -130
- package/dist/routes/entitlements.js.map +0 -1
- package/dist/routes/metrics.js +0 -281
- package/dist/routes/metrics.js.map +0 -1
- package/dist/routes/purchase.js +0 -204
- package/dist/routes/purchase.js.map +0 -1
- package/dist/routes/status.js +0 -55
- package/dist/routes/status.js.map +0 -1
- package/dist/routes/validate.js +0 -70
- package/dist/routes/validate.js.map +0 -1
- package/dist/routes/webhook-apple.js +0 -145
- package/dist/routes/webhook-apple.js.map +0 -1
- package/dist/routes/webhook-google.js +0 -204
- package/dist/routes/webhook-google.js.map +0 -1
- package/dist/routes/webhook.js +0 -19
- package/dist/routes/webhook.js.map +0 -1
- package/dist/store.js +0 -145
- package/dist/store.js.map +0 -1
- package/dist/stores/postgres.js +0 -329
- package/dist/stores/postgres.js.map +0 -1
- package/dist/stores/redis.js +0 -297
- package/dist/stores/redis.js.map +0 -1
- package/dist/stores/schema.js +0 -67
- package/dist/stores/schema.js.map +0 -1
- package/dist/tracing.js +0 -74
- package/dist/tracing.js.map +0 -1
- package/dist/webhook-events.js +0 -58
- package/dist/webhook-events.js.map +0 -1
- package/dist/webhook-queue.js +0 -108
- package/dist/webhook-queue.js.map +0 -1
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for OneSubServerConfig.refundPolicy.
|
|
3
|
-
*
|
|
4
|
-
* Covers:
|
|
5
|
-
* - Default ('immediate'): subscription REFUND/REVOKE/voided sets status=canceled
|
|
6
|
-
* - 'until_expiry': keeps status + expiresAt, only flips willRenew=false
|
|
7
|
-
* - IAP refund is unaffected by refundPolicy (always immediate)
|
|
8
|
-
* - status route's stale-record check (active && expiresAt > now)
|
|
9
|
-
*/
|
|
10
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
11
|
-
import express from 'express';
|
|
12
|
-
import { SUBSCRIPTION_STATUS } from '@onesub/shared';
|
|
13
|
-
import { createWebhookRouter } from '../routes/webhook.js';
|
|
14
|
-
import { createStatusRouter } from '../routes/status.js';
|
|
15
|
-
import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
|
|
16
|
-
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
17
|
-
function makeJws(payload) {
|
|
18
|
-
const header = Buffer.from(JSON.stringify({ alg: 'ES256' })).toString('base64url');
|
|
19
|
-
const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
|
|
20
|
-
return `${header}.${body}.fakesig`;
|
|
21
|
-
}
|
|
22
|
-
function spinUp(handler) {
|
|
23
|
-
return {
|
|
24
|
-
async request(method, path, body) {
|
|
25
|
-
const server = handler.listen(0);
|
|
26
|
-
const address = server.address();
|
|
27
|
-
const port = typeof address === 'object' && address ? address.port : 0;
|
|
28
|
-
try {
|
|
29
|
-
const url = `http://127.0.0.1:${port}${path}`;
|
|
30
|
-
const init = { method };
|
|
31
|
-
if (body !== undefined) {
|
|
32
|
-
init.headers = { 'Content-Type': 'application/json' };
|
|
33
|
-
init.body = JSON.stringify(body);
|
|
34
|
-
}
|
|
35
|
-
const resp = await fetch(url, init);
|
|
36
|
-
const text = await resp.text();
|
|
37
|
-
let parsed = text;
|
|
38
|
-
try {
|
|
39
|
-
parsed = JSON.parse(text);
|
|
40
|
-
}
|
|
41
|
-
catch { /* keep as text */ }
|
|
42
|
-
return { status: resp.status, body: parsed };
|
|
43
|
-
}
|
|
44
|
-
finally {
|
|
45
|
-
await new Promise((resolve) => server.close(() => resolve()));
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function buildWebhookServer(config) {
|
|
51
|
-
const store = new InMemorySubscriptionStore();
|
|
52
|
-
const purchaseStore = new InMemoryPurchaseStore();
|
|
53
|
-
const app = express();
|
|
54
|
-
app.use(express.json());
|
|
55
|
-
app.use(createWebhookRouter(config, store, purchaseStore));
|
|
56
|
-
return { store, purchaseStore, server: spinUp(app) };
|
|
57
|
-
}
|
|
58
|
-
function buildStatusServer() {
|
|
59
|
-
const store = new InMemorySubscriptionStore();
|
|
60
|
-
const app = express();
|
|
61
|
-
app.use(express.json());
|
|
62
|
-
app.use(createStatusRouter(store));
|
|
63
|
-
return { store, server: spinUp(app) };
|
|
64
|
-
}
|
|
65
|
-
const futureExpiry = '2099-01-01T00:00:00.000Z';
|
|
66
|
-
const pastExpiry = '2024-01-01T00:00:00.000Z';
|
|
67
|
-
const sampleSub = (overrides) => ({
|
|
68
|
-
userId: 'user_x',
|
|
69
|
-
productId: 'pro_monthly',
|
|
70
|
-
platform: 'apple',
|
|
71
|
-
status: 'active',
|
|
72
|
-
expiresAt: futureExpiry,
|
|
73
|
-
originalTransactionId: 'orig_x',
|
|
74
|
-
purchasedAt: '2026-01-01T00:00:00.000Z',
|
|
75
|
-
willRenew: true,
|
|
76
|
-
...overrides,
|
|
77
|
-
});
|
|
78
|
-
function appleSubRefundPayload(originalTransactionId) {
|
|
79
|
-
const signedTransactionInfo = makeJws({
|
|
80
|
-
bundleId: 'com.example.app',
|
|
81
|
-
type: 'Auto-Renewable Subscription',
|
|
82
|
-
productId: 'pro_monthly',
|
|
83
|
-
transactionId: 'tx_refund',
|
|
84
|
-
originalTransactionId,
|
|
85
|
-
purchaseDate: Date.now() - 30 * 86400000,
|
|
86
|
-
expiresDate: Date.now() + 30 * 86400000,
|
|
87
|
-
});
|
|
88
|
-
return {
|
|
89
|
-
signedPayload: makeJws({
|
|
90
|
-
notificationType: 'REFUND',
|
|
91
|
-
data: { signedTransactionInfo, signedRenewalInfo: makeJws({}) },
|
|
92
|
-
}),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function appleConsumableRefundPayload(transactionId) {
|
|
96
|
-
const signedTransactionInfo = makeJws({
|
|
97
|
-
bundleId: 'com.example.app',
|
|
98
|
-
type: 'Consumable',
|
|
99
|
-
productId: 'credits_100',
|
|
100
|
-
transactionId,
|
|
101
|
-
originalTransactionId: transactionId,
|
|
102
|
-
purchaseDate: Date.now(),
|
|
103
|
-
});
|
|
104
|
-
return {
|
|
105
|
-
signedPayload: makeJws({
|
|
106
|
-
notificationType: 'REFUND',
|
|
107
|
-
data: { signedTransactionInfo, signedRenewalInfo: makeJws({}) },
|
|
108
|
-
}),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function googleVoidedSubPayload(purchaseToken) {
|
|
112
|
-
const json = JSON.stringify({
|
|
113
|
-
version: '1.0',
|
|
114
|
-
packageName: 'com.example.app',
|
|
115
|
-
eventTimeMillis: String(Date.now()),
|
|
116
|
-
voidedPurchaseNotification: {
|
|
117
|
-
purchaseToken,
|
|
118
|
-
orderId: 'GPA.refunded',
|
|
119
|
-
productType: 1,
|
|
120
|
-
refundType: 1,
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
return {
|
|
124
|
-
message: { data: Buffer.from(json).toString('base64'), messageId: '1' },
|
|
125
|
-
subscription: 's',
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
const appleConfig = {
|
|
129
|
-
bundleId: 'com.example.app',
|
|
130
|
-
skipJwsVerification: true,
|
|
131
|
-
};
|
|
132
|
-
const googleConfig = {
|
|
133
|
-
packageName: 'com.example.app',
|
|
134
|
-
};
|
|
135
|
-
// ── refundPolicy default ('immediate') ──────────────────────────────────────
|
|
136
|
-
describe('refundPolicy default (immediate) — Apple subscription REFUND', () => {
|
|
137
|
-
let store;
|
|
138
|
-
let server;
|
|
139
|
-
beforeEach(() => {
|
|
140
|
-
const built = buildWebhookServer({ apple: appleConfig, database: { url: '' } });
|
|
141
|
-
store = built.store;
|
|
142
|
-
server = built.server;
|
|
143
|
-
});
|
|
144
|
-
it('marks status=canceled on REFUND', async () => {
|
|
145
|
-
await store.save(sampleSub({ originalTransactionId: 'orig_immed' }));
|
|
146
|
-
const resp = await server.request('POST', '/onesub/webhook/apple', appleSubRefundPayload('orig_immed'));
|
|
147
|
-
expect(resp.status).toBe(200);
|
|
148
|
-
expect((await store.getByTransactionId('orig_immed'))?.status).toBe(SUBSCRIPTION_STATUS.CANCELED);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
describe('refundPolicy default (immediate) — Google voided subscription', () => {
|
|
152
|
-
it('marks status=canceled on voidedPurchaseNotification productType=1', async () => {
|
|
153
|
-
const { store, server } = buildWebhookServer({ google: googleConfig, database: { url: '' } });
|
|
154
|
-
await store.save(sampleSub({ platform: 'google', originalTransactionId: 'tok_immed' }));
|
|
155
|
-
const resp = await server.request('POST', '/onesub/webhook/google', googleVoidedSubPayload('tok_immed'));
|
|
156
|
-
expect(resp.status).toBe(200);
|
|
157
|
-
expect((await store.getByTransactionId('tok_immed'))?.status).toBe(SUBSCRIPTION_STATUS.CANCELED);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
// ── refundPolicy 'until_expiry' ─────────────────────────────────────────────
|
|
161
|
-
describe("refundPolicy 'until_expiry' — Apple subscription REFUND", () => {
|
|
162
|
-
let store;
|
|
163
|
-
let server;
|
|
164
|
-
beforeEach(() => {
|
|
165
|
-
const built = buildWebhookServer({
|
|
166
|
-
apple: appleConfig,
|
|
167
|
-
database: { url: '' },
|
|
168
|
-
refundPolicy: 'until_expiry',
|
|
169
|
-
});
|
|
170
|
-
store = built.store;
|
|
171
|
-
server = built.server;
|
|
172
|
-
});
|
|
173
|
-
it('keeps status=active and expiresAt; only flips willRenew=false', async () => {
|
|
174
|
-
const original = sampleSub({
|
|
175
|
-
originalTransactionId: 'orig_keep',
|
|
176
|
-
status: 'active',
|
|
177
|
-
expiresAt: futureExpiry,
|
|
178
|
-
willRenew: true,
|
|
179
|
-
});
|
|
180
|
-
await store.save(original);
|
|
181
|
-
await server.request('POST', '/onesub/webhook/apple', appleSubRefundPayload('orig_keep'));
|
|
182
|
-
const updated = await store.getByTransactionId('orig_keep');
|
|
183
|
-
expect(updated?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
184
|
-
expect(updated?.expiresAt).toBe(futureExpiry);
|
|
185
|
-
expect(updated?.willRenew).toBe(false);
|
|
186
|
-
});
|
|
187
|
-
it('still routes Apple REVOKE the same way (refund-class signal)', async () => {
|
|
188
|
-
await store.save(sampleSub({ originalTransactionId: 'orig_revoke' }));
|
|
189
|
-
const signedTransactionInfo = makeJws({
|
|
190
|
-
bundleId: 'com.example.app',
|
|
191
|
-
type: 'Auto-Renewable Subscription',
|
|
192
|
-
productId: 'pro_monthly',
|
|
193
|
-
transactionId: 'tx_revoke',
|
|
194
|
-
originalTransactionId: 'orig_revoke',
|
|
195
|
-
purchaseDate: Date.now(),
|
|
196
|
-
expiresDate: Date.now() + 86400000,
|
|
197
|
-
});
|
|
198
|
-
const signedPayload = makeJws({
|
|
199
|
-
notificationType: 'REVOKE',
|
|
200
|
-
data: { signedTransactionInfo, signedRenewalInfo: makeJws({}) },
|
|
201
|
-
});
|
|
202
|
-
await server.request('POST', '/onesub/webhook/apple', { signedPayload });
|
|
203
|
-
const updated = await store.getByTransactionId('orig_revoke');
|
|
204
|
-
expect(updated?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
205
|
-
expect(updated?.willRenew).toBe(false);
|
|
206
|
-
});
|
|
207
|
-
it('still applies normal expiry mapping for non-refund notifications (DID_RENEW)', async () => {
|
|
208
|
-
await store.save(sampleSub({ originalTransactionId: 'orig_renew', willRenew: false }));
|
|
209
|
-
const signedTransactionInfo = makeJws({
|
|
210
|
-
bundleId: 'com.example.app',
|
|
211
|
-
type: 'Auto-Renewable Subscription',
|
|
212
|
-
productId: 'pro_monthly',
|
|
213
|
-
transactionId: 'tx_renew',
|
|
214
|
-
originalTransactionId: 'orig_renew',
|
|
215
|
-
purchaseDate: Date.now(),
|
|
216
|
-
expiresDate: Date.now() + 30 * 86400000,
|
|
217
|
-
});
|
|
218
|
-
const signedPayload = makeJws({
|
|
219
|
-
notificationType: 'DID_RENEW',
|
|
220
|
-
data: { signedTransactionInfo, signedRenewalInfo: makeJws({ autoRenewStatus: 1 }) },
|
|
221
|
-
});
|
|
222
|
-
await server.request('POST', '/onesub/webhook/apple', { signedPayload });
|
|
223
|
-
const updated = await store.getByTransactionId('orig_renew');
|
|
224
|
-
expect(updated?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
225
|
-
// willRenew should reflect the renewal info (true), not stay false
|
|
226
|
-
expect(updated?.willRenew).toBe(true);
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
describe("refundPolicy 'until_expiry' — Google voided subscription", () => {
|
|
230
|
-
it('keeps status=active and only flips willRenew=false', async () => {
|
|
231
|
-
const { store, server } = buildWebhookServer({
|
|
232
|
-
google: googleConfig,
|
|
233
|
-
database: { url: '' },
|
|
234
|
-
refundPolicy: 'until_expiry',
|
|
235
|
-
});
|
|
236
|
-
await store.save(sampleSub({
|
|
237
|
-
platform: 'google',
|
|
238
|
-
originalTransactionId: 'tok_keep',
|
|
239
|
-
status: 'active',
|
|
240
|
-
willRenew: true,
|
|
241
|
-
}));
|
|
242
|
-
await server.request('POST', '/onesub/webhook/google', googleVoidedSubPayload('tok_keep'));
|
|
243
|
-
const updated = await store.getByTransactionId('tok_keep');
|
|
244
|
-
expect(updated?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
245
|
-
expect(updated?.willRenew).toBe(false);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
// ── IAP refund is unaffected by refundPolicy ────────────────────────────────
|
|
249
|
-
describe('refundPolicy does NOT apply to one-time IAP refunds', () => {
|
|
250
|
-
const iap = (overrides) => ({
|
|
251
|
-
userId: 'u',
|
|
252
|
-
productId: 'credits_100',
|
|
253
|
-
platform: 'apple',
|
|
254
|
-
type: 'consumable',
|
|
255
|
-
transactionId: 'iap_tx',
|
|
256
|
-
purchasedAt: '2026-01-01T00:00:00.000Z',
|
|
257
|
-
quantity: 1,
|
|
258
|
-
...overrides,
|
|
259
|
-
});
|
|
260
|
-
it("Apple IAP REFUND removes the row even when refundPolicy='until_expiry'", async () => {
|
|
261
|
-
const { purchaseStore, server } = buildWebhookServer({
|
|
262
|
-
apple: appleConfig,
|
|
263
|
-
database: { url: '' },
|
|
264
|
-
refundPolicy: 'until_expiry',
|
|
265
|
-
});
|
|
266
|
-
await purchaseStore.savePurchase(iap({ transactionId: 'iap_keep_test' }));
|
|
267
|
-
await server.request('POST', '/onesub/webhook/apple', appleConsumableRefundPayload('iap_keep_test'));
|
|
268
|
-
expect(await purchaseStore.getPurchaseByTransactionId('iap_keep_test')).toBeNull();
|
|
269
|
-
});
|
|
270
|
-
it("Google voided IAP (productType=2) deletes the row even when refundPolicy='until_expiry'", async () => {
|
|
271
|
-
const { purchaseStore, server } = buildWebhookServer({
|
|
272
|
-
google: googleConfig,
|
|
273
|
-
database: { url: '' },
|
|
274
|
-
refundPolicy: 'until_expiry',
|
|
275
|
-
});
|
|
276
|
-
await purchaseStore.savePurchase(iap({ platform: 'google', transactionId: 'GPA.iap_keep' }));
|
|
277
|
-
const json = JSON.stringify({
|
|
278
|
-
version: '1.0',
|
|
279
|
-
packageName: 'com.example.app',
|
|
280
|
-
eventTimeMillis: String(Date.now()),
|
|
281
|
-
voidedPurchaseNotification: {
|
|
282
|
-
purchaseToken: 'tok',
|
|
283
|
-
orderId: 'GPA.iap_keep',
|
|
284
|
-
productType: 2,
|
|
285
|
-
refundType: 1,
|
|
286
|
-
},
|
|
287
|
-
});
|
|
288
|
-
await server.request('POST', '/onesub/webhook/google', {
|
|
289
|
-
message: { data: Buffer.from(json).toString('base64'), messageId: '1' },
|
|
290
|
-
subscription: 's',
|
|
291
|
-
});
|
|
292
|
-
expect(await purchaseStore.getPurchaseByTransactionId('GPA.iap_keep')).toBeNull();
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
// ── status route stale-record safety ────────────────────────────────────────
|
|
296
|
-
describe('status route — stale active record', () => {
|
|
297
|
-
it('returns active=false when status=active but expiresAt is in the past', async () => {
|
|
298
|
-
const { store, server } = buildStatusServer();
|
|
299
|
-
await store.save(sampleSub({ userId: 'u_stale', status: 'active', expiresAt: pastExpiry }));
|
|
300
|
-
const resp = await server.request('GET', '/onesub/status?userId=u_stale');
|
|
301
|
-
expect(resp.status).toBe(200);
|
|
302
|
-
expect(resp.body.active).toBe(false);
|
|
303
|
-
});
|
|
304
|
-
it('returns active=true when status=active and expiresAt is in the future (normal case)', async () => {
|
|
305
|
-
const { store, server } = buildStatusServer();
|
|
306
|
-
await store.save(sampleSub({ userId: 'u_ok', status: 'active', expiresAt: futureExpiry }));
|
|
307
|
-
const resp = await server.request('GET', '/onesub/status?userId=u_ok');
|
|
308
|
-
expect(resp.body.active).toBe(true);
|
|
309
|
-
});
|
|
310
|
-
it('returns active=false for grace_period record past expiresAt', async () => {
|
|
311
|
-
const { store, server } = buildStatusServer();
|
|
312
|
-
await store.save(sampleSub({ userId: 'u_grace_stale', status: 'grace_period', expiresAt: pastExpiry }));
|
|
313
|
-
const resp = await server.request('GET', '/onesub/status?userId=u_grace_stale');
|
|
314
|
-
expect(resp.body.active).toBe(false);
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
//# sourceMappingURL=refund-policy.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"refund-policy.test.js","sourceRoot":"","sources":["../../src/__tests__/refund-policy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { dirname, join } from 'node:path';
|
|
4
|
-
import { describe, it, expect } from 'vitest';
|
|
5
|
-
import { SUBSCRIPTIONS_SCHEMA_SQL, PURCHASES_SCHEMA_SQL } from '../stores/schema.js';
|
|
6
|
-
/**
|
|
7
|
-
* Parity check between the canonical SQL file (sql/schema.sql) and the
|
|
8
|
-
* embedded string constants used by initSchema() at runtime. If this fails,
|
|
9
|
-
* one of the two was edited without the other.
|
|
10
|
-
*/
|
|
11
|
-
describe('schema SQL parity', () => {
|
|
12
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
// __tests__ lives under src/, so sql/schema.sql is three levels up.
|
|
14
|
-
const schemaPath = join(here, '..', '..', 'sql', 'schema.sql');
|
|
15
|
-
const sqlFile = readFileSync(schemaPath, 'utf8');
|
|
16
|
-
// Normalize both sides to compare by semantics rather than whitespace:
|
|
17
|
-
// strip SQL line comments, collapse whitespace, lowercase.
|
|
18
|
-
// The leading \r normalisation matters on Windows checkouts (core.autocrlf):
|
|
19
|
-
// without it, `--.*$` fails to match because `$` doesn't see `\r` as a line
|
|
20
|
-
// terminator, leaving comments in place and breaking the parity check.
|
|
21
|
-
const normalize = (sql) => sql
|
|
22
|
-
.replace(/\r/g, '')
|
|
23
|
-
.split('\n')
|
|
24
|
-
.map((l) => l.replace(/--.*$/, ''))
|
|
25
|
-
.join('\n')
|
|
26
|
-
.replace(/\s+/g, ' ')
|
|
27
|
-
.trim()
|
|
28
|
-
.toLowerCase();
|
|
29
|
-
it('contains both table DDLs in the canonical .sql file', () => {
|
|
30
|
-
const n = normalize(sqlFile);
|
|
31
|
-
expect(n).toContain('create table if not exists onesub_subscriptions');
|
|
32
|
-
expect(n).toContain('create table if not exists onesub_purchases');
|
|
33
|
-
});
|
|
34
|
-
it('subscriptions DDL constant appears verbatim in schema.sql', () => {
|
|
35
|
-
expect(normalize(sqlFile)).toContain(normalize(SUBSCRIPTIONS_SCHEMA_SQL));
|
|
36
|
-
});
|
|
37
|
-
it('purchases DDL constant appears verbatim in schema.sql', () => {
|
|
38
|
-
expect(normalize(sqlFile)).toContain(normalize(PURCHASES_SCHEMA_SQL));
|
|
39
|
-
});
|
|
40
|
-
it('non-consumable partial unique index is present (0.2.0 semantics)', () => {
|
|
41
|
-
const n = normalize(sqlFile);
|
|
42
|
-
expect(n).toContain('create unique index if not exists idx_onesub_purchases_non_consumable');
|
|
43
|
-
expect(n).toContain("where type = 'non_consumable'");
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
//# sourceMappingURL=schema.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../../src/__tests__/schema.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAErF;;;;GAIG;AACH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,oEAAoE;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAEjD,uEAAuE;IACvE,2DAA2D;IAC3D,6EAA6E;IAC7E,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE,CAChC,GAAG;SACA,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;SAClC,IAAI,CAAC,IAAI,CAAC;SACV,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE;SACN,WAAW,EAAE,CAAC;IAEnB,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,iDAAiD,CAAC,CAAC;QACvE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,6CAA6C,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,uEAAuE,CAAC,CAAC;QAC7F,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
|
|
3
|
-
const makeSub = (overrides) => ({
|
|
4
|
-
userId: 'user_123',
|
|
5
|
-
productId: 'com.example.pro_monthly',
|
|
6
|
-
platform: 'apple',
|
|
7
|
-
status: 'active',
|
|
8
|
-
expiresAt: '2025-12-31T00:00:00.000Z',
|
|
9
|
-
originalTransactionId: 'txn_abc123',
|
|
10
|
-
purchasedAt: '2025-01-01T00:00:00.000Z',
|
|
11
|
-
willRenew: true,
|
|
12
|
-
...overrides,
|
|
13
|
-
});
|
|
14
|
-
describe('InMemorySubscriptionStore', () => {
|
|
15
|
-
let store;
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
store = new InMemorySubscriptionStore();
|
|
18
|
-
});
|
|
19
|
-
it('save() and getByUserId() returns the correct subscription', async () => {
|
|
20
|
-
const sub = makeSub();
|
|
21
|
-
await store.save(sub);
|
|
22
|
-
const result = await store.getByUserId('user_123');
|
|
23
|
-
expect(result).toEqual(sub);
|
|
24
|
-
});
|
|
25
|
-
it('save() and getByTransactionId() returns the correct subscription', async () => {
|
|
26
|
-
const sub = makeSub();
|
|
27
|
-
await store.save(sub);
|
|
28
|
-
const result = await store.getByTransactionId('txn_abc123');
|
|
29
|
-
expect(result).toEqual(sub);
|
|
30
|
-
});
|
|
31
|
-
it('getByUserId() returns null for an unknown user', async () => {
|
|
32
|
-
const result = await store.getByUserId('nonexistent_user');
|
|
33
|
-
expect(result).toBeNull();
|
|
34
|
-
});
|
|
35
|
-
it('getByTransactionId() returns null for an unknown transaction', async () => {
|
|
36
|
-
const result = await store.getByTransactionId('nonexistent_txn');
|
|
37
|
-
expect(result).toBeNull();
|
|
38
|
-
});
|
|
39
|
-
it('save() overwrites an existing subscription for the same userId', async () => {
|
|
40
|
-
const original = makeSub({ status: 'active', expiresAt: '2025-06-01T00:00:00.000Z' });
|
|
41
|
-
await store.save(original);
|
|
42
|
-
const updated = makeSub({
|
|
43
|
-
status: 'expired',
|
|
44
|
-
expiresAt: '2025-01-15T00:00:00.000Z',
|
|
45
|
-
originalTransactionId: 'txn_abc123',
|
|
46
|
-
});
|
|
47
|
-
await store.save(updated);
|
|
48
|
-
const result = await store.getByUserId('user_123');
|
|
49
|
-
expect(result?.status).toBe('expired');
|
|
50
|
-
expect(result?.expiresAt).toBe('2025-01-15T00:00:00.000Z');
|
|
51
|
-
});
|
|
52
|
-
it('save() overwrites an existing subscription indexed by the same transactionId', async () => {
|
|
53
|
-
const original = makeSub({ willRenew: true });
|
|
54
|
-
await store.save(original);
|
|
55
|
-
const updated = makeSub({ willRenew: false, originalTransactionId: 'txn_abc123' });
|
|
56
|
-
await store.save(updated);
|
|
57
|
-
const result = await store.getByTransactionId('txn_abc123');
|
|
58
|
-
expect(result?.willRenew).toBe(false);
|
|
59
|
-
});
|
|
60
|
-
it('stores multiple subscriptions independently', async () => {
|
|
61
|
-
const sub1 = makeSub({ userId: 'user_1', originalTransactionId: 'txn_1' });
|
|
62
|
-
const sub2 = makeSub({ userId: 'user_2', originalTransactionId: 'txn_2', platform: 'google' });
|
|
63
|
-
await store.save(sub1);
|
|
64
|
-
await store.save(sub2);
|
|
65
|
-
expect(await store.getByUserId('user_1')).toEqual(sub1);
|
|
66
|
-
expect(await store.getByUserId('user_2')).toEqual(sub2);
|
|
67
|
-
expect(await store.getByTransactionId('txn_1')).toEqual(sub1);
|
|
68
|
-
expect(await store.getByTransactionId('txn_2')).toEqual(sub2);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
// ── InMemoryPurchaseStore ────────────────────────────────────────────────────
|
|
72
|
-
const makePurchase = (overrides) => ({
|
|
73
|
-
userId: 'user_1',
|
|
74
|
-
productId: 'credits_10',
|
|
75
|
-
platform: 'apple',
|
|
76
|
-
type: 'consumable',
|
|
77
|
-
transactionId: 'txn_p1',
|
|
78
|
-
purchasedAt: '2026-01-01T00:00:00.000Z',
|
|
79
|
-
quantity: 1,
|
|
80
|
-
...overrides,
|
|
81
|
-
});
|
|
82
|
-
describe('InMemoryPurchaseStore', () => {
|
|
83
|
-
let purchaseStore;
|
|
84
|
-
beforeEach(() => {
|
|
85
|
-
purchaseStore = new InMemoryPurchaseStore();
|
|
86
|
-
});
|
|
87
|
-
it('savePurchase + getPurchasesByUserId returns purchases', async () => {
|
|
88
|
-
const p = makePurchase();
|
|
89
|
-
await purchaseStore.savePurchase(p);
|
|
90
|
-
const results = await purchaseStore.getPurchasesByUserId('user_1');
|
|
91
|
-
expect(results).toHaveLength(1);
|
|
92
|
-
expect(results[0]).toEqual(p);
|
|
93
|
-
});
|
|
94
|
-
it('getPurchaseByTransactionId returns the correct purchase', async () => {
|
|
95
|
-
const p = makePurchase();
|
|
96
|
-
await purchaseStore.savePurchase(p);
|
|
97
|
-
expect(await purchaseStore.getPurchaseByTransactionId('txn_p1')).toEqual(p);
|
|
98
|
-
expect(await purchaseStore.getPurchaseByTransactionId('unknown')).toBeNull();
|
|
99
|
-
});
|
|
100
|
-
it('hasPurchased returns true for existing purchase', async () => {
|
|
101
|
-
await purchaseStore.savePurchase(makePurchase({ type: 'non_consumable', productId: 'premium_unlock' }));
|
|
102
|
-
expect(await purchaseStore.hasPurchased('user_1', 'premium_unlock')).toBe(true);
|
|
103
|
-
expect(await purchaseStore.hasPurchased('user_1', 'other_product')).toBe(false);
|
|
104
|
-
expect(await purchaseStore.hasPurchased('user_2', 'premium_unlock')).toBe(false);
|
|
105
|
-
});
|
|
106
|
-
it('stores multiple consumable purchases for same user+product', async () => {
|
|
107
|
-
await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_1' }));
|
|
108
|
-
await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_2' }));
|
|
109
|
-
await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_3' }));
|
|
110
|
-
const results = await purchaseStore.getPurchasesByUserId('user_1');
|
|
111
|
-
expect(results).toHaveLength(3);
|
|
112
|
-
});
|
|
113
|
-
it('getPurchasesByUserId returns empty for unknown user', async () => {
|
|
114
|
-
expect(await purchaseStore.getPurchasesByUserId('unknown')).toEqual([]);
|
|
115
|
-
});
|
|
116
|
-
it('stores purchases for different users independently', async () => {
|
|
117
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
|
|
118
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'b', transactionId: 't2' }));
|
|
119
|
-
expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
|
|
120
|
-
expect(await purchaseStore.getPurchasesByUserId('b')).toHaveLength(1);
|
|
121
|
-
});
|
|
122
|
-
it('savePurchase is idempotent for same transactionId + same userId', async () => {
|
|
123
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
|
|
124
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
|
|
125
|
-
expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
|
|
126
|
-
});
|
|
127
|
-
it('savePurchase rejects same transactionId from a different userId', async () => {
|
|
128
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
|
|
129
|
-
await expect(purchaseStore.savePurchase(makePurchase({ userId: 'b', transactionId: 't1' }))).rejects.toMatchObject({ code: 'TRANSACTION_BELONGS_TO_OTHER_USER' });
|
|
130
|
-
// Original owner unchanged
|
|
131
|
-
expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
|
|
132
|
-
expect(await purchaseStore.getPurchasesByUserId('b')).toHaveLength(0);
|
|
133
|
-
});
|
|
134
|
-
it('reassignPurchase transfers ownership to a new userId', async () => {
|
|
135
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
|
|
136
|
-
expect(await purchaseStore.reassignPurchase('t1', 'b')).toBe(true);
|
|
137
|
-
expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(0);
|
|
138
|
-
expect(await purchaseStore.getPurchasesByUserId('b')).toHaveLength(1);
|
|
139
|
-
// now saving as 'b' is idempotent, 'a' is rejected
|
|
140
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'b', transactionId: 't1' }));
|
|
141
|
-
await expect(purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }))).rejects.toMatchObject({ code: 'TRANSACTION_BELONGS_TO_OTHER_USER' });
|
|
142
|
-
});
|
|
143
|
-
it('reassignPurchase returns false for unknown transaction', async () => {
|
|
144
|
-
expect(await purchaseStore.reassignPurchase('nope', 'a')).toBe(false);
|
|
145
|
-
});
|
|
146
|
-
it('deletePurchases removes matching rows', async () => {
|
|
147
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1', productId: 'pass' }));
|
|
148
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't2', productId: 'pass' }));
|
|
149
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't3', productId: 'other' }));
|
|
150
|
-
const deleted = await purchaseStore.deletePurchases('a', 'pass');
|
|
151
|
-
expect(deleted).toBe(2);
|
|
152
|
-
expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
|
|
153
|
-
});
|
|
154
|
-
it('deletePurchaseByTransactionId removes only the matching row', async () => {
|
|
155
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1', productId: 'coins' }));
|
|
156
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't2', productId: 'coins' }));
|
|
157
|
-
expect(await purchaseStore.deletePurchaseByTransactionId('t1')).toBe(true);
|
|
158
|
-
const remaining = await purchaseStore.getPurchasesByUserId('a');
|
|
159
|
-
expect(remaining).toHaveLength(1);
|
|
160
|
-
expect(remaining[0].transactionId).toBe('t2');
|
|
161
|
-
expect(await purchaseStore.getPurchaseByTransactionId('t1')).toBeNull();
|
|
162
|
-
});
|
|
163
|
-
it('deletePurchaseByTransactionId returns false for unknown transaction', async () => {
|
|
164
|
-
expect(await purchaseStore.deletePurchaseByTransactionId('nope')).toBe(false);
|
|
165
|
-
});
|
|
166
|
-
it('deletePurchaseByTransactionId removes the user index when last row is gone', async () => {
|
|
167
|
-
await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 'only' }));
|
|
168
|
-
expect(await purchaseStore.deletePurchaseByTransactionId('only')).toBe(true);
|
|
169
|
-
expect(await purchaseStore.getPurchasesByUserId('a')).toEqual([]);
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
//# sourceMappingURL=store.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../src/__tests__/store.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAG/E,MAAM,OAAO,GAAG,CAAC,SAAqC,EAAoB,EAAE,CAAC,CAAC;IAC5E,MAAM,EAAE,UAAU;IAClB,SAAS,EAAE,yBAAyB;IACpC,QAAQ,EAAE,OAAO;IACjB,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,0BAA0B;IACrC,qBAAqB,EAAE,YAAY;IACnC,WAAW,EAAE,0BAA0B;IACvC,SAAS,EAAE,IAAI;IACf,GAAG,SAAS;CACb,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,IAAI,KAAgC,CAAC;IAErC,UAAU,CAAC,GAAG,EAAE;QACd,KAAK,GAAG,IAAI,yBAAyB,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEtB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEtB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,QAAQ,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACtF,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE3B,MAAM,OAAO,GAAG,OAAO,CAAC;YACtB,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,0BAA0B;YACrC,qBAAqB,EAAE,YAAY;SACpC,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE1B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,QAAQ,GAAG,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE3B,MAAM,OAAO,GAAG,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,CAAC,CAAC;QACnF,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE1B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC/F,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEvB,MAAM,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,CAAC,MAAM,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAEhF,MAAM,YAAY,GAAG,CAAC,SAAiC,EAAgB,EAAE,CAAC,CAAC;IACzE,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,YAAY;IACvB,QAAQ,EAAE,OAAO;IACjB,IAAI,EAAE,YAAY;IAClB,aAAa,EAAE,QAAQ;IACvB,WAAW,EAAE,0BAA0B;IACvC,QAAQ,EAAE,CAAC;IACX,GAAG,SAAS;CACb,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,IAAI,aAAoC,CAAC;IAEzC,UAAU,CAAC,GAAG,EAAE;QACd,aAAa,GAAG,IAAI,qBAAqB,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,CAAC,GAAG,YAAY,EAAE,CAAC;QACzB,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAEpC,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QACnE,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,CAAC,GAAG,YAAY,EAAE,CAAC;QACzB,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAEpC,MAAM,CAAC,MAAM,aAAa,CAAC,0BAA0B,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,CAAC,MAAM,aAAa,CAAC,0BAA0B,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC;QAExG,MAAM,CAAC,MAAM,aAAa,CAAC,YAAY,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChF,MAAM,CAAC,MAAM,aAAa,CAAC,YAAY,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChF,MAAM,CAAC,MAAM,aAAa,CAAC,YAAY,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAC3E,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAC3E,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAE3E,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QACnE,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAErF,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,MAAM,CACV,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAC/E,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC,CAAC;QACvE,2BAA2B;QAC3B,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,aAAa,CAAC,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtE,mDAAmD;QACnD,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,MAAM,CACV,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAC/E,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,CAAC,MAAM,aAAa,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACxG,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACxG,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QACzG,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACjE,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QACzG,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QACzG,MAAM,CAAC,MAAM,aAAa,CAAC,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAChE,MAAM,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,aAAa,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,CAAC,MAAM,aAAa,CAAC,6BAA6B,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,MAAM,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACvF,MAAM,CAAC,MAAM,aAAa,CAAC,6BAA6B,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,MAAM,CAAC,MAAM,aAAa,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared test helpers — kept here so unit tests don't reinvent the wheel
|
|
3
|
-
* (and don't trip CodeQL with substring URL matching, which is unsafe in
|
|
4
|
-
* production code and inconsistent across files).
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Extract the hostname from a fetch input (string | URL | Request).
|
|
8
|
-
* Returns null when the input isn't a parseable absolute URL.
|
|
9
|
-
*
|
|
10
|
-
* Use this instead of `urlStr.includes('host.com')` — substring matching
|
|
11
|
-
* matches `https://attacker.example/?q=host.com` too, which CodeQL flags.
|
|
12
|
-
*/
|
|
13
|
-
export function urlHost(input) {
|
|
14
|
-
try {
|
|
15
|
-
return new URL(String(input)).hostname;
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* True for fetch URLs targeting the in-process test HTTP server. Used by
|
|
23
|
-
* fetch mocks that need to pass-through the test runner's own requests
|
|
24
|
-
* while intercepting outbound API calls.
|
|
25
|
-
*/
|
|
26
|
-
export function isLocalhostUrl(input) {
|
|
27
|
-
const host = urlHost(input);
|
|
28
|
-
return host === '127.0.0.1' || host === 'localhost';
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=test-utils.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../src/__tests__/test-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,CAAC;AACtD,CAAC"}
|