# Auto-CRRF E2E Test Cases Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add two E2E test cases for the Auto-CRRF feature: one for the DR-trigger path (with DR enable/disable cycling and secondary region switching) and one for the PiTR-trigger path.

**Architecture:** Two new test case files in `cases/dedicated/crrf/`, each implementing the `E2ECase` interface with `BaseCase` embedding. Two new helper methods added to `BackupValidator`. Cases registered in the dedicated case registry.

**Tech Stack:** Go, `retry-go/v4`, E2E framework (`BaseCase` step chaining), `BackupValidator`/`CRRFValidator` helpers, TiDB Cloud console API wrappers.

## Global Constraints

- All E2E cases implement `E2ECase` interface, embed `BaseCase`
- Constructor takes `CloudProviderType`; cases created per provider
- Steps chained via method returns (`Step().Expect().Step()`)
- Cleanup uses `defer` + `RetryDo` — set up cleanup before test steps
- Validators return `nil`/`error` for use with `Expect`
- Case names follow `{CloudProvider}{Feature}{Type}` pattern
- AWS-only for CRRF; non-AWS skips gracefully

---

### Task 1: Add `DisableDualRegion` and `GetDifferentSecondaryRegion` to BackupValidator

**Files:**
- Modify: `test/e2e/cases/dedicated/common/backup_validator.go`

**Interfaces:**
- Produces: `DisableDualRegion(tidbCluster *TiDBDedicatedEntity) error`
- Produces: `GetDifferentSecondaryRegion(tidbCluster *TiDBDedicatedEntity, excludeRegion string) (string, error)`

- [ ] **Step 1: Read the current backup_validator.go to locate the insertion point**

Read `test/e2e/cases/dedicated/common/backup_validator.go`. The new methods go after `EnableDualRegion` (around line 581) and before `ExpectCRRFStatus`.

- [ ] **Step 2: Add `DisableDualRegion` method**

Insert after the `EnableDualRegion` method (after line 581):

```go
// DisableDualRegion downgrades from dual-region to single-region backup
// by setting Regions to only the primary region.
func (v *BackupValidator) DisableDualRegion(tidbCluster *TiDBDedicatedEntity) error {
	req := &consoleapi.ModifyBackupSettingRequest{
		Regions: []*consoleapi.RegionInfoRequest{
			{Name: tidbCluster.Region},
		},
	}
	return v.backupW.ModifyBackupSetting(tidbCluster, req)
}
```

- [ ] **Step 3: Add `GetDifferentSecondaryRegion` method**

Insert after `DisableDualRegion`:

```go
// GetDifferentSecondaryRegion returns a secondary region from backup options
// that is NOT the excludeRegion. Used to test switching secondary regions.
func (v *BackupValidator) GetDifferentSecondaryRegion(tidbCluster *TiDBDedicatedEntity, excludeRegion string) (string, error) {
	params := console_br_params.NewGetBackupOptionsParamsWithContext(log.WithLogger(context.Background(), v.lgr)).
		WithOrgID(tidbCluster.OrgID).
		WithProjectID(tidbCluster.ProjectID).
		WithClusterID(tidbCluster.ClusterID)

	resp, err := consoleapi.BrBackupServiceClient().GetBackupOptions(params, nil)
	if err != nil {
		return "", err
	}

	if resp.Payload == nil || resp.Payload.DualRegionOptions == nil {
		return "", fmt.Errorf("no dual_region_options for cluster %s", tidbCluster.ClusterID)
	}

	for _, opt := range resp.Payload.DualRegionOptions.RegionOptions {
		if opt != nil && opt.Region != nil && opt.Region.Name != "" && opt.Region.Name != excludeRegion {
			return opt.Region.Name, nil
		}
	}
	return "", fmt.Errorf("no secondary region other than %s available for cluster %s", excludeRegion, tidbCluster.ClusterID)
}
```

- [ ] **Step 4: Verify the file compiles**

Run: `cd /home/nil/pingcap/workbench/repos/dataflow-service/test/e2e && go build ./cases/dedicated/common/...`

Expected: No errors.

- [ ] **Step 5: Commit**

```bash
git add test/e2e/cases/dedicated/common/backup_validator.go
git commit -m "feat(e2e): add DisableDualRegion and GetDifferentSecondaryRegion to BackupValidator"
```

---

### Task 2: Create Track 2 — PiTR-Trigger Path test case

This is the simpler of the two cases, so we implement it first.

**Files:**
- Create: `test/e2e/cases/dedicated/crrf/crrf_auto_pitr_trigger.go`

**Interfaces:**
- Consumes: `BackupValidator.EnableDualRegion`, `BackupValidator.EnablePiTR`, `BackupValidator.DisablePiTR`, `BackupValidator.TriggerSnapshotBackupAndWait`, `BackupValidator.WaitForBackupSettingReady`, `BackupValidator.ExpectCRRFStatus`, `BackupValidator.Client().QueryBackupSetting`, `CRRFValidator.DisableIfEnabled`
- Produces: `NewCRRFAutoPiTRTriggerCase(cloudProvider CloudProviderType) E2ECase`

- [ ] **Step 1: Create the test case file**

Create `test/e2e/cases/dedicated/crrf/crrf_auto_pitr_trigger.go`:

```go
package crrf

import (
	"fmt"
	"time"

	"github.com/avast/retry-go/v4"
	. "github.com/tidbcloud/dataflow-service-e2e/cases"
	"github.com/tidbcloud/dataflow-service-e2e/cases/dedicated/common"
	"github.com/tidbcloud/dataflow-service-e2e/internal/config"
	. "github.com/tidbcloud/dataflow-service-e2e/internal/model"
	"github.com/tidbcloud/dataflow-service-e2e/internal/tidbcloud/dedicated/consoleapi"
)

// crrfAutoPiTRTriggerCase tests the Auto-CRRF PiTR-trigger path:
// Enable DR → Enable PiTR → snapshot → CRRF auto-enables → disable PiTR → CRRF auto-disables.
type crrfAutoPiTRTriggerCase struct {
	BaseCase
	backupValidator *common.BackupValidator
	crrfValidator   *common.CRRFValidator
	cloudProvider   CloudProviderType
}

func NewCRRFAutoPiTRTriggerCase(cloudProvider CloudProviderType) E2ECase {
	return &crrfAutoPiTRTriggerCase{cloudProvider: cloudProvider}
}

func (m *crrfAutoPiTRTriggerCase) Name() string {
	return fmt.Sprintf("%sCRRFAutoPiTRTrigger", m.cloudProvider)
}

func (m *crrfAutoPiTRTriggerCase) Tags() map[string]string {
	return map[string]string{TagKeyDomain: TagValueDomainCRRF}
}

func (m *crrfAutoPiTRTriggerCase) DeclareDependencyRequirements() map[string]DependencyRequirement {
	return map[string]DependencyRequirement{
		"tidb": &TiDBDedicatedRequirement{
			Dedicated: true,
			TiDBDedicatedDimensions: TiDBDedicatedDimensions{
				CloudProvider: m.cloudProvider,
				Region:        config.GlobalConfig.DefaultRegionOf(m.cloudProvider),
			},
		},
	}
}

func (m *crrfAutoPiTRTriggerCase) Run() {
	tidbCluster := m.TiDBDedicated("tidb")

	m.backupValidator = common.NewBackupValidator(m.Logger())
	m.crrfValidator = common.NewCRRFValidator(m.Logger())

	// CRRF is AWS-only
	if m.cloudProvider != AWS {
		m.Logger().Info("skipping Auto-CRRF PiTR trigger test for non-AWS provider %s", m.cloudProvider)
		return
	}

	var (
		secondaryRegion       string
		originalPiTREnabled   bool
		originalRegions       []string
		originalStateCaptured bool
	)

	// Cleanup: restore original backup setting
	defer func() {
		if !originalStateCaptured {
			return
		}

		m.Logger().Info("BEGIN-cleanup: restoring original backup setting")

		// Disable CRRF if still enabled
		m.RetryDo("cleanup: disable CRRF if enabled", func() error {
			return m.crrfValidator.DisableIfEnabled(tidbCluster)
		}, 3, 20*time.Second)

		// Wait for backup setting ready
		m.RetryDo("cleanup: wait backup setting ready", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 3*time.Minute)
		}, 3, 20*time.Second)

		// Restore regions
		currentResp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err == nil && !equalStringSet(regionNamesFromResponse(currentResp.Regions), originalRegions) {
			m.RetryDo("cleanup: restore regions", func() error {
				return m.backupValidator.Client().ModifyBackupSetting(tidbCluster, &consoleapi.ModifyBackupSettingRequest{
					Regions: regionRequestsFromNames(originalRegions),
				})
			}, 3, 20*time.Second)
		}

		// Wait for backup setting ready
		m.RetryDo("cleanup: wait backup setting ready after region restore", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 3*time.Minute)
		}, 3, 20*time.Second)

		// Restore PiTR
		currentResp, err = m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err == nil {
			currentPiTR := currentResp.ContinuousBackupSchedule != nil && currentResp.ContinuousBackupSchedule.Enable
			if currentPiTR != originalPiTREnabled {
				m.RetryDo("cleanup: restore PiTR", func() error {
					return m.backupValidator.Client().ModifyBackupSetting(tidbCluster, &consoleapi.ModifyBackupSettingRequest{
						ContinuousBackupSchedule: &consoleapi.ContinuousBackupScheduleRequest{
							Enable: originalPiTREnabled,
						},
					})
				}, 3, 20*time.Second)
			}
		}

		m.Logger().Info("END-cleanup: original backup setting restored")
	}()

	m.Step("capture original backup setting", func() error {
		resp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err != nil {
			return err
		}
		originalPiTREnabled = resp.ContinuousBackupSchedule != nil && resp.ContinuousBackupSchedule.Enable
		originalRegions = regionNamesFromResponse(resp.Regions)
		originalStateCaptured = true
		return nil
	})

	m.
		Step("enable dual-region backup", func() error {
			var err error
			secondaryRegion, err = m.backupValidator.GetSecondaryRegion(tidbCluster)
			if err != nil {
				return err
			}
			return m.backupValidator.EnableDualRegion(tidbCluster, secondaryRegion)
		}).
		Step("wait backup setting ready after dual-region enable", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Step("enable PiTR", func() error {
			return m.backupValidator.EnablePiTR(tidbCluster)
		}).
		Step("wait backup setting ready after PiTR enable", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Step("trigger snapshot backup for CRRF activation", func() error {
			_, err := m.backupValidator.TriggerSnapshotBackupAndWait(
				tidbCluster,
				GenerateClusterName("crrf-pitr-trigger"),
				30*time.Minute,
			)
			return err
		}).
		Expect("CRRF auto enabled via PiTR trigger", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "enabled")
		}, 30, 30*time.Second).
		Step("wait backup setting ready before disabling PiTR", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Step("disable PiTR to auto disable CRRF", func() error {
			return m.backupValidator.DisablePiTR(tidbCluster)
		}).
		Step("wait backup setting ready after PiTR disable", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Expect("CRRF auto disabled via PiTR disable", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "disabled")
		}, 30, 20*time.Second)
}
```

- [ ] **Step 2: Verify the file compiles**

Run: `cd /home/nil/pingcap/workbench/repos/dataflow-service/test/e2e && go build ./cases/dedicated/crrf/...`

Expected: No errors.

- [ ] **Step 3: Commit**

```bash
git add test/e2e/cases/dedicated/crrf/crrf_auto_pitr_trigger.go
git commit -m "feat(e2e): add Auto-CRRF PiTR-trigger path test case"
```

---

### Task 3: Create Track 1 — DR-Trigger Path test case

**Files:**
- Create: `test/e2e/cases/dedicated/crrf/crrf_auto_dr_trigger.go`

**Interfaces:**
- Consumes: `BackupValidator.EnableDualRegion`, `BackupValidator.DisableDualRegion`, `BackupValidator.EnablePiTR`, `BackupValidator.DisablePiTR`, `BackupValidator.TriggerSnapshotBackupAndWait`, `BackupValidator.WaitForBackupSettingReady`, `BackupValidator.ExpectCRRFStatus`, `BackupValidator.GetSecondaryRegion`, `BackupValidator.GetDifferentSecondaryRegion`, `BackupValidator.Client().QueryBackupSetting`, `CRRFValidator.DisableIfEnabled`
- Produces: `NewCRRFAutoDRTriggerCase(cloudProvider CloudProviderType) E2ECase`

- [ ] **Step 1: Create the test case file**

Create `test/e2e/cases/dedicated/crrf/crrf_auto_dr_trigger.go`:

```go
package crrf

import (
	"fmt"
	"time"

	"github.com/avast/retry-go/v4"
	. "github.com/tidbcloud/dataflow-service-e2e/cases"
	"github.com/tidbcloud/dataflow-service-e2e/cases/dedicated/common"
	"github.com/tidbcloud/dataflow-service-e2e/internal/config"
	. "github.com/tidbcloud/dataflow-service-e2e/internal/model"
	"github.com/tidbcloud/dataflow-service-e2e/internal/tidbcloud/dedicated/consoleapi"
)

// crrfAutoDRTriggerCase tests the Auto-CRRF DR-trigger path:
// Enable PiTR → snapshot → enable DR → CRRF auto-enables → disable DR → CRRF auto-disables
// → re-enable DR (same region) → CRRF auto-enables → disable DR → CRRF auto-disables
// → re-enable DR (different region) → snapshot → CRRF auto-enables.
type crrfAutoDRTriggerCase struct {
	BaseCase
	backupValidator *common.BackupValidator
	crrfValidator   *common.CRRFValidator
	cloudProvider   CloudProviderType
}

func NewCRRFAutoDRTriggerCase(cloudProvider CloudProviderType) E2ECase {
	return &crrfAutoDRTriggerCase{cloudProvider: cloudProvider}
}

func (m *crrfAutoDRTriggerCase) Name() string {
	return fmt.Sprintf("%sCRRFAutoDRTrigger", m.cloudProvider)
}

func (m *crrfAutoDRTriggerCase) Tags() map[string]string {
	return map[string]string{TagKeyDomain: TagValueDomainCRRF}
}

func (m *crrfAutoDRTriggerCase) DeclareDependencyRequirements() map[string]DependencyRequirement {
	return map[string]DependencyRequirement{
		"tidb": &TiDBDedicatedRequirement{
			Dedicated: true,
			TiDBDedicatedDimensions: TiDBDedicatedDimensions{
				CloudProvider: m.cloudProvider,
				Region:        config.GlobalConfig.DefaultRegionOf(m.cloudProvider),
			},
		},
	}
}

func (m *crrfAutoDRTriggerCase) Run() {
	tidbCluster := m.TiDBDedicated("tidb")

	m.backupValidator = common.NewBackupValidator(m.Logger())
	m.crrfValidator = common.NewCRRFValidator(m.Logger())

	// CRRF is AWS-only
	if m.cloudProvider != AWS {
		m.Logger().Info("skipping Auto-CRRF DR trigger test for non-AWS provider %s", m.cloudProvider)
		return
	}

	var (
		secondaryRegion       string
		originalPiTREnabled   bool
		originalRegions       []string
		originalStateCaptured bool
	)

	// Cleanup: restore original backup setting
	defer func() {
		if !originalStateCaptured {
			return
		}

		m.Logger().Info("BEGIN-cleanup: restoring original backup setting")

		// Disable CRRF if still enabled
		m.RetryDo("cleanup: disable CRRF if enabled", func() error {
			return m.crrfValidator.DisableIfEnabled(tidbCluster)
		}, 3, 20*time.Second)

		// Wait for backup setting ready
		m.RetryDo("cleanup: wait backup setting ready", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 3*time.Minute)
		}, 3, 20*time.Second)

		// Restore regions
		currentResp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err == nil && !equalStringSet(regionNamesFromResponse(currentResp.Regions), originalRegions) {
			m.RetryDo("cleanup: restore regions", func() error {
				return m.backupValidator.Client().ModifyBackupSetting(tidbCluster, &consoleapi.ModifyBackupSettingRequest{
					Regions: regionRequestsFromNames(originalRegions),
				})
			}, 3, 20*time.Second)
		}

		// Wait for backup setting ready
		m.RetryDo("cleanup: wait backup setting ready after region restore", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 3*time.Minute)
		}, 3, 20*time.Second)

		// Restore PiTR
		currentResp, err = m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err == nil {
			currentPiTR := currentResp.ContinuousBackupSchedule != nil && currentResp.ContinuousBackupSchedule.Enable
			if currentPiTR != originalPiTREnabled {
				m.RetryDo("cleanup: restore PiTR", func() error {
					return m.backupValidator.Client().ModifyBackupSetting(tidbCluster, &consoleapi.ModifyBackupSettingRequest{
						ContinuousBackupSchedule: &consoleapi.ContinuousBackupScheduleRequest{
							Enable: originalPiTREnabled,
						},
					})
				}, 3, 20*time.Second)
			}
		}

		m.Logger().Info("END-cleanup: original backup setting restored")
	}()

	m.Step("capture original backup setting", func() error {
		resp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err != nil {
			return err
		}
		originalPiTREnabled = resp.ContinuousBackupSchedule != nil && resp.ContinuousBackupSchedule.Enable
		originalRegions = regionNamesFromResponse(resp.Regions)
		originalStateCaptured = true
		return nil
	})

	// Phase 1: Setup PiTR + snapshot
	m.
		Step("enable PiTR", func() error {
			return m.backupValidator.EnablePiTR(tidbCluster)
		}).
		Step("wait backup setting ready after PiTR enable", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Step("trigger snapshot backup to activate log backup", func() error {
			_, err := m.backupValidator.TriggerSnapshotBackupAndWait(
				tidbCluster,
				GenerateClusterName("crrf-dr-setup"),
				30*time.Minute,
			)
			return err
		})

	// Phase 2: First DR enable/disable cycle (secondary region S1)
	m.
		Step("resolve secondary region S1", func() error {
			var err error
			secondaryRegion, err = m.backupValidator.GetSecondaryRegion(tidbCluster)
			return err
		}).
		Step("enable dual-region backup with S1", func() error {
			return m.backupValidator.EnableDualRegion(tidbCluster, secondaryRegion)
		}).
		Step("wait backup setting ready after DR enable", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Expect("CRRF auto enabled (DR trigger, S1)", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "enabled")
		}, 30, 30*time.Second).
		Step("disable dual-region backup", func() error {
			return m.backupValidator.DisableDualRegion(tidbCluster)
		}).
		Step("wait backup setting ready after DR disable", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Expect("CRRF auto disabled (DR disable)", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "disabled")
		}, 30, 20*time.Second)

	// Phase 3: Second DR enable/disable cycle (same secondary region S1)
	m.
		Step("re-enable dual-region backup with S1", func() error {
			return m.backupValidator.EnableDualRegion(tidbCluster, secondaryRegion)
		}).
		Step("wait backup setting ready after DR re-enable", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Expect("CRRF auto enabled (DR re-trigger, S1)", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "enabled")
		}, 30, 30*time.Second).
		Step("disable dual-region backup again", func() error {
			return m.backupValidator.DisableDualRegion(tidbCluster)
		}).
		Step("wait backup setting ready after DR disable again", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Expect("CRRF auto disabled (DR disable again)", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "disabled")
		}, 30, 20*time.Second)

	// Phase 4: DR with different secondary region S2
	differentRegion, err := m.backupValidator.GetDifferentSecondaryRegion(tidbCluster, secondaryRegion)
	if err != nil {
		m.Logger().Info("skipping different-secondary-region test: only 1 secondary region available (%v)", err)
		return
	}

	m.
		Step("enable dual-region backup with S2 (different region)", func() error {
			return m.backupValidator.EnableDualRegion(tidbCluster, differentRegion)
		}).
		Step("wait backup setting ready after DR enable with S2", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Step("trigger snapshot backup for S2 activation", func() error {
			_, err := m.backupValidator.TriggerSnapshotBackupAndWait(
				tidbCluster,
				GenerateClusterName("crrf-dr-s2"),
				30*time.Minute,
			)
			return err
		}).
		Expect("CRRF auto enabled (DR trigger, S2)", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "enabled")
		}, 30, 30*time.Second)
}
```

- [ ] **Step 2: Verify the file compiles**

Run: `cd /home/nil/pingcap/workbench/repos/dataflow-service/test/e2e && go build ./cases/dedicated/crrf/...`

Expected: No errors.

- [ ] **Step 3: Commit**

```bash
git add test/e2e/cases/dedicated/crrf/crrf_auto_dr_trigger.go
git commit -m "feat(e2e): add Auto-CRRF DR-trigger path test case"
```

---

### Task 4: Register both cases in the case registry

**Files:**
- Modify: `test/e2e/internal/case_driver/cases_registry_dedicated.go`

**Interfaces:**
- Consumes: `crrf.NewCRRFAutoDRTriggerCase`, `crrf.NewCRRFAutoPiTRTriggerCase`

- [ ] **Step 1: Read the current registry to find the CRRF registration block**

Read `test/e2e/internal/case_driver/cases_registry_dedicated.go`. The CRRF cases are registered around line 148-150:

```go
awsCRRFEnableDisableSmokeCase := crrf.NewCRRFEnableDisableSmokeCase(AWS)
awsCRRFRestoreSmokeCase := crrf.NewCRRFRestoreSmokeCase(AWS)
addCases(awsCRRFEnableDisableSmokeCase, awsCRRFRestoreSmokeCase)
```

- [ ] **Step 2: Add new case registrations after the existing CRRF block**

After line 150, add:

```go
	awsCRRFAutoDRTriggerCase := crrf.NewCRRFAutoDRTriggerCase(AWS)
	awsCRRFAutoPiTRTriggerCase := crrf.NewCRRFAutoPiTRTriggerCase(AWS)
	addCases(awsCRRFAutoDRTriggerCase, awsCRRFAutoPiTRTriggerCase)
```

- [ ] **Step 3: Add both cases to the `awsWeeklyRegression` group**

In the `awsWeeklyRegression` group (around line 238), after `awsCRRFRestoreSmokeCase`, add:

```go
			awsCRRFAutoDRTriggerCase,
			awsCRRFAutoPiTRTriggerCase,
```

- [ ] **Step 4: Verify the file compiles**

Run: `cd /home/nil/pingcap/workbench/repos/dataflow-service/test/e2e && go build ./internal/case_driver/...`

Expected: No errors.

- [ ] **Step 5: Verify the full e2e module compiles**

Run: `cd /home/nil/pingcap/workbench/repos/dataflow-service/test/e2e && go build ./...`

Expected: No errors.

- [ ] **Step 6: Commit**

```bash
git add test/e2e/internal/case_driver/cases_registry_dedicated.go
git commit -m "feat(e2e): register Auto-CRRF DR-trigger and PiTR-trigger cases"
```
