feat: 实现密码重置功能与用户搜索API,优化注册登录流程

- 新增忘记密码页面与重置密码确认流程(前端+后端)
- 修复注册验证码页登录跳转路由
- 新增用户搜索API(按邮箱查询)
- 简化infra脚本,统一为app.sh
- 补充密码重置与用户API测试覆盖
- 更新runtime文档与AGENTS配置
This commit is contained in:
qzl
2026-02-27 15:22:42 +08:00
parent 0d4811fee5
commit e4e995854d
37 changed files with 2101 additions and 222 deletions
@@ -0,0 +1,65 @@
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:formz/formz.dart';
import 'package:mocktail/mocktail.dart';
import 'package:social_app/features/auth/data/auth_repository.dart';
import 'package:social_app/features/auth/presentation/cubits/reset_password_cubit.dart';
class MockAuthRepository extends Mock implements AuthRepository {}
void main() {
late ResetPasswordCubit cubit;
late MockAuthRepository mockRepository;
setUp(() {
mockRepository = MockAuthRepository();
cubit = ResetPasswordCubit(mockRepository);
});
tearDown(() async {
await cubit.close();
});
test(
'sendCode enters countdown immediately and prevents duplicate clicks',
() async {
final completer = Completer<void>();
when(
() => mockRepository.requestPasswordReset(any()),
).thenAnswer((_) => completer.future);
cubit.emailChanged('test@example.com');
final firstRequest = cubit.sendCode();
await Future<void>.delayed(Duration.zero);
expect(cubit.state.status, FormzSubmissionStatus.inProgress);
expect(cubit.state.codeSent, isTrue);
expect(cubit.state.resendCountdown, 60);
await cubit.sendCode();
verify(
() => mockRepository.requestPasswordReset('test@example.com'),
).called(1);
completer.complete();
await firstRequest;
},
);
test('sendCode failure cancels countdown and restores retry state', () async {
when(
() => mockRepository.requestPasswordReset(any()),
).thenThrow(Exception('network error'));
cubit.emailChanged('test@example.com');
await cubit.sendCode();
expect(cubit.state.status, FormzSubmissionStatus.failure);
expect(cubit.state.codeSent, isFalse);
expect(cubit.state.resendCountdown, 0);
expect(cubit.state.errorMessage, '网络错误,请稍后重试');
});
}