backend-manager 5.11.5 → 5.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/package.json +1 -1
  2. package/src/cli/commands/serve.js +49 -2
  3. package/src/manager/functions/core/actions/api/general/add-marketing-contact.js +4 -1
  4. package/src/manager/libraries/prompts/infer-contact.md +5 -0
  5. package/src/manager/routes/marketing/contact/post.js +4 -1
  6. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/metadata.json +14 -0
  7. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/newsletter.html +486 -0
  8. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/newsletter.md +41 -0
  9. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/newsletter.mjml +97 -0
  10. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/structure.json +42 -0
  11. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/summary.md +1 -0
  12. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/metadata.json +14 -0
  13. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/newsletter.html +486 -0
  14. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/newsletter.md +41 -0
  15. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/newsletter.mjml +97 -0
  16. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/structure.json +42 -0
  17. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/summary.md +1 -0
  18. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/metadata.json +14 -0
  19. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/newsletter.html +486 -0
  20. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/newsletter.md +41 -0
  21. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/newsletter.mjml +97 -0
  22. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/structure.json +42 -0
  23. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/summary.md +1 -0
  24. package/src/test/fixtures/firebase-project/.temp/test-mode.json +1 -1
  25. package/src/test/fixtures/firebase-project/database-debug.log +8 -8
  26. package/src/test/fixtures/firebase-project/firestore-debug.log +56 -54
  27. package/src/test/fixtures/firebase-project/pubsub-debug.log +4 -4
  28. package/test/email/marketing-lifecycle.js +5 -0
  29. package/test/email/validation.js +42 -7
  30. package/test/functions/admin/edit-post.js +14 -10
  31. package/test/functions/general/add-marketing-contact.js +56 -16
  32. package/test/routes/admin/post.js +14 -10
  33. package/test/routes/marketing/contact.js +43 -10
@@ -9,9 +9,11 @@
9
9
  // Test email patterns - look like real emails but +bem suffix identifies them for cleanup.
10
10
  // Names should be inferred by AI from the email local part.
11
11
  //
12
- // Fixed test domain (`acme.com`) — deterministic across brands. Using the running brand's
13
- // domain caused cross-brand state divergence in SendGrid/Beehiiv and non-deterministic
14
- // company inference (different domain different inferred company name).
12
+ // Fixed test domain (`itwcreativeworks.com`) — deterministic across brands (using the
13
+ // running brand's domain caused cross-brand state divergence in SendGrid/Beehiiv and
14
+ // non-deterministic company inference), and we control it: mailbox verdicts are stable
15
+ // and any double-opt-in email lands on our own domain. Was `acme.com`, but the AI now
16
+ // treats that domain as a placeholder (empty names, confidence 0), breaking inference.
15
17
  //
16
18
  // `valid`: use a name that won't be flagged as fictional/placeholder by the AI prompt.
17
19
  // (The infer-contact prompt rejects fictional names — e.g. "rachel.greene" sometimes
@@ -21,7 +23,7 @@
21
23
  // NOT start with "test"/"example" which are in BLOCKED_LOCAL_PATTERNS, NOT be on
22
24
  // a corporate/disposable domain). Real-looking name on a real domain with no actual
23
25
  // mailbox there is the safest pick.
24
- const TEST_DOMAIN = 'acme.com';
26
+ const TEST_DOMAIN = 'itwcreativeworks.com';
25
27
  const TEST_EMAILS = {
26
28
  valid: () => `sarah.martinez+bem@${TEST_DOMAIN}`, // Should infer: Sarah Martinez
27
29
  invalid: () => `nonexistent.user+bem@${TEST_DOMAIN}`, // No such mailbox — mailbox verification should flag as invalid
@@ -164,6 +166,10 @@ module.exports = {
164
166
  email: testEmail,
165
167
  source: 'bem-test',
166
168
  // No firstName/lastName - should be inferred
169
+ // skipValidation bypasses the mailbox verification check — this test's concern
170
+ // is AI name inference (which runs after validation); without it the mailbox
171
+ // check 400s the fabricated address before the inference asserts run.
172
+ skipValidation: true,
167
173
  });
168
174
 
169
175
  assert.isSuccess(response, 'Add marketing contact should succeed');
@@ -245,6 +251,27 @@ module.exports = {
245
251
  source: 'bem-test',
246
252
  });
247
253
 
254
+ // Outcome A: the provider rejected the fabricated mailbox → admin 400. Every
255
+ // earlier check passes deterministically for this address (format/localPart/
256
+ // disposable/corporate return their own dedicated messages, typo can't match,
257
+ // and the domain has real MX), so reaching the generic validation-failed 400
258
+ // proves the paid mailbox pipeline ran and rejected. Nothing was added, so
259
+ // cleanup is a no-op.
260
+ if (response.status === 400) {
261
+ assert.ok(
262
+ response.error?.includes('Email validation failed'),
263
+ `400 should be the validation-failed branch, got: ${response.error}`
264
+ );
265
+
266
+ if (response.error.includes('(dns)')) {
267
+ skip('Transient DNS failure — mailbox check never ran');
268
+ }
269
+
270
+ assert.ok(response.error.includes('(mailbox)'), `Failed check should be mailbox, got: ${response.error}`);
271
+ return;
272
+ }
273
+
274
+ // Outcome B: provider accepted (valid/catch-all/unknown) or failed open.
248
275
  assert.isSuccess(response, 'Add marketing contact should succeed');
249
276
 
250
277
  // Check that validation info is included
@@ -256,9 +283,10 @@ module.exports = {
256
283
 
257
284
  const mbResult = response.data.validation.checks.mailbox;
258
285
 
259
- // If out of credits, skip test - not a failure
260
- if (mbResult.error?.includes('out of credits')) {
261
- skip('Mailbox verification out of credits');
286
+ // Provider errors fail open as { valid: true, error, provider } with NO status
287
+ // field — out of credits, bad key, timeouts. Nothing assertable; skip.
288
+ if (mbResult.error) {
289
+ skip(`Mailbox verification unavailable: ${mbResult.error}`);
262
290
  }
263
291
 
264
292
  assert.hasProperty(mbResult, 'status', 'Mailbox check should return status');
@@ -301,13 +329,18 @@ module.exports = {
301
329
  // verify here is that mailbox check ran and didn't mark the email as `valid`.
302
330
  const mbResult = response.data?.validation?.checks?.mailbox;
303
331
 
304
- // If credits are out, the test can't actually exercise rejection skip.
305
- if (mbResult?.error?.includes('out of credits') || mbResult?.error?.includes('Invalid API key')) {
306
- skip('Mailbox verification out of credits');
332
+ // Provider errors fail open with no status — the test can't exercise rejection; skip.
333
+ if (mbResult?.error) {
334
+ skip(`Mailbox verification unavailable: ${mbResult.error}`);
307
335
  }
308
336
 
309
337
  // If the response was a 400, that's the legitimate rejection path — done.
310
338
  if (response.status === 400) {
339
+ if (response.error?.includes('(dns)')) {
340
+ skip('Transient DNS failure — mailbox check never ran');
341
+ }
342
+
343
+ assert.ok(response.error?.includes('(mailbox)'), `Failed check should be mailbox, got: ${response.error}`);
311
344
  return;
312
345
  }
313
346