feat: complete leave approval MVP

This commit is contained in:
selfrelease
2026-07-18 19:20:07 +08:00
parent 2105fe3bac
commit 090a7e33ce
133 changed files with 7845 additions and 100 deletions
@@ -0,0 +1,80 @@
import 'dart:convert';
import 'package:aioa_mobile/features/requests/data/leave_request_repository.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
final requestJson = {
'id': 'leave-1',
'type': 'PERSONAL',
'startsAt': '2026-07-20T01:00:00Z',
'endsAt': '2026-07-20T05:00:00Z',
'reason': '办理个人事务',
'status': 'DRAFT',
'version': 0,
'createdAt': '2026-07-18T08:00:00Z',
'updatedAt': '2026-07-18T08:00:00Z',
};
setUp(() {
SharedPreferences.setMockInitialValues({});
});
test('loads own leave requests', () async {
final repository = LeaveRequestRepository(
baseUrl: 'https://api.example.test/api/v1',
accessToken: 'token',
client: MockClient(
(_) async => http.Response(
jsonEncode([requestJson]),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
),
),
);
final requests = await repository.list();
expect(requests.single.status, 'DRAFT');
expect(requests.single.reason, '办理个人事务');
});
test('reuses transition idempotency key after network failure', () async {
final keys = <String>[];
var attempt = 0;
final repository = LeaveRequestRepository(
baseUrl: 'https://api.example.test/api/v1',
accessToken: 'token',
client: MockClient((request) async {
keys.add(
request.headers.entries
.firstWhere(
(entry) => entry.key.toLowerCase() == 'idempotency-key',
orElse: () => const MapEntry('', 'missing'),
)
.value,
);
attempt += 1;
if (attempt == 1) throw http.ClientException('offline');
return http.Response(
jsonEncode({...requestJson, 'status': 'PENDING', 'version': 1}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
}),
);
final request = LeaveRequestItem.fromJson(requestJson);
await expectLater(
repository.transition(request, 'submit'),
throwsA(isA<LeaveRequestException>()),
);
final submitted = await repository.transition(request, 'submit');
expect(submitted.status, 'PENDING');
expect(keys[1], keys[0]);
});
}