diff --git a/Database/init-db.sql b/Database/init-db.sql index 6e761f9c..5afb8ebc 100644 --- a/Database/init-db.sql +++ b/Database/init-db.sql @@ -3,7 +3,7 @@ CREATE SCHEMA users; CREATE SCHEMA common; CREATE TYPE common.participant_status AS ENUM ('Interested', 'Participating'); -CREATE TYPE common.event_status AS ENUM ('Completed', 'In progress', 'Approved', 'Waiting for approval'); +CREATE TYPE common.event_status AS ENUM ('Completed', 'InProgress', 'Approved', 'WaitingForApproval'); CREATE TYPE users.role AS ENUM ('User', 'Organizer', 'Admin'); CREATE TYPE common.object_type AS ENUM ('Event', 'User'); diff --git a/Database/init-dev-data.sql b/Database/init-dev-data.sql index 818e37d3..a2a85a21 100644 --- a/Database/init-dev-data.sql +++ b/Database/init-dev-data.sql @@ -26,14 +26,14 @@ SELECT setval('users.user_id_seq', (SELECT MAX(id) FROM users.user)); INSERT INTO events.event ("id", "name", "address_id", "description", "organizer_id", "start_at", "end_at", "created_at", "updated_at", "slug", "status") VALUES (1, 'Tech Conference', 1, 'Annual tech conference', 1, '2023-10-01 09:00:00', '2023-10-02 17:00:00', '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'tech-conference', 'Approved'), -(2, 'Health Symposium', 2, 'Health and wellness symposium', 2, '2023-11-05 09:00:00', '2023-11-06 17:00:00', '2023-10-05 08:00:00', '2023-10-05 08:00:00', 'health-symposium', 'In progress'), -(3, 'Koncert Rockowy', 3, 'Występ ulubionych zespołów rockowych', 2, CURRENT_TIMESTAMP - '1 day'::INTERVAL, CURRENT_TIMESTAMP + '4 hours'::INTERVAL, '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'koncert-rockowy', 'Waiting for approval'), +(2, 'Health Symposium', 2, 'Health and wellness symposium', 2, '2023-11-05 09:00:00', '2023-11-06 17:00:00', '2023-10-05 08:00:00', '2023-10-05 08:00:00', 'health-symposium', 'InProgress'), +(3, 'Koncert Rockowy', 3, 'Występ ulubionych zespołów rockowych', 2, CURRENT_TIMESTAMP - '1 day'::INTERVAL, CURRENT_TIMESTAMP + '4 hours'::INTERVAL, '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'koncert-rockowy', 'WaitingForApproval'), (4, 'Konferencja IT', 4, 'Coroczna konferencja technologiczna', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + '3 hours'::INTERVAL, '2023-10-02 17:00:00', '2023-10-02 18:00:00', 'konferencja-it', 'Completed'), -(5, 'Mecz Piłki Nożnej', 5, 'Mecz drużynowej rywalizacji w piłce nożnej', 3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + '3 hours'::INTERVAL, '2022-12-01 08:00:00', '2023-09-01 08:30:00', 'mecz-pilki-noznej', 'In progress'), -(6, 'Festiwal Elektroniczny', 6, 'Największe hity muzyki elektronicznej', 4, CURRENT_TIMESTAMP - '4 day'::INTERVAL, CURRENT_TIMESTAMP - '2 days'::INTERVAL, '2023-09-01 08:00:00', '2023-09-02 08:00:00', 'festiwal-elektroniczny', 'Waiting for approval'), +(5, 'Mecz Piłki Nożnej', 5, 'Mecz drużynowej rywalizacji w piłce nożnej', 3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + '3 hours'::INTERVAL, '2022-12-01 08:00:00', '2023-09-01 08:30:00', 'mecz-pilki-noznej', 'InProgress'), +(6, 'Festiwal Elektroniczny', 6, 'Największe hity muzyki elektronicznej', 4, CURRENT_TIMESTAMP - '4 day'::INTERVAL, CURRENT_TIMESTAMP - '2 days'::INTERVAL, '2023-09-01 08:00:00', '2023-09-02 08:00:00', 'festiwal-elektroniczny', 'WaitingForApproval'), (7, 'Koncert Hip-Hopowy', 7, 'Najnowsze hity hip-hopu w wykonaniu gwiazd', 6, CURRENT_TIMESTAMP + '2 days'::INTERVAL, CURRENT_TIMESTAMP + '2 days'::INTERVAL + '4 hours'::INTERVAL, '2023-10-08 17:00:00', '2023-11-01 09:19:22', 'koncert-hip-hopowy', 'Completed'), -(8, 'Wieczór Jazzowy', 8, 'Relaksujące dźwięki jazzu w kameralnej atmosferze', 8, CURRENT_TIMESTAMP + '1 day'::INTERVAL, CURRENT_TIMESTAMP + '1 day'::INTERVAL + '5 hours'::INTERVAL, '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'wieczor-jazzowy', 'In progress'), -(9, 'Koncert Klasyczny', 9, 'Muzyka klasyczna w wykonaniu renomowanych artystów', 10, CURRENT_TIMESTAMP + '1 day'::INTERVAL, CURRENT_TIMESTAMP + '1 day'::INTERVAL + '3 hours'::INTERVAL, '2023-01-01 15:00:00', '2023-02-01 08:00:00', 'koncert-klasyczny', 'Waiting for approval'), +(8, 'Wieczór Jazzowy', 8, 'Relaksujące dźwięki jazzu w kameralnej atmosferze', 8, CURRENT_TIMESTAMP + '1 day'::INTERVAL, CURRENT_TIMESTAMP + '1 day'::INTERVAL + '5 hours'::INTERVAL, '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'wieczor-jazzowy', 'InProgress'), +(9, 'Koncert Klasyczny', 9, 'Muzyka klasyczna w wykonaniu renomowanych artystów', 10, CURRENT_TIMESTAMP + '1 day'::INTERVAL, CURRENT_TIMESTAMP + '1 day'::INTERVAL + '3 hours'::INTERVAL, '2023-01-01 15:00:00', '2023-02-01 08:00:00', 'koncert-klasyczny', 'WaitingForApproval'), (10, 'Turniej w League of Legends', 10, 'Turniej LAN dla miłośnikow esportu i gry League of Legends', 10, CURRENT_TIMESTAMP - '1 day'::INTERVAL, CURRENT_TIMESTAMP - '3 hours'::INTERVAL, '2023-01-01 15:00:00', '2023-02-01 08:00:00', 'turniej-lol', 'Completed'); SELECT setval('events.event_id_seq', (SELECT MAX(id) FROM events.event)); diff --git a/Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj b/Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj index 1b46a3ed..3f9b022c 100644 --- a/Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj +++ b/Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj @@ -11,12 +11,18 @@ + + + + + + diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/AddressServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/AddressServiceTests.cs new file mode 100644 index 00000000..2707c98d --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/AddressServiceTests.cs @@ -0,0 +1,294 @@ +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Services; + +namespace ReasnAPI.Tests.Services; + +[TestClass] +public class AddressServiceTests +{ + [TestMethod] + public void GetAddressById_AddressExists_AddressReturned() + { + var mockContext = new Mock(); + + var address = new Address + { + Id = 1, + City = "City", + Country = "Country", + State = "State", + Street = "Street", + ZipCode = "ZipCode" + }; + + var fakeAddress = new FakeDbSet
{ address }; + + mockContext.Setup(c => c.Addresses).Returns(fakeAddress); + + var addressService = new AddressService(mockContext.Object); + + var result = addressService.GetAddressById(1); + + Assert.IsNotNull(result); + Assert.AreEqual("City", result.City); + Assert.AreEqual("Country", result.Country); + Assert.AreEqual("State", result.State); + Assert.AreEqual("Street", result.Street); + Assert.AreEqual("ZipCode", result.ZipCode); + } + + [TestMethod] + public void GetAddressById_AddressDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([]); + + var addressService = new AddressService(mockContext.Object); + + Assert.ThrowsException(() => addressService.GetAddressById(1)); + } + + [TestMethod] + public void GetAllAddresses_AddressesExist_AddressesReturned() + { + var mockContext = new Mock(); + + var address1 = new Address + { + Id = 1, + City = "City", + Country = "Country", + State = "State", + Street = "Street", + ZipCode = "ZipCode" + }; + + var address2 = new Address + { + Id = 2, + City = "City", + Country = "Country", + State = "State", + Street = "Street", + ZipCode = "ZipCode" + }; + + mockContext.Setup(c => c.Addresses).ReturnsDbSet([address1, address2]); + + var addressService = new AddressService(mockContext.Object); + + var result = addressService.GetAllAddresses(); + + Assert.IsNotNull(result); + Assert.AreEqual(2, result.Count()); + } + + [TestMethod] + public void GetAllAddresses_NoAddresses_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([]); + + var addressService = new AddressService(mockContext.Object); + + var result = addressService.GetAllAddresses(); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void GetAddressesByFilter_AddressesExist_AddressesReturned() + { + var mockContext = new Mock(); + + var address1 = new Address + { + Id = 1, + City = "City", + Country = "Country", + State = "State", + Street = "Street", + ZipCode = "ZipCode" + }; + + var address2 = new Address + { + Id = 2, + City = "City", + Country = "Country", + State = "State", + Street = "Street", + ZipCode = "ZipCode" + }; + + mockContext.Setup(c => c.Addresses).ReturnsDbSet([address1, address2]); + var addressService = new AddressService(mockContext.Object); + + var result = addressService.GetAddressesByFilter(r => r.Id == 1).ToList(); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void GetAddressesByFilter_NoAddresses_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([]); + + var addressService = new AddressService(mockContext.Object); + + var result = addressService.GetAddressesByFilter(r => r.Id == 1); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void CreateAddress_AddressCreated_AddressReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([]); + + var addressService = new AddressService(mockContext.Object); + + var addressDto = new AddressDto + { + City = "City", + Country = "Country", + State = "State", + Street = "Street", + ZipCode = "ZipCode" + }; + + var result = addressService.CreateAddress(addressDto); + + Assert.IsNotNull(result); + Assert.AreEqual("City", result.City); + Assert.AreEqual("Country", result.Country); + Assert.AreEqual("State", result.State); + Assert.AreEqual("Street", result.Street); + Assert.AreEqual("ZipCode", result.ZipCode); + } + + [TestMethod] + public void CreateAddress_AddressDtoIsNull_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([]); + + var addressService = new AddressService(mockContext.Object); + + Assert.ThrowsException(() => addressService.CreateAddress(null)); + } + + [TestMethod] + public void UpdateAddress_AddressUpdated_AddressReturned() + { + var mockContext = new Mock(); + + var address = new Address + { + Id = 1, + City = "City", + Country = "Country", + State = "State", + Street = "Street", + ZipCode = "ZipCode" + }; + + mockContext.Setup(c => c.Addresses).ReturnsDbSet([address]); + + var addressService = new AddressService(mockContext.Object); + + var addressDto = new AddressDto + { + City = "City2", + Country = "Country2", + State = "State2", + Street = "Street2", + ZipCode = "ZipCode2" + }; + + var result = addressService.UpdateAddress(1, addressDto); + + Assert.IsNotNull(result); + Assert.AreEqual("City2", result.City); + Assert.AreEqual("Country2", result.Country); + Assert.AreEqual("State2", result.State); + Assert.AreEqual("Street2", result.Street); + Assert.AreEqual("ZipCode2", result.ZipCode); + } + + [TestMethod] + public void UpdateAddress_AddressDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([]); + + var addressService = new AddressService(mockContext.Object); + + var addressDto = new AddressDto + { + City = "City2", + Country = "Country2", + State = "State2", + Street = "Street2", + ZipCode = "ZipCode2" + }; + + Assert.ThrowsException(() => addressService.UpdateAddress(1, addressDto)); + } + + [TestMethod] + public void UpdateAddress_AddressDtoIsNull_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([]); + + var addressService = new AddressService(mockContext.Object); + + Assert.ThrowsException(() => addressService.UpdateAddress(1, null)); + } + + [TestMethod] + public void DeleteAddress_AdddressExists_AddressDeleted() + { + var mockContext = new Mock(); + + var address = new Address + { + Id = 1, + City = "City", + Country = "Country", + State = "State", + Street = "Street", + ZipCode = "ZipCode" + }; + + mockContext.Setup(c => c.Addresses).ReturnsDbSet([address]); + + var addressService = new AddressService(mockContext.Object); + + addressService.DeleteAddress(1); + + mockContext.Verify(c => c.SaveChanges(), Times.Once); + } + + [TestMethod] + public void DeleteAddress_AddressDoesNotExist_NothingHappens() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([]); + + var addressService = new AddressService(mockContext.Object); + + Assert.ThrowsException(() => addressService.DeleteAddress(1)); + mockContext.Verify(c => c.SaveChanges(), Times.Never); + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/CommentServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/CommentServiceTests.cs new file mode 100644 index 00000000..26105028 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/CommentServiceTests.cs @@ -0,0 +1,362 @@ +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Models.Enums; +using ReasnAPI.Services; + +namespace ReasnAPI.Tests.Services; + +[TestClass] +public class CommentServiceTests +{ + [TestMethod] + public void GetCommentById_CommentExist_CommentReturned() + { + var mockContext = new Mock(); + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description", + }; + + var user = new User + { + Id = 1, + Username = "Username", + Email = "Email", + Password = "Password", + }; + + var comment = new Comment + { + Id = 1, + Content = "Content", + EventId = event1.Id, + UserId = user.Id + }; + + var fakeComment = new FakeDbSet { comment }; + var fakeEvent = new FakeDbSet { event1 }; + var fakeUser = new FakeDbSet { user }; + + mockContext.Setup(c => c.Users).Returns(fakeUser); + mockContext.Setup(c => c.Events).Returns(fakeEvent); + mockContext.Setup(c => c.Comments).Returns(fakeComment); + + var commentService = new CommentService(mockContext.Object); + + var result = commentService.GetCommentById(1); + + Assert.IsNotNull(result); + Assert.AreEqual("Content", result.Content); + Assert.AreEqual(1, result.EventId); + Assert.AreEqual(1, result.UserId); + } + + [TestMethod] + public void GetCommentById_CommentDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Comments).ReturnsDbSet([]); + + var commentService = new CommentService(mockContext.Object); + + Assert.ThrowsException(() => commentService.GetCommentById(1)); + } + + [TestMethod] + public void GetAllComments_CommentsExist_CommentsReturned() + { + var mockContext = new Mock(); + + var user = new User + { + Id = 1, + Username = "Username", + Email = "Email", + Password = "Password", + }; + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description", + }; + + var comment1 = new Comment + { + Id = 1, + Content = "Content", + EventId = event1.Id, + UserId = user.Id + }; + + var comment2 = new Comment + { + Id = 2, + Content = "Content", + EventId = event1.Id, + UserId = user.Id + }; + + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Comments).ReturnsDbSet([comment1, comment2]); + + var commentService = new CommentService(mockContext.Object); + + var result = commentService.GetAllComments(); + + Assert.IsNotNull(result); + Assert.AreEqual(2, result.Count()); + } + + [TestMethod] + public void GetAllComments_NoComments_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Comments).ReturnsDbSet([]); + + var commentService = new CommentService(mockContext.Object); + + var result = commentService.GetAllComments(); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void GetCommentsByFilter_CommentsExist_CommentsReturned() + { + var mockContext = new Mock(); + + var user = new User + { + Id = 1, + Username = "Username", + Email = "Email", + Password = "Password", + }; + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description", + }; + + var comment1 = new Comment + { + Id = 1, + Content = "Content", + EventId = event1.Id, + UserId = user.Id + }; + + var comment2 = new Comment + { + Id = 2, + Content = "Content", + EventId = event1.Id, + UserId = user.Id + }; + + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Comments).ReturnsDbSet([comment1, comment2]); + + var commentService = new CommentService(mockContext.Object); + + var result = commentService.GetCommentsByFilter(r => r.EventId == 1); + + Assert.IsNotNull(result); + Assert.AreEqual(2, result.Count()); + } + + [TestMethod] + public void GetCommentsByFilter_NoComments_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Comments).ReturnsDbSet([]); + + var commentService = new CommentService(mockContext.Object); + + var result = commentService.GetCommentsByFilter(r => r.EventId == 1); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void CreateComment_CommentCreated_CommentReturned() + { + var mockContext = new Mock(); + + var user = new User + { + Id = 1, + Username = "Username", + Email = "Email", + Password = "Password", + }; + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description", + }; + + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Comments).ReturnsDbSet([]); + + var commentService = new CommentService(mockContext.Object); + + var commentDto = new CommentDto + { + Content = "Content", + UserId = 1, + EventId = 1 + }; + + var result = commentService.CreateComment(commentDto); + + Assert.IsNotNull(result); + Assert.AreEqual("Content", result.Content); + Assert.AreEqual(1, result.EventId); + Assert.AreEqual(1, result.UserId); + } + + [TestMethod] + public void CreateComment_CommentDtoIsNull_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Comments).ReturnsDbSet([]); + + var commentService = new CommentService(mockContext.Object); + + Assert.ThrowsException(() => commentService.CreateComment(null)); + } + + [TestMethod] + public void UpdateComment_CommentUpdated_CommentReturned() + { + var mockContext = new Mock(); + + var user = new User + { + Id = 1, + Username = "Username", + Email = "Email", + Password = "Password", + }; + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description", + }; + + var comment = new Comment + { + Id = 1, + Content = "Content", + EventId = event1.Id, + UserId = user.Id + }; + + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Comments).ReturnsDbSet([comment]); + + var commentService = new CommentService(mockContext.Object); + + var result = commentService.UpdateComment(1, new CommentDto + { + Content = "UpdatedContent", + EventId = 2, + UserId = 1 + }); + + Assert.IsNotNull(result); + Assert.AreEqual("UpdatedContent", result.Content); + Assert.AreEqual(1, result.EventId); + Assert.AreEqual(1, result.UserId); + } + + [TestMethod] + public void UpdateComment_CommentDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Comments).ReturnsDbSet([]); + + var commentService = new CommentService(mockContext.Object); + + Assert.ThrowsException(() => commentService.UpdateComment(1, new CommentDto + { + Content = "UpdatedContent", + EventId = 2, + UserId = 1 + })); + } + + [TestMethod] + public void UpdateComment_CommentDtoIsNull_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Comments).ReturnsDbSet([]); + + var commentService = new CommentService(mockContext.Object); + + Assert.ThrowsException(() => commentService.UpdateComment(1, null)); + } + + [TestMethod] + public void DeleteComment_CommentExists_CommentDeleted() + { + var mockContext = new Mock(); + + var user = new User + { + Id = 1, + Username = "Username", + Email = "Email", + Password = "Password" + }; + + var comment = new Comment + { + Id = 1, + Content = "Content", + EventId = 1, + UserId = user.Id + }; + + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.Comments).ReturnsDbSet([comment]); + + var commentService = new CommentService(mockContext.Object); + + commentService.DeleteComment(1); + + mockContext.Verify(c => c.SaveChanges(), Times.Once); + } + + [TestMethod] + public void DeleteComment_CommentDoesNotExist_NothingHappens() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Comments).ReturnsDbSet([]); + + var commentService = new CommentService(mockContext.Object); + + Assert.ThrowsException(() => commentService.DeleteComment(1)); + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs new file mode 100644 index 00000000..d8c253a3 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +namespace ReasnAPI.Tests.Services +{ + public class FakeDbSet : DbSet, IAsyncEnumerable + where T : class + { + private readonly List _data; + + public FakeDbSet() + { + _data = []; + } + + public override T? Find(params object[] keyValues) + { + return _data.FirstOrDefault(); + } + + public override EntityEntry Add(T entity) + { + _data.Add(entity); + return null; // Return null for simplicity, adjust as needed + } + + public override IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new AsyncEnumerator(_data.GetEnumerator()); + } + + public override Microsoft.EntityFrameworkCore.Metadata.IEntityType EntityType => throw new NotImplementedException(); + + // Implement other methods... + + private class AsyncEnumerator(IEnumerator enumerator) : IAsyncEnumerator + { + private readonly IEnumerator _enumerator = enumerator; + + public ValueTask DisposeAsync() + { + _enumerator.Dispose(); + return ValueTask.CompletedTask; + } + + public ValueTask MoveNextAsync() + { + return new ValueTask(_enumerator.MoveNext()); + } + + public T Current => _enumerator.Current; + } + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs new file mode 100644 index 00000000..80057add --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs @@ -0,0 +1,365 @@ +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Models.Enums; +using ReasnAPI.Services; + +namespace ReasnAPI.Tests.Services; + +[TestClass] +public class ParticipantServiceTests +{ + [TestMethod] + public void GetParticipantById_ParticipantExists_ParticipantReturned() + { + var mockContext = new Mock(); + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description" + }; + + var user = new User + { + Id = 1, + Username = "User", + Email = "Email", + Password = "Password" + }; + + var participant = new Participant + { + Id = 1, + EventId = event1.Id, + UserId = user.Id, + Status = ParticipantStatus.Interested + }; + + var fakeParticipant = new FakeDbSet { participant }; + var fakeEvent = new FakeDbSet { event1 }; + var fakeUser = new FakeDbSet { user }; + + mockContext.Setup(c => c.Events).Returns(fakeEvent); + mockContext.Setup(c => c.Users).Returns(fakeUser); + mockContext.Setup(c => c.Participants).Returns(fakeParticipant); + + var participantService = new ParticipantService(mockContext.Object); + + var result = participantService.GetParticipantById(1); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.EventId); + Assert.AreEqual(1, result.UserId); + Assert.AreEqual(ParticipantStatus.Interested, result.Status); + } + + [TestMethod] + public void GetParticipantById_ParticipantDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Participants).ReturnsDbSet([]); + + var participantService = new ParticipantService(mockContext.Object); + + Assert.ThrowsException(() => participantService.GetParticipantById(1)); + } + + [TestMethod] + public void GetAllParticipants_ParticipantsExist_ParticipantsReturned() + { + var mockContext = new Mock(); + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description" + }; + + var user1 = new User + { + Id = 1, + Username = "User", + Email = "Email", + Password = "Password" + }; + + var user2 = new User + { + Id = 2, + Username = "User", + Email = "Email", + Password = "Password" + }; + + var participant1 = new Participant + { + Id = 1, + EventId = event1.Id, + UserId = user1.Id, + Status = ParticipantStatus.Interested + }; + + var participant2 = new Participant + { + Id = 2, + EventId = event1.Id, + UserId = user2.Id, + Status = ParticipantStatus.Participating + }; + + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Users).ReturnsDbSet([user1, user2]); + mockContext.Setup(c => c.Participants).ReturnsDbSet([participant1, participant2]); + + var participantService = new ParticipantService(mockContext.Object); + + var result = participantService.GetAllParticipants(); + + Assert.IsNotNull(result); + Assert.AreEqual(2, result.Count()); + } + + [TestMethod] + public void GetAllParticipants_NoParticipants_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Participants).ReturnsDbSet([]); + + var participantService = new ParticipantService(mockContext.Object); + + var result = participantService.GetAllParticipants(); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void GetParticipantsByFilter_ParticipantsExist_ParticipantsReturned() + { + var mockContext = new Mock(); + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description" + }; + + var user1 = new User + { + Id = 1, + Username = "User", + Email = "Email", + Password = "Password" + }; + + var user2 = new User + { + Id = 2, + Username = "User", + Email = "Email", + Password = "Password" + }; + + var participant1 = new Participant + { + Id = 1, + EventId = event1.Id, + UserId = user1.Id, + Status = ParticipantStatus.Interested + }; + + var participant2 = new Participant + { + Id = 2, + EventId = event1.Id, + UserId = user2.Id, + Status = ParticipantStatus.Participating + }; + + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Users).ReturnsDbSet([user1, user2]); + mockContext.Setup(c => c.Participants).ReturnsDbSet([participant1, participant2]); + + var participantService = new ParticipantService(mockContext.Object); + + var result = participantService.GetParticipantsByFilter(r => r.EventId == 1); + + Assert.IsNotNull(result); + Assert.AreEqual(2, result.Count()); + } + + [TestMethod] + public void GetParticipantsByFilter_ParticipantsDoNotExist_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Participants).ReturnsDbSet([]); + + var participantService = new ParticipantService(mockContext.Object); + + var result = participantService.GetParticipantsByFilter(r => r.EventId == 1); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void CreateParticipant_ParticipantCreated_ParticipantReturned() + { + var mockContext = new Mock(); + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description" + }; + + var user = new User + { + Id = 1, + Username = "User", + Email = "Email", + Password = "Password" + }; + + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.Participants).ReturnsDbSet([]); + + var participantService = new ParticipantService(mockContext.Object); + + var participantDto = new ParticipantDto + { + EventId = 1, + UserId = 1, + Status = ParticipantStatus.Interested + }; + + var result = participantService.CreateParticipant(participantDto); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.EventId); + Assert.AreEqual(1, result.UserId); + Assert.AreEqual(ParticipantStatus.Interested, result.Status); + } + + [TestMethod] + public void CreateParticipant_ParticipantDtoIsNull_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Participants).ReturnsDbSet([]); + + var participantService = new ParticipantService(mockContext.Object); + + Assert.ThrowsException(() => participantService.CreateParticipant(null)); + } + + [TestMethod] + public void UpdateParticipant_ParticipantExists_ParticipantReturned() + { + var mockContext = new Mock(); + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description" + }; + + var user = new User + { + Id = 1, + Username = "User", + Email = "Email", + Password = "Password" + }; + + var participant = new Participant + { + Id = 1, + EventId = event1.Id, + UserId = user.Id, + Status = ParticipantStatus.Interested + }; + + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.Participants).ReturnsDbSet([participant]); + + var participantService = new ParticipantService(mockContext.Object); + + var result = participantService.UpdateParticipant(1, new ParticipantDto + { + EventId = 2, + UserId = 2, + Status = ParticipantStatus.Participating + }); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.EventId); + Assert.AreEqual(1, result.UserId); + Assert.AreEqual(ParticipantStatus.Participating, result.Status); + } + + [TestMethod] + public void UpdateParticipant_ParticipantDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Participants).ReturnsDbSet([]); + + var participantService = new ParticipantService(mockContext.Object); + + Assert.ThrowsException(() => participantService.UpdateParticipant(1, new ParticipantDto + { + EventId = 2, + UserId = 2, + Status = ParticipantStatus.Participating + })); + } + + [TestMethod] + public void UpdateParticipant_ParticipantDtoIsNull_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Participants).ReturnsDbSet([ + new() { Id = 1, EventId = 1, UserId = 1, Status = ParticipantStatus.Participating } + ]); + + var participantService = new ParticipantService(mockContext.Object); + + Assert.ThrowsException(() => participantService.UpdateParticipant(1, null)); + } + + [TestMethod] + public void DeleteParticipant_ParticipantExists_ParticipantDeleted() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Participants).ReturnsDbSet([ + new() { Id = 1, EventId = 1, UserId = 1, Status = ParticipantStatus.Participating } + ]); + + var participantService = new ParticipantService(mockContext.Object); + + participantService.DeleteParticipant(1); + + mockContext.Verify(c => c.SaveChanges(), Times.Once); + } + + [TestMethod] + public void DeleteParticipant_ParticipantDoesNotExist_NothingDeleted() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Participants).ReturnsDbSet([]); + + var participantService = new ParticipantService(mockContext.Object); + + Assert.ThrowsException(() => participantService.DeleteParticipant(1)); + mockContext.Verify(c => c.SaveChanges(), Times.Never); + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/UserServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/UserServiceTests.cs new file mode 100644 index 00000000..a7758b7e --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/UserServiceTests.cs @@ -0,0 +1,214 @@ +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Models.Enums; +using ReasnAPI.Services; + +namespace ReasnAPI.Tests.Services; + +[TestClass] +public class UserServiceTests +{ + [TestMethod] + public void GetUserById_UserDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Users).ReturnsDbSet([]); + + var userService = new UserService(mockContext.Object); + + Assert.ThrowsException(() => userService.GetUserById(1)); + } + + [TestMethod] + public void GetUserById_UserExists_UserReturned() + { + var mockContext = new Mock(); + + var user = new User + { + Id = 1, + Name = "John", + Surname = "Doe", + Username = "Username", + Email = "Email" + }; + + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + + var userService = new UserService(mockContext.Object); + + var result = userService.GetUserById(1); + + Assert.IsNotNull(result); + Assert.AreEqual("John", result.Name); + Assert.AreEqual("Doe", result.Surname); + Assert.AreEqual("Username", result.Username); + Assert.AreEqual("Email", result.Email); + } + + [TestMethod] + public void GetUserByUsername_UserDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Users).ReturnsDbSet([]); + + var userService = new UserService(mockContext.Object); + + Assert.ThrowsException(() => userService.GetUserByUsername("username")); + } + + [TestMethod] + public void GetUserByUsername_UserExists_UserReturned() + { + var mockContext = new Mock(); + + var user = new User + { + Id = 1, + Name = "John", + Surname = "Doe", + Username = "Username", + Email = "Email" + }; + + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + + var userService = new UserService(mockContext.Object); + + var result = userService.GetUserByUsername("Username"); + + Assert.IsNotNull(result); + Assert.AreEqual("John", result.Name); + Assert.AreEqual("Doe", result.Surname); + Assert.AreEqual("Username", result.Username); + Assert.AreEqual("Email", result.Email); + } + + [TestMethod] + public void GetAllUsers_NoUsers_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Users).ReturnsDbSet([]); + + var userService = new UserService(mockContext.Object); + + var result = userService.GetAllUsers(); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void GetUsersByFilter_NoUsers_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Users).ReturnsDbSet([]); + + var userService = new UserService(mockContext.Object); + + var result = userService.GetUsersByFilter(u => u.Name == "John"); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void UpdateUser_UserUpdated_UserReturned() + { + var mockContext = new Mock(); + + var address = new Address + { + Id = 1, + City = "City", + Country = "Country", + Street = "Street", + State = "State", + ZipCode = "ZipCode" + }; + + var user = new User + { + Id = 1, + Name = "John", + Surname = "Doe", + Username = "Username", + Email = "Email", + AddressId = address.Id, + Role = UserRole.User + }; + + mockContext.Setup(c => c.Addresses).ReturnsDbSet([address]); + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + + var userService = new UserService(mockContext.Object); + + var userDto = new UserDto + { + Name = "Jane", + Surname = "Doe", + Username = "username", + Email = "Email", + Phone = "Phone", + AddressId = address.Id, + Role = UserRole.User + }; + + var result = userService.UpdateUser(1, userDto); + + Assert.IsNotNull(result); + Assert.AreEqual("Jane", result.Name); + Assert.AreEqual("Doe", result.Surname); + Assert.AreEqual("username", result.Username); + Assert.AreEqual("Email", result.Email); + Assert.AreEqual("Phone", result.Phone); + Assert.AreEqual(UserRole.User, result.Role); + Assert.AreEqual(1, result.AddressId); + } + + [TestMethod] + public void UpdateUser_UserDtoIsNull_NullReturned() + { + var mockContext = new Mock(); + + var user = new User + { + Id = 1, + Name = "John", + Surname = "Doe", + Username = "Username", + Email = "Email" + }; + + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + + var userService = new UserService(mockContext.Object); + + Assert.ThrowsException(() => userService.UpdateUser(1, null)); + } + + [TestMethod] + public void UpdateUser_UserDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Users).ReturnsDbSet([]); + + var userService = new UserService(mockContext.Object); + + var userDto = new UserDto + { + Name = "Jane", + Surname = "Doe", + Username = "Username", + Email = "Email", + Phone = "Phone", + Role = UserRole.User, + AddressId = 1 + }; + + Assert.ThrowsException(() => userService.UpdateUser(1, userDto)); + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI.Tests/UnitTest1.cs b/Server/ReasnAPI/ReasnAPI.Tests/UnitTest1.cs deleted file mode 100644 index 46dac6f2..00000000 --- a/Server/ReasnAPI/ReasnAPI.Tests/UnitTest1.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace ReasnAPI.Tests -{ - [TestClass] - public class UnitTest1 - { - [TestMethod] - public void TestAdd() - { - Assert.AreEqual(17, 17); - } - } -} diff --git a/Server/ReasnAPI/ReasnAPI/Exceptions/BadRequestException.cs b/Server/ReasnAPI/ReasnAPI/Exceptions/BadRequestException.cs new file mode 100644 index 00000000..b0198d2c --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Exceptions/BadRequestException.cs @@ -0,0 +1,10 @@ +namespace ReasnAPI.Exceptions; + +public class BadRequestException : Exception +{ + public BadRequestException() { } + + public BadRequestException(string message) : base(message) { } + + public BadRequestException(string message, Exception inner) : base(message, inner) { } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Exceptions/NotFoundException.cs b/Server/ReasnAPI/ReasnAPI/Exceptions/NotFoundException.cs new file mode 100644 index 00000000..1ca7f981 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Exceptions/NotFoundException.cs @@ -0,0 +1,10 @@ +namespace ReasnAPI.Exceptions; + +public class NotFoundException : Exception +{ + public NotFoundException() { } + + public NotFoundException(string message) : base(message) { } + + public NotFoundException(string message, Exception innerException) : base(message, innerException) { } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/AddressMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/AddressMapper.cs new file mode 100644 index 00000000..8fe399dc --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/AddressMapper.cs @@ -0,0 +1,36 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers; + +public static class AddressMapper +{ + public static AddressDto ToDto(this Address address) + { + return new AddressDto + { + Street = address.Street, + Country = address.Country, + City = address.City, + State = address.State, + ZipCode = address.ZipCode + }; + } + + public static List ToDtoList(this IEnumerable
addresses) + { + return addresses.Select(ToDto).ToList(); + } + + public static Address ToEntity(this AddressDto addressDto) + { + return new Address + { + City = addressDto.City, + Country = addressDto.Country, + State = addressDto.State, + Street = addressDto.Street, + ZipCode = addressDto.ZipCode + }; + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/CommentMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/CommentMapper.cs new file mode 100644 index 00000000..fc785ef4 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/CommentMapper.cs @@ -0,0 +1,34 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers; + +public static class CommentMapper +{ + public static CommentDto ToDto(this Comment comment) + { + return new CommentDto + { + EventId = comment.EventId, + Content = comment.Content, + UserId = comment.UserId, + CreatedAt = comment.CreatedAt + }; + } + + public static List ToDtoList(this IEnumerable comments) + { + return comments.Select(ToDto).ToList(); + } + + public static Comment ToEntity(this CommentDto commentDto) + { + return new Comment + { + EventId = commentDto.EventId, + Content = commentDto.Content, + UserId = commentDto.UserId, + CreatedAt = commentDto.CreatedAt + }; + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs new file mode 100644 index 00000000..f6f75f72 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs @@ -0,0 +1,28 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers; + +public static class InterestMapper +{ + public static InterestDto ToDto(this Interest interest) + { + return new InterestDto + { + Name = interest.Name + }; + } + + public static List ToDtoList(this IEnumerable interests) + { + return interests.Select(ToDto).ToList(); + } + + public static Interest ToEntity(this InterestDto interestDto) + { + return new Interest + { + Name = interestDto.Name + }; + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs new file mode 100644 index 00000000..66fee4e6 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs @@ -0,0 +1,31 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers; +public static class ParticipantMapper +{ + public static ParticipantDto ToDto(this Participant participant) + { + return new ParticipantDto + { + EventId = participant.EventId, + UserId = participant.UserId, + Status = participant.Status + }; + } + + public static List ToDtoList(this IEnumerable participants) + { + return participants.Select(ToDto).ToList(); + } + + public static Participant ToEntity(this ParticipantDto participantDto) + { + return new Participant + { + EventId = participantDto.EventId, + UserId = participantDto.UserId, + Status = participantDto.Status + }; + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/UserInterestMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/UserInterestMapper.cs new file mode 100644 index 00000000..9d1ba415 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/UserInterestMapper.cs @@ -0,0 +1,30 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers; + +public static class UserInterestMapper +{ + public static UserInterestDto ToDto(this UserInterest userInterest) + { + return new UserInterestDto + { + Interest = userInterest.Interest.ToDto(), + Level = userInterest.Level + }; + } + + public static List ToDtoList(this IEnumerable userInterests) + { + return userInterests.Select(ToDto).ToList(); + } + + public static UserInterest ToEntity(this UserInterestDto userInterestDto) + { + return new UserInterest + { + Interest = userInterestDto.Interest.ToEntity(), + Level = userInterestDto.Level + }; + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs new file mode 100644 index 00000000..836f77b7 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs @@ -0,0 +1,27 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers; + +public static class UserMapper +{ + public static UserDto ToDto(this User user) + { + return new UserDto + { + Username = user.Username, + Name = user.Name, + Surname = user.Surname, + Email = user.Email, + Phone = user.Phone, + Role = user.Role, + AddressId = user.AddressId, + Interests = user.UserInterests.ToDtoList() + }; + } + + public static List ToDtoList(this IEnumerable users) + { + return users.Select(ToDto).ToList(); + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs b/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs index f06d2717..cc71de38 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs @@ -1,7 +1,9 @@ using ReasnAPI.Models.Enums; -namespace ReasnAPI.Models.DTOs { - public class UserDto { +namespace ReasnAPI.Models.DTOs +{ + public class UserDto + { public string Username { get; set; } = null!; public string Name { get; set; } = null!; public string Surname { get; set; } = null!; diff --git a/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs b/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs index 041cf80b..9a2c04d1 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs @@ -37,7 +37,12 @@ public ReasnContext(DbContextOptions options) public virtual DbSet UserInterests { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseNpgsql("name=ConnectionStrings:DefaultValue"); + { + if (!optionsBuilder.IsConfigured) + { + optionsBuilder.UseNpgsql("name=ConnectionStrings:DefaultValue"); + } + } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -47,26 +52,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasPostgresEnum("common", "participant_status", new[] { "Interested", "Participating" }) .HasPostgresEnum("users", "role", new[] { "User", "Organizer", "Admin" }); - modelBuilder - .Entity() - .Property(u => u.Role) - .HasConversion(); - - modelBuilder - .Entity() - .Property(u => u.Status) - .HasConversion(); - - modelBuilder - .Entity() - .Property(u => u.ObjectType) - .HasConversion (); - - modelBuilder - .Entity() - .Property(u => u.Status) - .HasConversion(); - modelBuilder.Entity
(entity => { entity.HasKey(e => e.Id).HasName("address_pkey"); diff --git a/Server/ReasnAPI/ReasnAPI/Models/Enums/EventStatus.cs b/Server/ReasnAPI/ReasnAPI/Models/Enums/EventStatus.cs index beeb82e1..40c96dba 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/Enums/EventStatus.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/Enums/EventStatus.cs @@ -1,9 +1,15 @@ -namespace ReasnAPI.Models.Enums; +using NpgsqlTypes; + +namespace ReasnAPI.Models.Enums; public enum EventStatus { - Completed, - Inprogress, - Approved, + [PgName("Completed")] + Completed, + [PgName("InProgress")] + Inprogress, + [PgName("Approved")] + Approved, + [PgName("WaitingForApproval")] WaitingForApproval } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Models/Enums/ObjectType.cs b/Server/ReasnAPI/ReasnAPI/Models/Enums/ObjectType.cs index bb194063..b2ea9d6b 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/Enums/ObjectType.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/Enums/ObjectType.cs @@ -1,7 +1,11 @@ -namespace ReasnAPI.Models.Enums; +using NpgsqlTypes; + +namespace ReasnAPI.Models.Enums; public enum ObjectType { + [PgName("Event")] Event, + [PgName("User")] User } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Models/Enums/ParticipantStatus.cs b/Server/ReasnAPI/ReasnAPI/Models/Enums/ParticipantStatus.cs index 0584f578..057bbdb4 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/Enums/ParticipantStatus.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/Enums/ParticipantStatus.cs @@ -1,7 +1,11 @@ -namespace ReasnAPI.Models.Enums; +using NpgsqlTypes; + +namespace ReasnAPI.Models.Enums; public enum ParticipantStatus { + [PgName("Interested")] Interested, + [PgName("Participating")] Participating } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Models/Enums/UserRole.cs b/Server/ReasnAPI/ReasnAPI/Models/Enums/UserRole.cs index 4a35be42..c9ced2b9 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/Enums/UserRole.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/Enums/UserRole.cs @@ -1,8 +1,13 @@ -namespace ReasnAPI.Models.Enums; +using NpgsqlTypes; + +namespace ReasnAPI.Models.Enums; public enum UserRole { + [PgName("User")] User, + [PgName("Organizer")] Organizer, + [PgName("Admin")] Admin } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Program.cs b/Server/ReasnAPI/ReasnAPI/Program.cs index 67d181aa..8b2051c9 100644 --- a/Server/ReasnAPI/ReasnAPI/Program.cs +++ b/Server/ReasnAPI/ReasnAPI/Program.cs @@ -7,21 +7,25 @@ using System; using System.Text.Json.Serialization; using ReasnAPI.Models.Database; +using ReasnAPI.Services; var builder = WebApplication.CreateSlimBuilder(args); -builder.Services.AddControllers(); -var dataSourceBuilder = new NpgsqlDataSourceBuilder(builder.Configuration.GetConnectionString("DefaultValue")); -dataSourceBuilder.MapEnum("events.participant.status"); -dataSourceBuilder.MapEnum("events.event.status"); -dataSourceBuilder.MapEnum("common.image.object_type"); -dataSourceBuilder.MapEnum("users.user.role"); - -var dataSource = dataSourceBuilder.Build(); -// todo: uncomment after creating DbContext and change context name and if needed - connection string localized in appsettings.json -builder.Services.AddDbContext(options => +var connectionString = builder.Configuration.GetConnectionString("DefaultValue"); + +var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); +dataSourceBuilder.MapEnum("common.object_type"); +dataSourceBuilder.MapEnum("common.event_status"); +dataSourceBuilder.MapEnum("common.participant_status"); +dataSourceBuilder.MapEnum("users.role"); + +var dataSource = dataSourceBuilder.Build(); + +builder.Services.AddDbContext(options => options.UseNpgsql(dataSource)); +builder.Services.AddControllers(); + builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo diff --git a/Server/ReasnAPI/ReasnAPI/Services/AddressService.cs b/Server/ReasnAPI/ReasnAPI/Services/AddressService.cs index 04f944f5..86e6eb8c 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/AddressService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/AddressService.cs @@ -1,16 +1,83 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class AddressService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} +using ReasnAPI.Exceptions; +using ReasnAPI.Mappers; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; + +namespace ReasnAPI.Services; + +public class AddressService(ReasnContext context) +{ + public AddressDto CreateAddress(AddressDto addressDto) + { + ArgumentNullException.ThrowIfNull(addressDto); + + context.Addresses.Add(addressDto.ToEntity()); + context.SaveChanges(); + + return addressDto; + } + + public AddressDto UpdateAddress(int addressId, AddressDto addressDto) + { + ArgumentNullException.ThrowIfNull(addressDto); + + var address = context.Addresses.FirstOrDefault(r => r.Id == addressId); + + if (address is null) + { + throw new NotFoundException("Address not found"); + } + + address.City = addressDto.City; + address.Country = addressDto.Country; + address.Street = addressDto.Street; + address.State = addressDto.State; + address.ZipCode = addressDto.ZipCode; + + context.Addresses.Update(address); + context.SaveChanges(); + + return address.ToDto(); + } + + public void DeleteAddress(int addressId) + { + var address = context.Addresses.FirstOrDefault(r => r.Id == addressId); + + if (address is null) + { + throw new NotFoundException("Address not found"); + } + + context.Addresses.Remove(address); + context.SaveChanges(); + } + + public AddressDto GetAddressById(int addressId) + { + var address = context.Addresses.Find(addressId); + + if (address is null) + { + throw new NotFoundException("Address not found"); + } + + return address.ToDto(); + } + + public IEnumerable GetAddressesByFilter(Expression> filter) + { + return context.Addresses + .Where(filter) + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetAllAddresses() + { + return context.Addresses + .ToDtoList() + .AsEnumerable(); + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Services/CommentService.cs b/Server/ReasnAPI/ReasnAPI/Services/CommentService.cs index 7b0767c0..95eac468 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/CommentService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/CommentService.cs @@ -1,16 +1,88 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class CommentService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} +using ReasnAPI.Exceptions; +using ReasnAPI.Mappers; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; + +namespace ReasnAPI.Services; + +public class CommentService(ReasnContext context) +{ + public CommentDto CreateComment(CommentDto commentDto) + { + ArgumentNullException.ThrowIfNull(commentDto); + + commentDto.CreatedAt = DateTime.UtcNow; + + context.Comments.Add(commentDto.ToEntity()); + context.SaveChanges(); + + return commentDto; + } + + public CommentDto UpdateComment(int commentId, CommentDto commentDto) + { + ArgumentNullException.ThrowIfNull(commentDto); + + var comment = context.Comments.FirstOrDefault(r => r.Id == commentId); + + if (comment is null) + { + throw new NotFoundException("Comment not found"); + } + + var user = context.Users.FirstOrDefault(r => r.Id == commentDto.UserId); + + if (user is null) + { + throw new NotFoundException("User not found"); + } + + comment.Content = commentDto.Content; + + context.Comments.Update(comment); + context.SaveChanges(); + + return comment.ToDto(); + } + + public void DeleteComment(int commentId) + { + var comment = context.Comments.FirstOrDefault(r => r.Id == commentId); + + if (comment is null) + { + throw new NotFoundException("Comment not found"); + } + + context.Comments.Remove(comment); + context.SaveChanges(); + } + + public CommentDto GetCommentById(int commentId) + { + var comment = context.Comments.Find(commentId); + + if (comment is null) + { + throw new NotFoundException("Comment not found"); + } + + return comment.ToDto(); + } + + public IEnumerable GetCommentsByFilter(Expression> filter) + { + return context.Comments + .Where(filter) + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetAllComments() + { + return context.Comments + .ToDtoList() + .AsEnumerable(); + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Services/ObjectTypeService.cs b/Server/ReasnAPI/ReasnAPI/Services/ObjectTypeService.cs deleted file mode 100644 index 84ba4abc..00000000 --- a/Server/ReasnAPI/ReasnAPI/Services/ObjectTypeService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class ObjectTypeService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} diff --git a/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs b/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs index c3f4cd68..2b568ddb 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs @@ -1,16 +1,99 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class ParticipantService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} +using ReasnAPI.Exceptions; +using ReasnAPI.Mappers; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; + +namespace ReasnAPI.Services; + +public class ParticipantService(ReasnContext context) +{ + public ParticipantDto CreateParticipant(ParticipantDto participantDto) + { + ArgumentNullException.ThrowIfNull(participantDto); + + var participantDb = context.Participants.FirstOrDefault(r => r.Event.Id == participantDto.EventId && r.User.Id == participantDto.UserId); + + if (participantDb is not null) + { + throw new BadRequestException("Participant already exists"); + } + + var userInDb = context.Users.FirstOrDefault(r => r.Id == participantDto.UserId); + + if (userInDb is null) + { + throw new NotFoundException("User not found"); + } + + var eventInDb = context.Events.FirstOrDefault(r => r.Id == participantDto.EventId); + + if (eventInDb is null) + { + throw new NotFoundException("Event not found"); + } + + context.Participants.Add(participantDto.ToEntity()); + context.SaveChanges(); + + return participantDto; + } + + public ParticipantDto UpdateParticipant(int participantId, ParticipantDto participantDto) + { + ArgumentNullException.ThrowIfNull(participantDto); + + var participant = context.Participants.FirstOrDefault(r => r.Id == participantId); + if (participant is null) + { + throw new NotFoundException("Participant not found"); + } + + participant.Status = participantDto.Status; + + context.Participants.Update(participant); + context.SaveChanges(); + + return participant.ToDto(); + } + + public void DeleteParticipant(int participantId) + { + var participant = context.Participants.FirstOrDefault(r => r.Id == participantId); + + if (participant is null) + { + throw new NotFoundException("Participant not found"); + } + + context.Participants.Remove(participant); + context.SaveChanges(); + } + + public ParticipantDto GetParticipantById(int participantId) + { + var participant = context.Participants.Find(participantId); + + if (participant is null) + { + throw new NotFoundException("Participant not found"); + } + + return participant.ToDto(); + } + + public IEnumerable GetParticipantsByFilter(Expression> filter) + { + return context.Participants + .Where(filter) + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetAllParticipants() + { + return context.Participants + .ToDtoList() + .AsEnumerable(); + } +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Services/RoleService.cs b/Server/ReasnAPI/ReasnAPI/Services/RoleService.cs deleted file mode 100644 index 6dea686b..00000000 --- a/Server/ReasnAPI/ReasnAPI/Services/RoleService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class RoleService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} diff --git a/Server/ReasnAPI/ReasnAPI/Services/StatusService.cs b/Server/ReasnAPI/ReasnAPI/Services/StatusService.cs deleted file mode 100644 index 2af9b1ad..00000000 --- a/Server/ReasnAPI/ReasnAPI/Services/StatusService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class StatusService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} diff --git a/Server/ReasnAPI/ReasnAPI/Services/UserService.cs b/Server/ReasnAPI/ReasnAPI/Services/UserService.cs index f657fcd8..92a187a7 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/UserService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/UserService.cs @@ -1,16 +1,152 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class UserService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} +using Microsoft.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Mappers; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; +using System.Transactions; + +namespace ReasnAPI.Services; + +public class UserService(ReasnContext context) +{ + public UserDto UpdateUser(int userId, UserDto userDto) + { + using (var scope = new TransactionScope()) + { + ArgumentNullException.ThrowIfNull(userDto); + + var user = context.Users + .Include(u => u.UserInterests) + .ThenInclude(ui => ui.Interest) + .FirstOrDefault(r => r.Id == userId); + + if (user is null) + { + throw new NotFoundException("User not found"); + } + + var usernameExists = context.Users.Any(r => r.Username == userDto.Username && r.Id != userId); + + if (usernameExists) + { + throw new BadRequestException("User with given username already exists"); + } + + var emailExists = context.Users.Any(r => r.Email == userDto.Email && r.Id != userId); + + if (emailExists) + { + throw new BadRequestException("User with given email already exists"); + } + + var phoneExists = context.Users.Any(r => r.Phone == userDto.Phone && r.Id != userId); + + if (phoneExists) + { + throw new BadRequestException("User with given phone number already exists"); + } + + user.Username = userDto.Username; + user.Name = userDto.Name; + user.Surname = userDto.Surname; + user.Username = userDto.Username; + user.Email = userDto.Email; + user.Phone = userDto.Phone; + user.Role = userDto.Role; + user.AddressId = userDto.AddressId; + user.UpdatedAt = DateTime.UtcNow; + + context.Users.Update(user); + + if (userDto.Interests is null || userDto.Interests.Count == 0) + { + context.SaveChanges(); + scope.Complete(); + return userDto; + } + + var interestsToRemove = user.UserInterests + .Where(ui => !userDto.Interests.Exists(uid => uid.Interest.Name == ui.Interest.Name)); + + context.UserInterests.RemoveRange(interestsToRemove); + + var interestsToAdd = userDto.Interests + .Where(uid => !user.UserInterests.Any(ui => ui.Interest.Name == uid.Interest.Name)) + .Select(uid => uid.ToEntity()) + .ToList(); + + context.UserInterests.AddRange(interestsToAdd); + + var interestsToUpdate = user.UserInterests + .Where(ui => userDto.Interests.Exists(uid => uid.Interest.Name == ui.Interest.Name)) + .ToList(); + + foreach (var interest in interestsToUpdate) + { + var updatedInterest = userDto.Interests.Find(uid => uid.Interest.Name == interest.Interest.Name); + + if (updatedInterest is null) + { + continue; + } + + interest.Level = updatedInterest.Level; + context.UserInterests.Update(interest); + } + + context.SaveChanges(); + scope.Complete(); + } + + return userDto; + } + + public UserDto GetUserById(int userId) + { + var user = context.Users + .Include(u => u.UserInterests) + .FirstOrDefault(u => u.Id == userId); + + if (user is null) + { + throw new NotFoundException("User not found"); + } + + return user.ToDto(); + } + + public UserDto GetUserByUsername(string username) + { + var user = context.Users + .Include(u => u.UserInterests) + .FirstOrDefault(u => u.Username == username); + + if (user is null) + { + throw new NotFoundException("User not found"); + } + + return user.ToDto(); + } + + public IEnumerable GetUsersByFilter(Expression> filter) + { + return context.Users + .Include(u => u.UserInterests) + .ThenInclude(ui => ui.Interest) + .Where(filter) + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetAllUsers() + { + var users = context.Users + .Include(u => u.UserInterests) + .ThenInclude(ui => ui.Interest) + .ToList(); + + return users.ToDtoList().AsEnumerable(); + } +} \ No newline at end of file