diff --git a/mobile/lib/core/data/database.dart b/mobile/lib/core/data/database.dart index 04c2ce35..615beefc 100644 --- a/mobile/lib/core/data/database.dart +++ b/mobile/lib/core/data/database.dart @@ -1,8 +1,11 @@ import 'package:mobile/features/class_management/data/models/classroom_model.dart'; +import 'package:mobile/features/school_management/data/models/school_model.dart'; import 'package:mobile/features/student_management/data/models/student_model.dart'; import 'package:mobile/features/text_management/data/models/text_model.dart'; import 'package:mobile/features/user_management/data/models/user_model.dart'; -import 'package:mobile/features/user_management/domain/entities/user.dart'; +import "../../features/user_management/domain/entities/user.dart"; +import "../../features/school_management/domain/entities/school.dart"; +import 'serializer.dart'; import 'package:moor/ffi.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as p; @@ -26,7 +29,13 @@ LazyDatabase openConnection() { }); } -@UseMoor(tables: [UserModels, ClassroomModels, StudentModels, TextModels]) +@UseMoor(tables: [ + UserModels, + ClassroomModels, + StudentModels, + TextModels, + SchoolModels +]) class Database extends _$Database { Database(QueryExecutor e) : super(e); Database.customExecutor(QueryExecutor executor) : super(executor); @@ -120,6 +129,26 @@ class Database extends _$Database { return update(textModels).replace(entry); } + /// returns the pk of the added entry + Future insertSchool(SchoolModelsCompanion modelCompanion) async { + return into(schoolModels).insert(modelCompanion); + } + + ///Returns the number of deleted rows + Future deleteSchool(int id) async { + return (delete(schoolModels)..where((t) => t.localId.equals(id))).go(); + } + + ///Returns a list of schoolModels, and an empty list when the table is empty + Future> getSchools(int userId) async { + return (select(schoolModels)..where((t) => t.userId.equals(userId))).get(); + } + + ///Returns true if the school was updated, false otherwise + Future updateSchool(SchoolModel entry) async { + return update(schoolModels).replace(entry); + } + @override int get schemaVersion => 3; } diff --git a/mobile/lib/core/data/database.g.dart b/mobile/lib/core/data/database.g.dart index 0e8d4a38..d5158e5e 100644 --- a/mobile/lib/core/data/database.g.dart +++ b/mobile/lib/core/data/database.g.dart @@ -451,11 +451,13 @@ class ClassroomModel extends DataClass implements Insertable { final int grade; final String name; final int tutorId; + final int schoolId; ClassroomModel( {@required this.localId, @required this.grade, @required this.name, - @required this.tutorId}); + @required this.tutorId, + @required this.schoolId}); factory ClassroomModel.fromData( Map data, GeneratedDatabase db, {String prefix}) { @@ -469,6 +471,8 @@ class ClassroomModel extends DataClass implements Insertable { name: stringType.mapFromDatabaseResponse(data['${effectivePrefix}name']), tutorId: intType.mapFromDatabaseResponse(data['${effectivePrefix}tutor_id']), + schoolId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}school_id']), ); } @override @@ -486,6 +490,9 @@ class ClassroomModel extends DataClass implements Insertable { if (!nullToAbsent || tutorId != null) { map['tutor_id'] = Variable(tutorId); } + if (!nullToAbsent || schoolId != null) { + map['school_id'] = Variable(schoolId); + } return map; } @@ -500,6 +507,9 @@ class ClassroomModel extends DataClass implements Insertable { tutorId: tutorId == null && nullToAbsent ? const Value.absent() : Value(tutorId), + schoolId: schoolId == null && nullToAbsent + ? const Value.absent() + : Value(schoolId), ); } @@ -511,6 +521,7 @@ class ClassroomModel extends DataClass implements Insertable { grade: serializer.fromJson(json['grade']), name: serializer.fromJson(json['name']), tutorId: serializer.fromJson(json['tutor_id']), + schoolId: serializer.fromJson(json['school_id']), ); } @override @@ -521,15 +532,18 @@ class ClassroomModel extends DataClass implements Insertable { 'grade': serializer.toJson(grade), 'name': serializer.toJson(name), 'tutor_id': serializer.toJson(tutorId), + 'school_id': serializer.toJson(schoolId), }; } - ClassroomModel copyWith({int localId, int grade, String name, int tutorId}) => + ClassroomModel copyWith( + {int localId, int grade, String name, int tutorId, int schoolId}) => ClassroomModel( localId: localId ?? this.localId, grade: grade ?? this.grade, name: name ?? this.name, tutorId: tutorId ?? this.tutorId, + schoolId: schoolId ?? this.schoolId, ); @override String toString() { @@ -537,14 +551,17 @@ class ClassroomModel extends DataClass implements Insertable { ..write('localId: $localId, ') ..write('grade: $grade, ') ..write('name: $name, ') - ..write('tutorId: $tutorId') + ..write('tutorId: $tutorId, ') + ..write('schoolId: $schoolId') ..write(')')) .toString(); } @override - int get hashCode => $mrjf($mrjc(localId.hashCode, - $mrjc(grade.hashCode, $mrjc(name.hashCode, tutorId.hashCode)))); + int get hashCode => $mrjf($mrjc( + localId.hashCode, + $mrjc(grade.hashCode, + $mrjc(name.hashCode, $mrjc(tutorId.hashCode, schoolId.hashCode))))); @override bool operator ==(dynamic other) => identical(this, other) || @@ -552,7 +569,8 @@ class ClassroomModel extends DataClass implements Insertable { other.localId == this.localId && other.grade == this.grade && other.name == this.name && - other.tutorId == this.tutorId); + other.tutorId == this.tutorId && + other.schoolId == this.schoolId); } class ClassroomModelsCompanion extends UpdateCompanion { @@ -560,31 +578,37 @@ class ClassroomModelsCompanion extends UpdateCompanion { final Value grade; final Value name; final Value tutorId; + final Value schoolId; const ClassroomModelsCompanion({ this.localId = const Value.absent(), this.grade = const Value.absent(), this.name = const Value.absent(), this.tutorId = const Value.absent(), + this.schoolId = const Value.absent(), }); ClassroomModelsCompanion.insert({ this.localId = const Value.absent(), @required int grade, @required String name, @required int tutorId, + @required int schoolId, }) : grade = Value(grade), name = Value(name), - tutorId = Value(tutorId); + tutorId = Value(tutorId), + schoolId = Value(schoolId); static Insertable custom({ Expression localId, Expression grade, Expression name, Expression tutorId, + Expression schoolId, }) { return RawValuesInsertable({ if (localId != null) 'local_id': localId, if (grade != null) 'grade': grade, if (name != null) 'name': name, if (tutorId != null) 'tutor_id': tutorId, + if (schoolId != null) 'school_id': schoolId, }); } @@ -592,12 +616,14 @@ class ClassroomModelsCompanion extends UpdateCompanion { {Value localId, Value grade, Value name, - Value tutorId}) { + Value tutorId, + Value schoolId}) { return ClassroomModelsCompanion( localId: localId ?? this.localId, grade: grade ?? this.grade, name: name ?? this.name, tutorId: tutorId ?? this.tutorId, + schoolId: schoolId ?? this.schoolId, ); } @@ -616,6 +642,9 @@ class ClassroomModelsCompanion extends UpdateCompanion { if (tutorId.present) { map['tutor_id'] = Variable(tutorId.value); } + if (schoolId.present) { + map['school_id'] = Variable(schoolId.value); + } return map; } @@ -625,7 +654,8 @@ class ClassroomModelsCompanion extends UpdateCompanion { ..write('localId: $localId, ') ..write('grade: $grade, ') ..write('name: $name, ') - ..write('tutorId: $tutorId') + ..write('tutorId: $tutorId, ') + ..write('schoolId: $schoolId') ..write(')')) .toString(); } @@ -678,8 +708,18 @@ class $ClassroomModelsTable extends ClassroomModels $customConstraints: 'NOT NULL REFERENCES user_models(local_id)'); } + final VerificationMeta _schoolIdMeta = const VerificationMeta('schoolId'); + GeneratedIntColumn _schoolId; + @override + GeneratedIntColumn get schoolId => _schoolId ??= _constructSchoolId(); + GeneratedIntColumn _constructSchoolId() { + return GeneratedIntColumn('school_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES school_models(local_id)'); + } + @override - List get $columns => [localId, grade, name, tutorId]; + List get $columns => + [localId, grade, name, tutorId, schoolId]; @override $ClassroomModelsTable get asDslTable => this; @override @@ -713,6 +753,12 @@ class $ClassroomModelsTable extends ClassroomModels } else if (isInserting) { context.missing(_tutorIdMeta); } + if (data.containsKey('school_id')) { + context.handle(_schoolIdMeta, + schoolId.isAcceptableOrUnknown(data['school_id'], _schoolIdMeta)); + } else if (isInserting) { + context.missing(_schoolIdMeta); + } return context; } @@ -1310,6 +1356,489 @@ class $TextModelsTable extends TextModels } } +class SchoolModel extends DataClass implements Insertable { + final int localId; + final int zipCode; + final Modality modality; + final String state; + final String city; + final String neighborhood; + final String name; + final int userId; + SchoolModel( + {@required this.localId, + @required this.zipCode, + @required this.modality, + @required this.state, + @required this.city, + @required this.neighborhood, + @required this.name, + @required this.userId}); + factory SchoolModel.fromData(Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + return SchoolModel( + localId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}local_id']), + zipCode: + intType.mapFromDatabaseResponse(data['${effectivePrefix}zip_code']), + modality: $SchoolModelsTable.$converter0.mapToDart( + intType.mapFromDatabaseResponse(data['${effectivePrefix}modality'])), + state: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}state']), + city: stringType.mapFromDatabaseResponse(data['${effectivePrefix}city']), + neighborhood: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}neighborhood']), + name: stringType.mapFromDatabaseResponse(data['${effectivePrefix}name']), + userId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || localId != null) { + map['local_id'] = Variable(localId); + } + if (!nullToAbsent || zipCode != null) { + map['zip_code'] = Variable(zipCode); + } + if (!nullToAbsent || modality != null) { + final converter = $SchoolModelsTable.$converter0; + map['modality'] = Variable(converter.mapToSql(modality)); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || neighborhood != null) { + map['neighborhood'] = Variable(neighborhood); + } + if (!nullToAbsent || name != null) { + map['name'] = Variable(name); + } + if (!nullToAbsent || userId != null) { + map['user_id'] = Variable(userId); + } + return map; + } + + SchoolModelsCompanion toCompanion(bool nullToAbsent) { + return SchoolModelsCompanion( + localId: localId == null && nullToAbsent + ? const Value.absent() + : Value(localId), + zipCode: zipCode == null && nullToAbsent + ? const Value.absent() + : Value(zipCode), + modality: modality == null && nullToAbsent + ? const Value.absent() + : Value(modality), + state: + state == null && nullToAbsent ? const Value.absent() : Value(state), + city: city == null && nullToAbsent ? const Value.absent() : Value(city), + neighborhood: neighborhood == null && nullToAbsent + ? const Value.absent() + : Value(neighborhood), + name: name == null && nullToAbsent ? const Value.absent() : Value(name), + userId: + userId == null && nullToAbsent ? const Value.absent() : Value(userId), + ); + } + + factory SchoolModel.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return SchoolModel( + localId: serializer.fromJson(json['local_id']), + zipCode: serializer.fromJson(json['zip_code']), + modality: serializer.fromJson(json['modality']), + state: serializer.fromJson(json['state']), + city: serializer.fromJson(json['city']), + neighborhood: serializer.fromJson(json['neighborhood']), + name: serializer.fromJson(json['name']), + userId: serializer.fromJson(json['user_id']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'local_id': serializer.toJson(localId), + 'zip_code': serializer.toJson(zipCode), + 'modality': serializer.toJson(modality), + 'state': serializer.toJson(state), + 'city': serializer.toJson(city), + 'neighborhood': serializer.toJson(neighborhood), + 'name': serializer.toJson(name), + 'user_id': serializer.toJson(userId), + }; + } + + SchoolModel copyWith( + {int localId, + int zipCode, + Modality modality, + String state, + String city, + String neighborhood, + String name, + int userId}) => + SchoolModel( + localId: localId ?? this.localId, + zipCode: zipCode ?? this.zipCode, + modality: modality ?? this.modality, + state: state ?? this.state, + city: city ?? this.city, + neighborhood: neighborhood ?? this.neighborhood, + name: name ?? this.name, + userId: userId ?? this.userId, + ); + @override + String toString() { + return (StringBuffer('SchoolModel(') + ..write('localId: $localId, ') + ..write('zipCode: $zipCode, ') + ..write('modality: $modality, ') + ..write('state: $state, ') + ..write('city: $city, ') + ..write('neighborhood: $neighborhood, ') + ..write('name: $name, ') + ..write('userId: $userId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc( + localId.hashCode, + $mrjc( + zipCode.hashCode, + $mrjc( + modality.hashCode, + $mrjc( + state.hashCode, + $mrjc( + city.hashCode, + $mrjc(neighborhood.hashCode, + $mrjc(name.hashCode, userId.hashCode)))))))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is SchoolModel && + other.localId == this.localId && + other.zipCode == this.zipCode && + other.modality == this.modality && + other.state == this.state && + other.city == this.city && + other.neighborhood == this.neighborhood && + other.name == this.name && + other.userId == this.userId); +} + +class SchoolModelsCompanion extends UpdateCompanion { + final Value localId; + final Value zipCode; + final Value modality; + final Value state; + final Value city; + final Value neighborhood; + final Value name; + final Value userId; + const SchoolModelsCompanion({ + this.localId = const Value.absent(), + this.zipCode = const Value.absent(), + this.modality = const Value.absent(), + this.state = const Value.absent(), + this.city = const Value.absent(), + this.neighborhood = const Value.absent(), + this.name = const Value.absent(), + this.userId = const Value.absent(), + }); + SchoolModelsCompanion.insert({ + this.localId = const Value.absent(), + @required int zipCode, + @required Modality modality, + @required String state, + @required String city, + @required String neighborhood, + @required String name, + @required int userId, + }) : zipCode = Value(zipCode), + modality = Value(modality), + state = Value(state), + city = Value(city), + neighborhood = Value(neighborhood), + name = Value(name), + userId = Value(userId); + static Insertable custom({ + Expression localId, + Expression zipCode, + Expression modality, + Expression state, + Expression city, + Expression neighborhood, + Expression name, + Expression userId, + }) { + return RawValuesInsertable({ + if (localId != null) 'local_id': localId, + if (zipCode != null) 'zip_code': zipCode, + if (modality != null) 'modality': modality, + if (state != null) 'state': state, + if (city != null) 'city': city, + if (neighborhood != null) 'neighborhood': neighborhood, + if (name != null) 'name': name, + if (userId != null) 'user_id': userId, + }); + } + + SchoolModelsCompanion copyWith( + {Value localId, + Value zipCode, + Value modality, + Value state, + Value city, + Value neighborhood, + Value name, + Value userId}) { + return SchoolModelsCompanion( + localId: localId ?? this.localId, + zipCode: zipCode ?? this.zipCode, + modality: modality ?? this.modality, + state: state ?? this.state, + city: city ?? this.city, + neighborhood: neighborhood ?? this.neighborhood, + name: name ?? this.name, + userId: userId ?? this.userId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (localId.present) { + map['local_id'] = Variable(localId.value); + } + if (zipCode.present) { + map['zip_code'] = Variable(zipCode.value); + } + if (modality.present) { + final converter = $SchoolModelsTable.$converter0; + map['modality'] = Variable(converter.mapToSql(modality.value)); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (neighborhood.present) { + map['neighborhood'] = Variable(neighborhood.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SchoolModelsCompanion(') + ..write('localId: $localId, ') + ..write('zipCode: $zipCode, ') + ..write('modality: $modality, ') + ..write('state: $state, ') + ..write('city: $city, ') + ..write('neighborhood: $neighborhood, ') + ..write('name: $name, ') + ..write('userId: $userId') + ..write(')')) + .toString(); + } +} + +class $SchoolModelsTable extends SchoolModels + with TableInfo<$SchoolModelsTable, SchoolModel> { + final GeneratedDatabase _db; + final String _alias; + $SchoolModelsTable(this._db, [this._alias]); + final VerificationMeta _localIdMeta = const VerificationMeta('localId'); + GeneratedIntColumn _localId; + @override + GeneratedIntColumn get localId => _localId ??= _constructLocalId(); + GeneratedIntColumn _constructLocalId() { + return GeneratedIntColumn('local_id', $tableName, false, + hasAutoIncrement: true, declaredAsPrimaryKey: true); + } + + final VerificationMeta _zipCodeMeta = const VerificationMeta('zipCode'); + GeneratedIntColumn _zipCode; + @override + GeneratedIntColumn get zipCode => _zipCode ??= _constructZipCode(); + GeneratedIntColumn _constructZipCode() { + return GeneratedIntColumn( + 'zip_code', + $tableName, + false, + ); + } + + final VerificationMeta _modalityMeta = const VerificationMeta('modality'); + GeneratedIntColumn _modality; + @override + GeneratedIntColumn get modality => _modality ??= _constructModality(); + GeneratedIntColumn _constructModality() { + return GeneratedIntColumn( + 'modality', + $tableName, + false, + ); + } + + final VerificationMeta _stateMeta = const VerificationMeta('state'); + GeneratedTextColumn _state; + @override + GeneratedTextColumn get state => _state ??= _constructState(); + GeneratedTextColumn _constructState() { + return GeneratedTextColumn( + 'state', + $tableName, + false, + ); + } + + final VerificationMeta _cityMeta = const VerificationMeta('city'); + GeneratedTextColumn _city; + @override + GeneratedTextColumn get city => _city ??= _constructCity(); + GeneratedTextColumn _constructCity() { + return GeneratedTextColumn( + 'city', + $tableName, + false, + ); + } + + final VerificationMeta _neighborhoodMeta = + const VerificationMeta('neighborhood'); + GeneratedTextColumn _neighborhood; + @override + GeneratedTextColumn get neighborhood => + _neighborhood ??= _constructNeighborhood(); + GeneratedTextColumn _constructNeighborhood() { + return GeneratedTextColumn( + 'neighborhood', + $tableName, + false, + ); + } + + final VerificationMeta _nameMeta = const VerificationMeta('name'); + GeneratedTextColumn _name; + @override + GeneratedTextColumn get name => _name ??= _constructName(); + GeneratedTextColumn _constructName() { + return GeneratedTextColumn( + 'name', + $tableName, + false, + ); + } + + final VerificationMeta _userIdMeta = const VerificationMeta('userId'); + GeneratedIntColumn _userId; + @override + GeneratedIntColumn get userId => _userId ??= _constructUserId(); + GeneratedIntColumn _constructUserId() { + return GeneratedIntColumn('user_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES user_models(local_id)'); + } + + @override + List get $columns => + [localId, zipCode, modality, state, city, neighborhood, name, userId]; + @override + $SchoolModelsTable get asDslTable => this; + @override + String get $tableName => _alias ?? 'school_models'; + @override + final String actualTableName = 'school_models'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('local_id')) { + context.handle(_localIdMeta, + localId.isAcceptableOrUnknown(data['local_id'], _localIdMeta)); + } + if (data.containsKey('zip_code')) { + context.handle(_zipCodeMeta, + zipCode.isAcceptableOrUnknown(data['zip_code'], _zipCodeMeta)); + } else if (isInserting) { + context.missing(_zipCodeMeta); + } + context.handle(_modalityMeta, const VerificationResult.success()); + if (data.containsKey('state')) { + context.handle( + _stateMeta, state.isAcceptableOrUnknown(data['state'], _stateMeta)); + } else if (isInserting) { + context.missing(_stateMeta); + } + if (data.containsKey('city')) { + context.handle( + _cityMeta, city.isAcceptableOrUnknown(data['city'], _cityMeta)); + } else if (isInserting) { + context.missing(_cityMeta); + } + if (data.containsKey('neighborhood')) { + context.handle( + _neighborhoodMeta, + neighborhood.isAcceptableOrUnknown( + data['neighborhood'], _neighborhoodMeta)); + } else if (isInserting) { + context.missing(_neighborhoodMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, name.isAcceptableOrUnknown(data['name'], _nameMeta)); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('user_id')) { + context.handle(_userIdMeta, + userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + } else if (isInserting) { + context.missing(_userIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {localId}; + @override + SchoolModel map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return SchoolModel.fromData(data, _db, prefix: effectivePrefix); + } + + @override + $SchoolModelsTable createAlias(String alias) { + return $SchoolModelsTable(_db, alias); + } + + static TypeConverter $converter0 = + const EnumIndexConverter(Modality.values); +} + abstract class _$Database extends GeneratedDatabase { _$Database(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); $UserModelsTable _userModels; @@ -1322,9 +1851,12 @@ abstract class _$Database extends GeneratedDatabase { _studentModels ??= $StudentModelsTable(this); $TextModelsTable _textModels; $TextModelsTable get textModels => _textModels ??= $TextModelsTable(this); + $SchoolModelsTable _schoolModels; + $SchoolModelsTable get schoolModels => + _schoolModels ??= $SchoolModelsTable(this); @override Iterable get allTables => allSchemaEntities.whereType(); @override List get allSchemaEntities => - [userModels, classroomModels, studentModels, textModels]; + [userModels, classroomModels, studentModels, textModels, schoolModels]; } diff --git a/mobile/lib/core/data/serializer.dart b/mobile/lib/core/data/serializer.dart new file mode 100644 index 00000000..212ff0a8 --- /dev/null +++ b/mobile/lib/core/data/serializer.dart @@ -0,0 +1,29 @@ +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; +import 'package:moor/moor.dart'; + +class Serializer extends ValueSerializer { + @override + T fromJson(json) { + if (T == Role) + return Role.values[json] as T; + else if (T == Modality) + return Modality.values[json] as T; + else if (json == 'set to null') + return null; + else + return json as T; + } + + @override + toJson(T value) { + if (T == Role) + return (value as Role).index; + else if (T == Modality) + return (value as Modality).index; + else if (value == null) + return 'set to null'; + else + return value; + } +} diff --git a/mobile/lib/features/class_management/data/models/classroom_model.dart b/mobile/lib/features/class_management/data/models/classroom_model.dart index 256f54c5..561d526a 100644 --- a/mobile/lib/features/class_management/data/models/classroom_model.dart +++ b/mobile/lib/features/class_management/data/models/classroom_model.dart @@ -10,6 +10,9 @@ class ClassroomModels extends Table { @JsonKey("tutor_id") IntColumn get tutorId => integer().customConstraint('NOT NULL REFERENCES user_models(local_id)')(); + @JsonKey("school_id") + IntColumn get schoolId => + integer().customConstraint("NOT NULL REFERENCES school_models(local_id)")(); } Classroom classroomModelToEntity(ClassroomModel model) { @@ -17,7 +20,8 @@ Classroom classroomModelToEntity(ClassroomModel model) { id: model.localId, grade: model.grade, name: model.name, - tutorId: model.tutorId); + tutorId: model.tutorId, + schoolId: model.schoolId); } ClassroomModel classroomEntityToModel(Classroom entity) { @@ -25,6 +29,7 @@ ClassroomModel classroomEntityToModel(Classroom entity) { localId: entity.id, grade: entity.grade, name: entity.name, - tutorId: entity.tutorId); + tutorId: entity.tutorId, + schoolId: entity.schoolId); } diff --git a/mobile/lib/features/class_management/domain/entities/classroom.dart b/mobile/lib/features/class_management/domain/entities/classroom.dart index 2a29e4ac..9df8ee0d 100644 --- a/mobile/lib/features/class_management/domain/entities/classroom.dart +++ b/mobile/lib/features/class_management/domain/entities/classroom.dart @@ -2,9 +2,9 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; - class Classroom extends Equatable { final int tutorId; + final int schoolId; final int id; final int grade; final String name; @@ -13,8 +13,9 @@ class Classroom extends Equatable { {@required this.tutorId, @required this.grade, @required this.name, + @required this.schoolId, this.id}); @override - List get props => [tutorId, grade, name, id]; + List get props => [tutorId, grade, name, id, schoolId]; } diff --git a/mobile/lib/features/school_management/data/data_sources/school_local_data_source.dart b/mobile/lib/features/school_management/data/data_sources/school_local_data_source.dart new file mode 100644 index 00000000..7b1962f0 --- /dev/null +++ b/mobile/lib/features/school_management/data/data_sources/school_local_data_source.dart @@ -0,0 +1,88 @@ +import 'package:mobile/core/data/database.dart'; +import 'package:mobile/core/error/exceptions.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:moor/ffi.dart'; +import 'package:moor/moor.dart'; + +abstract class SchoolLocalDataSource { + /// Gets the cached list of [School]. + /// + /// Returns an empty list if no [School] is cached. + /// + /// Throws [CacheException] if something wrong happens. + Future> getSchoolsFromCache(UserModel userModel); + + /// Deletes the [School] passed. + /// + /// Throws [CacheException] if the [School] is not cached. + Future deleteSchoolFromCache(SchoolModel schoolModel); + + /// Caches the new [School] passed. + /// + /// Throws [CacheException] if something wrong happens. + Future cacheNewSchool(SchoolModel schoolModel); + + /// Updated in cache the [School] passed. + /// + /// Throws [CacheException] if [School] is not cached. + Future updateCachedSchool(SchoolModel schoolModel); +} + +class SchoolLocalDataSourceImpl implements SchoolLocalDataSource { + final Database database; + + SchoolLocalDataSourceImpl({@required this.database}); + + @override + Future cacheNewSchool(SchoolModel schoolModel) async { + try { + bool nullToAbsent = true; + final schoolCompanion = schoolModel.toCompanion(nullToAbsent); + final schoolPk = await this.database.insertSchool(schoolCompanion); + return SchoolModel( + userId: schoolModel.userId, + zipCode: schoolModel.zipCode, + modality: schoolModel.modality, + state: schoolModel.state, + city: schoolModel.city, + neighborhood: schoolModel.neighborhood, + name: schoolModel.name, + localId: schoolPk); + } on SqliteException { + throw CacheException(); + } + } + + @override + Future deleteSchoolFromCache(SchoolModel schoolModel) async { + var pk = schoolModel.localId; + try { + var done = await this.database.deleteSchool(pk); + if (done != 1) { + throw SqliteException(787, "The table doesn't have this entry"); + } + } on SqliteException { + throw CacheException(); + } + } + + @override + Future> getSchoolsFromCache(UserModel userModel) async { + final userId = userModel.localId; + try { + return await this.database.getSchools(userId); + } on SqliteException { + throw CacheException(); + } + } + + @override + Future updateCachedSchool(SchoolModel schoolModel) async { + bool updated = await this.database.updateSchool(schoolModel); + if (updated) { + return schoolModel; + } else { + throw CacheException(); + } + } +} diff --git a/mobile/lib/features/school_management/data/models/school_model.dart b/mobile/lib/features/school_management/data/models/school_model.dart new file mode 100644 index 00000000..8efa8373 --- /dev/null +++ b/mobile/lib/features/school_management/data/models/school_model.dart @@ -0,0 +1,42 @@ +import 'package:mobile/core/data/database.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:moor/moor.dart'; + +class SchoolModels extends Table { + @JsonKey("local_id") + IntColumn get localId => integer().autoIncrement()(); + @JsonKey("zip_code") + IntColumn get zipCode => integer()(); + IntColumn get modality => intEnum()(); + TextColumn get state => text()(); + TextColumn get city => text()(); + TextColumn get neighborhood => text()(); + TextColumn get name => text()(); + @JsonKey("user_id") + IntColumn get userId => + integer().customConstraint('NOT NULL REFERENCES user_models(local_id)')(); +} + +School schoolModelToEntity(SchoolModel model) { + return School( + id: model.localId, + zipCode: model.zipCode, + modality: model.modality, + state: model.state, + city: model.city, + neighborhood: model.neighborhood, + name: model.name, + userId: model.userId); +} + +SchoolModel schoolEntityToModel(School entity) { + return SchoolModel( + localId: entity.id, + zipCode: entity.zipCode, + modality: entity.modality, + state: entity.state, + city: entity.city, + neighborhood: entity.neighborhood, + name: entity.name, + userId: entity.userId); +} diff --git a/mobile/lib/features/school_management/data/repositories/school_repository_impl.dart b/mobile/lib/features/school_management/data/repositories/school_repository_impl.dart new file mode 100644 index 00000000..6635ccf5 --- /dev/null +++ b/mobile/lib/features/school_management/data/repositories/school_repository_impl.dart @@ -0,0 +1,85 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mobile/core/error/exceptions.dart'; +import 'package:mobile/core/error/failures.dart'; +import 'package:dartz/dartz.dart'; +import 'package:mobile/features/school_management/data/models/school_model.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/domain/repositories/school_repository.dart'; +import 'package:mobile/features/user_management/data/models/user_model.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; + +import '../data_sources/school_local_data_source.dart'; + +class SchoolRepositoryImpl implements SchoolRepository { + final SchoolLocalDataSource localDataSource; + + SchoolRepositoryImpl({ + @required this.localDataSource, + }); + + @override + Future> createSchool(School school) async { + return await _tryCacheSchool(school); + } + + Future> _tryCacheSchool(School school) async { + try { + var model = schoolEntityToModel(school); + var localModel = await localDataSource.cacheNewSchool(model); + var localSchool = schoolModelToEntity(localModel); + return Right(localSchool); + } on CacheException { + return Left(CacheFailure()); + } + } + + @override + Future> deleteSchool(School school) async { + return await _tryDeleteSchool(school); + } + + Future> _tryDeleteSchool(School school) async { + try { + var model = schoolEntityToModel(school); + await localDataSource.deleteSchoolFromCache(model); + } on CacheException { + return Left(CacheFailure()); + } + } + + @override + Future>> getSchools(User user) async { + return await _tryGetSchools(user); + } + + Future>> _tryGetSchools(User user) async { + try { + var userModel = userEntityToModel(user); + var listSchoolModel = + await localDataSource.getSchoolsFromCache(userModel); + var listSchoolEntity = [ + for (var model in listSchoolModel) schoolModelToEntity(model) + ]; + return Right(listSchoolEntity); + } on CacheException { + return Left(CacheFailure()); + } + } + + @override + Future> updateSchool(School school) async { + return await _tryUpdateSchool(school); + } + + Future> _tryUpdateSchool(School school) async { + try { + var model = schoolEntityToModel(school); + var localModel = await localDataSource.updateCachedSchool(model); + var localSchool = schoolModelToEntity(localModel); + return Right(localSchool); + } on CacheException { + return Left(CacheFailure()); + } + } +} diff --git a/mobile/lib/features/school_management/domain/entities/school.dart b/mobile/lib/features/school_management/domain/entities/school.dart new file mode 100644 index 00000000..6f01fc9f --- /dev/null +++ b/mobile/lib/features/school_management/domain/entities/school.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; + +enum Modality { municipal, public, federal, private } + +class School extends Equatable { + final int id; + final int userId; + final int zipCode; + final Modality modality; + final String state; + final String city; + final String neighborhood; + final String name; + + School( + {@required this.userId, + @required this.zipCode, + @required this.modality, + @required this.state, + @required this.city, + @required this.neighborhood, + @required this.name, + this.id}); + + @override + List get props => + [userId, zipCode, modality, state, city, neighborhood, name, id]; +} diff --git a/mobile/lib/features/school_management/domain/repositories/school_repository.dart b/mobile/lib/features/school_management/domain/repositories/school_repository.dart new file mode 100644 index 00000000..6b080419 --- /dev/null +++ b/mobile/lib/features/school_management/domain/repositories/school_repository.dart @@ -0,0 +1,11 @@ +import 'package:dartz/dartz.dart'; +import '../../../user_management/domain/entities/user.dart'; +import '../../../../core/error/failures.dart'; +import '../entities/school.dart'; + +abstract class SchoolRepository { + Future> createSchool(School classroom); + Future> updateSchool(School classroom); + Future>> getSchools(User user); + Future> deleteSchool(School school); +} diff --git a/mobile/lib/features/school_management/domain/use_cases/create_school_use_case.dart b/mobile/lib/features/school_management/domain/use_cases/create_school_use_case.dart new file mode 100644 index 00000000..548661d3 --- /dev/null +++ b/mobile/lib/features/school_management/domain/use_cases/create_school_use_case.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:mobile/core/error/failures.dart'; +import 'package:dartz/dartz.dart'; +import 'package:mobile/core/use_cases/use_case.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/domain/repositories/school_repository.dart'; +import 'package:mobile/features/school_management/domain/use_cases/school_params.dart'; + +class CreateSchool implements UseCase { + final SchoolRepository repository; + + CreateSchool({@required this.repository}); + + @override + Future> call(SchoolParams params) => + repository.createSchool(params.school); +} diff --git a/mobile/lib/features/school_management/domain/use_cases/delete_school_use_case.dart b/mobile/lib/features/school_management/domain/use_cases/delete_school_use_case.dart new file mode 100644 index 00000000..82af1257 --- /dev/null +++ b/mobile/lib/features/school_management/domain/use_cases/delete_school_use_case.dart @@ -0,0 +1,16 @@ +import 'package:flutter/foundation.dart'; +import 'package:mobile/core/error/failures.dart'; +import 'package:dartz/dartz.dart'; +import '../../../../core/use_cases/use_case.dart'; +import '../repositories/school_repository.dart'; +import 'school_params.dart'; + +class DeleteSchool implements UseCase { + final SchoolRepository repository; + + DeleteSchool({@required this.repository}); + + @override + Future> call(SchoolParams params) => + repository.deleteSchool(params.school); +} diff --git a/mobile/lib/features/school_management/domain/use_cases/get_schools_use_case.dart b/mobile/lib/features/school_management/domain/use_cases/get_schools_use_case.dart new file mode 100644 index 00000000..26faf8f9 --- /dev/null +++ b/mobile/lib/features/school_management/domain/use_cases/get_schools_use_case.dart @@ -0,0 +1,27 @@ +import 'package:dartz/dartz.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mobile/core/error/failures.dart'; +import 'package:mobile/core/use_cases/use_case.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/domain/repositories/school_repository.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; + +class GetSchools implements UseCase, UserParams> { + final SchoolRepository repository; + + GetSchools({@required this.repository}); + + @override + Future>> call(UserParams params) => + repository.getSchools(params.user); +} + +class UserParams extends Equatable { + final User user; + + UserParams(this.user); + + @override + List get props => [UserParams]; +} diff --git a/mobile/lib/features/school_management/domain/use_cases/school_params.dart b/mobile/lib/features/school_management/domain/use_cases/school_params.dart new file mode 100644 index 00000000..f20be275 --- /dev/null +++ b/mobile/lib/features/school_management/domain/use_cases/school_params.dart @@ -0,0 +1,12 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; + +class SchoolParams extends Equatable { + final School school; + + SchoolParams({@required this.school}); + + @override + List get props => [SchoolParams]; +} diff --git a/mobile/lib/features/school_management/domain/use_cases/update_school_use_case.dart b/mobile/lib/features/school_management/domain/use_cases/update_school_use_case.dart new file mode 100644 index 00000000..52e2329b --- /dev/null +++ b/mobile/lib/features/school_management/domain/use_cases/update_school_use_case.dart @@ -0,0 +1,20 @@ +import 'package:dartz/dartz.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mobile/core/error/failures.dart'; +import 'package:mobile/core/use_cases/use_case.dart'; +import 'package:mobile/features/class_management/domain/entities/classroom.dart'; +import 'package:mobile/features/class_management/domain/repositories/classroom_repository.dart'; +import 'package:mobile/features/class_management/domain/use_cases/classroom_params.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/domain/repositories/school_repository.dart'; +import 'package:mobile/features/school_management/domain/use_cases/school_params.dart'; + +class UpdateSchool implements UseCase { + final SchoolRepository repository; + + UpdateSchool({@required this.repository}); + + @override + Future> call(SchoolParams params) => + repository.updateSchool(params.school); +} diff --git a/mobile/lib/features/student_management/domain/use_cases/delete_student_use_case.dart b/mobile/lib/features/student_management/domain/use_cases/delete_student_use_case.dart index b53b442d..e6bc6cdd 100644 --- a/mobile/lib/features/student_management/domain/use_cases/delete_student_use_case.dart +++ b/mobile/lib/features/student_management/domain/use_cases/delete_student_use_case.dart @@ -6,7 +6,7 @@ import 'package:mobile/features/student_management/domain/entities/student.dart' import 'package:mobile/features/student_management/domain/repositories/student_repository.dart'; import 'package:mobile/features/student_management/domain/use_cases/student_params.dart'; -class DeleteStudent implements UseCase { +class DeleteStudent implements UseCase { final StudentRepository repository; DeleteStudent({@required this.repository}); diff --git a/mobile/lib/features/user_management/data/models/user_model.dart b/mobile/lib/features/user_management/data/models/user_model.dart index 0d5d0a1a..9fc83401 100644 --- a/mobile/lib/features/user_management/data/models/user_model.dart +++ b/mobile/lib/features/user_management/data/models/user_model.dart @@ -2,28 +2,6 @@ import 'package:mobile/core/data/database.dart'; import 'package:mobile/features/user_management/domain/entities/user.dart'; import 'package:moor/moor.dart'; -class UserSerializer extends ValueSerializer { - @override - T fromJson(json) { - if (T == Role) - return Role.values[json] as T; - else if (json == 'set to null') - return null; - else - return json as T; - } - - @override - toJson(T value) { - if (T == Role) - return (value as Role).index; - else if (value == null) - return 'set to null'; - else - return value; - } -} - class UserModels extends Table { @JsonKey('local_id') IntColumn get localId => integer().autoIncrement()(); diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index c37f4d27..1301bb8a 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -6,6 +6,8 @@ import 'package:mobile/features/text_management/presentation/bloc/text_bloc.dart import 'package:mobile/features/user_management/presentation/bloc/user_bloc.dart'; import 'package:moor/moor.dart'; + +import 'core/data/serializer.dart'; import 'core/presentation/pages/route_generator.dart'; import 'features/class_management/presentation/bloc/classroom_bloc.dart'; import 'features/user_management/data/models/user_model.dart'; @@ -15,7 +17,7 @@ const IS_IN_DEVELOPMENT = true; void main() async { // This setting overrides the default serializer to our custom one - moorRuntimeOptions.defaultSerializer = UserSerializer(); + moorRuntimeOptions.defaultSerializer = Serializer(); WidgetsFlutterBinding.ensureInitialized(); await setUpLocator(); diff --git a/mobile/test/core/data/classroom_database_test.dart b/mobile/test/core/data/classroom_database_test.dart index 37e5a563..26aac2a1 100644 --- a/mobile/test/core/data/classroom_database_test.dart +++ b/mobile/test/core/data/classroom_database_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mobile/core/data/database.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; import 'package:mobile/features/user_management/domain/entities/user.dart'; import 'package:moor/ffi.dart'; import 'package:moor/moor.dart'; @@ -7,6 +8,7 @@ import 'package:matcher/matcher.dart'; void main() { final tValidClassroomPk1 = 1; + final tValidSchoolPk1 = 1; final tValidClassroomPk2 = 2; final tInvalidClassroomPk = 2; final tValidUserPk = 1; @@ -14,36 +16,57 @@ void main() { final grade = 1; final name = "A"; final updateName = "B"; + final zipCode = 1; + final Modality modality = Modality.public; + final String state = "PA"; + final String city = "Belém"; + final String neighborhood = "Nazaré"; final tValidClassCompanion = ClassroomModelsCompanion( - grade: Value(grade), name: Value(name), tutorId: Value(tValidUserPk)); + grade: Value(grade), name: Value(name), tutorId: Value(tValidUserPk), + schoolId: Value(tValidSchoolPk1)); final tInvalidClassCompanion = ClassroomModelsCompanion( - grade: Value(grade), name: Value(name), tutorId: Value(tInvalidUserPk)); + grade: Value(grade), name: Value(name), tutorId: Value(tInvalidUserPk), + schoolId: Value(tValidSchoolPk1)); + + final tValidSchoolCompanion = SchoolModel( + userId: tValidUserPk, + name: name, + zipCode: zipCode, + modality: modality, + state: state, + city: city, + neighborhood: neighborhood, + ).toCompanion(true); final tValidClassModel1 = ClassroomModel( grade: grade, name: name, tutorId: tValidUserPk, - localId: tValidClassroomPk1); + localId: tValidClassroomPk1, + schoolId: tValidSchoolPk1); final tValidClassModel2 = ClassroomModel( grade: grade, name: name, tutorId: tValidUserPk, - localId: tValidClassroomPk2); + localId: tValidClassroomPk2, + schoolId: tValidSchoolPk1); final tValidUpdateClassModel = ClassroomModel( grade: grade, name: updateName, tutorId: tValidUserPk, - localId: tValidClassroomPk1); + localId: tValidClassroomPk1, + schoolId: tValidSchoolPk1); final tInvalidUpdateClassModel = ClassroomModel( localId: tInvalidClassroomPk, grade: grade, name: updateName, - tutorId: tValidUserPk); + tutorId: tValidUserPk, + schoolId: tValidSchoolPk1); final tUserCompanion = UserModelsCompanion( firstName: Value("cana"), @@ -63,6 +86,7 @@ void main() { }); database = Database(vmDatabase); await database.into(database.userModels).insert(tUserCompanion); + await database.into(database.schoolModels).insert(tValidSchoolCompanion); } setUp(() async { diff --git a/mobile/test/core/data/school_database_test.dart b/mobile/test/core/data/school_database_test.dart new file mode 100644 index 00000000..2f68ef06 --- /dev/null +++ b/mobile/test/core/data/school_database_test.dart @@ -0,0 +1,192 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mobile/core/data/database.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:moor/ffi.dart'; +import 'package:moor/moor.dart'; +import 'package:matcher/matcher.dart'; + +void main() { + final tValidSchoolPk1 = 1; + final tValidSchoolPk2 = 2; + final tInvalidSchoolPk = 2; + + final tValidUserPk = 1; + final tInvalidUserPk = 2; + + final zipCode = 1; + final Modality modality = Modality.public; + final String state = "PA"; + final String city = "Belém"; + final String neighborhood = "Nazaré"; + + final name = "A"; + final updateName = "B"; + + final tValidSchoolCompanion = SchoolModelsCompanion( + name: Value(name), + zipCode: Value(zipCode), + modality: Value(modality), + state: Value(state), + city: Value(city), + neighborhood: Value(neighborhood), + userId: Value(tValidUserPk), + ); + + final tInvalidSchoolCompanion = SchoolModelsCompanion( + name: Value(name), + zipCode: Value(zipCode), + modality: Value(modality), + state: Value(state), + city: Value(city), + neighborhood: Value(neighborhood), + userId: Value(tInvalidUserPk), + ); + + final tValidSchoolModel1 = SchoolModel( + localId: tValidSchoolPk1, + userId: tValidUserPk, + name: name, + zipCode: zipCode, + modality: modality, + state: state, + city: city, + neighborhood: neighborhood, + ); + + final tValidSchoolModel2 = SchoolModel( + localId: tValidSchoolPk2, + userId: tValidUserPk, + name: name, + zipCode: zipCode, + modality: modality, + state: state, + city: city, + neighborhood: neighborhood, + ); + + final tValidUpdateSchoolModel = SchoolModel( + localId: tValidSchoolPk1, + userId: tValidUserPk, + name: updateName, + zipCode: zipCode, + modality: modality, + state: state, + city: city, + neighborhood: neighborhood, + ); + + final tInvalidUpdateSchoolModel = SchoolModel( + localId: tValidSchoolPk2, + userId: tInvalidUserPk, + name: name, + zipCode: zipCode, + modality: modality, + state: state, + city: city, + neighborhood: neighborhood, + ); + + final tUserCompanion = UserModelsCompanion( + firstName: Value("cana"), + lastName: Value("varro"), + email: Value("dede@.com"), + username: Value("dede@.com"), + role: Value(Role.teacher), + password: Value("1234"), + ); + + Database database; + VmDatabase vmDatabase; + + Future connectDatabase() async { + vmDatabase = VmDatabase.memory(setup: (db) { + db.execute('PRAGMA foreign_keys = ON'); + }); + database = Database(vmDatabase); + await database.into(database.userModels).insert(tUserCompanion); + } + + setUp(() async { + await connectDatabase(); + }); + + tearDown(() async { + await closeDatabase(database); + }); + + group("insertClassroom", () { + test("should return the pk of a valid school when inserted", () async { + final pk = await database.insertSchool(tValidSchoolCompanion); + + expect(pk, tValidSchoolPk1); + }); + + test("should return a SQLite error", () async { + expect(() => database.insertSchool(tInvalidSchoolCompanion), + throwsA(TypeMatcher())); + }); + }); + + group("deleteSchool", () { + setUp(() async { + await database.insertSchool(tValidSchoolCompanion); + }); + + test( + "should return 1 indicating that a row was deleted when the input is a valid pk", + () async { + final deleted = await database.deleteSchool(tValidSchoolPk1); + expect(deleted, 1); + }); + + test( + "should return 0 indicating that no rows were deleted when the input is an invalid pk", + () async { + final deleted = await database.deleteSchool(tInvalidSchoolPk); + expect(deleted, 0); + }); + }); + + group("getSchools", () { + test("should return an empty list of schools", () async { + final schools = await database.getSchools(tValidUserPk); + expect(schools, []); + }); + + test("should return a list with one school", () async { + await database.insertSchool(tValidSchoolCompanion); + + final schools = await database.getSchools(tValidUserPk); + expect(schools, [tValidSchoolModel1]); + }); + + test("should return a list with two schools", () async { + await database.insertSchool(tValidSchoolCompanion); + await database.insertSchool(tValidSchoolCompanion); + + final schools = await database.getSchools(tValidUserPk); + expect(schools, [tValidSchoolModel1, tValidSchoolModel2]); + }); + }); + + group("getSchools", () { + setUp(() async { + await database.insertSchool(tValidSchoolCompanion); + }); + + test("should return true when updating a valid school", () async { + final done = await database.updateSchool(tValidUpdateSchoolModel); + expect(done, true); + }); + + test("should return false when updating an invalid school", () async { + final done = await database.updateSchool(tInvalidUpdateSchoolModel); + expect(done, false); + }); + }); +} + +void closeDatabase(Database database) async { + await database.close(); +} diff --git a/mobile/test/core/data/student_database_test.dart b/mobile/test/core/data/student_database_test.dart index 7bb1ccc8..8d32efab 100644 --- a/mobile/test/core/data/student_database_test.dart +++ b/mobile/test/core/data/student_database_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mobile/core/data/database.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; import 'package:mobile/features/user_management/domain/entities/user.dart'; import 'package:moor/ffi.dart'; import 'package:moor/moor.dart'; @@ -39,6 +40,21 @@ void main() { lastName: lastName, localId: tValidStudentPk2); + final zipCode = 1; + final Modality modality = Modality.public; + final String state = "PA"; + final String city = "Belém"; + final String neighborhood = "Nazaré"; + final tValidSchoolCompanion = SchoolModel( + userId: 1, + name: "name", + zipCode: zipCode, + modality: modality, + state: state, + city: city, + neighborhood: neighborhood, + ).toCompanion(true); + final tValidUpdateStudentModel = StudentModel( classroomId: tValidClassroomPk, firstName: updateFirstName, @@ -52,7 +68,7 @@ void main() { localId: tInvalidStudentPk); final tClassCompanion = ClassroomModelsCompanion( - name: Value("A"), grade: Value(1), tutorId: Value(1)); + name: Value("A"), grade: Value(1), tutorId: Value(1), schoolId: Value(1)); final tUserCompanion = UserModelsCompanion( firstName: Value('v'), @@ -72,6 +88,7 @@ void main() { }); database = Database(vmDatabase); await database.into(database.userModels).insert(tUserCompanion); + await database.into(database.schoolModels).insert(tValidSchoolCompanion); await database.into(database.classroomModels).insert(tClassCompanion); } diff --git a/mobile/test/core/data/text_database_test.dart b/mobile/test/core/data/text_database_test.dart index 750e28c9..e3c5e109 100644 --- a/mobile/test/core/data/text_database_test.dart +++ b/mobile/test/core/data/text_database_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mobile/core/data/database.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; import 'package:mobile/features/user_management/domain/entities/user.dart'; import 'package:moor/ffi.dart'; import 'package:moor/moor.dart'; @@ -58,10 +59,26 @@ void main() { password: Value('123'), ); + final zipCode = 1; + final Modality modality = Modality.public; + final String state = "PA"; + final String city = "Belém"; + final String neighborhood = "Nazaré"; + final tValidSchoolCompanion = SchoolModel( + userId: 1, + name: "name", + zipCode: zipCode, + modality: modality, + state: state, + city: city, + neighborhood: neighborhood, + ).toCompanion(true); + final tClassroomCompanion = ClassroomModelsCompanion( grade: Value(1), name: Value("varro"), tutorId: Value(1), + schoolId: Value(1), ); Database database; @@ -73,6 +90,7 @@ void main() { }); database = Database(vmDatabase); await database.into(database.userModels).insert(tUserCompanion); + await database.into(database.schoolModels).insert(tValidSchoolCompanion); await database.into(database.classroomModels).insert(tClassroomCompanion); } diff --git a/mobile/test/core/data/database_test.dart b/mobile/test/core/data/user_database_test.dart similarity index 100% rename from mobile/test/core/data/database_test.dart rename to mobile/test/core/data/user_database_test.dart diff --git a/mobile/test/core/fixtures/classroom.json b/mobile/test/core/fixtures/classroom.json index daec4e4b..f8f88795 100644 --- a/mobile/test/core/fixtures/classroom.json +++ b/mobile/test/core/fixtures/classroom.json @@ -2,5 +2,6 @@ "local_id": 2, "grade": 1, "name": "A", - "tutor_id": 3 + "tutor_id": 3, + "school_id": 1 } \ No newline at end of file diff --git a/mobile/test/core/fixtures/school.json b/mobile/test/core/fixtures/school.json new file mode 100644 index 00000000..59f42e70 --- /dev/null +++ b/mobile/test/core/fixtures/school.json @@ -0,0 +1,11 @@ +{ + "local_id": 1, + "user_id": 1, + "zip_code": 0, + "modality": 0, + "state": "B", + "city": "C", + "neighborhood": "D", + "name": "A" + } + \ No newline at end of file diff --git a/mobile/test/features/class_management/data/models/classroom_model_test.dart b/mobile/test/features/class_management/data/models/classroom_model_test.dart index 9fd54db5..90e74a6b 100644 --- a/mobile/test/features/class_management/data/models/classroom_model_test.dart +++ b/mobile/test/features/class_management/data/models/classroom_model_test.dart @@ -9,9 +9,9 @@ import 'package:mobile/features/class_management/data/models/classroom_model.dar void main() { final tClassModel = - ClassroomModel(grade: 1, localId: 2, name: "A", tutorId: 3); + ClassroomModel(grade: 1, localId: 2, name: "A", tutorId: 3, schoolId: 1); - final tClassEntity = Classroom(grade: 1, id: 2, name: "A", tutorId: 3); + final tClassEntity = Classroom(grade: 1, id: 2, name: "A", tutorId: 3, schoolId: 1); final tClassCompanion = ClassroomModelsCompanion(grade: Value(1), name: Value("A"), tutorId: Value(3)); @@ -35,6 +35,7 @@ void main() { "grade": 1, "name": "A", "tutor_id": 3, + "school_id": 1, }; expect(result, expectedMap); diff --git a/mobile/test/features/school_management/data/data_sources/school_local_data_source_test.dart b/mobile/test/features/school_management/data/data_sources/school_local_data_source_test.dart new file mode 100644 index 00000000..4cc2aff1 --- /dev/null +++ b/mobile/test/features/school_management/data/data_sources/school_local_data_source_test.dart @@ -0,0 +1,180 @@ +import 'dart:ffi'; +import 'dart:typed_data'; + +import 'package:dartz/dartz.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mobile/core/data/database.dart'; +import 'package:mobile/core/error/exceptions.dart'; +import 'package:mobile/core/error/failures.dart'; +import 'package:mobile/core/network/network_info.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/data/data_sources/school_local_data_source.dart'; +import 'package:mobile/features/user_management/data/models/user_model.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; +import 'package:http/http.dart' as http; +import 'package:mockito/mockito.dart'; +import 'package:moor/ffi.dart'; +import 'package:moor/moor.dart'; +import 'package:matcher/matcher.dart'; + +class MockDatabase extends Mock implements Database {} + +Future main() { + MockDatabase mockDatabase; + SchoolLocalDataSourceImpl schoolLocalDataSourceImpl; + + final tValidPk = 1; + + final tUser = User( + firstName: 'v', + lastName: 'c', + email: 'v@g.com', + role: Role.teacher, + password: '123', + id: 1, + ); + + final tUserModel = userEntityToModel(tUser); + + final tSchoolInputModel1 = SchoolModel( + localId: null, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + final tSchoolInputModel2 = SchoolModel( + localId: null, + userId: 1, + name: 'AA', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + final tSchoolModel1 = SchoolModel( + localId: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + final tSchoolCompanion1 = tSchoolInputModel1.toCompanion(true); + + final tSchoolModels = [tSchoolInputModel1, tSchoolInputModel2]; + + setUp(() async { + mockDatabase = MockDatabase(); + + schoolLocalDataSourceImpl = SchoolLocalDataSourceImpl( + database: mockDatabase, + ); + }); + + group("cacheSchool", () { + test("should correctly cache and return a valid school", () async { + when(mockDatabase.insertSchool(tSchoolCompanion1)) + .thenAnswer((_) async => tValidPk); + + final result = + await schoolLocalDataSourceImpl.cacheNewSchool(tSchoolInputModel1); + verify(mockDatabase.insertSchool(tSchoolCompanion1)); + expect(result, tSchoolModel1); + }); + + test("should throw CacheException when trying to insert an invalid school", + () { + when(mockDatabase.insertSchool(tSchoolCompanion1)) + .thenThrow(SqliteException(787, "")); + expect( + () async => await schoolLocalDataSourceImpl + .cacheNewSchool(tSchoolInputModel1), + throwsA(TypeMatcher())); + }); + }); + + group("deleteSchool", () { + test("should correctly delete a cached school", () async { + when(mockDatabase.deleteSchool(tValidPk)).thenAnswer((_) async => 1); + + await schoolLocalDataSourceImpl.deleteSchoolFromCache(tSchoolModel1); + verify(mockDatabase.deleteSchool(tValidPk)); + }); + + test("should throw CacheException when trying to delete an ivalid school", + () { + when(mockDatabase.deleteSchool(tValidPk)).thenAnswer((_) async => 0); + expect( + () async => await schoolLocalDataSourceImpl + .deleteSchoolFromCache(tSchoolModel1), + throwsA(TypeMatcher())); + }); + }); + + group("getSchool", () { + test("should correctly return a list of schools", () async { + when(mockDatabase.getSchools(tValidPk)) + .thenAnswer((_) async => tSchoolModels); + + final result = + await schoolLocalDataSourceImpl.getSchoolsFromCache(tUserModel); + + verify(mockDatabase.getSchools(tValidPk)); + final testResult = listEquals(result, tSchoolModels); + equals(testResult); + }); + + test("should correctly return an empty list", () async { + when(mockDatabase.getSchools(tValidPk)).thenAnswer((_) async => []); + + final result = + await schoolLocalDataSourceImpl.getSchoolsFromCache(tUserModel); + + verify(mockDatabase.getSchools(tValidPk)); + final testResult = listEquals(result, []); + equals(testResult); + }); + + test("should throw a CacheException", () async { + when(mockDatabase.getSchools(tValidPk)) + .thenThrow(SqliteException(787, "")); + + expect( + () async => + await schoolLocalDataSourceImpl.getSchoolsFromCache(tUserModel), + throwsA(TypeMatcher())); + }); + }); + + group("updateSchool", () { + test("should correctly update a cached school", () async { + when(mockDatabase.updateSchool(tSchoolModel1)) + .thenAnswer((_) async => true); + + await schoolLocalDataSourceImpl.updateCachedSchool(tSchoolModel1); + verify(mockDatabase.updateSchool(tSchoolModel1)); + }); + + test("should throw a cache expection if the update was not completed", + () async { + when(mockDatabase.updateSchool(tSchoolModel1)) + .thenAnswer((_) async => false); + + expect( + () async => + await schoolLocalDataSourceImpl.updateCachedSchool(tSchoolModel1), + throwsA(TypeMatcher())); + }); + }); +} diff --git a/mobile/test/features/school_management/data/models/school_model_test.dart b/mobile/test/features/school_management/data/models/school_model_test.dart new file mode 100644 index 00000000..051c07ca --- /dev/null +++ b/mobile/test/features/school_management/data/models/school_model_test.dart @@ -0,0 +1,80 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mobile/core/data/database.dart'; +import 'package:mobile/core/data/serializer.dart'; +import 'package:mobile/features/school_management/data/models/school_model.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:moor/moor.dart'; +import '../../../../core/fixtures/fixture_reader.dart'; + +void main() { + moorRuntimeOptions.defaultSerializer = Serializer(); + + final tSchoolModel = SchoolModel( + localId: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.municipal, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + final tSchoolEntity = School( + id: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.municipal, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + group("from json", () { + test("should return a valid School model", () async { + final Map jsonMap = json.decode(fixture('school')); + + final result = SchoolModel.fromJson(jsonMap); + + expect(result, tSchoolModel); + }); + }); + + group('toJson', () { + test('should return a School JSON map with proper data', () async { + final result = tSchoolModel.toJson(); + + final expectedMap = { + "local_id": 1, + "user_id": 1, + "zip_code": 0, + "modality": 0, + "state": "B", + "city": "C", + "neighborhood": "D", + "name": "A", + }; + + expect(result, expectedMap); + }); + }); + + group('modelToEntity', () { + test('should return a School entity with proper data', () async { + final result = schoolModelToEntity(tSchoolModel); + + expect(result, tSchoolEntity); + }); + }); + + group('entityToModel', () { + test('should return a School model with proper data', () async { + final result = schoolEntityToModel(tSchoolEntity); + + expect(result, tSchoolModel); + }); + }); +} diff --git a/mobile/test/features/school_management/data/repositories/school_repository_impl_test.dart b/mobile/test/features/school_management/data/repositories/school_repository_impl_test.dart new file mode 100644 index 00000000..7f271e27 --- /dev/null +++ b/mobile/test/features/school_management/data/repositories/school_repository_impl_test.dart @@ -0,0 +1,150 @@ +import 'package:dartz/dartz.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mobile/core/error/exceptions.dart'; +import 'package:mobile/core/error/failures.dart'; +import 'package:mobile/features/class_management/data/models/classroom_model.dart'; +import 'package:mobile/features/class_management/data/repositories/classroom_repository_impl.dart'; +import 'package:mobile/features/class_management/domain/entities/classroom.dart'; +import 'package:mobile/features/school_management/data/data_sources/school_local_data_source.dart'; +import 'package:mobile/features/school_management/data/models/school_model.dart'; +import 'package:mobile/features/school_management/data/repositories/school_repository_impl.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/user_management/data/models/user_model.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; +import 'package:mockito/mockito.dart'; + +class MockSchoolLocalDataSource extends Mock implements SchoolLocalDataSource {} + +void main() { + MockSchoolLocalDataSource mockLocalDataSource; + SchoolRepositoryImpl repository; + + final tUser = User( + firstName: 'v', + lastName: 'c', + email: 'v@g.com', + role: Role.teacher, + password: '123', + id: 1, + ); + + final tSchool = School( + id: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + final tSchoolModel = schoolEntityToModel(tSchool); + final tUserModel = userEntityToModel(tUser); + + final tSchoolModels = [tSchoolModel, tSchoolModel]; + final tSchools = [tSchool, tSchool]; + + setUp(() { + mockLocalDataSource = MockSchoolLocalDataSource(); + + repository = SchoolRepositoryImpl( + localDataSource: mockLocalDataSource, + ); + }); + + group('createSchool', () { + test('should return CacheFailure when cache is unsuccessful', () async { + when(mockLocalDataSource.cacheNewSchool(tSchoolModel)) + .thenThrow(CacheException()); + + final result = await repository.createSchool(tSchool); + + expect(result, Left(CacheFailure())); + }); + + test('should cache newly created school', () async { + when(mockLocalDataSource.cacheNewSchool(tSchoolModel)) + .thenAnswer((_) async => tSchoolModel); + + final result = await repository.createSchool(tSchool); + + verify(mockLocalDataSource.cacheNewSchool(tSchoolModel)); + expect(result, Right(tSchool)); + verifyNoMoreInteractions(mockLocalDataSource); + }); + }); + + group('delete', () { + test('should delete a school', () async { + when(mockLocalDataSource.deleteSchoolFromCache(tSchoolModel)) + .thenAnswer((_) async => _); + + await repository.deleteSchool(tSchool); + + verify(mockLocalDataSource.deleteSchoolFromCache(tSchoolModel)); + verifyNoMoreInteractions(mockLocalDataSource); + }); + + test('should return a CacheFailure when a CacheException is throw', + () async { + when(mockLocalDataSource.deleteSchoolFromCache(tSchoolModel)) + .thenThrow(CacheException()); + + final result = await repository.deleteSchool(tSchool); + verify(mockLocalDataSource.deleteSchoolFromCache(tSchoolModel)); + + expect(result, Left(CacheFailure())); + verifyNoMoreInteractions(mockLocalDataSource); + }); + }); + + group('update', () { + test('should return a classroom when updateSchool is called', () async { + when(mockLocalDataSource.updateCachedSchool(tSchoolModel)) + .thenAnswer((_) async => tSchoolModel); + + final result = await repository.updateSchool(tSchool); + + verify(mockLocalDataSource.updateCachedSchool(tSchoolModel)); + expect(result, Right(tSchool)); + verifyNoMoreInteractions(mockLocalDataSource); + }); + + test('should return a CacheFailure when a CacheException is throw', + () async { + when(mockLocalDataSource.updateCachedSchool(tSchoolModel)) + .thenThrow(CacheException()); + + final result = await repository.updateSchool(tSchool); + + verify(mockLocalDataSource.updateCachedSchool(tSchoolModel)); + expect(result, Left(CacheFailure())); + verifyNoMoreInteractions(mockLocalDataSource); + }); + }); + + group('get', () { + test('should return a list of schools when getSchools is called', () async { + when(mockLocalDataSource.getSchoolsFromCache(tUserModel)) + .thenAnswer((_) async => tSchoolModels); + + final result = await repository.getSchools(tUser); + final List resultList = result.getOrElse(() => null); + + final resultTest = listEquals(resultList, tSchools); + equals(resultTest); + }); + + test('should return a CacheFailure when a CacheException is throw', + () async { + when(mockLocalDataSource.getSchoolsFromCache(tUserModel)) + .thenThrow(CacheException()); + + final result = await repository.getSchools(tUser); + + expect(result, Left(CacheFailure())); + }); + }); +} diff --git a/mobile/test/features/school_management/domain/use_cases/create_school_use_case_test.dart b/mobile/test/features/school_management/domain/use_cases/create_school_use_case_test.dart new file mode 100644 index 00000000..4724a61c --- /dev/null +++ b/mobile/test/features/school_management/domain/use_cases/create_school_use_case_test.dart @@ -0,0 +1,49 @@ +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/domain/repositories/school_repository.dart'; +import 'package:mobile/features/school_management/domain/use_cases/create_school_use_case.dart'; +import 'package:mobile/features/school_management/domain/use_cases/school_params.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; +import 'package:mockito/mockito.dart'; + +class MockClassroomRepository extends Mock implements SchoolRepository {} + +void main() { + CreateSchool useCase; + MockClassroomRepository mockClassroomRepository; + + setUp(() { + mockClassroomRepository = MockClassroomRepository(); + useCase = CreateSchool(repository: mockClassroomRepository); + }); + + final tUser = User( + firstName: 'v', + lastName: 'c', + email: 'v@g.com', + role: Role.teacher, + password: '123', + ); + + final tSchool = School( + id: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + test('should return a correct response when creating a school', () async { + when(mockClassroomRepository.createSchool(tSchool)) + .thenAnswer((_) async => Right(tSchool)); + + final result = await useCase(SchoolParams(school: tSchool)); + + expect(result, Right(tSchool)); + verify(mockClassroomRepository.createSchool(tSchool)); + verifyNoMoreInteractions(mockClassroomRepository); + }); +} diff --git a/mobile/test/features/school_management/domain/use_cases/delete_school_use_case_test.dart b/mobile/test/features/school_management/domain/use_cases/delete_school_use_case_test.dart new file mode 100644 index 00000000..99e6d468 --- /dev/null +++ b/mobile/test/features/school_management/domain/use_cases/delete_school_use_case_test.dart @@ -0,0 +1,46 @@ +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/domain/repositories/school_repository.dart'; +import 'package:mobile/features/school_management/domain/use_cases/delete_school_use_case.dart'; +import 'package:mobile/features/school_management/domain/use_cases/school_params.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; +import 'package:mockito/mockito.dart'; +import 'package:http/http.dart' as http; + +class MockClassroomRepository extends Mock implements SchoolRepository {} + +void main() { + DeleteSchool useCase; + MockClassroomRepository mockClassroomRepository; + + setUp(() { + mockClassroomRepository = MockClassroomRepository(); + useCase = DeleteSchool(repository: mockClassroomRepository); + }); + + final tUser = User( + firstName: 'v', + lastName: 'c', + email: 'v@g.com', + role: Role.teacher, + password: '123', + ); + + final tSchool = School( + id: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + test('should return nothing when deleting a school', () async { + await useCase(SchoolParams(school: tSchool)); + + verify(mockClassroomRepository.deleteSchool(tSchool)); + verifyNoMoreInteractions(mockClassroomRepository); + }); +} diff --git a/mobile/test/features/school_management/domain/use_cases/get_schools_use_case_test.dart b/mobile/test/features/school_management/domain/use_cases/get_schools_use_case_test.dart new file mode 100644 index 00000000..9f944e40 --- /dev/null +++ b/mobile/test/features/school_management/domain/use_cases/get_schools_use_case_test.dart @@ -0,0 +1,75 @@ +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/domain/repositories/school_repository.dart'; +import 'package:mobile/features/school_management/domain/use_cases/get_schools_use_case.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; +import 'package:mockito/mockito.dart'; + +class MockSchoolRepository extends Mock implements SchoolRepository {} + +void main() { + GetSchools useCase; + MockSchoolRepository mockSchoolRepository; + + setUp(() { + mockSchoolRepository = MockSchoolRepository(); + useCase = GetSchools(repository: mockSchoolRepository); + }); + + final tUser = User( + firstName: 'v', + lastName: 'c', + email: 'v@g.com', + role: Role.teacher, + password: '123', + ); + + final tSchool1 = School( + id: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + final tSchool2 = School( + id: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + final List tTwoSchools = [tSchool1, tSchool2]; + final List tEmptySchools = []; + + test('should return an empty list of schools if there is no schools', + () async { + when(mockSchoolRepository.getSchools(tUser)) + .thenAnswer((_) async => Right(tEmptySchools)); + + final result = await useCase(UserParams(tUser)); + + expect(result, Right(tEmptySchools)); + verify(mockSchoolRepository.getSchools(tUser)); + verifyNoMoreInteractions(mockSchoolRepository); + }); + + test('should return list of schools', () async { + when(mockSchoolRepository.getSchools(tUser)) + .thenAnswer((_) async => Right(tTwoSchools)); + + final result = await useCase(UserParams(tUser)); + + expect(result, Right(tTwoSchools)); + verify(mockSchoolRepository.getSchools(tUser)); + verifyNoMoreInteractions(mockSchoolRepository); + }); +} diff --git a/mobile/test/features/school_management/domain/use_cases/update_schools_use_case_test.dart b/mobile/test/features/school_management/domain/use_cases/update_schools_use_case_test.dart new file mode 100644 index 00000000..21243543 --- /dev/null +++ b/mobile/test/features/school_management/domain/use_cases/update_schools_use_case_test.dart @@ -0,0 +1,54 @@ +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mobile/features/class_management/domain/entities/classroom.dart'; +import 'package:mobile/features/class_management/domain/repositories/classroom_repository.dart'; +import 'package:mobile/features/class_management/domain/use_cases/classroom_params.dart'; +import 'package:mobile/features/class_management/domain/use_cases/update_classroom_use_case.dart'; +import 'package:mobile/features/school_management/domain/entities/school.dart'; +import 'package:mobile/features/school_management/domain/repositories/school_repository.dart'; +import 'package:mobile/features/school_management/domain/use_cases/school_params.dart'; +import 'package:mobile/features/school_management/domain/use_cases/update_school_use_case.dart'; +import 'package:mobile/features/user_management/domain/entities/user.dart'; +import 'package:mockito/mockito.dart'; + +class MockSchoolRepository extends Mock implements SchoolRepository {} + +void main() { + UpdateSchool useCase; + MockSchoolRepository mockClassroomRepository; + + setUp(() { + mockClassroomRepository = MockSchoolRepository(); + useCase = UpdateSchool(repository: mockClassroomRepository); + }); + + final tUser = User( + firstName: 'v', + lastName: 'c', + email: 'v@g.com', + role: Role.teacher, + password: '123', + ); + + final tSchool = School( + id: 1, + userId: 1, + name: 'A', + zipCode: 0, + modality: Modality.public, + state: 'B', + city: 'C', + neighborhood: 'D', + ); + + test('should return a correct response when updating a school', () async { + when(mockClassroomRepository.updateSchool(tSchool)) + .thenAnswer((_) async => Right(tSchool)); + + final result = await useCase(SchoolParams(school: tSchool)); + + expect(result, Right(tSchool)); + verify(mockClassroomRepository.updateSchool(tSchool)); + verifyNoMoreInteractions(mockClassroomRepository); + }); +} diff --git a/mobile/test/features/user_management/data/data_sources/user_remote_data_source_test.dart b/mobile/test/features/user_management/data/data_sources/user_remote_data_source_test.dart index 527fd0dd..a8b7e98d 100644 --- a/mobile/test/features/user_management/data/data_sources/user_remote_data_source_test.dart +++ b/mobile/test/features/user_management/data/data_sources/user_remote_data_source_test.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:mobile/core/data/database.dart'; +import 'package:mobile/core/data/serializer.dart'; import 'package:mobile/core/error/exceptions.dart'; import 'package:mobile/core/network/response.dart'; import 'package:mobile/features/user_management/data/data_sources/user_remote_data_source.dart'; @@ -32,7 +33,7 @@ void main() { mockHttpClient = MockHttpClient(); dataSource = UserRemoteDataSourceImpl(client: mockHttpClient); - moorRuntimeOptions.defaultSerializer = UserSerializer(); + moorRuntimeOptions.defaultSerializer = Serializer(); }); group('createUser', () { diff --git a/mobile/test/features/user_management/data/models/user_model_test.dart b/mobile/test/features/user_management/data/models/user_model_test.dart index e68945b5..dcf215a9 100644 --- a/mobile/test/features/user_management/data/models/user_model_test.dart +++ b/mobile/test/features/user_management/data/models/user_model_test.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:mobile/core/data/database.dart'; +import 'package:mobile/core/data/serializer.dart'; import 'package:mobile/features/user_management/data/models/user_model.dart'; import 'package:mobile/features/user_management/domain/entities/user.dart'; import 'package:moor/moor.dart'; @@ -9,7 +10,7 @@ import 'package:moor/moor.dart'; import '../../../../core/fixtures/fixture_reader.dart'; void main() { - moorRuntimeOptions.defaultSerializer = UserSerializer(); + moorRuntimeOptions.defaultSerializer = Serializer(); final tUserModel = UserModel( localId: 1,