@tellescope/sdk 1.256.2 → 1.256.3
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/lib/cjs/tests/api_tests/metriport_push_form_response.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/metriport_push_form_response.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/metriport_push_form_response.test.js +298 -0
- package/lib/cjs/tests/api_tests/metriport_push_form_response.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/phone_calls_move_enduser.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/phone_calls_move_enduser.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/phone_calls_move_enduser.test.js +274 -0
- package/lib/cjs/tests/api_tests/phone_calls_move_enduser.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/stripe_webhook_triggers.test.d.ts +18 -0
- package/lib/cjs/tests/api_tests/stripe_webhook_triggers.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/stripe_webhook_triggers.test.js +964 -0
- package/lib/cjs/tests/api_tests/stripe_webhook_triggers.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/welle_partner_token.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/welle_partner_token.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/welle_partner_token.test.js +313 -0
- package/lib/cjs/tests/api_tests/welle_partner_token.test.js.map +1 -0
- package/lib/cjs/tests/stripe_webhook_helpers.d.ts +204 -0
- package/lib/cjs/tests/stripe_webhook_helpers.d.ts.map +1 -0
- package/lib/cjs/tests/stripe_webhook_helpers.js +286 -0
- package/lib/cjs/tests/stripe_webhook_helpers.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +192 -180
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/cjs/tests/unit_tests/translation.test.d.ts +3 -0
- package/lib/cjs/tests/unit_tests/translation.test.d.ts.map +1 -0
- package/lib/cjs/tests/unit_tests/translation.test.js.map +1 -0
- package/lib/esm/tests/api_tests/metriport_push_form_response.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/metriport_push_form_response.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/metriport_push_form_response.test.js +294 -0
- package/lib/esm/tests/api_tests/metriport_push_form_response.test.js.map +1 -0
- package/lib/esm/tests/api_tests/phone_calls_move_enduser.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/phone_calls_move_enduser.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/phone_calls_move_enduser.test.js +270 -0
- package/lib/esm/tests/api_tests/phone_calls_move_enduser.test.js.map +1 -0
- package/lib/esm/tests/api_tests/stripe_webhook_triggers.test.d.ts +18 -0
- package/lib/esm/tests/api_tests/stripe_webhook_triggers.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/stripe_webhook_triggers.test.js +960 -0
- package/lib/esm/tests/api_tests/stripe_webhook_triggers.test.js.map +1 -0
- package/lib/esm/tests/api_tests/welle_partner_token.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/welle_partner_token.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/welle_partner_token.test.js +309 -0
- package/lib/esm/tests/api_tests/welle_partner_token.test.js.map +1 -0
- package/lib/esm/tests/stripe_webhook_helpers.d.ts +204 -0
- package/lib/esm/tests/stripe_webhook_helpers.d.ts.map +1 -0
- package/lib/esm/tests/stripe_webhook_helpers.js +267 -0
- package/lib/esm/tests/stripe_webhook_helpers.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +192 -180
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/esm/tests/unit_tests/translation.test.d.ts +3 -0
- package/lib/esm/tests/unit_tests/translation.test.d.ts.map +1 -0
- package/lib/esm/tests/unit_tests/translation.test.js +52 -0
- package/lib/esm/tests/unit_tests/translation.test.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/tests/api_tests/metriport_push_form_response.test.ts +226 -0
- package/src/tests/api_tests/stripe_webhook_triggers.test.ts +676 -0
- package/src/tests/api_tests/welle_partner_token.test.ts +249 -0
- package/src/tests/stripe_webhook_helpers.ts +240 -0
- package/src/tests/tests.ts +6 -0
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,964 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
50
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
51
|
+
if (ar || !(i in from)) {
|
|
52
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
53
|
+
ar[i] = from[i];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
57
|
+
};
|
|
58
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
|
+
exports.stripe_webhook_triggers_tests = void 0;
|
|
60
|
+
require('source-map-support').install();
|
|
61
|
+
var sdk_1 = require("../../sdk");
|
|
62
|
+
var testing_1 = require("@tellescope/testing");
|
|
63
|
+
var setup_1 = require("../setup");
|
|
64
|
+
var stripe_webhook_helpers_1 = require("../stripe_webhook_helpers");
|
|
65
|
+
var host = process.env.API_URL || "http://localhost:8080";
|
|
66
|
+
/**
|
|
67
|
+
* End-to-end coverage of the custom Stripe webhook -> automation trigger path, driven by synthetic
|
|
68
|
+
* but genuinely-signed payloads (see ../stripe_webhook_helpers).
|
|
69
|
+
*
|
|
70
|
+
* Fixture rules, all forced by handle_actions_for_triggers:
|
|
71
|
+
* - 'Add Tags' is the action because it applies inline via $addToSet, with no worker involved.
|
|
72
|
+
* - Every webhook POST gets its own enduser: triggers throttle per (trigger, enduser) for 60s, and
|
|
73
|
+
* neither handle_payment_failed nor handle_subscription_ended opts out.
|
|
74
|
+
* - No journeyId (the handler skips the action entirely for journey-scoped triggers), no
|
|
75
|
+
* oncePerEnduser, no enduserCondition, no weeklyAvailabilities.
|
|
76
|
+
* - Tags are run-unique, so a stale tag can't satisfy an assertion and 'Add Tags' can't no-op.
|
|
77
|
+
*/
|
|
78
|
+
var stripe_webhook_triggers_tests = function (_a) {
|
|
79
|
+
var sdk = _a.sdk, sdkNonAdmin = _a.sdkNonAdmin;
|
|
80
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
81
|
+
var RUN, tag, createdEnduserIds, createdTriggerIds, createdProductIds, _b, stripePublicKey, webhooksSecret, cleanup, new_enduser, new_trigger, new_product, poll_for_tag, expect_fired, expect_not_fired, B1_TAG, b1Enduser, b1Event, b1Res, b2Res, B3_TAG, b3Enduser, b3Res, B4_TAG, b4Enduser, b4Res, stripeProductA_1, stripeProductB, productA, productB, B5_TAG, b5Enduser, b5Res, B6_TAG, b6Enduser, B7_TAG, b7Enduser, B8_TAG, b8Enduser, run_scoped_payment_failed_case, f7Scoped, f7Fetched, rejectedInvalidProductId, e_1, run_pause_case, p14Scoped, p14Fetched, rejectedInvalidPausedProductId, e_2;
|
|
82
|
+
var _c, _d;
|
|
83
|
+
return __generator(this, function (_e) {
|
|
84
|
+
switch (_e.label) {
|
|
85
|
+
case 0:
|
|
86
|
+
(0, testing_1.log_header)("Stripe Webhook Triggers");
|
|
87
|
+
RUN = "".concat(Date.now());
|
|
88
|
+
tag = function (name) { return "stripe-wh-".concat(name, "-").concat(RUN); };
|
|
89
|
+
createdEnduserIds = [];
|
|
90
|
+
createdTriggerIds = [];
|
|
91
|
+
createdProductIds = [];
|
|
92
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.setup_stripe_test_integration)(sdk)
|
|
93
|
+
// Each enduser is matched by the handler on stripeCustomerId, so it must be unique per case.
|
|
94
|
+
];
|
|
95
|
+
case 1:
|
|
96
|
+
_b = _e.sent(), stripePublicKey = _b.stripePublicKey, webhooksSecret = _b.webhooksSecret, cleanup = _b.cleanup;
|
|
97
|
+
new_enduser = function (label) { return __awaiter(void 0, void 0, void 0, function () {
|
|
98
|
+
var stripeCustomerId, enduser;
|
|
99
|
+
return __generator(this, function (_a) {
|
|
100
|
+
switch (_a.label) {
|
|
101
|
+
case 0:
|
|
102
|
+
stripeCustomerId = "cus_test_".concat(label, "_").concat(RUN);
|
|
103
|
+
return [4 /*yield*/, sdk.api.endusers.createOne({
|
|
104
|
+
fname: 'Stripe',
|
|
105
|
+
lname: "Webhook ".concat(label),
|
|
106
|
+
email: "stripe-webhook-".concat(label, "-").concat(RUN, "@tellescope.com"),
|
|
107
|
+
// settable by a staff session, but declared readonly in the client types
|
|
108
|
+
stripeCustomerId: stripeCustomerId,
|
|
109
|
+
})];
|
|
110
|
+
case 1:
|
|
111
|
+
enduser = _a.sent();
|
|
112
|
+
createdEnduserIds.push(enduser.id);
|
|
113
|
+
return [2 /*return*/, { id: enduser.id, stripeCustomerId: stripeCustomerId }];
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}); };
|
|
117
|
+
new_trigger = function (_a) {
|
|
118
|
+
var title = _a.title, event = _a.event, tags = _a.tags;
|
|
119
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
120
|
+
var trigger;
|
|
121
|
+
return __generator(this, function (_b) {
|
|
122
|
+
switch (_b.label) {
|
|
123
|
+
case 0: return [4 /*yield*/, sdk.api.automation_triggers.createOne({
|
|
124
|
+
title: title,
|
|
125
|
+
status: 'Active',
|
|
126
|
+
event: event,
|
|
127
|
+
action: { type: 'Add Tags', info: { tags: tags } },
|
|
128
|
+
})];
|
|
129
|
+
case 1:
|
|
130
|
+
trigger = _b.sent();
|
|
131
|
+
createdTriggerIds.push(trigger.id);
|
|
132
|
+
return [2 /*return*/, trigger];
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
new_product = function (label, stripeProductId) { return __awaiter(void 0, void 0, void 0, function () {
|
|
138
|
+
var product;
|
|
139
|
+
return __generator(this, function (_a) {
|
|
140
|
+
switch (_a.label) {
|
|
141
|
+
case 0: return [4 /*yield*/, sdk.api.products.createOne({
|
|
142
|
+
title: "Stripe Webhook Test ".concat(label, " ").concat(RUN),
|
|
143
|
+
cost: { amount: 1999, currency: 'USD' },
|
|
144
|
+
processor: 'Stripe',
|
|
145
|
+
stripeSubscriptionId: stripeProductId,
|
|
146
|
+
})];
|
|
147
|
+
case 1:
|
|
148
|
+
product = _a.sent();
|
|
149
|
+
createdProductIds.push(product.id);
|
|
150
|
+
return [2 /*return*/, product];
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}); };
|
|
154
|
+
poll_for_tag = function (enduserId, expected, iterations) {
|
|
155
|
+
if (iterations === void 0) { iterations = 20; }
|
|
156
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
157
|
+
var i, enduser;
|
|
158
|
+
var _a;
|
|
159
|
+
return __generator(this, function (_b) {
|
|
160
|
+
switch (_b.label) {
|
|
161
|
+
case 0:
|
|
162
|
+
i = 0;
|
|
163
|
+
_b.label = 1;
|
|
164
|
+
case 1:
|
|
165
|
+
if (!(i < iterations)) return [3 /*break*/, 5];
|
|
166
|
+
return [4 /*yield*/, (0, testing_1.wait)(undefined, 500)];
|
|
167
|
+
case 2:
|
|
168
|
+
_b.sent();
|
|
169
|
+
return [4 /*yield*/, sdk.api.endusers.getOne(enduserId)];
|
|
170
|
+
case 3:
|
|
171
|
+
enduser = _b.sent();
|
|
172
|
+
if ((_a = enduser.tags) === null || _a === void 0 ? void 0 : _a.includes(expected))
|
|
173
|
+
return [2 /*return*/, true];
|
|
174
|
+
_b.label = 4;
|
|
175
|
+
case 4:
|
|
176
|
+
i++;
|
|
177
|
+
return [3 /*break*/, 1];
|
|
178
|
+
case 5: return [2 /*return*/, false];
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
expect_fired = function (enduserId, expected, title) { return __awaiter(void 0, void 0, void 0, function () {
|
|
184
|
+
var _a;
|
|
185
|
+
return __generator(this, function (_b) {
|
|
186
|
+
switch (_b.label) {
|
|
187
|
+
case 0:
|
|
188
|
+
_a = testing_1.assert;
|
|
189
|
+
return [4 /*yield*/, poll_for_tag(enduserId, expected)];
|
|
190
|
+
case 1:
|
|
191
|
+
_a.apply(void 0, [_b.sent(), "expected tag ".concat(expected, " to be added by the trigger"), title]);
|
|
192
|
+
return [2 /*return*/];
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}); };
|
|
196
|
+
expect_not_fired = function (enduserId, expected, title) { return __awaiter(void 0, void 0, void 0, function () {
|
|
197
|
+
var enduser;
|
|
198
|
+
var _a;
|
|
199
|
+
return __generator(this, function (_b) {
|
|
200
|
+
switch (_b.label) {
|
|
201
|
+
case 0: return [4 /*yield*/, (0, testing_1.wait)(undefined, 2500)];
|
|
202
|
+
case 1:
|
|
203
|
+
_b.sent();
|
|
204
|
+
return [4 /*yield*/, sdk.api.endusers.getOne(enduserId)];
|
|
205
|
+
case 2:
|
|
206
|
+
enduser = _b.sent();
|
|
207
|
+
(0, testing_1.assert)(!((_a = enduser.tags) === null || _a === void 0 ? void 0 : _a.includes(expected)), "expected the trigger NOT to fire, but tag ".concat(expected, " was added"), title);
|
|
208
|
+
return [2 /*return*/];
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}); };
|
|
212
|
+
_e.label = 2;
|
|
213
|
+
case 2:
|
|
214
|
+
_e.trys.push([2, , 69, 72]);
|
|
215
|
+
B1_TAG = tag('b1-bad-signature');
|
|
216
|
+
return [4 /*yield*/, new_enduser('b1')];
|
|
217
|
+
case 3:
|
|
218
|
+
b1Enduser = _e.sent();
|
|
219
|
+
return [4 /*yield*/, new_trigger({
|
|
220
|
+
title: "Stripe payment failed (bad signature) ".concat(RUN),
|
|
221
|
+
event: { type: 'Subscription Payment Failed', info: {} },
|
|
222
|
+
tags: [B1_TAG],
|
|
223
|
+
})];
|
|
224
|
+
case 4:
|
|
225
|
+
_e.sent();
|
|
226
|
+
b1Event = (0, stripe_webhook_helpers_1.invoice_payment_failed_event)({ stripeCustomerId: b1Enduser.stripeCustomerId });
|
|
227
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
228
|
+
stripePublicKey: stripePublicKey,
|
|
229
|
+
secret: webhooksSecret,
|
|
230
|
+
event: b1Event,
|
|
231
|
+
signature: (0, stripe_webhook_helpers_1.sign_stripe_payload)('{"tampered":true}', webhooksSecret),
|
|
232
|
+
})];
|
|
233
|
+
case 5:
|
|
234
|
+
b1Res = _e.sent();
|
|
235
|
+
(0, testing_1.assert)(b1Res.status === 400, "expected 400 for a mismatched signature, got ".concat(b1Res.status), 'invalid signature rejected');
|
|
236
|
+
return [4 /*yield*/, expect_not_fired(b1Enduser.id, B1_TAG, 'invalid signature does not fire triggers')
|
|
237
|
+
// B2: unknown public key in the URL -> no integration -> 404
|
|
238
|
+
];
|
|
239
|
+
case 6:
|
|
240
|
+
_e.sent();
|
|
241
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
242
|
+
stripePublicKey: "pk_test_missing_".concat(RUN),
|
|
243
|
+
secret: webhooksSecret,
|
|
244
|
+
event: (0, stripe_webhook_helpers_1.invoice_payment_failed_event)({ stripeCustomerId: "cus_test_missing_".concat(RUN) }),
|
|
245
|
+
})];
|
|
246
|
+
case 7:
|
|
247
|
+
b2Res = _e.sent();
|
|
248
|
+
(0, testing_1.assert)(b2Res.status === 404, "expected 404 for an unknown public key, got ".concat(b2Res.status), 'unknown integration rejected');
|
|
249
|
+
B3_TAG = tag('b3-unknown-customer');
|
|
250
|
+
return [4 /*yield*/, new_enduser('b3')];
|
|
251
|
+
case 8:
|
|
252
|
+
b3Enduser = _e.sent();
|
|
253
|
+
return [4 /*yield*/, new_trigger({
|
|
254
|
+
title: "Stripe payment failed (unknown customer) ".concat(RUN),
|
|
255
|
+
event: { type: 'Subscription Payment Failed', info: {} },
|
|
256
|
+
tags: [B3_TAG],
|
|
257
|
+
})];
|
|
258
|
+
case 9:
|
|
259
|
+
_e.sent();
|
|
260
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
261
|
+
stripePublicKey: stripePublicKey,
|
|
262
|
+
secret: webhooksSecret,
|
|
263
|
+
event: (0, stripe_webhook_helpers_1.invoice_payment_failed_event)({ stripeCustomerId: "cus_test_nobody_".concat(RUN) }),
|
|
264
|
+
})];
|
|
265
|
+
case 10:
|
|
266
|
+
b3Res = _e.sent();
|
|
267
|
+
(0, testing_1.assert)(b3Res.status === 200, "expected 200 for an unmatched customer, got ".concat(b3Res.status), 'unmatched customer accepted');
|
|
268
|
+
return [4 /*yield*/, expect_not_fired(b3Enduser.id, B3_TAG, 'unmatched customer does not fire triggers')
|
|
269
|
+
// B4: the baseline behavior the productIds feature must preserve — a Subscription Payment Failed
|
|
270
|
+
// trigger with no product scoping fires for any failed invoice.
|
|
271
|
+
];
|
|
272
|
+
case 11:
|
|
273
|
+
_e.sent();
|
|
274
|
+
B4_TAG = tag('b4-unscoped');
|
|
275
|
+
return [4 /*yield*/, new_enduser('b4')];
|
|
276
|
+
case 12:
|
|
277
|
+
b4Enduser = _e.sent();
|
|
278
|
+
return [4 /*yield*/, new_trigger({
|
|
279
|
+
title: "Stripe payment failed (unscoped) ".concat(RUN),
|
|
280
|
+
event: { type: 'Subscription Payment Failed', info: {} },
|
|
281
|
+
tags: [B4_TAG],
|
|
282
|
+
})];
|
|
283
|
+
case 13:
|
|
284
|
+
_e.sent();
|
|
285
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
286
|
+
stripePublicKey: stripePublicKey,
|
|
287
|
+
secret: webhooksSecret,
|
|
288
|
+
event: (0, stripe_webhook_helpers_1.invoice_payment_failed_event)({
|
|
289
|
+
stripeCustomerId: b4Enduser.stripeCustomerId,
|
|
290
|
+
lines: [(0, stripe_webhook_helpers_1.line_with_price_product)("prod_test_b4_".concat(RUN))],
|
|
291
|
+
}),
|
|
292
|
+
})];
|
|
293
|
+
case 14:
|
|
294
|
+
b4Res = _e.sent();
|
|
295
|
+
(0, testing_1.assert)(b4Res.status === 200, "expected 200, got ".concat(b4Res.status, " (").concat(b4Res.data, ")"), 'payment failed webhook accepted');
|
|
296
|
+
return [4 /*yield*/, expect_fired(b4Enduser.id, B4_TAG, 'unscoped Subscription Payment Failed trigger fires')
|
|
297
|
+
/* ===================== customer.subscription.deleted ===================== */
|
|
298
|
+
// Products A and B exist to distinguish "the subscription's product" from "some other product".
|
|
299
|
+
];
|
|
300
|
+
case 15:
|
|
301
|
+
_e.sent();
|
|
302
|
+
stripeProductA_1 = "prod_test_a_".concat(RUN);
|
|
303
|
+
stripeProductB = "prod_test_b_".concat(RUN);
|
|
304
|
+
return [4 /*yield*/, new_product('A', stripeProductA_1)];
|
|
305
|
+
case 16:
|
|
306
|
+
productA = _e.sent();
|
|
307
|
+
return [4 /*yield*/, new_product('B', stripeProductB)
|
|
308
|
+
// B5: unscoped Subscription Ended fires
|
|
309
|
+
];
|
|
310
|
+
case 17:
|
|
311
|
+
productB = _e.sent();
|
|
312
|
+
B5_TAG = tag('b5-ended-unscoped');
|
|
313
|
+
return [4 /*yield*/, new_enduser('b5')];
|
|
314
|
+
case 18:
|
|
315
|
+
b5Enduser = _e.sent();
|
|
316
|
+
return [4 /*yield*/, new_trigger({
|
|
317
|
+
title: "Stripe subscription ended (unscoped) ".concat(RUN),
|
|
318
|
+
event: { type: 'Subscription Ended', info: {} },
|
|
319
|
+
tags: [B5_TAG],
|
|
320
|
+
})];
|
|
321
|
+
case 19:
|
|
322
|
+
_e.sent();
|
|
323
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
324
|
+
stripePublicKey: stripePublicKey,
|
|
325
|
+
secret: webhooksSecret,
|
|
326
|
+
event: (0, stripe_webhook_helpers_1.subscription_deleted_event)({
|
|
327
|
+
stripeCustomerId: b5Enduser.stripeCustomerId,
|
|
328
|
+
stripeProductIds: [stripeProductA_1],
|
|
329
|
+
}),
|
|
330
|
+
})];
|
|
331
|
+
case 20:
|
|
332
|
+
b5Res = _e.sent();
|
|
333
|
+
(0, testing_1.assert)(b5Res.status === 200, "expected 200, got ".concat(b5Res.status, " (").concat(b5Res.data, ")"), 'subscription deleted webhook accepted');
|
|
334
|
+
return [4 /*yield*/, expect_fired(b5Enduser.id, B5_TAG, 'unscoped Subscription Ended trigger fires')
|
|
335
|
+
// B6: an explicitly empty productIds list means "all products"
|
|
336
|
+
];
|
|
337
|
+
case 21:
|
|
338
|
+
_e.sent();
|
|
339
|
+
B6_TAG = tag('b6-ended-empty-list');
|
|
340
|
+
return [4 /*yield*/, new_enduser('b6')];
|
|
341
|
+
case 22:
|
|
342
|
+
b6Enduser = _e.sent();
|
|
343
|
+
return [4 /*yield*/, new_trigger({
|
|
344
|
+
title: "Stripe subscription ended (empty productIds) ".concat(RUN),
|
|
345
|
+
event: { type: 'Subscription Ended', info: { productIds: [] } },
|
|
346
|
+
tags: [B6_TAG],
|
|
347
|
+
})];
|
|
348
|
+
case 23:
|
|
349
|
+
_e.sent();
|
|
350
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
351
|
+
stripePublicKey: stripePublicKey,
|
|
352
|
+
secret: webhooksSecret,
|
|
353
|
+
event: (0, stripe_webhook_helpers_1.subscription_deleted_event)({
|
|
354
|
+
stripeCustomerId: b6Enduser.stripeCustomerId,
|
|
355
|
+
stripeProductIds: [stripeProductA_1],
|
|
356
|
+
}),
|
|
357
|
+
})];
|
|
358
|
+
case 24:
|
|
359
|
+
_e.sent();
|
|
360
|
+
return [4 /*yield*/, expect_fired(b6Enduser.id, B6_TAG, 'Subscription Ended trigger with empty productIds fires')
|
|
361
|
+
// B7/B8 pin the existing product-matching semantics end-to-end, so any refactor of the
|
|
362
|
+
// productIds query clause is provably behavior-preserving.
|
|
363
|
+
// B7: productIds matching the subscription's product fires
|
|
364
|
+
];
|
|
365
|
+
case 25:
|
|
366
|
+
_e.sent();
|
|
367
|
+
B7_TAG = tag('b7-ended-matching');
|
|
368
|
+
return [4 /*yield*/, new_enduser('b7')];
|
|
369
|
+
case 26:
|
|
370
|
+
b7Enduser = _e.sent();
|
|
371
|
+
return [4 /*yield*/, new_trigger({
|
|
372
|
+
title: "Stripe subscription ended (matching product) ".concat(RUN),
|
|
373
|
+
event: { type: 'Subscription Ended', info: { productIds: [productA.id] } },
|
|
374
|
+
tags: [B7_TAG],
|
|
375
|
+
})];
|
|
376
|
+
case 27:
|
|
377
|
+
_e.sent();
|
|
378
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
379
|
+
stripePublicKey: stripePublicKey,
|
|
380
|
+
secret: webhooksSecret,
|
|
381
|
+
event: (0, stripe_webhook_helpers_1.subscription_deleted_event)({
|
|
382
|
+
stripeCustomerId: b7Enduser.stripeCustomerId,
|
|
383
|
+
stripeProductIds: [stripeProductA_1],
|
|
384
|
+
}),
|
|
385
|
+
})];
|
|
386
|
+
case 28:
|
|
387
|
+
_e.sent();
|
|
388
|
+
return [4 /*yield*/, expect_fired(b7Enduser.id, B7_TAG, 'Subscription Ended trigger scoped to the matching product fires')
|
|
389
|
+
// B8: productIds naming a different product does not fire
|
|
390
|
+
];
|
|
391
|
+
case 29:
|
|
392
|
+
_e.sent();
|
|
393
|
+
B8_TAG = tag('b8-ended-nonmatching');
|
|
394
|
+
return [4 /*yield*/, new_enduser('b8')];
|
|
395
|
+
case 30:
|
|
396
|
+
b8Enduser = _e.sent();
|
|
397
|
+
return [4 /*yield*/, new_trigger({
|
|
398
|
+
title: "Stripe subscription ended (other product) ".concat(RUN),
|
|
399
|
+
event: { type: 'Subscription Ended', info: { productIds: [productB.id] } },
|
|
400
|
+
tags: [B8_TAG],
|
|
401
|
+
})];
|
|
402
|
+
case 31:
|
|
403
|
+
_e.sent();
|
|
404
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
405
|
+
stripePublicKey: stripePublicKey,
|
|
406
|
+
secret: webhooksSecret,
|
|
407
|
+
event: (0, stripe_webhook_helpers_1.subscription_deleted_event)({
|
|
408
|
+
stripeCustomerId: b8Enduser.stripeCustomerId,
|
|
409
|
+
stripeProductIds: [stripeProductA_1],
|
|
410
|
+
}),
|
|
411
|
+
})];
|
|
412
|
+
case 32:
|
|
413
|
+
_e.sent();
|
|
414
|
+
return [4 /*yield*/, expect_not_fired(b8Enduser.id, B8_TAG, 'Subscription Ended trigger scoped to another product does not fire')
|
|
415
|
+
/* ============ invoice.payment_failed, scoped to products ============ */
|
|
416
|
+
// Shared shape for the cases below: a trigger scoped to productA, and an invoice whose line
|
|
417
|
+
// items are varied per case to cover how Stripe can reference a product.
|
|
418
|
+
];
|
|
419
|
+
case 33:
|
|
420
|
+
_e.sent();
|
|
421
|
+
run_scoped_payment_failed_case = function (_a) {
|
|
422
|
+
var label = _a.label, productIds = _a.productIds, lines = _a.lines, shouldFire = _a.shouldFire, title = _a.title;
|
|
423
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
424
|
+
var caseTag, enduser, res;
|
|
425
|
+
return __generator(this, function (_b) {
|
|
426
|
+
switch (_b.label) {
|
|
427
|
+
case 0:
|
|
428
|
+
caseTag = tag(label);
|
|
429
|
+
return [4 /*yield*/, new_enduser(label)];
|
|
430
|
+
case 1:
|
|
431
|
+
enduser = _b.sent();
|
|
432
|
+
return [4 /*yield*/, new_trigger({
|
|
433
|
+
title: "Stripe payment failed (".concat(label, ") ").concat(RUN),
|
|
434
|
+
event: { type: 'Subscription Payment Failed', info: { productIds: productIds } },
|
|
435
|
+
tags: [caseTag],
|
|
436
|
+
})];
|
|
437
|
+
case 2:
|
|
438
|
+
_b.sent();
|
|
439
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
440
|
+
stripePublicKey: stripePublicKey,
|
|
441
|
+
secret: webhooksSecret,
|
|
442
|
+
event: (0, stripe_webhook_helpers_1.invoice_payment_failed_event)({ stripeCustomerId: enduser.stripeCustomerId, lines: lines }),
|
|
443
|
+
})];
|
|
444
|
+
case 3:
|
|
445
|
+
res = _b.sent();
|
|
446
|
+
(0, testing_1.assert)(res.status === 200, "expected 200, got ".concat(res.status, " (").concat(res.data, ")"), "".concat(label, " webhook accepted"));
|
|
447
|
+
if (!shouldFire) return [3 /*break*/, 5];
|
|
448
|
+
return [4 /*yield*/, expect_fired(enduser.id, caseTag, title)];
|
|
449
|
+
case 4:
|
|
450
|
+
_b.sent();
|
|
451
|
+
return [3 /*break*/, 7];
|
|
452
|
+
case 5: return [4 /*yield*/, expect_not_fired(enduser.id, caseTag, title)];
|
|
453
|
+
case 6:
|
|
454
|
+
_b.sent();
|
|
455
|
+
_b.label = 7;
|
|
456
|
+
case 7: return [2 /*return*/];
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
// F1: the feature — a trigger scoped to the product that actually failed fires
|
|
462
|
+
return [4 /*yield*/, run_scoped_payment_failed_case({
|
|
463
|
+
label: 'f1-matching',
|
|
464
|
+
productIds: [productA.id],
|
|
465
|
+
lines: [(0, stripe_webhook_helpers_1.line_with_price_product)(stripeProductA_1)],
|
|
466
|
+
shouldFire: true,
|
|
467
|
+
title: 'Subscription Payment Failed scoped to the failed product fires',
|
|
468
|
+
})
|
|
469
|
+
// F2: the point of the feature — a trigger scoped to a different product stays quiet
|
|
470
|
+
];
|
|
471
|
+
case 34:
|
|
472
|
+
// F1: the feature — a trigger scoped to the product that actually failed fires
|
|
473
|
+
_e.sent();
|
|
474
|
+
// F2: the point of the feature — a trigger scoped to a different product stays quiet
|
|
475
|
+
return [4 /*yield*/, run_scoped_payment_failed_case({
|
|
476
|
+
label: 'f2-nonmatching',
|
|
477
|
+
productIds: [productB.id],
|
|
478
|
+
lines: [(0, stripe_webhook_helpers_1.line_with_price_product)(stripeProductA_1)],
|
|
479
|
+
shouldFire: false,
|
|
480
|
+
title: 'Subscription Payment Failed scoped to another product does not fire',
|
|
481
|
+
})
|
|
482
|
+
// F3: "leave blank for all" — an empty list is not a filter
|
|
483
|
+
];
|
|
484
|
+
case 35:
|
|
485
|
+
// F2: the point of the feature — a trigger scoped to a different product stays quiet
|
|
486
|
+
_e.sent();
|
|
487
|
+
// F3: "leave blank for all" — an empty list is not a filter
|
|
488
|
+
return [4 /*yield*/, run_scoped_payment_failed_case({
|
|
489
|
+
label: 'f3-empty-list',
|
|
490
|
+
productIds: [],
|
|
491
|
+
lines: [(0, stripe_webhook_helpers_1.line_with_price_product)(stripeProductA_1)],
|
|
492
|
+
shouldFire: true,
|
|
493
|
+
title: 'Subscription Payment Failed with empty productIds fires',
|
|
494
|
+
})
|
|
495
|
+
// F4: price.product may arrive expanded as an object rather than a bare id
|
|
496
|
+
];
|
|
497
|
+
case 36:
|
|
498
|
+
// F3: "leave blank for all" — an empty list is not a filter
|
|
499
|
+
_e.sent();
|
|
500
|
+
// F4: price.product may arrive expanded as an object rather than a bare id
|
|
501
|
+
return [4 /*yield*/, run_scoped_payment_failed_case({
|
|
502
|
+
label: 'f4-expanded-product',
|
|
503
|
+
productIds: [productA.id],
|
|
504
|
+
lines: [(0, stripe_webhook_helpers_1.line_with_expanded_price_product)(stripeProductA_1)],
|
|
505
|
+
shouldFire: true,
|
|
506
|
+
title: 'expanded price.product object resolves to the product',
|
|
507
|
+
})
|
|
508
|
+
// F5: subscription/proration lines can carry plan.product with no price
|
|
509
|
+
];
|
|
510
|
+
case 37:
|
|
511
|
+
// F4: price.product may arrive expanded as an object rather than a bare id
|
|
512
|
+
_e.sent();
|
|
513
|
+
// F5: subscription/proration lines can carry plan.product with no price
|
|
514
|
+
return [4 /*yield*/, run_scoped_payment_failed_case({
|
|
515
|
+
label: 'f5-plan-product',
|
|
516
|
+
productIds: [productA.id],
|
|
517
|
+
lines: [(0, stripe_webhook_helpers_1.line_with_plan_product)(stripeProductA_1)],
|
|
518
|
+
shouldFire: true,
|
|
519
|
+
title: 'plan.product fallback resolves to the product',
|
|
520
|
+
})
|
|
521
|
+
// F6: one matching line among several is enough
|
|
522
|
+
];
|
|
523
|
+
case 38:
|
|
524
|
+
// F5: subscription/proration lines can carry plan.product with no price
|
|
525
|
+
_e.sent();
|
|
526
|
+
// F6: one matching line among several is enough
|
|
527
|
+
return [4 /*yield*/, run_scoped_payment_failed_case({
|
|
528
|
+
label: 'f6-multi-line',
|
|
529
|
+
productIds: [productA.id],
|
|
530
|
+
lines: [(0, stripe_webhook_helpers_1.line_with_price_product)(stripeProductB), (0, stripe_webhook_helpers_1.line_with_price_product)(stripeProductA_1)],
|
|
531
|
+
shouldFire: true,
|
|
532
|
+
title: 'a matching line among several fires the scoped trigger',
|
|
533
|
+
})
|
|
534
|
+
/* ===================== validator ===================== */
|
|
535
|
+
// F7: productIds must round-trip, and an omitted info must stay valid — it was accepted before
|
|
536
|
+
// productIds existed, so requiring it would break existing API clients.
|
|
537
|
+
];
|
|
538
|
+
case 39:
|
|
539
|
+
// F6: one matching line among several is enough
|
|
540
|
+
_e.sent();
|
|
541
|
+
return [4 /*yield*/, new_trigger({
|
|
542
|
+
title: "Stripe payment failed (validator round-trip) ".concat(RUN),
|
|
543
|
+
event: { type: 'Subscription Payment Failed', info: { productIds: [productA.id] } },
|
|
544
|
+
tags: [tag('f7')],
|
|
545
|
+
})];
|
|
546
|
+
case 40:
|
|
547
|
+
f7Scoped = _e.sent();
|
|
548
|
+
return [4 /*yield*/, sdk.api.automation_triggers.getOne(f7Scoped.id)];
|
|
549
|
+
case 41:
|
|
550
|
+
f7Fetched = _e.sent();
|
|
551
|
+
(0, testing_1.assert)(JSON.stringify((_c = f7Fetched.event.info) === null || _c === void 0 ? void 0 : _c.productIds) === JSON.stringify([productA.id]), "expected productIds to round-trip, got ".concat(JSON.stringify(f7Fetched.event)), 'productIds round-trips through the validator');
|
|
552
|
+
return [4 /*yield*/, new_trigger({
|
|
553
|
+
title: "Stripe payment failed (empty info) ".concat(RUN),
|
|
554
|
+
event: { type: 'Subscription Payment Failed', info: {} },
|
|
555
|
+
tags: [tag('f7-empty-info')],
|
|
556
|
+
})];
|
|
557
|
+
case 42:
|
|
558
|
+
_e.sent();
|
|
559
|
+
(0, testing_1.assert)(true, '', 'empty info accepted');
|
|
560
|
+
return [4 /*yield*/, new_trigger({
|
|
561
|
+
title: "Stripe payment failed (no info) ".concat(RUN),
|
|
562
|
+
event: { type: 'Subscription Payment Failed' },
|
|
563
|
+
tags: [tag('f7-no-info')],
|
|
564
|
+
})];
|
|
565
|
+
case 43:
|
|
566
|
+
_e.sent();
|
|
567
|
+
(0, testing_1.assert)(true, '', 'omitted info still accepted');
|
|
568
|
+
rejectedInvalidProductId = false;
|
|
569
|
+
_e.label = 44;
|
|
570
|
+
case 44:
|
|
571
|
+
_e.trys.push([44, 46, , 47]);
|
|
572
|
+
return [4 /*yield*/, new_trigger({
|
|
573
|
+
title: "Stripe payment failed (invalid productId) ".concat(RUN),
|
|
574
|
+
event: { type: 'Subscription Payment Failed', info: { productIds: ['not-a-mongo-id'] } },
|
|
575
|
+
tags: [tag('f7-invalid')],
|
|
576
|
+
})];
|
|
577
|
+
case 45:
|
|
578
|
+
_e.sent();
|
|
579
|
+
return [3 /*break*/, 47];
|
|
580
|
+
case 46:
|
|
581
|
+
e_1 = _e.sent();
|
|
582
|
+
rejectedInvalidProductId = true;
|
|
583
|
+
return [3 /*break*/, 47];
|
|
584
|
+
case 47:
|
|
585
|
+
(0, testing_1.assert)(rejectedInvalidProductId, 'expected a non-ObjectId productId to be rejected', 'invalid productId rejected');
|
|
586
|
+
run_pause_case = function (_a) {
|
|
587
|
+
var label = _a.label, type = _a.type, productIds = _a.productIds, buildEvent = _a.event, shouldFire = _a.shouldFire, title = _a.title;
|
|
588
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
589
|
+
var caseTag, enduser, res;
|
|
590
|
+
return __generator(this, function (_b) {
|
|
591
|
+
switch (_b.label) {
|
|
592
|
+
case 0:
|
|
593
|
+
caseTag = tag(label);
|
|
594
|
+
return [4 /*yield*/, new_enduser(label)];
|
|
595
|
+
case 1:
|
|
596
|
+
enduser = _b.sent();
|
|
597
|
+
return [4 /*yield*/, new_trigger({
|
|
598
|
+
title: "Stripe ".concat(type, " (").concat(label, ") ").concat(RUN),
|
|
599
|
+
event: __assign({ type: type }, productIds !== undefined ? { info: { productIds: productIds } } : {}),
|
|
600
|
+
tags: [caseTag],
|
|
601
|
+
})];
|
|
602
|
+
case 2:
|
|
603
|
+
_b.sent();
|
|
604
|
+
return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
|
|
605
|
+
stripePublicKey: stripePublicKey,
|
|
606
|
+
secret: webhooksSecret,
|
|
607
|
+
event: buildEvent(enduser.stripeCustomerId),
|
|
608
|
+
})];
|
|
609
|
+
case 3:
|
|
610
|
+
res = _b.sent();
|
|
611
|
+
(0, testing_1.assert)(res.status === 200, "expected 200, got ".concat(res.status, " (").concat(res.data, ")"), "".concat(label, " webhook accepted"));
|
|
612
|
+
if (!shouldFire) return [3 /*break*/, 5];
|
|
613
|
+
return [4 /*yield*/, expect_fired(enduser.id, caseTag, title)];
|
|
614
|
+
case 4:
|
|
615
|
+
_b.sent();
|
|
616
|
+
return [3 /*break*/, 7];
|
|
617
|
+
case 5: return [4 /*yield*/, expect_not_fired(enduser.id, caseTag, title)];
|
|
618
|
+
case 6:
|
|
619
|
+
_b.sent();
|
|
620
|
+
_b.label = 7;
|
|
621
|
+
case 7: return [2 /*return*/];
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
};
|
|
626
|
+
// P1: the core pause path — pause_collection goes null -> set on customer.subscription.updated.
|
|
627
|
+
// This is how a pause from the billing portal or dashboard actually arrives; there is no
|
|
628
|
+
// dedicated event for it.
|
|
629
|
+
return [4 /*yield*/, run_pause_case({
|
|
630
|
+
label: 'p1-pause-collection-set',
|
|
631
|
+
type: 'Subscription Paused',
|
|
632
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
633
|
+
stripeCustomerId: stripeCustomerId,
|
|
634
|
+
previousAttributes: { pause_collection: null },
|
|
635
|
+
pauseCollection: stripe_webhook_helpers_1.PAUSE_COLLECTION,
|
|
636
|
+
}); },
|
|
637
|
+
shouldFire: true,
|
|
638
|
+
title: 'pause_collection null -> set fires Subscription Paused',
|
|
639
|
+
})
|
|
640
|
+
// P2: the mirror — pause_collection cleared is a resume
|
|
641
|
+
];
|
|
642
|
+
case 48:
|
|
643
|
+
// P1: the core pause path — pause_collection goes null -> set on customer.subscription.updated.
|
|
644
|
+
// This is how a pause from the billing portal or dashboard actually arrives; there is no
|
|
645
|
+
// dedicated event for it.
|
|
646
|
+
_e.sent();
|
|
647
|
+
// P2: the mirror — pause_collection cleared is a resume
|
|
648
|
+
return [4 /*yield*/, run_pause_case({
|
|
649
|
+
label: 'p2-pause-collection-cleared',
|
|
650
|
+
type: 'Subscription Resumed',
|
|
651
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
652
|
+
stripeCustomerId: stripeCustomerId,
|
|
653
|
+
previousAttributes: { pause_collection: stripe_webhook_helpers_1.PAUSE_COLLECTION },
|
|
654
|
+
pauseCollection: null,
|
|
655
|
+
}); },
|
|
656
|
+
shouldFire: true,
|
|
657
|
+
title: 'pause_collection set -> null fires Subscription Resumed',
|
|
658
|
+
})
|
|
659
|
+
// P3: a pause that is only *modified* (behavior or resumes_at changed) is neither a pause nor a
|
|
660
|
+
// resume — firing here would re-trigger every time a customer adjusts an existing pause
|
|
661
|
+
];
|
|
662
|
+
case 49:
|
|
663
|
+
// P2: the mirror — pause_collection cleared is a resume
|
|
664
|
+
_e.sent();
|
|
665
|
+
// P3: a pause that is only *modified* (behavior or resumes_at changed) is neither a pause nor a
|
|
666
|
+
// resume — firing here would re-trigger every time a customer adjusts an existing pause
|
|
667
|
+
return [4 /*yield*/, run_pause_case({
|
|
668
|
+
label: 'p3-pause-modified',
|
|
669
|
+
type: 'Subscription Paused',
|
|
670
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
671
|
+
stripeCustomerId: stripeCustomerId,
|
|
672
|
+
previousAttributes: { pause_collection: { behavior: 'void', resumes_at: null } },
|
|
673
|
+
pauseCollection: { behavior: 'keep_as_draft', resumes_at: null },
|
|
674
|
+
}); },
|
|
675
|
+
shouldFire: false,
|
|
676
|
+
title: 'a modified pause_collection fires neither trigger',
|
|
677
|
+
})
|
|
678
|
+
// P4: the volume guard. customer.subscription.updated fires on every renewal, price change and
|
|
679
|
+
// cancel_at_period_end toggle; with no pause_collection key in previous_attributes the handler
|
|
680
|
+
// must do nothing (and, in the API, must not even reach the enduser lookup).
|
|
681
|
+
];
|
|
682
|
+
case 50:
|
|
683
|
+
// P3: a pause that is only *modified* (behavior or resumes_at changed) is neither a pause nor a
|
|
684
|
+
// resume — firing here would re-trigger every time a customer adjusts an existing pause
|
|
685
|
+
_e.sent();
|
|
686
|
+
// P4: the volume guard. customer.subscription.updated fires on every renewal, price change and
|
|
687
|
+
// cancel_at_period_end toggle; with no pause_collection key in previous_attributes the handler
|
|
688
|
+
// must do nothing (and, in the API, must not even reach the enduser lookup).
|
|
689
|
+
return [4 /*yield*/, run_pause_case({
|
|
690
|
+
label: 'p4-unrelated-update',
|
|
691
|
+
type: 'Subscription Paused',
|
|
692
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
693
|
+
stripeCustomerId: stripeCustomerId,
|
|
694
|
+
previousAttributes: { items: { object: 'list', data: [] } },
|
|
695
|
+
}); },
|
|
696
|
+
shouldFire: false,
|
|
697
|
+
title: 'an unrelated subscription update fires neither trigger',
|
|
698
|
+
})
|
|
699
|
+
// P4b: the idempotency case. previous_attributes carries only what changed, so an unrelated
|
|
700
|
+
// update to an already-paused subscription has no pause_collection key even though
|
|
701
|
+
// pause_collection is currently set. Firing here would re-trigger on every renewal, quantity
|
|
702
|
+
// change and coupon edit for the whole duration of a pause — which is what would happen if the
|
|
703
|
+
// gate were ever "simplified" to read current state instead of the transition.
|
|
704
|
+
];
|
|
705
|
+
case 51:
|
|
706
|
+
// P4: the volume guard. customer.subscription.updated fires on every renewal, price change and
|
|
707
|
+
// cancel_at_period_end toggle; with no pause_collection key in previous_attributes the handler
|
|
708
|
+
// must do nothing (and, in the API, must not even reach the enduser lookup).
|
|
709
|
+
_e.sent();
|
|
710
|
+
// P4b: the idempotency case. previous_attributes carries only what changed, so an unrelated
|
|
711
|
+
// update to an already-paused subscription has no pause_collection key even though
|
|
712
|
+
// pause_collection is currently set. Firing here would re-trigger on every renewal, quantity
|
|
713
|
+
// change and coupon edit for the whole duration of a pause — which is what would happen if the
|
|
714
|
+
// gate were ever "simplified" to read current state instead of the transition.
|
|
715
|
+
return [4 /*yield*/, run_pause_case({
|
|
716
|
+
label: 'p4b-update-while-paused',
|
|
717
|
+
type: 'Subscription Paused',
|
|
718
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
719
|
+
stripeCustomerId: stripeCustomerId,
|
|
720
|
+
previousAttributes: { quantity: 1 },
|
|
721
|
+
pauseCollection: stripe_webhook_helpers_1.PAUSE_COLLECTION,
|
|
722
|
+
}); },
|
|
723
|
+
shouldFire: false,
|
|
724
|
+
title: 'an unrelated update while already paused does not re-fire Subscription Paused',
|
|
725
|
+
})
|
|
726
|
+
// P5/P6: the dedicated events, which Stripe emits for status='paused' transitions only
|
|
727
|
+
];
|
|
728
|
+
case 52:
|
|
729
|
+
// P4b: the idempotency case. previous_attributes carries only what changed, so an unrelated
|
|
730
|
+
// update to an already-paused subscription has no pause_collection key even though
|
|
731
|
+
// pause_collection is currently set. Firing here would re-trigger on every renewal, quantity
|
|
732
|
+
// change and coupon edit for the whole duration of a pause — which is what would happen if the
|
|
733
|
+
// gate were ever "simplified" to read current state instead of the transition.
|
|
734
|
+
_e.sent();
|
|
735
|
+
// P5/P6: the dedicated events, which Stripe emits for status='paused' transitions only
|
|
736
|
+
return [4 /*yield*/, run_pause_case({
|
|
737
|
+
label: 'p5-dedicated-paused',
|
|
738
|
+
type: 'Subscription Paused',
|
|
739
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_paused_event)({ stripeCustomerId: stripeCustomerId }); },
|
|
740
|
+
shouldFire: true,
|
|
741
|
+
title: 'customer.subscription.paused fires Subscription Paused',
|
|
742
|
+
})];
|
|
743
|
+
case 53:
|
|
744
|
+
// P5/P6: the dedicated events, which Stripe emits for status='paused' transitions only
|
|
745
|
+
_e.sent();
|
|
746
|
+
return [4 /*yield*/, run_pause_case({
|
|
747
|
+
label: 'p6-dedicated-resumed',
|
|
748
|
+
type: 'Subscription Resumed',
|
|
749
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_resumed_event)({ stripeCustomerId: stripeCustomerId }); },
|
|
750
|
+
shouldFire: true,
|
|
751
|
+
title: 'customer.subscription.resumed fires Subscription Resumed',
|
|
752
|
+
})
|
|
753
|
+
// P7: a Paused trigger must not fire on a resume, and vice versa — the two are separate types
|
|
754
|
+
];
|
|
755
|
+
case 54:
|
|
756
|
+
_e.sent();
|
|
757
|
+
// P7: a Paused trigger must not fire on a resume, and vice versa — the two are separate types
|
|
758
|
+
return [4 /*yield*/, run_pause_case({
|
|
759
|
+
label: 'p7-wrong-direction',
|
|
760
|
+
type: 'Subscription Paused',
|
|
761
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_resumed_event)({ stripeCustomerId: stripeCustomerId }); },
|
|
762
|
+
shouldFire: false,
|
|
763
|
+
title: 'a resume does not fire a Subscription Paused trigger',
|
|
764
|
+
})
|
|
765
|
+
// P8: the dedupe guard — the .updated that accompanies a status='paused' transition must not
|
|
766
|
+
// fire on top of the dedicated event. Stripe isn't expected to send this combination at all;
|
|
767
|
+
// the case pins the guard so a future refactor can't quietly drop it.
|
|
768
|
+
];
|
|
769
|
+
case 55:
|
|
770
|
+
// P7: a Paused trigger must not fire on a resume, and vice versa — the two are separate types
|
|
771
|
+
_e.sent();
|
|
772
|
+
// P8: the dedupe guard — the .updated that accompanies a status='paused' transition must not
|
|
773
|
+
// fire on top of the dedicated event. Stripe isn't expected to send this combination at all;
|
|
774
|
+
// the case pins the guard so a future refactor can't quietly drop it.
|
|
775
|
+
return [4 /*yield*/, run_pause_case({
|
|
776
|
+
label: 'p8-paused-status-suppressed',
|
|
777
|
+
type: 'Subscription Paused',
|
|
778
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
779
|
+
stripeCustomerId: stripeCustomerId,
|
|
780
|
+
previousAttributes: { pause_collection: null, status: 'trialing' },
|
|
781
|
+
pauseCollection: stripe_webhook_helpers_1.PAUSE_COLLECTION,
|
|
782
|
+
status: 'paused',
|
|
783
|
+
}); },
|
|
784
|
+
shouldFire: false,
|
|
785
|
+
title: 'a pause_collection change coinciding with status=paused is suppressed',
|
|
786
|
+
})
|
|
787
|
+
/* ---- product scoping, over the pause_collection path ---- */
|
|
788
|
+
// P9-P12: the same matrix the payment-failed cases cover, applied to the new triggers
|
|
789
|
+
];
|
|
790
|
+
case 56:
|
|
791
|
+
// P8: the dedupe guard — the .updated that accompanies a status='paused' transition must not
|
|
792
|
+
// fire on top of the dedicated event. Stripe isn't expected to send this combination at all;
|
|
793
|
+
// the case pins the guard so a future refactor can't quietly drop it.
|
|
794
|
+
_e.sent();
|
|
795
|
+
/* ---- product scoping, over the pause_collection path ---- */
|
|
796
|
+
// P9-P12: the same matrix the payment-failed cases cover, applied to the new triggers
|
|
797
|
+
return [4 /*yield*/, run_pause_case({
|
|
798
|
+
label: 'p9-scoped-matching',
|
|
799
|
+
type: 'Subscription Paused',
|
|
800
|
+
productIds: [productA.id],
|
|
801
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
802
|
+
stripeCustomerId: stripeCustomerId,
|
|
803
|
+
items: [(0, stripe_webhook_helpers_1.item_with_price_product)(stripeProductA_1)],
|
|
804
|
+
previousAttributes: { pause_collection: null },
|
|
805
|
+
pauseCollection: stripe_webhook_helpers_1.PAUSE_COLLECTION,
|
|
806
|
+
}); },
|
|
807
|
+
shouldFire: true,
|
|
808
|
+
title: 'Subscription Paused scoped to the paused product fires',
|
|
809
|
+
})];
|
|
810
|
+
case 57:
|
|
811
|
+
/* ---- product scoping, over the pause_collection path ---- */
|
|
812
|
+
// P9-P12: the same matrix the payment-failed cases cover, applied to the new triggers
|
|
813
|
+
_e.sent();
|
|
814
|
+
return [4 /*yield*/, run_pause_case({
|
|
815
|
+
label: 'p10-scoped-nonmatching',
|
|
816
|
+
type: 'Subscription Paused',
|
|
817
|
+
productIds: [productB.id],
|
|
818
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
819
|
+
stripeCustomerId: stripeCustomerId,
|
|
820
|
+
items: [(0, stripe_webhook_helpers_1.item_with_price_product)(stripeProductA_1)],
|
|
821
|
+
previousAttributes: { pause_collection: null },
|
|
822
|
+
pauseCollection: stripe_webhook_helpers_1.PAUSE_COLLECTION,
|
|
823
|
+
}); },
|
|
824
|
+
shouldFire: false,
|
|
825
|
+
title: 'Subscription Paused scoped to another product does not fire',
|
|
826
|
+
})];
|
|
827
|
+
case 58:
|
|
828
|
+
_e.sent();
|
|
829
|
+
return [4 /*yield*/, run_pause_case({
|
|
830
|
+
label: 'p11-empty-list',
|
|
831
|
+
type: 'Subscription Paused',
|
|
832
|
+
productIds: [],
|
|
833
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
834
|
+
stripeCustomerId: stripeCustomerId,
|
|
835
|
+
items: [(0, stripe_webhook_helpers_1.item_with_price_product)(stripeProductA_1)],
|
|
836
|
+
previousAttributes: { pause_collection: null },
|
|
837
|
+
pauseCollection: stripe_webhook_helpers_1.PAUSE_COLLECTION,
|
|
838
|
+
}); },
|
|
839
|
+
shouldFire: true,
|
|
840
|
+
title: 'Subscription Paused with empty productIds fires',
|
|
841
|
+
})
|
|
842
|
+
// plan.product and expanded price.product objects both have to resolve, same as the invoice path
|
|
843
|
+
];
|
|
844
|
+
case 59:
|
|
845
|
+
_e.sent();
|
|
846
|
+
// plan.product and expanded price.product objects both have to resolve, same as the invoice path
|
|
847
|
+
return [4 /*yield*/, run_pause_case({
|
|
848
|
+
label: 'p12-plan-product',
|
|
849
|
+
type: 'Subscription Resumed',
|
|
850
|
+
productIds: [productA.id],
|
|
851
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_updated_event)({
|
|
852
|
+
stripeCustomerId: stripeCustomerId,
|
|
853
|
+
items: [(0, stripe_webhook_helpers_1.item_with_plan_product)(stripeProductA_1)],
|
|
854
|
+
previousAttributes: { pause_collection: stripe_webhook_helpers_1.PAUSE_COLLECTION },
|
|
855
|
+
pauseCollection: null,
|
|
856
|
+
}); },
|
|
857
|
+
shouldFire: true,
|
|
858
|
+
title: 'plan.product resolves for a scoped Subscription Resumed trigger',
|
|
859
|
+
})];
|
|
860
|
+
case 60:
|
|
861
|
+
// plan.product and expanded price.product objects both have to resolve, same as the invoice path
|
|
862
|
+
_e.sent();
|
|
863
|
+
return [4 /*yield*/, run_pause_case({
|
|
864
|
+
label: 'p13-expanded-product',
|
|
865
|
+
type: 'Subscription Resumed',
|
|
866
|
+
productIds: [productA.id],
|
|
867
|
+
event: function (stripeCustomerId) { return (0, stripe_webhook_helpers_1.subscription_resumed_event)({
|
|
868
|
+
stripeCustomerId: stripeCustomerId,
|
|
869
|
+
items: [(0, stripe_webhook_helpers_1.item_with_expanded_price_product)(stripeProductA_1)],
|
|
870
|
+
}); },
|
|
871
|
+
shouldFire: true,
|
|
872
|
+
title: 'expanded price.product resolves on the dedicated resumed event',
|
|
873
|
+
})
|
|
874
|
+
/* ---- validator ---- */
|
|
875
|
+
// P14: same round-trip guarantees as F7, for both new types
|
|
876
|
+
];
|
|
877
|
+
case 61:
|
|
878
|
+
_e.sent();
|
|
879
|
+
return [4 /*yield*/, new_trigger({
|
|
880
|
+
title: "Stripe subscription paused (validator round-trip) ".concat(RUN),
|
|
881
|
+
event: { type: 'Subscription Paused', info: { productIds: [productA.id] } },
|
|
882
|
+
tags: [tag('p14')],
|
|
883
|
+
})];
|
|
884
|
+
case 62:
|
|
885
|
+
p14Scoped = _e.sent();
|
|
886
|
+
return [4 /*yield*/, sdk.api.automation_triggers.getOne(p14Scoped.id)];
|
|
887
|
+
case 63:
|
|
888
|
+
p14Fetched = _e.sent();
|
|
889
|
+
(0, testing_1.assert)(JSON.stringify((_d = p14Fetched.event.info) === null || _d === void 0 ? void 0 : _d.productIds) === JSON.stringify([productA.id]), "expected productIds to round-trip, got ".concat(JSON.stringify(p14Fetched.event)), 'Subscription Paused productIds round-trips through the validator');
|
|
890
|
+
return [4 /*yield*/, new_trigger({
|
|
891
|
+
title: "Stripe subscription resumed (no info) ".concat(RUN),
|
|
892
|
+
event: { type: 'Subscription Resumed' },
|
|
893
|
+
tags: [tag('p14-no-info')],
|
|
894
|
+
})];
|
|
895
|
+
case 64:
|
|
896
|
+
_e.sent();
|
|
897
|
+
(0, testing_1.assert)(true, '', 'Subscription Resumed with omitted info accepted');
|
|
898
|
+
rejectedInvalidPausedProductId = false;
|
|
899
|
+
_e.label = 65;
|
|
900
|
+
case 65:
|
|
901
|
+
_e.trys.push([65, 67, , 68]);
|
|
902
|
+
return [4 /*yield*/, new_trigger({
|
|
903
|
+
title: "Stripe subscription paused (invalid productId) ".concat(RUN),
|
|
904
|
+
event: { type: 'Subscription Paused', info: { productIds: ['not-a-mongo-id'] } },
|
|
905
|
+
tags: [tag('p14-invalid')],
|
|
906
|
+
})];
|
|
907
|
+
case 66:
|
|
908
|
+
_e.sent();
|
|
909
|
+
return [3 /*break*/, 68];
|
|
910
|
+
case 67:
|
|
911
|
+
e_2 = _e.sent();
|
|
912
|
+
rejectedInvalidPausedProductId = true;
|
|
913
|
+
return [3 /*break*/, 68];
|
|
914
|
+
case 68:
|
|
915
|
+
(0, testing_1.assert)(rejectedInvalidPausedProductId, 'expected a non-ObjectId productId to be rejected', 'invalid Subscription Paused productId rejected');
|
|
916
|
+
console.log("All Stripe webhook trigger tests passed!");
|
|
917
|
+
return [3 /*break*/, 72];
|
|
918
|
+
case 69:
|
|
919
|
+
// Best-effort only: the harness exits the process on the first failed assertion, which skips
|
|
920
|
+
// this block. setup_stripe_test_integration clears integration residue on the next run.
|
|
921
|
+
return [4 /*yield*/, cleanup().catch(console.error)];
|
|
922
|
+
case 70:
|
|
923
|
+
// Best-effort only: the harness exits the process on the first failed assertion, which skips
|
|
924
|
+
// this block. setup_stripe_test_integration clears integration residue on the next run.
|
|
925
|
+
_e.sent();
|
|
926
|
+
return [4 /*yield*/, Promise.all(__spreadArray(__spreadArray(__spreadArray([], createdTriggerIds.map(function (id) { return sdk.api.automation_triggers.deleteOne(id).catch(console.error); }), true), createdProductIds.map(function (id) { return sdk.api.products.deleteOne(id).catch(console.error); }), true), createdEnduserIds.map(function (id) { return sdk.api.endusers.deleteOne(id).catch(console.error); }), true))];
|
|
927
|
+
case 71:
|
|
928
|
+
_e.sent();
|
|
929
|
+
return [7 /*endfinally*/];
|
|
930
|
+
case 72: return [2 /*return*/];
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
};
|
|
935
|
+
exports.stripe_webhook_triggers_tests = stripe_webhook_triggers_tests;
|
|
936
|
+
// Allow running this test file independently
|
|
937
|
+
if (require.main === module) {
|
|
938
|
+
console.log("Using API URL: ".concat(host));
|
|
939
|
+
var sdk_2 = new sdk_1.Session({ host: host });
|
|
940
|
+
var sdkNonAdmin_1 = new sdk_1.Session({ host: host });
|
|
941
|
+
var runTests = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
942
|
+
return __generator(this, function (_a) {
|
|
943
|
+
switch (_a.label) {
|
|
944
|
+
case 0: return [4 /*yield*/, (0, setup_1.setup_tests)(sdk_2, sdkNonAdmin_1)];
|
|
945
|
+
case 1:
|
|
946
|
+
_a.sent();
|
|
947
|
+
return [4 /*yield*/, (0, exports.stripe_webhook_triggers_tests)({ sdk: sdk_2, sdkNonAdmin: sdkNonAdmin_1 })];
|
|
948
|
+
case 2:
|
|
949
|
+
_a.sent();
|
|
950
|
+
return [2 /*return*/];
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
}); };
|
|
954
|
+
runTests()
|
|
955
|
+
.then(function () {
|
|
956
|
+
console.log("Stripe webhook triggers test suite completed successfully");
|
|
957
|
+
process.exit(0);
|
|
958
|
+
})
|
|
959
|
+
.catch(function (error) {
|
|
960
|
+
console.error("Stripe webhook triggers test suite failed:", error);
|
|
961
|
+
process.exit(1);
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
//# sourceMappingURL=stripe_webhook_triggers.test.js.map
|