fix(server): mail test & retry (#15044)

#### PR Dependency Tree


* **PR #15044** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Stop sending notifications to disabled users; skip member invites when
workspace names contain URLs/domains
* Improve mail retry handling (per-recipient exhaustion, expiry, and
cache cleanup)
  * Make many email headers/lead lines more generic and consistent
  * Fail-safe workspace content parsing to avoid crashes

* **New Features**
* 24-hour signup protection for sharing, invites, and invite-link
creation
  * Job-queue: remove jobs by payload predicate

* **Tests**
* Expanded tests for mail jobs, SMTP hostname handling, payment
checkout, job-queue removal, and abuse-detection utilities
  * Updated test fixtures to set createdAt timestamps for new users

* **Chores**
  * Added required name input for test-email mutation
  * Database flush retry with deadlock detection/backoff

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/toeverything/AFFiNE/pull/15044?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-05-31 00:06:29 +08:00
committed by GitHub
parent 2bd920fea6
commit b05c387f96
27 changed files with 702 additions and 117 deletions
@@ -328,25 +328,9 @@ test('also move children when dedent collapsed heading', async ({ page }) => {
const paragraph = page.locator('affine-note affine-paragraph');
const subParagraph = paragraph.nth(0).locator('affine-paragraph');
expect(await subParagraph.count()).toBe(2);
expect(
await subParagraph
.nth(0)
.evaluate(
(block: ParagraphBlockComponent) =>
block.model.props.type === 'h1' &&
block.model.props.text.toString() === 'bbb'
)
).toBeTruthy();
expect(
await subParagraph
.nth(1)
.evaluate(
(block: ParagraphBlockComponent) =>
block.model.props.type === 'text' &&
block.model.props.text.toString() === 'ccc'
)
).toBeTruthy();
await expect.poll(() => subParagraph.count()).toBe(2);
await expectParagraphState(subParagraph, 0, 'h1', 'bbb');
await expectParagraphState(subParagraph, 1, 'text', 'ccc');
expect(await subParagraph.nth(1).isVisible()).toBeTruthy();
await subParagraph
@@ -442,7 +426,12 @@ test('unfold collapsed heading when its other blocks indented to be its sibling'
await type(page, '# bbb\nddd');
await page.keyboard.press('ArrowUp');
await pressTab(page);
await page.keyboard.press('ArrowRight');
const paragraph = page.locator('affine-note affine-paragraph');
const subParagraph = paragraph.nth(0).locator('affine-paragraph');
await expect.poll(() => subParagraph.count()).toBe(1);
await expectParagraphState(subParagraph, 0, 'h1', 'bbb');
await subParagraph.nth(0).click();
await page.keyboard.press('End');
await pressEnter(page);
await type(page, 'ccc');
@@ -453,7 +442,6 @@ test('unfold collapsed heading when its other blocks indented to be its sibling'
* ddd
*/
const paragraph = page.locator('affine-note affine-paragraph');
await expectParagraphVisibility(paragraph, 2, true);
await expectParagraphState(paragraph, 2, 'text', 'ccc');
await paragraph.locator('blocksuite-toggle-button .toggle-icon').click();
+1
View File
@@ -116,6 +116,7 @@ export async function createRandomUser(): Promise<{
data: {
...user,
emailVerifiedAt: new Date(),
createdAt: new Date(Date.now() - 25 * 60 * 60 * 1000),
password: await hash(user.password),
features: {
create: {