tests: tests for dart repositories
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import 'package:backend_dart/domain/data/database.dart';
|
||||
import 'package:backend_dart/domain/data/project_data_source.dart';
|
||||
import 'package:backend_dart/domain/data/project_task_data_source.dart';
|
||||
import 'package:backend_dart/domain/data/time_entry_data_source.dart';
|
||||
import 'package:backend_dart/domain/data/user_data_source.dart';
|
||||
import 'package:backend_dart/domain/entities/project.dart';
|
||||
import 'package:backend_dart/domain/entities/project_task.dart';
|
||||
import 'package:backend_dart/domain/entities/time_entry.dart';
|
||||
import 'package:backend_dart/domain/entities/user.dart';
|
||||
|
||||
import 'mock_project_data_source.dart';
|
||||
import 'mock_project_task_data_source.dart';
|
||||
import 'mock_time_entry_data_source.dart';
|
||||
import 'mock_user_data_source.dart';
|
||||
|
||||
class MockDatabase implements IDatabase {
|
||||
final Map<String, User> _usersStore = {};
|
||||
final Map<String, ProjectTask> _tasksStore = {};
|
||||
final Map<String, Project> _projectsStore = {};
|
||||
final Map<String, TimeEntry> _timeEntriesStore = {};
|
||||
@override
|
||||
Future<void> close() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
ProjectDataSource get projects => MockProjectDataSource(_projectsStore);
|
||||
|
||||
@override
|
||||
ProjectTaskDataSource get tasks => MockProjectTaskDataSource(_tasksStore);
|
||||
|
||||
@override
|
||||
TimeEntryDataSource get timeEntries =>
|
||||
MockTimeEntryDataSource(_timeEntriesStore);
|
||||
|
||||
@override
|
||||
UserDataSource get users => MockUserDataSource(_usersStore);
|
||||
|
||||
void clear() {
|
||||
_usersStore.clear();
|
||||
_tasksStore.clear();
|
||||
_projectsStore.clear();
|
||||
_timeEntriesStore.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import 'package:backend_dart/domain/data/project_data_source.dart';
|
||||
import 'package:backend_dart/domain/entities/project.dart';
|
||||
import 'package:backend_dart/domain/errors/app_error.dart';
|
||||
import 'package:backend_dart/domain/errors/error.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class MockProjectDataSource implements ProjectDataSource {
|
||||
MockProjectDataSource(this._store);
|
||||
final Map<String, Project> _store;
|
||||
final Uuid _uuid = Uuid();
|
||||
|
||||
@override
|
||||
TaskEither<IError, Project> create(ProjectCreate project) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final id = _uuid.v4();
|
||||
final newProject = Project(
|
||||
id: id,
|
||||
userId: project.userId,
|
||||
name: project.name,
|
||||
description: project.description,
|
||||
clientId: project.clientId,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
_store[id] = newProject;
|
||||
return newProject;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to create project: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, Project> findById(String id) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final project = _store[id];
|
||||
if (project == null) {
|
||||
throw AppError.notFound('Project with ID $id not found');
|
||||
}
|
||||
return project;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to find project by ID: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, List<Project>> findByUserId(String userId) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final projects =
|
||||
_store.values.where((project) => project.userId == userId).toList();
|
||||
return projects;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message:
|
||||
'Failed to fetch projects for user $userId: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, Project> update(ProjectUpdate project) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final existingProject = _store[project.id];
|
||||
if (existingProject == null) {
|
||||
throw AppError.notFound('Project with ID ${project.id} not found');
|
||||
}
|
||||
final updatedProject = existingProject.copyWith(
|
||||
name: project.name ?? existingProject.name,
|
||||
description: project.description ?? existingProject.description,
|
||||
clientId: project.clientId ?? existingProject.clientId,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
_store[project.id] = updatedProject;
|
||||
return updatedProject;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to update project: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, Project> delete(String id) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final project = _store.remove(id);
|
||||
if (project == null) {
|
||||
throw AppError.notFound('Project with ID $id not found');
|
||||
}
|
||||
return project;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to delete project: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, List<Project>> findAll() {
|
||||
return TaskEither.tryCatch(
|
||||
() async => _store.values.toList(),
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to fetch all projects: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, String> generateId() {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
String id;
|
||||
do {
|
||||
id = _uuid.v4();
|
||||
} while (_store.containsKey(id));
|
||||
return id;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to generate ID: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import 'package:backend_dart/domain/data/project_task_data_source.dart';
|
||||
import 'package:backend_dart/domain/entities/project_task.dart';
|
||||
import 'package:backend_dart/domain/errors/app_error.dart';
|
||||
import 'package:backend_dart/domain/errors/error.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class MockProjectTaskDataSource implements ProjectTaskDataSource {
|
||||
MockProjectTaskDataSource(this._store);
|
||||
final Map<String, ProjectTask> _store;
|
||||
final Uuid _uuid = Uuid();
|
||||
|
||||
@override
|
||||
TaskEither<IError, ProjectTask> create(ProjectTaskCreate task) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final id = _uuid.v4();
|
||||
final newTask = ProjectTask(
|
||||
id: id,
|
||||
projectId: task.projectId,
|
||||
name: task.name,
|
||||
description: task.description,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
_store[id] = newTask;
|
||||
return newTask;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to create project task: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, ProjectTask> findById(String id) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final task = _store[id];
|
||||
if (task == null) {
|
||||
throw AppError.notFound('Project task with ID $id not found');
|
||||
}
|
||||
return task;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to find project task by ID: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, List<ProjectTask>> findByProjectId(String projectId) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final tasks =
|
||||
_store.values.where((task) => task.projectId == projectId).toList();
|
||||
return tasks;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message:
|
||||
'Failed to fetch tasks for project $projectId: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, ProjectTask> update(ProjectTaskUpdate task) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final existingTask = _store[task.id];
|
||||
if (existingTask == null) {
|
||||
throw AppError.notFound('Project task with ID ${task.id} not found');
|
||||
}
|
||||
final updatedTask = existingTask.copyWith(
|
||||
name: task.name ?? existingTask.name,
|
||||
description: task.description ?? existingTask.description,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
_store[task.id] = updatedTask;
|
||||
return updatedTask;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to update project task: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, ProjectTask> delete(String id) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final task = _store.remove(id);
|
||||
if (task == null) {
|
||||
throw AppError.notFound('Project task with ID $id not found');
|
||||
}
|
||||
return task;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to delete project task: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, List<ProjectTask>> findAll() {
|
||||
return TaskEither.tryCatch(
|
||||
() async => _store.values.toList(),
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to fetch all project tasks: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, String> generateId() {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
String id;
|
||||
do {
|
||||
id = _uuid.v4();
|
||||
} while (_store.containsKey(id));
|
||||
return id;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to generate ID: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import 'package:backend_dart/domain/data/time_entry_data_source.dart';
|
||||
import 'package:backend_dart/domain/entities/time_entry.dart';
|
||||
import 'package:backend_dart/domain/errors/app_error.dart';
|
||||
import 'package:backend_dart/domain/errors/error.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class MockTimeEntryDataSource implements TimeEntryDataSource {
|
||||
MockTimeEntryDataSource(this._store);
|
||||
final Map<String, TimeEntry> _store;
|
||||
final Uuid _uuid = Uuid();
|
||||
|
||||
@override
|
||||
TaskEither<IError, TimeEntry> create(TimeEntryCreate timeEntry) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final id = _uuid.v4();
|
||||
final newEntry = TimeEntry(
|
||||
id: id,
|
||||
userId: timeEntry.userId,
|
||||
projectId: timeEntry.projectId,
|
||||
startTime: timeEntry.startTime,
|
||||
endTime: timeEntry.endTime,
|
||||
description: timeEntry.description,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
_store[id] = newEntry;
|
||||
return newEntry;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to create time entry: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, TimeEntry> findById(String id) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final entry = _store[id];
|
||||
if (entry == null) {
|
||||
throw AppError.notFound('Time entry with ID $id not found');
|
||||
}
|
||||
return entry;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to find time entry by ID: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, List<TimeEntry>> findByUserId(String userId) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final entries =
|
||||
_store.values.where((entry) => entry.userId == userId).toList();
|
||||
return entries;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message:
|
||||
'Failed to fetch time entries for user $userId: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, List<TimeEntry>> findByProjectId(String projectId) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final entries = _store.values
|
||||
.where((entry) => entry.projectId == projectId)
|
||||
.toList();
|
||||
return entries;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message:
|
||||
'Failed to fetch time entries for project $projectId: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, TimeEntry> update(TimeEntryUpdate timeEntry) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final existingEntry = _store[timeEntry.id];
|
||||
if (existingEntry == null) {
|
||||
throw AppError.notFound(
|
||||
'Time entry with ID ${timeEntry.id} not found');
|
||||
}
|
||||
final updatedEntry = existingEntry.copyWith(
|
||||
startTime: timeEntry.startTime ?? existingEntry.startTime,
|
||||
endTime: timeEntry.endTime ?? existingEntry.endTime,
|
||||
description: timeEntry.description ?? existingEntry.description,
|
||||
projectId: timeEntry.projectId ?? existingEntry.projectId,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
_store[timeEntry.id] = updatedEntry;
|
||||
return updatedEntry;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to update time entry: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, TimeEntry> delete(String id) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final entry = _store.remove(id);
|
||||
if (entry == null) {
|
||||
throw AppError.notFound('Time entry with ID $id not found');
|
||||
}
|
||||
return entry;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to delete time entry: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, List<TimeEntry>> findAll() {
|
||||
return TaskEither.tryCatch(
|
||||
() async => _store.values.toList(),
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to fetch all time entries: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, String> generateId() {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
String id;
|
||||
do {
|
||||
id = _uuid.v4();
|
||||
} while (_store.containsKey(id));
|
||||
return id;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to generate ID: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import 'package:backend_dart/common/extensions.dart';
|
||||
import 'package:backend_dart/common/secure_hash.dart';
|
||||
import 'package:backend_dart/domain/data/user_data_source.dart';
|
||||
import 'package:backend_dart/domain/entities/user.dart';
|
||||
import 'package:backend_dart/domain/errors/app_error.dart';
|
||||
import 'package:backend_dart/domain/errors/error.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class MockUserDataSource implements UserDataSource {
|
||||
MockUserDataSource(this.store);
|
||||
final Map<String, User> store;
|
||||
final Uuid _uuid = Uuid();
|
||||
|
||||
@override
|
||||
TaskEither<IError, User> create(UserCreate user) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final id = _uuid.v4();
|
||||
final newUser = User(
|
||||
id: id,
|
||||
email: user.email,
|
||||
passwordHash: generateSecureHash(user.password),
|
||||
name: user.name,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now());
|
||||
store[id] = newUser;
|
||||
return newUser;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to create user: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, User> findByEmail(String email) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final user = store.values
|
||||
.where(
|
||||
(user) => user.email == email,
|
||||
)
|
||||
.firstOrNull;
|
||||
if (user == null) {
|
||||
throw AppError.notFound('User with email $email not found');
|
||||
}
|
||||
return user;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to find user by email: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, User> findById(String id) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final user = store[id];
|
||||
if (user == null) {
|
||||
throw AppError.notFound('User with ID $id not found');
|
||||
}
|
||||
return user;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to find user by ID: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, User> update(UserUpdate user) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final existingUser = store[user.id];
|
||||
if (existingUser == null) {
|
||||
throw AppError.notFound('User with ID ${user.id} not found');
|
||||
}
|
||||
final updatedUser = existingUser.copyWith(
|
||||
email: user.email ?? existingUser.email,
|
||||
passwordHash: user.password.let(generateSecureHash) ??
|
||||
existingUser.passwordHash,
|
||||
name: user.name ?? existingUser.name,
|
||||
);
|
||||
store[user.id] = updatedUser;
|
||||
return updatedUser;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to update user: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, User> delete(String id) {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
final user = store.remove(id);
|
||||
if (user == null) {
|
||||
throw AppError.notFound('User with ID $id not found');
|
||||
}
|
||||
return user;
|
||||
},
|
||||
(error, _) => error is AppError
|
||||
? error
|
||||
: AppError.databaseError(
|
||||
message: 'Failed to delete user: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, List<User>> findAll() {
|
||||
return TaskEither.tryCatch(
|
||||
() async => store.values.toList(),
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to fetch all users: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TaskEither<IError, String> generateId() {
|
||||
return TaskEither.tryCatch(
|
||||
() async {
|
||||
String id;
|
||||
do {
|
||||
id = _uuid.v4();
|
||||
} while (store.containsKey(id));
|
||||
return id;
|
||||
},
|
||||
(error, _) => AppError.databaseError(
|
||||
message: 'Failed to generate ID: ${error.toString()}',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user