verifyAuthEmailBackendConformance function

Future<void> verifyAuthEmailBackendConformance(
  1. AuthEmailBackendConformanceFixtureFactory createFixture
)

Verifies atomic one-time semantics required from durable email adapters.

The factory must return a fresh isolated database namespace per case. Passing this suite does not replace adapter-specific commit-fault tests; durable implementations must also prove that a failed database batch or transaction leaves both credential and user state unchanged.

Implementation

Future<void> verifyAuthEmailBackendConformance(
  AuthEmailBackendConformanceFixtureFactory createFixture,
) async {
  await _case('magic-link.concurrent-one-winner', () async {
    final fixture = await createFixture();
    final now = DateTime.utc(2030);
    await fixture.magicLinks.issueMagicLink(
      AuthMagicLinkIssueCommand(_magicRecord('magic-token', now: now)),
    );
    final results = await Future.wait([
      for (var index = 0; index < 16; index++)
        Future.sync(
          () => fixture.magicLinks.consumeMagicLink(
            _magicConsume(
              'magic-token',
              now: now,
              candidateId: 'magic-user-$index',
            ),
          ),
        ),
    ]);
    _check(
      results
              .where(
                (result) =>
                    result.status == AuthMagicLinkConsumeStatus.consumed,
              )
              .length ==
          1,
      'magic-link contention did not have exactly one winner',
    );
    _check(
      results.every(
        (result) =>
            result.status == AuthMagicLinkConsumeStatus.consumed ||
            result.status == AuthMagicLinkConsumeStatus.invalid,
      ),
      'magic-link contention returned an unexpected status',
    );
  });

  await _case('magic-link.provider-isolation', () async {
    final fixture = await createFixture();
    final now = DateTime.utc(2030);
    await fixture.magicLinks.issueMagicLink(
      AuthMagicLinkIssueCommand(_magicRecord('provider-a', now: now)),
    );
    await fixture.magicLinks.issueMagicLink(
      AuthMagicLinkIssueCommand(
        _magicRecord('provider-b', now: now, providerId: 'secondary'),
      ),
    );
    final first = await fixture.magicLinks.consumeMagicLink(
      _magicConsume('provider-a', now: now, candidateId: 'provider-a-user'),
    );
    final second = await fixture.magicLinks.consumeMagicLink(
      _magicConsume(
        'provider-b',
        now: now,
        providerId: 'secondary',
        candidateId: 'provider-b-user',
      ),
    );
    _check(
      first.status == AuthMagicLinkConsumeStatus.consumed &&
          second.status == AuthMagicLinkConsumeStatus.consumed,
      'one provider replaced another provider magic-link digest',
    );
  });

  await _case('email-otp.concurrent-one-winner', () async {
    final fixture = await createFixture();
    final now = DateTime.utc(2030);
    await fixture.emailOtps.issueEmailOtp(
      AuthEmailOtpIssueCommand(_otp('123456', now: now, maxAttempts: 32)),
    );
    final digest = _otpDigest('123456');
    final results = await Future.wait([
      for (var index = 0; index < 16; index++)
        Future.sync(
          () => fixture.emailOtps.verifyEmailOtp(
            AuthEmailOtpVerifyCommand(
              email: _email,
              type: AuthEmailOtpType.signIn,
              codeHash: digest,
              now: now,
            ),
          ),
        ),
    ]);
    _check(
      results
              .where(
                (result) =>
                    result.status == AuthEmailOtpVerificationStatus.verified,
              )
              .length ==
          1,
      'email OTP contention did not have exactly one winner',
    );
  });

  await _case('email-otp.attempt-lockout', () async {
    final fixture = await createFixture();
    final now = DateTime.utc(2030);
    await fixture.emailOtps.issueEmailOtp(
      AuthEmailOtpIssueCommand(_otp('123456', now: now, maxAttempts: 2)),
    );
    final first = await fixture.emailOtps.verifyEmailOtp(
      AuthEmailOtpVerifyCommand(
        email: _email,
        type: AuthEmailOtpType.signIn,
        codeHash: _otpDigest('000000'),
        now: now,
      ),
    );
    final second = await fixture.emailOtps.verifyEmailOtp(
      AuthEmailOtpVerifyCommand(
        email: _email,
        type: AuthEmailOtpType.signIn,
        codeHash: _otpDigest('000000'),
        now: now,
      ),
    );
    final validAfterLock = await fixture.emailOtps.verifyEmailOtp(
      AuthEmailOtpVerifyCommand(
        email: _email,
        type: AuthEmailOtpType.signIn,
        codeHash: _otpDigest('123456'),
        now: now,
      ),
    );
    _check(
      first.status == AuthEmailOtpVerificationStatus.invalid &&
          second.status == AuthEmailOtpVerificationStatus.tooManyAttempts &&
          validAfterLock.status ==
              AuthEmailOtpVerificationStatus.tooManyAttempts,
      'email OTP attempt accounting was not atomic and monotonic',
    );
  });

  await _case('email-otp.sign-in-user-transaction', () async {
    final fixture = await createFixture();
    final now = DateTime.utc(2030);
    await fixture.emailOtps.issueEmailOtp(
      AuthEmailOtpIssueCommand(_otp('654321', now: now, maxAttempts: 32)),
    );
    final results = await Future.wait([
      for (var index = 0; index < 16; index++)
        Future.sync(
          () => fixture.emailOtps.signInWithEmailOtp(
            AuthEmailOtpSignInCommand(
              email: _email,
              codeHash: _otpDigest('654321'),
              now: now,
              candidate: AuthUser(id: 'otp-user-$index', email: _email),
              disableSignUp: false,
            ),
          ),
        ),
    ]);
    final winners = results
        .where(
          (result) => result.status == AuthEmailOtpUserTransitionStatus.applied,
        )
        .toList(growable: false);
    _check(winners.length == 1, 'OTP sign-in had more than one winner');
    final user = await fixture.users.findByEmail(_email);
    _check(
      user != null && user.attributes['emailVerified'] == true,
      'OTP sign-in did not commit one verified user',
    );
  });

  await _case('email-otp.verify-user-binding', () async {
    final fixture = await createFixture();
    final now = DateTime.utc(2030);
    const userId = 'verify-user';
    await fixture.users.create(AuthUser(id: userId, email: _email));
    await fixture.emailOtps.issueEmailOtp(
      AuthEmailOtpIssueCommand(
        _otp('112233', now: now, type: AuthEmailOtpType.emailVerification),
      ),
    );
    final wrong = await fixture.emailOtps.verifyUserEmailWithOtp(
      AuthEmailOtpVerifyUserCommand(
        userId: 'another-user',
        email: _email,
        codeHash: _otpDigest('112233'),
        now: now,
      ),
    );
    _check(
      wrong.status != AuthEmailOtpUserTransitionStatus.applied,
      'email verification ignored the current-user binding',
    );
  });
}