250 lines
11 KiB
Dart
250 lines
11 KiB
Dart
import 'dart:io';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:metatron_student_erp/core/api/api_client.dart';
|
|
import 'package:metatron_student_erp/core/storage/secure_storage.dart';
|
|
import 'package:metatron_student_erp/repositories/announcement_repository.dart';
|
|
import 'package:metatron_student_erp/repositories/attendance_repository.dart';
|
|
import 'package:metatron_student_erp/repositories/auth_repository.dart';
|
|
import 'package:metatron_student_erp/repositories/exam_repository.dart';
|
|
import 'package:metatron_student_erp/repositories/fee_repository.dart';
|
|
import 'package:metatron_student_erp/repositories/result_repository.dart';
|
|
import 'package:metatron_student_erp/repositories/student_repository.dart';
|
|
import 'package:metatron_student_erp/repositories/timetable_repository.dart';
|
|
|
|
class TestSecureStorage extends SecureStorage {
|
|
final Map<String, String> _map = {};
|
|
|
|
TestSecureStorage() : super();
|
|
|
|
@override
|
|
Future<void> saveSessionCookie(String cookie) async {
|
|
_map['session_cookie'] = cookie;
|
|
}
|
|
|
|
@override
|
|
Future<String?> getSessionCookie() async {
|
|
return _map['session_cookie'];
|
|
}
|
|
|
|
@override
|
|
Future<void> clearSessionCookie() async {
|
|
_map.remove('session_cookie');
|
|
}
|
|
|
|
@override
|
|
Future<void> clearAll() async {
|
|
_map.clear();
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
group('Live Odoo Integration Test (End-to-End)', () {
|
|
late ApiClient apiClient;
|
|
late TestSecureStorage storage;
|
|
late AuthRepository authRepo;
|
|
late StudentRepository studentRepo;
|
|
late AttendanceRepository attendanceRepo;
|
|
late TimetableRepository timetableRepo;
|
|
late ExamRepository examRepo;
|
|
late ResultRepository resultRepo;
|
|
late AnnouncementRepository announcementRepo;
|
|
late FeeRepository feeRepo;
|
|
|
|
setUpAll(() async {
|
|
storage = TestSecureStorage();
|
|
apiClient = ApiClient(storage: storage);
|
|
authRepo = AuthRepository(apiClient: apiClient);
|
|
studentRepo = StudentRepository(apiClient: apiClient);
|
|
attendanceRepo = AttendanceRepository(apiClient: apiClient);
|
|
timetableRepo = TimetableRepository(apiClient: apiClient);
|
|
examRepo = ExamRepository(apiClient: apiClient);
|
|
resultRepo = ResultRepository(apiClient: apiClient);
|
|
announcementRepo = AnnouncementRepository(apiClient: apiClient);
|
|
feeRepo = FeeRepository(apiClient: apiClient);
|
|
});
|
|
|
|
test('1. Flutter Login against live Odoo backend returns session and user', () async {
|
|
final res = await authRepo.login(
|
|
email: 'demo.student',
|
|
password: 'Demo@Student2026',
|
|
);
|
|
|
|
expect(res.user.email, 'demo.student');
|
|
expect(res.user.role, 'student');
|
|
expect(res.student, isNotNull);
|
|
expect(res.student!.id, isPositive);
|
|
|
|
final cookie = await storage.getSessionCookie();
|
|
expect(cookie, isNotNull);
|
|
expect(cookie, contains('session_id='));
|
|
stdout.writeln('--> Captured Session Cookie: [REDACTED (session_id present)]');
|
|
});
|
|
|
|
test('2. GET /api/mobile/v1/auth/me returns active session', () async {
|
|
final me = await authRepo.getMe();
|
|
expect(me.user.email, 'demo.student');
|
|
expect(me.student, isNotNull);
|
|
stdout.writeln('--> Auth Me verified: ${me.user.name}');
|
|
});
|
|
|
|
test('3. GET /api/mobile/v1/student/profile parses student profile', () async {
|
|
final profile = await studentRepo.getProfile();
|
|
expect(profile.id, isPositive);
|
|
expect(profile.studentId, isNotEmpty);
|
|
expect(profile.fullName, isNotEmpty);
|
|
stdout.writeln('--> Profile verified: ${profile.fullName} (${profile.studentId})');
|
|
});
|
|
|
|
test('4. GET /api/mobile/v1/student/dashboard returns dashboard data', () async {
|
|
final dashboard = await studentRepo.getDashboard();
|
|
expect(dashboard.attendance, isNotNull);
|
|
expect(dashboard.attendance.percentage, isNonNegative);
|
|
stdout.writeln('--> Dashboard verified. Attendance %: ${dashboard.attendance.percentage}');
|
|
});
|
|
|
|
test('5. GET /api/mobile/v1/student/attendance returns attendance summary', () async {
|
|
final attendance = await attendanceRepo.getAttendance();
|
|
expect(attendance.summary.percentage, isNonNegative);
|
|
expect(attendance.records, isNotEmpty);
|
|
expect(attendance.summary.present, greaterThanOrEqualTo(3));
|
|
stdout.writeln('--> Attendance verified: ${attendance.records.length} records, percentage: ${attendance.summary.percentage}%');
|
|
});
|
|
|
|
test('6. GET /api/mobile/v1/student/timetable returns timetable slots', () async {
|
|
final slots = await timetableRepo.getTimetable();
|
|
expect(slots, isA<List>());
|
|
expect(slots.length, greaterThan(0));
|
|
final firstSlot = slots.first;
|
|
expect(firstSlot.id, isPositive);
|
|
expect(firstSlot.date, isNotEmpty);
|
|
expect(firstSlot.startTime, isNotEmpty);
|
|
expect(firstSlot.endTime, isNotEmpty);
|
|
expect(firstSlot.subject, isNotNull);
|
|
expect(firstSlot.subject!.name, isNotEmpty);
|
|
expect(firstSlot.room, isNotNull);
|
|
stdout.writeln('--> Timetable verified: ${slots.length} slots loaded with subjects and rooms');
|
|
});
|
|
|
|
test('7. GET /api/mobile/v1/student/exams returns exams list', () async {
|
|
final exams = await examRepo.getExams();
|
|
expect(exams, isA<List>());
|
|
stdout.writeln('--> Exams verified: ${exams.length} exams found');
|
|
});
|
|
|
|
test('8. GET /api/mobile/v1/student/results returns results', () async {
|
|
final results = await resultRepo.getResults();
|
|
expect(results, isA<List>());
|
|
stdout.writeln('--> Results verified: ${results.length} results found');
|
|
});
|
|
|
|
test('9. GET /api/mobile/v1/student/announcements and detail return valid notice board data', () async {
|
|
final res = await announcementRepo.getAnnouncements();
|
|
expect(res.announcements, isNotEmpty);
|
|
expect(res.totalCount, isPositive);
|
|
final firstAnn = res.announcements.first;
|
|
expect(firstAnn.id, isPositive);
|
|
expect(firstAnn.title, isNotEmpty);
|
|
expect(firstAnn.type, isNotEmpty);
|
|
stdout.writeln('--> Announcements verified: ${res.announcements.length} loaded (first: "${firstAnn.title}")');
|
|
|
|
final detail = await announcementRepo.getAnnouncementDetail(firstAnn.id);
|
|
expect(detail.id, firstAnn.id);
|
|
expect(detail.title, firstAnn.title);
|
|
stdout.writeln('--> Announcement detail verified: #${detail.id} "${detail.title}"');
|
|
});
|
|
|
|
test('10. GET /api/mobile/v1/student/fees/* endpoints return fee records', () async {
|
|
// Summary
|
|
final summary = await feeRepo.getFeeSummary();
|
|
expect(summary.totalFees, greaterThan(0));
|
|
expect(summary.totalPaid, greaterThanOrEqualTo(0));
|
|
expect(summary.outstandingBalance, isNonNegative);
|
|
expect(summary.paymentStatus, isNotEmpty);
|
|
stdout.writeln('--> Fee Summary verified: total=${summary.totalFees}, paid=${summary.totalPaid}, balance=${summary.outstandingBalance}, status=${summary.paymentStatus}');
|
|
|
|
// Invoices
|
|
final invoicesRes = await feeRepo.getInvoices();
|
|
expect(invoicesRes.invoices, isNotEmpty);
|
|
final firstInv = invoicesRes.invoices.first;
|
|
expect(firstInv.id, isPositive);
|
|
expect(firstInv.invoiceNumber, isNotEmpty);
|
|
expect(firstInv.amount, greaterThan(0));
|
|
stdout.writeln('--> Fee Invoices verified: ${invoicesRes.invoices.length} invoices found (first: ${firstInv.invoiceNumber} - ${firstInv.title})');
|
|
|
|
// Invoice Detail
|
|
final invoiceDetail = await feeRepo.getInvoiceDetail(firstInv.id);
|
|
expect(invoiceDetail.id, firstInv.id);
|
|
expect(invoiceDetail.invoiceNumber, firstInv.invoiceNumber);
|
|
expect(invoiceDetail.lines, isNotEmpty);
|
|
stdout.writeln('--> Fee Invoice Detail verified: #${invoiceDetail.id} has ${invoiceDetail.lines.length} breakdown lines and ${invoiceDetail.payments.length} payments');
|
|
|
|
// Payments
|
|
final paymentsRes = await feeRepo.getPayments();
|
|
expect(paymentsRes.payments, isNotEmpty);
|
|
final firstPay = paymentsRes.payments.first;
|
|
expect(firstPay.id, isPositive);
|
|
expect(firstPay.receiptNumber, isNotEmpty);
|
|
expect(firstPay.amount, greaterThan(0));
|
|
stdout.writeln('--> Fee Payments verified: ${paymentsRes.payments.length} payments found (first: ${firstPay.receiptNumber} - amount: ${firstPay.amount})');
|
|
});
|
|
|
|
test('11. Phase 10: Profile Update, Document Upload, and Certificate Request', () async {
|
|
// 1. Profile Update
|
|
final updatedProfile = await studentRepo.updateProfile({
|
|
'mobile': '+1-555-0199',
|
|
'blood_group': 'B+',
|
|
'emergency_contact': 'Jane Student',
|
|
'emergency_phone': '+1-555-0198',
|
|
'city': 'Metropolis',
|
|
});
|
|
expect(updatedProfile.mobile, '+1-555-0199');
|
|
expect(updatedProfile.bloodGroup?.toUpperCase(), 'B+');
|
|
expect(updatedProfile.emergencyContact, 'Jane Student');
|
|
expect(updatedProfile.city, 'Metropolis');
|
|
expect(updatedProfile.profileCompletion, greaterThanOrEqualTo(50));
|
|
stdout.writeln('--> Profile updated successfully. Completion: ${updatedProfile.profileCompletion}%');
|
|
|
|
// 2. Document Upload
|
|
final uploadedDoc = await studentRepo.uploadDocument(
|
|
name: 'Birth Certificate',
|
|
category: 'identity',
|
|
fileContentBase64: 'JVBERi0xLjQKJcTl8uXrCg==', // Dummy base64 PDF header
|
|
fileName: 'birth_cert.pdf',
|
|
);
|
|
expect(uploadedDoc.id, isPositive);
|
|
expect(uploadedDoc.name, 'Birth Certificate');
|
|
expect(uploadedDoc.verificationStatus, 'submitted');
|
|
stdout.writeln('--> Document uploaded: #${uploadedDoc.id} "${uploadedDoc.name}" [${uploadedDoc.verificationStatus}]');
|
|
|
|
// 3. Document Listing
|
|
final docs = await studentRepo.getDocuments();
|
|
expect(docs, isNotEmpty);
|
|
expect(docs.any((d) => d.id == uploadedDoc.id), isTrue);
|
|
stdout.writeln('--> Document list verified: ${docs.length} documents found');
|
|
|
|
// 4. Certificate Request
|
|
final requestedCert = await studentRepo.requestCertificate(
|
|
certificateType: 'bonafide',
|
|
remarks: 'Applying for bus pass',
|
|
);
|
|
expect(requestedCert.id, isPositive);
|
|
expect(requestedCert.certificateType, 'bonafide');
|
|
expect(requestedCert.state, 'requested');
|
|
stdout.writeln('--> Certificate requested: #${requestedCert.id} "${requestedCert.name}" [${requestedCert.state}]');
|
|
|
|
// 5. Certificate Listing
|
|
final certs = await studentRepo.getCertificates();
|
|
expect(certs, isNotEmpty);
|
|
expect(certs.any((c) => c.id == requestedCert.id), isTrue);
|
|
stdout.writeln('--> Certificate list verified: ${certs.length} certificates found');
|
|
});
|
|
|
|
test('12. POST /api/mobile/v1/auth/logout ends session', () async {
|
|
await authRepo.logout();
|
|
final cookie = await storage.getSessionCookie();
|
|
expect(cookie, isNull);
|
|
stdout.writeln('--> Logout verified. Session cleared.');
|
|
});
|
|
});
|
|
}
|