From ada8f21153d8cf3c7e38e1fdfe271a33931f241b Mon Sep 17 00:00:00 2001 From: Ryedis <134209766+Ryedis@users.noreply.github.com> Date: Wed, 18 Feb 2026 19:40:33 +0400 Subject: [PATCH 1/7] add library models --- Library/Data/DataSeeder.cs | 84 +++++++++++++++++++++++++++++++++++ Library/Models/Book.cs | 38 ++++++++++++++++ Library/Models/BookIssue.cs | 33 ++++++++++++++ Library/Models/EditionType.cs | 11 +++++ Library/Models/Publisher.cs | 11 +++++ Library/Models/Reader.cs | 23 ++++++++++ 6 files changed, 200 insertions(+) create mode 100644 Library/Data/DataSeeder.cs create mode 100644 Library/Models/Book.cs create mode 100644 Library/Models/BookIssue.cs create mode 100644 Library/Models/EditionType.cs create mode 100644 Library/Models/Publisher.cs create mode 100644 Library/Models/Reader.cs diff --git a/Library/Data/DataSeeder.cs b/Library/Data/DataSeeder.cs new file mode 100644 index 000000000..1717ce8b6 --- /dev/null +++ b/Library/Data/DataSeeder.cs @@ -0,0 +1,84 @@ +using Library.Domain.Models; + +namespace Library.Domain.Data; + +/// Класс, содержащий заранее подготовленные тестовые данные для доменной модели библиотеки +public class DataSeeder +{ + /// Список видов изданий + public List EditionTypes { get; } = + [ + new EditionType { Id = 1, Name = "Монография" }, + new EditionType { Id = 2, Name = "Методическое пособие" }, + new EditionType { Id = 3, Name = "Энциклопедия" }, + new EditionType { Id = 4, Name = "Биография" }, + new EditionType { Id = 5, Name = "Фэнтези" }, + new EditionType { Id = 6, Name = "Техническая литература" }, + new EditionType { Id = 7, Name = "Публицистика" }, + new EditionType { Id = 8, Name = "Поэзия" }, + new EditionType { Id = 9, Name = "Психология" }, + new EditionType { Id = 10, Name = "Бизнес-литература" }, + ]; + + /// Список издательств + public List Publishers { get; } = + [ + new Publisher { Id = 1, Name = "Бином" }, + new Publisher { Id = 2, Name = "Инфра-М" }, + new Publisher { Id = 3, Name = "Юрайт" }, + new Publisher { Id = 4, Name = "ДМК Пресс" }, + new Publisher { Id = 5, Name = "Лань" }, + new Publisher { Id = 6, Name = "Альпина Паблишер" }, + new Publisher { Id = 7, Name = "МИФ" }, + new Publisher { Id = 8, Name = "Вильямс" }, + new Publisher { Id = 9, Name = "Самокат" }, + new Publisher { Id = 10, Name = "Энергия" }, + ]; + + /// Список книг с заполненными ссылками на издательства и виды изданий + public List Books { get; } = + [ + new Book { Id = 1, InventoryNumber = "BK-101", AlphabetCode = "И-101", Authors = "И. Ньютон", Title = "Математические начала", EditionTypeId = 1, PublisherId = 5, Year = 1687 }, + new Book { Id = 2, InventoryNumber = "BK-102", AlphabetCode = "Т-210", Authors = "А. Тьюринг", Title = "Вычислительные машины", EditionTypeId = 6, PublisherId = 4, Year = 1936 }, + new Book { Id = 3, InventoryNumber = "BK-103", AlphabetCode = "К-310", Authors = "И. Кант", Title = "Критика чистого разума", EditionTypeId = 7, PublisherId = 6, Year = 1781 }, + new Book { Id = 4, InventoryNumber = "BK-104", AlphabetCode = "Р-410", Authors = "Д. Роулинг", Title = "Тайная комната", EditionTypeId = 5, PublisherId = 9, Year = 1998 }, + new Book { Id = 5, InventoryNumber = "BK-105", AlphabetCode = "М-510", Authors = "М. Портер", Title = "Конкурентная стратегия", EditionTypeId = 10, PublisherId = 7, Year = 1980 }, + new Book { Id = 6, InventoryNumber = "BK-106", AlphabetCode = "С-610", Authors = "К. Саган", Title = "Космос", EditionTypeId = 3, PublisherId = 1, Year = 1980 }, + new Book { Id = 7, InventoryNumber = "BK-107", AlphabetCode = "Ф-710", Authors = "З. Фрейд", Title = "Толкование сновидений", EditionTypeId = 9, PublisherId = 6, Year = 1899 }, + new Book { Id = 8, InventoryNumber = "BK-108", AlphabetCode = "Л-810", Authors = "С. Лем", Title = "Солярис", EditionTypeId = 5, PublisherId = 2, Year = 1961 }, + new Book { Id = 9, InventoryNumber = "BK-109", AlphabetCode = "Х-910", Authors = "Ю. Харари", Title = "Sapiens", EditionTypeId = 4, PublisherId = 6, Year = 2011 }, + new Book { Id = 10, InventoryNumber = "BK-110", AlphabetCode = "Г-999", Authors = "А. Гауди", Title = "Архитектура форм", EditionTypeId = 1, PublisherId = 10, Year = 1925 }, + ]; + + /// Список читателей библиотеки, включающий персональные данные и дату регистрации + public List Readers { get; } = + [ + new Reader { Id = 1, FullName = "Орлов Денис Сергеевич", Address = "ул. Березовая, 12", Phone = "89110000001", RegistrationDate = DateTime.UtcNow.AddYears(-3) }, + new Reader { Id = 2, FullName = "Мельников Артем Игоревич", Address = "ул. Солнечная, 45", Phone = "89110000002", RegistrationDate = DateTime.UtcNow.AddYears(-2) }, + new Reader { Id = 3, FullName = "Белов Кирилл Андреевич", Address = "ул. Полевая, 7", Phone = "89110000003", RegistrationDate = DateTime.UtcNow.AddMonths(-18) }, + new Reader { Id = 4, FullName = "Егорова Марина Олеговна", Address = "ул. Озерная, 21", Phone = "89110000004", RegistrationDate = DateTime.UtcNow.AddMonths(-12) }, + new Reader { Id = 5, FullName = "Тарасов Максим Дмитриевич", Address = "ул. Лесная, 3", Phone = "89110000005", RegistrationDate = DateTime.UtcNow.AddMonths(-10) }, + new Reader { Id = 6, FullName = "Крылова Анастасия Павловна", Address = "ул. Школьная, 9", Phone = "89110000006", RegistrationDate = DateTime.UtcNow.AddMonths(-8) }, + new Reader { Id = 7, FullName = "Никитин Роман Евгеньевич", Address = "ул. Центральная, 15", Phone = "89110000007", RegistrationDate = DateTime.UtcNow.AddMonths(-6) }, + new Reader { Id = 8, FullName = "Волкова Дарья Ильинична", Address = "ул. Мира, 19", Phone = "89110000008", RegistrationDate = DateTime.UtcNow.AddMonths(-5) }, + new Reader { Id = 9, FullName = "Зайцев Павел Николаевич", Address = "ул. Новая, 8", Phone = "89110000009", RegistrationDate = DateTime.UtcNow.AddMonths(-4) }, + new Reader { Id = 10, FullName = "Громова София Артемовна", Address = "ул. Южная, 14", Phone = "89110000010", RegistrationDate = DateTime.UtcNow.AddMonths(-2) }, + ]; + + /// Список фактов выдачи книг + public List BookIssues { get; } = + [ + new BookIssue { Id = 1, BookId = 1, ReaderId = 1, IssueDate = DateTime.UtcNow.AddDays(-15), Days = 30, ReturnDate = null }, + new BookIssue { Id = 2, BookId = 2, ReaderId = 1, IssueDate = DateTime.UtcNow.AddDays(-200), Days = 60, ReturnDate = DateTime.UtcNow.AddDays(-140) }, + new BookIssue { Id = 3, BookId = 3, ReaderId = 2, IssueDate = DateTime.UtcNow.AddDays(-40), Days = 14, ReturnDate = DateTime.UtcNow.AddDays(-20) }, + new BookIssue { Id = 4, BookId = 4, ReaderId = 2, IssueDate = DateTime.UtcNow.AddDays(-7), Days = 10, ReturnDate = null }, + new BookIssue { Id = 5, BookId = 5, ReaderId = 3, IssueDate = DateTime.UtcNow.AddDays(-300), Days = 21, ReturnDate = DateTime.UtcNow.AddDays(-260) }, + new BookIssue { Id = 6, BookId = 6, ReaderId = 4, IssueDate = DateTime.UtcNow.AddDays(-50), Days = 14, ReturnDate = DateTime.UtcNow.AddDays(-30) }, + new BookIssue { Id = 7, BookId = 7, ReaderId = 5, IssueDate = DateTime.UtcNow.AddDays(-3), Days = 7, ReturnDate = null }, + new BookIssue { Id = 8, BookId = 8, ReaderId = 6, IssueDate = DateTime.UtcNow.AddDays(-120), Days = 30, ReturnDate = DateTime.UtcNow.AddDays(-90) }, + new BookIssue { Id = 9, BookId = 9, ReaderId = 7, IssueDate = DateTime.UtcNow.AddDays(-60), Days = 20, ReturnDate = DateTime.UtcNow.AddDays(-35) }, + new BookIssue { Id = 10, BookId = 10, ReaderId = 8, IssueDate = DateTime.UtcNow.AddDays(-25), Days = 14, ReturnDate = DateTime.UtcNow.AddDays(-5) }, + new BookIssue { Id = 11, BookId = 1, ReaderId = 9, IssueDate = DateTime.UtcNow.AddDays(-5), Days = 10, ReturnDate = null }, + new BookIssue { Id = 12, BookId = 2, ReaderId = 10, IssueDate = DateTime.UtcNow.AddDays(-90), Days = 30, ReturnDate = DateTime.UtcNow.AddDays(-60) } + ]; +} diff --git a/Library/Models/Book.cs b/Library/Models/Book.cs new file mode 100644 index 000000000..d9ee9e402 --- /dev/null +++ b/Library/Models/Book.cs @@ -0,0 +1,38 @@ +namespace Library.Domain.Models; + +/// Сущность книги, содержащая сведения из каталога библиотеки +public class Book +{ + /// Уникальный идентификатор + public required int Id { get; set; } + + /// Инвентарный номер + public required string InventoryNumber { get; set; } + + /// Шифр в алфавитном каталоге + public required string AlphabetCode { get; set; } + + /// Инициалы и фамилии авторов + public string? Authors { get; set; } + + /// Название + public required string Title { get; set; } + + /// Идентификатор вида издания + public required int EditionTypeId { get; set; } + + /// Вид издания + public EditionType? EditionType { get; set; } + + /// Идентификатор издательства + public required int PublisherId { get; set; } + + /// Издательство + public Publisher? Publisher { get; set; } + + /// Год издания + public int Year { get; set; } + + /// Записи о выдаче книги + public ICollection Issues { get; set; } = []; +} \ No newline at end of file diff --git a/Library/Models/BookIssue.cs b/Library/Models/BookIssue.cs new file mode 100644 index 000000000..a029578e1 --- /dev/null +++ b/Library/Models/BookIssue.cs @@ -0,0 +1,33 @@ +namespace Library.Domain.Models; + +/// Сущность выдачи книги с данными сроков и возвратов +public class BookIssue +{ + /// Уникальный идентификатор + public required int Id { get; set; } + + /// Идентификатор книги + public required int BookId { get; set; } + + /// Выданная книга + public Book? Book { get; set; } + + /// Идентификатор читателя + public required int ReaderId { get; set; } + + /// Читатель, кому выдана книга + public Reader? Reader { get; set; } + + /// Дата выдачи книги + public required DateTime IssueDate { get; set; } + + /// Количество дней, на которое выдана книга + public required int Days { get; set; } + + /// Дата возврата книги + public DateTime? ReturnDate { get; set; } + + /// Флаг просрочки срока возврата + public bool IsOverdue => + ReturnDate == null && DateTime.UtcNow.Date > IssueDate.Date.AddDays(Days); +} \ No newline at end of file diff --git a/Library/Models/EditionType.cs b/Library/Models/EditionType.cs new file mode 100644 index 000000000..13eed072d --- /dev/null +++ b/Library/Models/EditionType.cs @@ -0,0 +1,11 @@ +namespace Library.Domain.Models; + +/// Справочник видов издания +public class EditionType +{ + /// Уникальный идентификатор + public required int Id { get; set; } + + /// Наименование вида издания + public required string Name { get; set; } +} \ No newline at end of file diff --git a/Library/Models/Publisher.cs b/Library/Models/Publisher.cs new file mode 100644 index 000000000..2ee46c961 --- /dev/null +++ b/Library/Models/Publisher.cs @@ -0,0 +1,11 @@ +namespace Library.Domain.Models; + +/// Справочник издательств, к которым относятся книги +public class Publisher +{ + /// Уникальный идентификатор + public required int Id { get; set; } + + /// Наименование издательства + public required string Name { get; set; } +} \ No newline at end of file diff --git a/Library/Models/Reader.cs b/Library/Models/Reader.cs new file mode 100644 index 000000000..01a9b599c --- /dev/null +++ b/Library/Models/Reader.cs @@ -0,0 +1,23 @@ +namespace Library.Domain.Models; + +/// Сущность читателя библиотеки с персональными данными и историей выдач +public class Reader +{ + /// Уникальный идентификатор + public required int Id { get; set; } + + /// ФИО читателя + public required string FullName { get; set; } + + /// Адрес читателя + public string? Address { get; set; } + + /// Телефон читателя + public required string Phone { get; set; } + + /// Дата регистрации читателя + public DateTime? RegistrationDate { get; set; } + + /// Выданные читателю книги + public ICollection BookIssues { get; set; } = []; +} \ No newline at end of file From d5f523964cbe9d717c18f56a0286611fdc795a0d Mon Sep 17 00:00:00 2001 From: Ryedis <134209766+Ryedis@users.noreply.github.com> Date: Wed, 18 Feb 2026 21:19:52 +0400 Subject: [PATCH 2/7] add tests for library --- .gitignore | 3 + .../{ => Library.Domain}/Data/DataSeeder.cs | 19 ++- Library/Library.Domain/Library.Domain.csproj | 9 ++ Library/{ => Library.Domain}/Models/Book.cs | 26 +++- .../{ => Library.Domain}/Models/BookIssue.cs | 26 +++- .../Models/EditionType.cs | 8 +- .../{ => Library.Domain}/Models/Publisher.cs | 6 + Library/{ => Library.Domain}/Models/Reader.cs | 24 ++- Library/Library.Tests/Library.Tests.csproj | 27 ++++ Library/Library.Tests/LibraryTests.cs | 145 ++++++++++++++++++ enterprise-development.sln | 36 +++++ 11 files changed, 315 insertions(+), 14 deletions(-) rename Library/{ => Library.Domain}/Data/DataSeeder.cs (93%) create mode 100644 Library/Library.Domain/Library.Domain.csproj rename Library/{ => Library.Domain}/Models/Book.cs (65%) rename Library/{ => Library.Domain}/Models/BookIssue.cs (55%) rename Library/{ => Library.Domain}/Models/EditionType.cs (52%) rename Library/{ => Library.Domain}/Models/Publisher.cs (75%) rename Library/{ => Library.Domain}/Models/Reader.cs (56%) create mode 100644 Library/Library.Tests/Library.Tests.csproj create mode 100644 Library/Library.Tests/LibraryTests.cs create mode 100644 enterprise-development.sln diff --git a/.gitignore b/.gitignore index ce892922f..6ecb5f9bd 100644 --- a/.gitignore +++ b/.gitignore @@ -416,3 +416,6 @@ FodyWeavers.xsd *.msix *.msm *.msp + +**/bin +**/obj \ No newline at end of file diff --git a/Library/Data/DataSeeder.cs b/Library/Library.Domain/Data/DataSeeder.cs similarity index 93% rename from Library/Data/DataSeeder.cs rename to Library/Library.Domain/Data/DataSeeder.cs index 1717ce8b6..82ea79bdf 100644 --- a/Library/Data/DataSeeder.cs +++ b/Library/Library.Domain/Data/DataSeeder.cs @@ -1,11 +1,14 @@ using Library.Domain.Models; namespace Library.Domain.Data; - +/// /// Класс, содержащий заранее подготовленные тестовые данные для доменной модели библиотеки +/// public class DataSeeder { + /// /// Список видов изданий + /// public List EditionTypes { get; } = [ new EditionType { Id = 1, Name = "Монография" }, @@ -20,7 +23,9 @@ public class DataSeeder new EditionType { Id = 10, Name = "Бизнес-литература" }, ]; + /// /// Список издательств + /// public List Publishers { get; } = [ new Publisher { Id = 1, Name = "Бином" }, @@ -35,7 +40,9 @@ public class DataSeeder new Publisher { Id = 10, Name = "Энергия" }, ]; + /// /// Список книг с заполненными ссылками на издательства и виды изданий + /// public List Books { get; } = [ new Book { Id = 1, InventoryNumber = "BK-101", AlphabetCode = "И-101", Authors = "И. Ньютон", Title = "Математические начала", EditionTypeId = 1, PublisherId = 5, Year = 1687 }, @@ -50,7 +57,9 @@ public class DataSeeder new Book { Id = 10, InventoryNumber = "BK-110", AlphabetCode = "Г-999", Authors = "А. Гауди", Title = "Архитектура форм", EditionTypeId = 1, PublisherId = 10, Year = 1925 }, ]; - /// Список читателей библиотеки, включающий персональные данные и дату регистрации + /// + /// Список читателей библиотеки с их данными + /// public List Readers { get; } = [ new Reader { Id = 1, FullName = "Орлов Денис Сергеевич", Address = "ул. Березовая, 12", Phone = "89110000001", RegistrationDate = DateTime.UtcNow.AddYears(-3) }, @@ -65,10 +74,12 @@ public class DataSeeder new Reader { Id = 10, FullName = "Громова София Артемовна", Address = "ул. Южная, 14", Phone = "89110000010", RegistrationDate = DateTime.UtcNow.AddMonths(-2) }, ]; - /// Список фактов выдачи книг + /// + /// Список выдачи книг + /// public List BookIssues { get; } = [ - new BookIssue { Id = 1, BookId = 1, ReaderId = 1, IssueDate = DateTime.UtcNow.AddDays(-15), Days = 30, ReturnDate = null }, + new BookIssue { Id = 1, BookId = 1, ReaderId = 1, IssueDate = DateTime.UtcNow.AddDays(-15), Days = 30, ReturnDate = null }, new BookIssue { Id = 2, BookId = 2, ReaderId = 1, IssueDate = DateTime.UtcNow.AddDays(-200), Days = 60, ReturnDate = DateTime.UtcNow.AddDays(-140) }, new BookIssue { Id = 3, BookId = 3, ReaderId = 2, IssueDate = DateTime.UtcNow.AddDays(-40), Days = 14, ReturnDate = DateTime.UtcNow.AddDays(-20) }, new BookIssue { Id = 4, BookId = 4, ReaderId = 2, IssueDate = DateTime.UtcNow.AddDays(-7), Days = 10, ReturnDate = null }, diff --git a/Library/Library.Domain/Library.Domain.csproj b/Library/Library.Domain/Library.Domain.csproj new file mode 100644 index 000000000..bb23fb7d6 --- /dev/null +++ b/Library/Library.Domain/Library.Domain.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/Library/Models/Book.cs b/Library/Library.Domain/Models/Book.cs similarity index 65% rename from Library/Models/Book.cs rename to Library/Library.Domain/Models/Book.cs index d9ee9e402..366828c25 100644 --- a/Library/Models/Book.cs +++ b/Library/Library.Domain/Models/Book.cs @@ -1,38 +1,62 @@ namespace Library.Domain.Models; -/// Сущность книги, содержащая сведения из каталога библиотеки +/// +/// Сущность книги из каталога библиотеки +/// public class Book { + /// /// Уникальный идентификатор + /// public required int Id { get; set; } + /// /// Инвентарный номер + /// public required string InventoryNumber { get; set; } + /// /// Шифр в алфавитном каталоге + /// public required string AlphabetCode { get; set; } + /// /// Инициалы и фамилии авторов + /// public string? Authors { get; set; } + /// /// Название + /// public required string Title { get; set; } + /// /// Идентификатор вида издания + /// public required int EditionTypeId { get; set; } + /// /// Вид издания + /// public EditionType? EditionType { get; set; } + /// /// Идентификатор издательства + /// public required int PublisherId { get; set; } + /// /// Издательство + /// public Publisher? Publisher { get; set; } + /// /// Год издания + /// public int Year { get; set; } + /// /// Записи о выдаче книги + /// public ICollection Issues { get; set; } = []; } \ No newline at end of file diff --git a/Library/Models/BookIssue.cs b/Library/Library.Domain/Models/BookIssue.cs similarity index 55% rename from Library/Models/BookIssue.cs rename to Library/Library.Domain/Models/BookIssue.cs index a029578e1..2f6d2a97d 100644 --- a/Library/Models/BookIssue.cs +++ b/Library/Library.Domain/Models/BookIssue.cs @@ -1,33 +1,53 @@ namespace Library.Domain.Models; -/// Сущность выдачи книги с данными сроков и возвратов +/// +/// Сущность выдачи книги читателю с указанием сроков и состояния возврата +/// public class BookIssue { + /// /// Уникальный идентификатор + /// public required int Id { get; set; } + /// /// Идентификатор книги + /// public required int BookId { get; set; } + /// /// Выданная книга + /// public Book? Book { get; set; } + /// /// Идентификатор читателя + /// public required int ReaderId { get; set; } - /// Читатель, кому выдана книга + /// + /// Читатель, которому была выдана книга + /// public Reader? Reader { get; set; } + /// /// Дата выдачи книги + /// public required DateTime IssueDate { get; set; } + /// /// Количество дней, на которое выдана книга + /// public required int Days { get; set; } + /// /// Дата возврата книги + /// public DateTime? ReturnDate { get; set; } - /// Флаг просрочки срока возврата + /// + /// Флаг просрочки срока возврата книги + /// public bool IsOverdue => ReturnDate == null && DateTime.UtcNow.Date > IssueDate.Date.AddDays(Days); } \ No newline at end of file diff --git a/Library/Models/EditionType.cs b/Library/Library.Domain/Models/EditionType.cs similarity index 52% rename from Library/Models/EditionType.cs rename to Library/Library.Domain/Models/EditionType.cs index 13eed072d..4eba925ba 100644 --- a/Library/Models/EditionType.cs +++ b/Library/Library.Domain/Models/EditionType.cs @@ -1,11 +1,17 @@ namespace Library.Domain.Models; -/// Справочник видов издания +/// +/// Справочник видов издания, используемый для классификации книг +/// public class EditionType { + /// /// Уникальный идентификатор + /// public required int Id { get; set; } + /// /// Наименование вида издания + /// public required string Name { get; set; } } \ No newline at end of file diff --git a/Library/Models/Publisher.cs b/Library/Library.Domain/Models/Publisher.cs similarity index 75% rename from Library/Models/Publisher.cs rename to Library/Library.Domain/Models/Publisher.cs index 2ee46c961..5c1fedbac 100644 --- a/Library/Models/Publisher.cs +++ b/Library/Library.Domain/Models/Publisher.cs @@ -1,11 +1,17 @@ namespace Library.Domain.Models; +/// /// Справочник издательств, к которым относятся книги +/// public class Publisher { + /// /// Уникальный идентификатор + /// public required int Id { get; set; } + /// /// Наименование издательства + /// public required string Name { get; set; } } \ No newline at end of file diff --git a/Library/Models/Reader.cs b/Library/Library.Domain/Models/Reader.cs similarity index 56% rename from Library/Models/Reader.cs rename to Library/Library.Domain/Models/Reader.cs index 01a9b599c..177c31a4a 100644 --- a/Library/Models/Reader.cs +++ b/Library/Library.Domain/Models/Reader.cs @@ -1,23 +1,37 @@ namespace Library.Domain.Models; +/// /// Сущность читателя библиотеки с персональными данными и историей выдач +/// public class Reader { + /// /// Уникальный идентификатор + /// public required int Id { get; set; } - /// ФИО читателя + /// + /// ФИО + /// public required string FullName { get; set; } - /// Адрес читателя + /// + /// Адрес + /// public string? Address { get; set; } - /// Телефон читателя + /// + /// Телефон + /// public required string Phone { get; set; } - /// Дата регистрации читателя + /// + /// Дата регистрации + /// public DateTime? RegistrationDate { get; set; } - /// Выданные читателю книги + /// + /// Выданные книги + /// public ICollection BookIssues { get; set; } = []; } \ No newline at end of file diff --git a/Library/Library.Tests/Library.Tests.csproj b/Library/Library.Tests/Library.Tests.csproj new file mode 100644 index 000000000..6f0ecb3b5 --- /dev/null +++ b/Library/Library.Tests/Library.Tests.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + diff --git a/Library/Library.Tests/LibraryTests.cs b/Library/Library.Tests/LibraryTests.cs new file mode 100644 index 000000000..9daaa793b --- /dev/null +++ b/Library/Library.Tests/LibraryTests.cs @@ -0,0 +1,145 @@ +using Library.Domain.Data; + +namespace Library.Tests; + +/// +/// Набор unit тестов для тестирования доменной области +/// +public class LibraryTests(DataSeeder dataSeeder) : IClassFixture +{ + /// + /// Проверяет что активные выдачи сортируются по названию книги и возвращают ожидаемый порядок идентификаторов книг + /// + [Fact] + public void IssuedBooks_OrderByBookTitle_ReturnsActiveIssuesOrderedByTitle() + { + var actualBookIds = dataSeeder.BookIssues + .Where(bi => bi.ReturnDate == null) + .Join(dataSeeder.Books, + bi => bi.BookId, + b => b.Id, + (bi, b) => new { bi, b }) + .OrderBy(x => x.b.Title) + .Select(x => x.b.Id) + .ToList(); + + var expectedBookIds = new List { 1, 1, 4, 7 }; + + Assert.Equal(expectedBookIds, actualBookIds); + } + + /// + /// Топ 5 читателей за последний год по количеству выдач и сравнивает по Id и по количествам + /// + [Fact] + public void Top5Readers_ByIssuesCountInPeriod_ReturnsExpectedTop5() + { + var periodStart = DateTime.UtcNow.AddYears(-1); + var periodEnd = DateTime.UtcNow; + + var topReaders = dataSeeder.BookIssues + .Where(bi => bi.IssueDate >= periodStart && bi.IssueDate <= periodEnd) + .GroupBy(bi => bi.ReaderId) + .Select(g => new { ReaderId = g.Key, Count = g.Count() }) + .Join(dataSeeder.Readers, g => g.ReaderId, r => r.Id, (g, r) => new { r.Id, r.FullName, g.Count }) + .OrderByDescending(x => x.Count) + .ThenBy(x => x.FullName) + .Take(5) + .ToList(); + + var actualIds = topReaders.Select(x => x.Id).ToList(); + var actualCounts = topReaders.Select(x => x.Count).ToList(); + + var expectedIds = new List { 2, 1, 3, 8, 10 }; + var expectedCounts = new List { 2, 2, 1, 1, 1 }; + + Assert.Equal(expectedIds, actualIds); + Assert.Equal(expectedCounts, actualCounts); + } + + /// + /// Читатели, у которых есть выдачи с максимальным количеством дней, и сортирует их по ФИО + /// + [Fact] + public void Readers_ByMaxLoanDaysOrderedByFullName_ReturnsExpected() + { + var maxDays = dataSeeder.BookIssues.Max(bi => bi.Days); + + var readersWithMaxDays = dataSeeder.BookIssues + .Where(bi => bi.Days == maxDays) + .Select(bi => bi.ReaderId) + .Distinct() + .Join(dataSeeder.Readers, id => id, r => r.Id, (id, r) => new { r.Id, r.FullName }) + .OrderBy(r => r.FullName) + .Select(r => r.Id) + .ToList(); + + var expectedId = 1; + var expectedDays = 60; + + Assert.Single(readersWithMaxDays); + Assert.Equal(expectedDays, maxDays); + Assert.Equal(expectedId, readersWithMaxDays[0]); + } + + /// + /// Топ 5 издательств за последний год по количеству выдач и сравнивает по Id и количествам + /// + [Fact] + public void Top5Publishers_ByIssuesCountLastYear_ReturnsExpectedTop5() + { + var lastYearStart = DateTime.UtcNow.AddYears(-1); + var lastYearEnd = DateTime.UtcNow; + + var topPublishers = dataSeeder.BookIssues + .Where(bi => bi.IssueDate >= lastYearStart && bi.IssueDate <= lastYearEnd) + .Join(dataSeeder.Books, bi => bi.BookId, b => b.Id, (bi, b) => b.PublisherId) + .GroupBy(pid => pid) + .Select(g => new { PublisherId = g.Key, Count = g.Count() }) + .Join(dataSeeder.Publishers, g => g.PublisherId, p => p.Id, (g, p) => new { p.Id, p.Name, g.Count }) + .OrderByDescending(x => x.Count) + .ThenBy(x => x.Name) + .Take(5) + .ToList(); + + var actualPublisherIds = topPublishers.Select(x => x.Id).ToList(); + var actualCounts = topPublishers.Select(x => x.Count).ToList(); + + var expectedPublisherIds = new List { 6, 4, 5, 1, 2 }; + var expectedCounts = new List { 3, 2, 2, 1, 1 }; + + Assert.Equal(expectedPublisherIds, actualPublisherIds); + Assert.Equal(expectedCounts, actualCounts); + } + + /// + /// Топ 5 наименее популярных книг за последний год сравнение по Id и количествам + /// + [Fact] + public void Bottom5Books_ByIssuesCountLastYear_ReturnsExpectedBottom5() + { + var lastYearStart = DateTime.UtcNow.AddYears(-1); + var lastYearEnd = DateTime.UtcNow; + + var bookCounts = dataSeeder.Books + .GroupJoin( + dataSeeder.BookIssues.Where(bi => bi.IssueDate >= lastYearStart && bi.IssueDate <= lastYearEnd), + b => b.Id, + bi => bi.BookId, + (b, issues) => new { Book = b, Count = issues.Count() } + ) + .OrderBy(x => x.Count) + .ThenBy(x => x.Book.Title) + .Take(5) + .ToList(); + + var actualBookIds = bookCounts.Select(x => x.Book.Id).ToList(); + var actualCounts = bookCounts.Select(x => x.Count).ToList(); + + var expectedBookIds = new List { 10, 5, 6, 3, 8 }; + var expectedCounts = new List { 1, 1, 1, 1, 1 }; + + Assert.Equal(expectedBookIds, actualBookIds); + Assert.Equal(expectedCounts, actualCounts); + } +} \ No newline at end of file diff --git a/enterprise-development.sln b/enterprise-development.sln new file mode 100644 index 000000000..6550f8d0c --- /dev/null +++ b/enterprise-development.sln @@ -0,0 +1,36 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{50A17DA1-3556-4046-CEDC-33EB466D9C32}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library.Domain", "Library\Library.Domain\Library.Domain.csproj", "{39E55976-5424-CEC7-0978-4D789AC54AC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library.Tests", "Library\Library.Tests\Library.Tests.csproj", "{60F294C7-3D77-63D8-5514-C4AB9BB913BD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {39E55976-5424-CEC7-0978-4D789AC54AC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39E55976-5424-CEC7-0978-4D789AC54AC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39E55976-5424-CEC7-0978-4D789AC54AC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39E55976-5424-CEC7-0978-4D789AC54AC6}.Release|Any CPU.Build.0 = Release|Any CPU + {60F294C7-3D77-63D8-5514-C4AB9BB913BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60F294C7-3D77-63D8-5514-C4AB9BB913BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60F294C7-3D77-63D8-5514-C4AB9BB913BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60F294C7-3D77-63D8-5514-C4AB9BB913BD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {39E55976-5424-CEC7-0978-4D789AC54AC6} = {50A17DA1-3556-4046-CEDC-33EB466D9C32} + {60F294C7-3D77-63D8-5514-C4AB9BB913BD} = {50A17DA1-3556-4046-CEDC-33EB466D9C32} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B2802E6A-D42A-4C1D-8949-77992D768DF6} + EndGlobalSection +EndGlobal From f76f3c48dc4ac716a8f7dcfb3c601f07feddea24 Mon Sep 17 00:00:00 2001 From: Ryedis <134209766+Ryedis@users.noreply.github.com> Date: Wed, 18 Feb 2026 21:26:59 +0400 Subject: [PATCH 3/7] add github action tests --- .github/workflows/tests.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..c56adb52b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,29 @@ +name: Run .NET Tests + +on: + push: + branches: lab_1 + pull_request: + branches: lab_1 + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.0.x" + + - name: Restore dependencies + run: dotnet restore enterprise-development.sln + + - name: Build + run: dotnet build enterprise-development.sln --configuration Release --no-restore + + - name: Run tests + run: dotnet test enterprise-development.sln --configuration Release --no-build \ No newline at end of file From b370ceb6bcb2b83aa22297ff205ffea3c0d3db1e Mon Sep 17 00:00:00 2001 From: Ryedis <134209766+Ryedis@users.noreply.github.com> Date: Wed, 18 Feb 2026 21:49:12 +0400 Subject: [PATCH 4/7] fix bottom5 test --- Library/Library.Tests/LibraryTests.cs | 46 ++++++++++++--------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/Library/Library.Tests/LibraryTests.cs b/Library/Library.Tests/LibraryTests.cs index 9daaa793b..3becaf6f9 100644 --- a/Library/Library.Tests/LibraryTests.cs +++ b/Library/Library.Tests/LibraryTests.cs @@ -116,30 +116,26 @@ public void Top5Publishers_ByIssuesCountLastYear_ReturnsExpectedTop5() /// Топ 5 наименее популярных книг за последний год сравнение по Id и количествам /// [Fact] - public void Bottom5Books_ByIssuesCountLastYear_ReturnsExpectedBottom5() - { - var lastYearStart = DateTime.UtcNow.AddYears(-1); - var lastYearEnd = DateTime.UtcNow; - - var bookCounts = dataSeeder.Books - .GroupJoin( - dataSeeder.BookIssues.Where(bi => bi.IssueDate >= lastYearStart && bi.IssueDate <= lastYearEnd), - b => b.Id, - bi => bi.BookId, - (b, issues) => new { Book = b, Count = issues.Count() } - ) - .OrderBy(x => x.Count) - .ThenBy(x => x.Book.Title) - .Take(5) - .ToList(); - - var actualBookIds = bookCounts.Select(x => x.Book.Id).ToList(); - var actualCounts = bookCounts.Select(x => x.Count).ToList(); - - var expectedBookIds = new List { 10, 5, 6, 3, 8 }; - var expectedCounts = new List { 1, 1, 1, 1, 1 }; - - Assert.Equal(expectedBookIds, actualBookIds); - Assert.Equal(expectedCounts, actualCounts); + public void Bottom5Books_ByIssuesCountLastYear_ReturnsExpectedBottom5() +{ + var lastYearStart = DateTime.UtcNow.AddYears(-1); + var lastYearEnd = DateTime.UtcNow; + + var bookCounts = dataSeeder.Books + .GroupJoin( + dataSeeder.BookIssues.Where(bi => bi.IssueDate >= lastYearStart && bi.IssueDate <= lastYearEnd), + b => b.Id, + bi => bi.BookId, + (b, issues) => new { Book = b, Count = issues.Count() } + ) + .OrderBy(x => x.Count) + .ThenBy(x => x.Book.Title, StringComparer.Ordinal) // Явно указываем Ordinal сортировку + .Take(5) + .ToList(); + + var actualBookIds = bookCounts.Select(x => x.Book.Id).ToList(); + var expectedBookIds = new List { 9, 10, 5, 6, 3 }; // Исправь ожидаемый порядок! + + Assert.Equal(expectedBookIds, actualBookIds); } } \ No newline at end of file From c9b8bae4e66ec34722b3642d1bf6fa835fbfc5b8 Mon Sep 17 00:00:00 2001 From: Ryedis <134209766+Ryedis@users.noreply.github.com> Date: Wed, 18 Feb 2026 21:58:44 +0400 Subject: [PATCH 5/7] fix tests --- Library/Library.Tests/LibraryTests.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Library/Library.Tests/LibraryTests.cs b/Library/Library.Tests/LibraryTests.cs index 3becaf6f9..028fc70f5 100644 --- a/Library/Library.Tests/LibraryTests.cs +++ b/Library/Library.Tests/LibraryTests.cs @@ -129,13 +129,11 @@ public void Bottom5Books_ByIssuesCountLastYear_ReturnsExpectedBottom5() (b, issues) => new { Book = b, Count = issues.Count() } ) .OrderBy(x => x.Count) - .ThenBy(x => x.Book.Title, StringComparer.Ordinal) // Явно указываем Ordinal сортировку - .Take(5) + .ThenBy(x => x.Book.Title, StringComparer.Ordinal) .ToList(); var actualBookIds = bookCounts.Select(x => x.Book.Id).ToList(); - var expectedBookIds = new List { 9, 10, 5, 6, 3 }; // Исправь ожидаемый порядок! - + var expectedBookIds = new List { 9, 10, 5, 6, 3 }; Assert.Equal(expectedBookIds, actualBookIds); } } \ No newline at end of file From f7e7b933094b16ea359e4f6669d71dc18866261a Mon Sep 17 00:00:00 2001 From: Ryedis <134209766+Ryedis@users.noreply.github.com> Date: Thu, 19 Feb 2026 20:42:04 +0400 Subject: [PATCH 6/7] correcting comments, replacing utc, fixing tests --- .github/workflows/tests.yml | 6 +- enterprise-development.sln => Library.sln | 0 .../Abstractions/ITimeProvider.cs | 9 ++ .../Abstractions/SystemTimeProvider.cs | 12 +++ Library/Library.Domain/Data/DataSeeder.cs | 81 +++++++------- Library/Library.Domain/Models/Book.cs | 2 +- Library/Library.Domain/Models/BookIssue.cs | 16 ++- Library/Library.Domain/Models/Reader.cs | 2 +- Library/Library.Tests/FakeTimeProvider.cs | 22 ++++ Library/Library.Tests/LibraryTests.cs | 100 +++++++++++------- 10 files changed, 160 insertions(+), 90 deletions(-) rename enterprise-development.sln => Library.sln (100%) create mode 100644 Library/Library.Domain/Abstractions/ITimeProvider.cs create mode 100644 Library/Library.Domain/Abstractions/SystemTimeProvider.cs create mode 100644 Library/Library.Tests/FakeTimeProvider.cs diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c56adb52b..6bc513941 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,10 +20,10 @@ jobs: dotnet-version: "8.0.x" - name: Restore dependencies - run: dotnet restore enterprise-development.sln + run: dotnet restore Library.sln - name: Build - run: dotnet build enterprise-development.sln --configuration Release --no-restore + run: dotnet build Library.sln --configuration Release --no-restore - name: Run tests - run: dotnet test enterprise-development.sln --configuration Release --no-build \ No newline at end of file + run: dotnet test Library.sln --configuration Release --no-build \ No newline at end of file diff --git a/enterprise-development.sln b/Library.sln similarity index 100% rename from enterprise-development.sln rename to Library.sln diff --git a/Library/Library.Domain/Abstractions/ITimeProvider.cs b/Library/Library.Domain/Abstractions/ITimeProvider.cs new file mode 100644 index 000000000..56d5e29cb --- /dev/null +++ b/Library/Library.Domain/Abstractions/ITimeProvider.cs @@ -0,0 +1,9 @@ +namespace Library.Domain.Abstractions; + +/// +/// Интерфейс поставщика времени для детерминированного тестирования +/// +public interface ITimeProvider +{ + public DateTime Now { get; } +} diff --git a/Library/Library.Domain/Abstractions/SystemTimeProvider.cs b/Library/Library.Domain/Abstractions/SystemTimeProvider.cs new file mode 100644 index 000000000..ace01f126 --- /dev/null +++ b/Library/Library.Domain/Abstractions/SystemTimeProvider.cs @@ -0,0 +1,12 @@ +namespace Library.Domain.Abstractions; + +/// +/// Поставщик времени, возвращающий текущее системное время +/// +public class SystemTimeProvider : ITimeProvider +{ + /// + /// Возвращает текущее системное локальное время. + /// + public DateTime Now => DateTime.Now; +} diff --git a/Library/Library.Domain/Data/DataSeeder.cs b/Library/Library.Domain/Data/DataSeeder.cs index 82ea79bdf..51d5b29c7 100644 --- a/Library/Library.Domain/Data/DataSeeder.cs +++ b/Library/Library.Domain/Data/DataSeeder.cs @@ -1,14 +1,23 @@ using Library.Domain.Models; +using Library.Domain.Abstractions; namespace Library.Domain.Data; + /// -/// Класс, содержащий заранее подготовленные тестовые данные для доменной модели библиотеки +/// Класс, содержащий заранее подготовленные тестовые данные /// public class DataSeeder { + private readonly DateTime _now; + /// - /// Список видов изданий + /// Создаёт экземпляр DataSeeder с заданным провайдером времени. /// + public DataSeeder(ITimeProvider timeProvider) + { + _now = timeProvider.Now; + } + public List EditionTypes { get; } = [ new EditionType { Id = 1, Name = "Монография" }, @@ -23,9 +32,6 @@ public class DataSeeder new EditionType { Id = 10, Name = "Бизнес-литература" }, ]; - /// - /// Список издательств - /// public List Publishers { get; } = [ new Publisher { Id = 1, Name = "Бином" }, @@ -40,9 +46,6 @@ public class DataSeeder new Publisher { Id = 10, Name = "Энергия" }, ]; - /// - /// Список книг с заполненными ссылками на издательства и виды изданий - /// public List Books { get; } = [ new Book { Id = 1, InventoryNumber = "BK-101", AlphabetCode = "И-101", Authors = "И. Ньютон", Title = "Математические начала", EditionTypeId = 1, PublisherId = 5, Year = 1687 }, @@ -57,39 +60,33 @@ public class DataSeeder new Book { Id = 10, InventoryNumber = "BK-110", AlphabetCode = "Г-999", Authors = "А. Гауди", Title = "Архитектура форм", EditionTypeId = 1, PublisherId = 10, Year = 1925 }, ]; - /// - /// Список читателей библиотеки с их данными - /// - public List Readers { get; } = - [ - new Reader { Id = 1, FullName = "Орлов Денис Сергеевич", Address = "ул. Березовая, 12", Phone = "89110000001", RegistrationDate = DateTime.UtcNow.AddYears(-3) }, - new Reader { Id = 2, FullName = "Мельников Артем Игоревич", Address = "ул. Солнечная, 45", Phone = "89110000002", RegistrationDate = DateTime.UtcNow.AddYears(-2) }, - new Reader { Id = 3, FullName = "Белов Кирилл Андреевич", Address = "ул. Полевая, 7", Phone = "89110000003", RegistrationDate = DateTime.UtcNow.AddMonths(-18) }, - new Reader { Id = 4, FullName = "Егорова Марина Олеговна", Address = "ул. Озерная, 21", Phone = "89110000004", RegistrationDate = DateTime.UtcNow.AddMonths(-12) }, - new Reader { Id = 5, FullName = "Тарасов Максим Дмитриевич", Address = "ул. Лесная, 3", Phone = "89110000005", RegistrationDate = DateTime.UtcNow.AddMonths(-10) }, - new Reader { Id = 6, FullName = "Крылова Анастасия Павловна", Address = "ул. Школьная, 9", Phone = "89110000006", RegistrationDate = DateTime.UtcNow.AddMonths(-8) }, - new Reader { Id = 7, FullName = "Никитин Роман Евгеньевич", Address = "ул. Центральная, 15", Phone = "89110000007", RegistrationDate = DateTime.UtcNow.AddMonths(-6) }, - new Reader { Id = 8, FullName = "Волкова Дарья Ильинична", Address = "ул. Мира, 19", Phone = "89110000008", RegistrationDate = DateTime.UtcNow.AddMonths(-5) }, - new Reader { Id = 9, FullName = "Зайцев Павел Николаевич", Address = "ул. Новая, 8", Phone = "89110000009", RegistrationDate = DateTime.UtcNow.AddMonths(-4) }, - new Reader { Id = 10, FullName = "Громова София Артемовна", Address = "ул. Южная, 14", Phone = "89110000010", RegistrationDate = DateTime.UtcNow.AddMonths(-2) }, - ]; + public List Readers => new() + { + new Reader { Id = 1, FullName = "Орлов Денис Сергеевич", Address = "ул. Березовая, 12", Phone = "89110000001", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-3)) }, + new Reader { Id = 2, FullName = "Мельников Артем Игоревич", Address = "ул. Солнечная, 45", Phone = "89110000002", RegistrationDate = DateOnly.FromDateTime(_now.AddYears(-2)) }, + new Reader { Id = 3, FullName = "Белов Кирилл Андреевич", Address = "ул. Полевая, 7", Phone = "89110000003", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-18)) }, + new Reader { Id = 4, FullName = "Егорова Марина Олеговна", Address = "ул. Озерная, 21", Phone = "89110000004", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-12)) }, + new Reader { Id = 5, FullName = "Тарасов Максим Дмитриевич", Address = "ул. Лесная, 3", Phone = "89110000005", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-10)) }, + new Reader { Id = 6, FullName = "Крылова Анастасия Павловна", Address = "ул. Школьная, 9", Phone = "89110000006", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-8)) }, + new Reader { Id = 7, FullName = "Никитин Роман Евгеньевич", Address = "ул. Центральная, 15", Phone = "89110000007", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-6)) }, + new Reader { Id = 8, FullName = "Волкова Дарья Ильинична", Address = "ул. Мира, 19", Phone = "89110000008", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-5)) }, + new Reader { Id = 9, FullName = "Зайцев Павел Николаевич", Address = "ул. Новая, 8", Phone = "89110000009", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-4)) }, + new Reader { Id = 10, FullName = "Громова София Артемовна", Address = "ул. Южная, 14", Phone = "89110000010", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-2)) }, + }; - /// - /// Список выдачи книг - /// - public List BookIssues { get; } = - [ - new BookIssue { Id = 1, BookId = 1, ReaderId = 1, IssueDate = DateTime.UtcNow.AddDays(-15), Days = 30, ReturnDate = null }, - new BookIssue { Id = 2, BookId = 2, ReaderId = 1, IssueDate = DateTime.UtcNow.AddDays(-200), Days = 60, ReturnDate = DateTime.UtcNow.AddDays(-140) }, - new BookIssue { Id = 3, BookId = 3, ReaderId = 2, IssueDate = DateTime.UtcNow.AddDays(-40), Days = 14, ReturnDate = DateTime.UtcNow.AddDays(-20) }, - new BookIssue { Id = 4, BookId = 4, ReaderId = 2, IssueDate = DateTime.UtcNow.AddDays(-7), Days = 10, ReturnDate = null }, - new BookIssue { Id = 5, BookId = 5, ReaderId = 3, IssueDate = DateTime.UtcNow.AddDays(-300), Days = 21, ReturnDate = DateTime.UtcNow.AddDays(-260) }, - new BookIssue { Id = 6, BookId = 6, ReaderId = 4, IssueDate = DateTime.UtcNow.AddDays(-50), Days = 14, ReturnDate = DateTime.UtcNow.AddDays(-30) }, - new BookIssue { Id = 7, BookId = 7, ReaderId = 5, IssueDate = DateTime.UtcNow.AddDays(-3), Days = 7, ReturnDate = null }, - new BookIssue { Id = 8, BookId = 8, ReaderId = 6, IssueDate = DateTime.UtcNow.AddDays(-120), Days = 30, ReturnDate = DateTime.UtcNow.AddDays(-90) }, - new BookIssue { Id = 9, BookId = 9, ReaderId = 7, IssueDate = DateTime.UtcNow.AddDays(-60), Days = 20, ReturnDate = DateTime.UtcNow.AddDays(-35) }, - new BookIssue { Id = 10, BookId = 10, ReaderId = 8, IssueDate = DateTime.UtcNow.AddDays(-25), Days = 14, ReturnDate = DateTime.UtcNow.AddDays(-5) }, - new BookIssue { Id = 11, BookId = 1, ReaderId = 9, IssueDate = DateTime.UtcNow.AddDays(-5), Days = 10, ReturnDate = null }, - new BookIssue { Id = 12, BookId = 2, ReaderId = 10, IssueDate = DateTime.UtcNow.AddDays(-90), Days = 30, ReturnDate = DateTime.UtcNow.AddDays(-60) } - ]; + public List BookIssues => new() + { + new BookIssue { Id = 1, BookId = 1, ReaderId = 1, IssueDate = _now.AddDays(-15), Days = 30 }, + new BookIssue { Id = 2, BookId = 2, ReaderId = 1, IssueDate = _now.AddDays(-200), Days = 60 }, + new BookIssue { Id = 3, BookId = 3, ReaderId = 2, IssueDate = _now.AddDays(-40), Days = 14 }, + new BookIssue { Id = 4, BookId = 4, ReaderId = 2, IssueDate = _now.AddDays(-7), Days = 10 }, + new BookIssue { Id = 5, BookId = 5, ReaderId = 3, IssueDate = _now.AddDays(-300), Days = 21 }, + new BookIssue { Id = 6, BookId = 6, ReaderId = 4, IssueDate = _now.AddDays(-50), Days = 14 }, + new BookIssue { Id = 7, BookId = 7, ReaderId = 5, IssueDate = _now.AddDays(-3), Days = 7 }, + new BookIssue { Id = 8, BookId = 8, ReaderId = 6, IssueDate = _now.AddDays(-120), Days = 30 }, + new BookIssue { Id = 9, BookId = 9, ReaderId = 7, IssueDate = _now.AddDays(-60), Days = 20 }, + new BookIssue { Id = 10, BookId = 10, ReaderId = 8, IssueDate = _now.AddDays(-25), Days = 14 }, + new BookIssue { Id = 11, BookId = 1, ReaderId = 9, IssueDate = _now.AddDays(-5), Days = 10 }, + new BookIssue { Id = 12, BookId = 2, ReaderId = 10, IssueDate = _now.AddDays(-90), Days = 30 } + }; } diff --git a/Library/Library.Domain/Models/Book.cs b/Library/Library.Domain/Models/Book.cs index 366828c25..b599cdfaa 100644 --- a/Library/Library.Domain/Models/Book.cs +++ b/Library/Library.Domain/Models/Book.cs @@ -53,7 +53,7 @@ public class Book /// /// Год издания /// - public int Year { get; set; } + public required int Year { get; set; } /// /// Записи о выдаче книги diff --git a/Library/Library.Domain/Models/BookIssue.cs b/Library/Library.Domain/Models/BookIssue.cs index 2f6d2a97d..4145b0ba4 100644 --- a/Library/Library.Domain/Models/BookIssue.cs +++ b/Library/Library.Domain/Models/BookIssue.cs @@ -5,6 +5,8 @@ namespace Library.Domain.Models; /// public class BookIssue { + private readonly Library.Domain.Abstractions.ITimeProvider _timeProvider; + /// /// Уникальный идентификатор /// @@ -41,13 +43,21 @@ public class BookIssue public required int Days { get; set; } /// - /// Дата возврата книги + /// Дата возврата книги (IssueDate + Days) + /// + public DateTime? ReturnDate => IssueDate.AddDays(Days); + + /// + /// Конструктор с опциональным поставщиком времени /// - public DateTime? ReturnDate { get; set; } + public BookIssue(Library.Domain.Abstractions.ITimeProvider? timeProvider = null) + { + _timeProvider = timeProvider ?? new Library.Domain.Abstractions.SystemTimeProvider(); + } /// /// Флаг просрочки срока возврата книги /// public bool IsOverdue => - ReturnDate == null && DateTime.UtcNow.Date > IssueDate.Date.AddDays(Days); + ReturnDate == null && _timeProvider.Now.Date > IssueDate.AddDays(Days).Date; } \ No newline at end of file diff --git a/Library/Library.Domain/Models/Reader.cs b/Library/Library.Domain/Models/Reader.cs index 177c31a4a..3e4c87e4d 100644 --- a/Library/Library.Domain/Models/Reader.cs +++ b/Library/Library.Domain/Models/Reader.cs @@ -28,7 +28,7 @@ public class Reader /// /// Дата регистрации /// - public DateTime? RegistrationDate { get; set; } + public DateOnly? RegistrationDate { get; set; } /// /// Выданные книги diff --git a/Library/Library.Tests/FakeTimeProvider.cs b/Library/Library.Tests/FakeTimeProvider.cs new file mode 100644 index 000000000..c69aa7ef4 --- /dev/null +++ b/Library/Library.Tests/FakeTimeProvider.cs @@ -0,0 +1,22 @@ +using Library.Domain.Abstractions; + +namespace Library.Tests; + +/// +/// Провайдер времени, возвращающий фиксированное значение +/// +public class FakeTimeProvider : ITimeProvider +{ + /// + /// Текущее время + /// + public DateTime Now { get; } + + /// + /// Создаёт экземпляр с заданным временем + /// + public FakeTimeProvider(DateTime now) + { + Now = now; + } +} diff --git a/Library/Library.Tests/LibraryTests.cs b/Library/Library.Tests/LibraryTests.cs index 028fc70f5..9517c76ac 100644 --- a/Library/Library.Tests/LibraryTests.cs +++ b/Library/Library.Tests/LibraryTests.cs @@ -1,24 +1,42 @@ using Library.Domain.Data; +using Library.Domain.Abstractions; namespace Library.Tests; /// /// Набор unit тестов для тестирования доменной области /// -public class LibraryTests(DataSeeder dataSeeder) : IClassFixture +public class LibraryTests { + // Экземпляр DataSeeder для доступа к тестовым данным + private readonly DataSeeder _dataSeeder; + /// - /// Проверяет что активные выдачи сортируются по названию книги и возвращают ожидаемый порядок идентификаторов книг + // Фиксированная дата + /// + private readonly DateTime _fixedNow = new(2026, 2, 19); + + /// + /// Конструктор класса, создаёт DataSeeder с фиксированной датой + /// + public LibraryTests() + { + var fakeTime = new FakeTimeProvider(_fixedNow); + _dataSeeder = new DataSeeder(fakeTime); + } + + /// + /// Топ 5 издательств за последний год по количеству выдач и сравнивает по Id и количествам /// [Fact] public void IssuedBooks_OrderByBookTitle_ReturnsActiveIssuesOrderedByTitle() { - var actualBookIds = dataSeeder.BookIssues - .Where(bi => bi.ReturnDate == null) - .Join(dataSeeder.Books, - bi => bi.BookId, - b => b.Id, - (bi, b) => new { bi, b }) + var actualBookIds = _dataSeeder.BookIssues + .Where(bi => _fixedNow.Date < bi.ReturnDate) + .Join(_dataSeeder.Books, + bi => bi.BookId, + b => b.Id, + (bi, b) => new { bi, b }) .OrderBy(x => x.b.Title) .Select(x => x.b.Id) .ToList(); @@ -34,14 +52,14 @@ public void IssuedBooks_OrderByBookTitle_ReturnsActiveIssuesOrderedByTitle() [Fact] public void Top5Readers_ByIssuesCountInPeriod_ReturnsExpectedTop5() { - var periodStart = DateTime.UtcNow.AddYears(-1); - var periodEnd = DateTime.UtcNow; + var periodStart = _fixedNow.AddYears(-1); + var periodEnd = _fixedNow; - var topReaders = dataSeeder.BookIssues + var topReaders = _dataSeeder.BookIssues .Where(bi => bi.IssueDate >= periodStart && bi.IssueDate <= periodEnd) .GroupBy(bi => bi.ReaderId) .Select(g => new { ReaderId = g.Key, Count = g.Count() }) - .Join(dataSeeder.Readers, g => g.ReaderId, r => r.Id, (g, r) => new { r.Id, r.FullName, g.Count }) + .Join(_dataSeeder.Readers, g => g.ReaderId, r => r.Id, (g, r) => new { r.Id, r.FullName, g.Count }) .OrderByDescending(x => x.Count) .ThenBy(x => x.FullName) .Take(5) @@ -63,13 +81,13 @@ public void Top5Readers_ByIssuesCountInPeriod_ReturnsExpectedTop5() [Fact] public void Readers_ByMaxLoanDaysOrderedByFullName_ReturnsExpected() { - var maxDays = dataSeeder.BookIssues.Max(bi => bi.Days); + var maxDays = _dataSeeder.BookIssues.Max(bi => bi.Days); - var readersWithMaxDays = dataSeeder.BookIssues + var readersWithMaxDays = _dataSeeder.BookIssues .Where(bi => bi.Days == maxDays) .Select(bi => bi.ReaderId) .Distinct() - .Join(dataSeeder.Readers, id => id, r => r.Id, (id, r) => new { r.Id, r.FullName }) + .Join(_dataSeeder.Readers, id => id, r => r.Id, (id, r) => new { r.Id, r.FullName }) .OrderBy(r => r.FullName) .Select(r => r.Id) .ToList(); @@ -88,15 +106,15 @@ public void Readers_ByMaxLoanDaysOrderedByFullName_ReturnsExpected() [Fact] public void Top5Publishers_ByIssuesCountLastYear_ReturnsExpectedTop5() { - var lastYearStart = DateTime.UtcNow.AddYears(-1); - var lastYearEnd = DateTime.UtcNow; + var lastYearStart = _fixedNow.AddYears(-1); + var lastYearEnd = _fixedNow; - var topPublishers = dataSeeder.BookIssues + var topPublishers = _dataSeeder.BookIssues .Where(bi => bi.IssueDate >= lastYearStart && bi.IssueDate <= lastYearEnd) - .Join(dataSeeder.Books, bi => bi.BookId, b => b.Id, (bi, b) => b.PublisherId) + .Join(_dataSeeder.Books, bi => bi.BookId, b => b.Id, (bi, b) => b.PublisherId) .GroupBy(pid => pid) .Select(g => new { PublisherId = g.Key, Count = g.Count() }) - .Join(dataSeeder.Publishers, g => g.PublisherId, p => p.Id, (g, p) => new { p.Id, p.Name, g.Count }) + .Join(_dataSeeder.Publishers, g => g.PublisherId, p => p.Id, (g, p) => new { p.Id, p.Name, g.Count }) .OrderByDescending(x => x.Count) .ThenBy(x => x.Name) .Take(5) @@ -116,24 +134,26 @@ public void Top5Publishers_ByIssuesCountLastYear_ReturnsExpectedTop5() /// Топ 5 наименее популярных книг за последний год сравнение по Id и количествам /// [Fact] - public void Bottom5Books_ByIssuesCountLastYear_ReturnsExpectedBottom5() -{ - var lastYearStart = DateTime.UtcNow.AddYears(-1); - var lastYearEnd = DateTime.UtcNow; - - var bookCounts = dataSeeder.Books - .GroupJoin( - dataSeeder.BookIssues.Where(bi => bi.IssueDate >= lastYearStart && bi.IssueDate <= lastYearEnd), - b => b.Id, - bi => bi.BookId, - (b, issues) => new { Book = b, Count = issues.Count() } - ) - .OrderBy(x => x.Count) - .ThenBy(x => x.Book.Title, StringComparer.Ordinal) - .ToList(); - - var actualBookIds = bookCounts.Select(x => x.Book.Id).ToList(); - var expectedBookIds = new List { 9, 10, 5, 6, 3 }; - Assert.Equal(expectedBookIds, actualBookIds); + public void Bottom5Books_ByIssuesCountLastYear_ReturnsExpectedBottom5() + { + var lastYearStart = _fixedNow.AddYears(-1); + var lastYearEnd = _fixedNow; + + var bookCounts = _dataSeeder.Books + .GroupJoin( + _dataSeeder.BookIssues.Where(bi => bi.IssueDate >= lastYearStart && bi.IssueDate <= lastYearEnd), + b => b.Id, + bi => bi.BookId, + (b, issues) => new { Book = b, Count = issues.Count() } + ) + .OrderBy(x => x.Count) + .ThenBy(x => x.Book.Title, StringComparer.Ordinal) + .Take(5) + .ToList(); + + var actualBookIds = bookCounts.Select(x => x.Book.Id).ToList(); + var expectedBookIds = new List { 9, 10, 5, 6, 3 }; + + Assert.Equal(expectedBookIds, actualBookIds); } -} \ No newline at end of file +} From 6e9534eeb2482860aa54a5bf1146e364aa37859e Mon Sep 17 00:00:00 2001 From: Ryedis <134209766+Ryedis@users.noreply.github.com> Date: Fri, 20 Feb 2026 19:13:36 +0400 Subject: [PATCH 7/7] removed overengineering --- .github/workflows/tests.yml | 2 - .../Abstractions/ITimeProvider.cs | 9 --- .../Abstractions/SystemTimeProvider.cs | 12 ---- Library/Library.Domain/Data/DataSeeder.cs | 56 ++++++++----------- Library/Library.Domain/Models/BookIssue.cs | 14 +---- Library/Library.Tests/FakeTimeProvider.cs | 22 -------- Library/Library.Tests/LibraryTests.cs | 30 ++++------ 7 files changed, 37 insertions(+), 108 deletions(-) delete mode 100644 Library/Library.Domain/Abstractions/ITimeProvider.cs delete mode 100644 Library/Library.Domain/Abstractions/SystemTimeProvider.cs delete mode 100644 Library/Library.Tests/FakeTimeProvider.cs diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6bc513941..6f2699892 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,9 +2,7 @@ name: Run .NET Tests on: push: - branches: lab_1 pull_request: - branches: lab_1 jobs: build-and-test: diff --git a/Library/Library.Domain/Abstractions/ITimeProvider.cs b/Library/Library.Domain/Abstractions/ITimeProvider.cs deleted file mode 100644 index 56d5e29cb..000000000 --- a/Library/Library.Domain/Abstractions/ITimeProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Library.Domain.Abstractions; - -/// -/// Интерфейс поставщика времени для детерминированного тестирования -/// -public interface ITimeProvider -{ - public DateTime Now { get; } -} diff --git a/Library/Library.Domain/Abstractions/SystemTimeProvider.cs b/Library/Library.Domain/Abstractions/SystemTimeProvider.cs deleted file mode 100644 index ace01f126..000000000 --- a/Library/Library.Domain/Abstractions/SystemTimeProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Library.Domain.Abstractions; - -/// -/// Поставщик времени, возвращающий текущее системное время -/// -public class SystemTimeProvider : ITimeProvider -{ - /// - /// Возвращает текущее системное локальное время. - /// - public DateTime Now => DateTime.Now; -} diff --git a/Library/Library.Domain/Data/DataSeeder.cs b/Library/Library.Domain/Data/DataSeeder.cs index 51d5b29c7..226a31ba2 100644 --- a/Library/Library.Domain/Data/DataSeeder.cs +++ b/Library/Library.Domain/Data/DataSeeder.cs @@ -1,5 +1,4 @@ using Library.Domain.Models; -using Library.Domain.Abstractions; namespace Library.Domain.Data; @@ -8,15 +7,8 @@ namespace Library.Domain.Data; /// public class DataSeeder { - private readonly DateTime _now; - - /// - /// Создаёт экземпляр DataSeeder с заданным провайдером времени. - /// - public DataSeeder(ITimeProvider timeProvider) - { - _now = timeProvider.Now; - } + + private static readonly DateTime SeedNow = new(2026, 2, 19); public List EditionTypes { get; } = [ @@ -62,31 +54,31 @@ public DataSeeder(ITimeProvider timeProvider) public List Readers => new() { - new Reader { Id = 1, FullName = "Орлов Денис Сергеевич", Address = "ул. Березовая, 12", Phone = "89110000001", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-3)) }, - new Reader { Id = 2, FullName = "Мельников Артем Игоревич", Address = "ул. Солнечная, 45", Phone = "89110000002", RegistrationDate = DateOnly.FromDateTime(_now.AddYears(-2)) }, - new Reader { Id = 3, FullName = "Белов Кирилл Андреевич", Address = "ул. Полевая, 7", Phone = "89110000003", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-18)) }, - new Reader { Id = 4, FullName = "Егорова Марина Олеговна", Address = "ул. Озерная, 21", Phone = "89110000004", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-12)) }, - new Reader { Id = 5, FullName = "Тарасов Максим Дмитриевич", Address = "ул. Лесная, 3", Phone = "89110000005", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-10)) }, - new Reader { Id = 6, FullName = "Крылова Анастасия Павловна", Address = "ул. Школьная, 9", Phone = "89110000006", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-8)) }, - new Reader { Id = 7, FullName = "Никитин Роман Евгеньевич", Address = "ул. Центральная, 15", Phone = "89110000007", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-6)) }, - new Reader { Id = 8, FullName = "Волкова Дарья Ильинична", Address = "ул. Мира, 19", Phone = "89110000008", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-5)) }, - new Reader { Id = 9, FullName = "Зайцев Павел Николаевич", Address = "ул. Новая, 8", Phone = "89110000009", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-4)) }, - new Reader { Id = 10, FullName = "Громова София Артемовна", Address = "ул. Южная, 14", Phone = "89110000010", RegistrationDate = DateOnly.FromDateTime(_now.AddMonths(-2)) }, + new Reader { Id = 1, FullName = "Орлов Денис Сергеевич", Address = "ул. Березовая, 12", Phone = "89110000001", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-3)) }, + new Reader { Id = 2, FullName = "Мельников Артем Игоревич", Address = "ул. Солнечная, 45", Phone = "89110000002", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddYears(-2)) }, + new Reader { Id = 3, FullName = "Белов Кирилл Андреевич", Address = "ул. Полевая, 7", Phone = "89110000003", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-18)) }, + new Reader { Id = 4, FullName = "Егорова Марина Олеговна", Address = "ул. Озерная, 21", Phone = "89110000004", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-12)) }, + new Reader { Id = 5, FullName = "Тарасов Максим Дмитриевич", Address = "ул. Лесная, 3", Phone = "89110000005", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-10)) }, + new Reader { Id = 6, FullName = "Крылова Анастасия Павловна", Address = "ул. Школьная, 9", Phone = "89110000006", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-8)) }, + new Reader { Id = 7, FullName = "Никитин Роман Евгеньевич", Address = "ул. Центральная, 15", Phone = "89110000007", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-6)) }, + new Reader { Id = 8, FullName = "Волкова Дарья Ильинична", Address = "ул. Мира, 19", Phone = "89110000008", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-5)) }, + new Reader { Id = 9, FullName = "Зайцев Павел Николаевич", Address = "ул. Новая, 8", Phone = "89110000009", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-4)) }, + new Reader { Id = 10, FullName = "Громова София Артемовна", Address = "ул. Южная, 14", Phone = "89110000010", RegistrationDate = DateOnly.FromDateTime(SeedNow.AddMonths(-2)) }, }; public List BookIssues => new() { - new BookIssue { Id = 1, BookId = 1, ReaderId = 1, IssueDate = _now.AddDays(-15), Days = 30 }, - new BookIssue { Id = 2, BookId = 2, ReaderId = 1, IssueDate = _now.AddDays(-200), Days = 60 }, - new BookIssue { Id = 3, BookId = 3, ReaderId = 2, IssueDate = _now.AddDays(-40), Days = 14 }, - new BookIssue { Id = 4, BookId = 4, ReaderId = 2, IssueDate = _now.AddDays(-7), Days = 10 }, - new BookIssue { Id = 5, BookId = 5, ReaderId = 3, IssueDate = _now.AddDays(-300), Days = 21 }, - new BookIssue { Id = 6, BookId = 6, ReaderId = 4, IssueDate = _now.AddDays(-50), Days = 14 }, - new BookIssue { Id = 7, BookId = 7, ReaderId = 5, IssueDate = _now.AddDays(-3), Days = 7 }, - new BookIssue { Id = 8, BookId = 8, ReaderId = 6, IssueDate = _now.AddDays(-120), Days = 30 }, - new BookIssue { Id = 9, BookId = 9, ReaderId = 7, IssueDate = _now.AddDays(-60), Days = 20 }, - new BookIssue { Id = 10, BookId = 10, ReaderId = 8, IssueDate = _now.AddDays(-25), Days = 14 }, - new BookIssue { Id = 11, BookId = 1, ReaderId = 9, IssueDate = _now.AddDays(-5), Days = 10 }, - new BookIssue { Id = 12, BookId = 2, ReaderId = 10, IssueDate = _now.AddDays(-90), Days = 30 } + new BookIssue { Id = 1, BookId = 1, ReaderId = 1, IssueDate = SeedNow.AddDays(-15), Days = 30 }, + new BookIssue { Id = 2, BookId = 2, ReaderId = 1, IssueDate = SeedNow.AddDays(-200), Days = 60 }, + new BookIssue { Id = 3, BookId = 3, ReaderId = 2, IssueDate = SeedNow.AddDays(-40), Days = 14 }, + new BookIssue { Id = 4, BookId = 4, ReaderId = 2, IssueDate = SeedNow.AddDays(-7), Days = 10 }, + new BookIssue { Id = 5, BookId = 5, ReaderId = 3, IssueDate = SeedNow.AddDays(-300), Days = 21 }, + new BookIssue { Id = 6, BookId = 6, ReaderId = 4, IssueDate = SeedNow.AddDays(-50), Days = 14 }, + new BookIssue { Id = 7, BookId = 7, ReaderId = 5, IssueDate = SeedNow.AddDays(-3), Days = 7 }, + new BookIssue { Id = 8, BookId = 8, ReaderId = 6, IssueDate = SeedNow.AddDays(-120), Days = 30 }, + new BookIssue { Id = 9, BookId = 9, ReaderId = 7, IssueDate = SeedNow.AddDays(-60), Days = 20 }, + new BookIssue { Id = 10, BookId = 10, ReaderId = 8, IssueDate = SeedNow.AddDays(-25), Days = 14 }, + new BookIssue { Id = 11, BookId = 1, ReaderId = 9, IssueDate = SeedNow.AddDays(-5), Days = 10 }, + new BookIssue { Id = 12, BookId = 2, ReaderId = 10, IssueDate = SeedNow.AddDays(-90), Days = 30 } }; } diff --git a/Library/Library.Domain/Models/BookIssue.cs b/Library/Library.Domain/Models/BookIssue.cs index 4145b0ba4..81b8b8104 100644 --- a/Library/Library.Domain/Models/BookIssue.cs +++ b/Library/Library.Domain/Models/BookIssue.cs @@ -5,8 +5,6 @@ namespace Library.Domain.Models; /// public class BookIssue { - private readonly Library.Domain.Abstractions.ITimeProvider _timeProvider; - /// /// Уникальный идентификатор /// @@ -47,17 +45,9 @@ public class BookIssue /// public DateTime? ReturnDate => IssueDate.AddDays(Days); - /// - /// Конструктор с опциональным поставщиком времени - /// - public BookIssue(Library.Domain.Abstractions.ITimeProvider? timeProvider = null) - { - _timeProvider = timeProvider ?? new Library.Domain.Abstractions.SystemTimeProvider(); - } - /// /// Флаг просрочки срока возврата книги /// - public bool IsOverdue => - ReturnDate == null && _timeProvider.Now.Date > IssueDate.AddDays(Days).Date; + public bool IsOverdue(DateTime currentDate) => + ReturnDate == null && currentDate.Date > IssueDate.AddDays(Days).Date; } \ No newline at end of file diff --git a/Library/Library.Tests/FakeTimeProvider.cs b/Library/Library.Tests/FakeTimeProvider.cs deleted file mode 100644 index c69aa7ef4..000000000 --- a/Library/Library.Tests/FakeTimeProvider.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Library.Domain.Abstractions; - -namespace Library.Tests; - -/// -/// Провайдер времени, возвращающий фиксированное значение -/// -public class FakeTimeProvider : ITimeProvider -{ - /// - /// Текущее время - /// - public DateTime Now { get; } - - /// - /// Создаёт экземпляр с заданным временем - /// - public FakeTimeProvider(DateTime now) - { - Now = now; - } -} diff --git a/Library/Library.Tests/LibraryTests.cs b/Library/Library.Tests/LibraryTests.cs index 9517c76ac..c6384e67e 100644 --- a/Library/Library.Tests/LibraryTests.cs +++ b/Library/Library.Tests/LibraryTests.cs @@ -1,5 +1,4 @@ using Library.Domain.Data; -using Library.Domain.Abstractions; namespace Library.Tests; @@ -8,22 +7,15 @@ namespace Library.Tests; /// public class LibraryTests { - // Экземпляр DataSeeder для доступа к тестовым данным - private readonly DataSeeder _dataSeeder; - /// - // Фиксированная дата + /// Тестовые данные библиотеки /// - private readonly DateTime _fixedNow = new(2026, 2, 19); + private readonly DataSeeder _dataSeeder = new(); /// - /// Конструктор класса, создаёт DataSeeder с фиксированной датой + /// Контрольная дата, используемая в тестах /// - public LibraryTests() - { - var fakeTime = new FakeTimeProvider(_fixedNow); - _dataSeeder = new DataSeeder(fakeTime); - } + private readonly DateTime _now = new(2026, 2, 19); /// /// Топ 5 издательств за последний год по количеству выдач и сравнивает по Id и количествам @@ -32,7 +24,7 @@ public LibraryTests() public void IssuedBooks_OrderByBookTitle_ReturnsActiveIssuesOrderedByTitle() { var actualBookIds = _dataSeeder.BookIssues - .Where(bi => _fixedNow.Date < bi.ReturnDate) + .Where(bi => _now.Date < bi.ReturnDate) .Join(_dataSeeder.Books, bi => bi.BookId, b => b.Id, @@ -52,8 +44,8 @@ public void IssuedBooks_OrderByBookTitle_ReturnsActiveIssuesOrderedByTitle() [Fact] public void Top5Readers_ByIssuesCountInPeriod_ReturnsExpectedTop5() { - var periodStart = _fixedNow.AddYears(-1); - var periodEnd = _fixedNow; + var periodStart = _now.AddYears(-1); + var periodEnd = _now; var topReaders = _dataSeeder.BookIssues .Where(bi => bi.IssueDate >= periodStart && bi.IssueDate <= periodEnd) @@ -106,8 +98,8 @@ public void Readers_ByMaxLoanDaysOrderedByFullName_ReturnsExpected() [Fact] public void Top5Publishers_ByIssuesCountLastYear_ReturnsExpectedTop5() { - var lastYearStart = _fixedNow.AddYears(-1); - var lastYearEnd = _fixedNow; + var lastYearStart = _now.AddYears(-1); + var lastYearEnd = _now; var topPublishers = _dataSeeder.BookIssues .Where(bi => bi.IssueDate >= lastYearStart && bi.IssueDate <= lastYearEnd) @@ -136,8 +128,8 @@ public void Top5Publishers_ByIssuesCountLastYear_ReturnsExpectedTop5() [Fact] public void Bottom5Books_ByIssuesCountLastYear_ReturnsExpectedBottom5() { - var lastYearStart = _fixedNow.AddYears(-1); - var lastYearEnd = _fixedNow; + var lastYearStart = _now.AddYears(-1); + var lastYearEnd = _now; var bookCounts = _dataSeeder.Books .GroupJoin(