Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using Moq;
using TickAPI.Categories.Abstractions;
using TickAPI.Categories.Controllers;
using TickAPI.Categories.DTOs;
using TickAPI.Categories.DTOs.Response;
using TickAPI.Categories.Models;
using TickAPI.Common.Pagination.Responses;
using TickAPI.Common.Results;
using TickAPI.Common.Results.Generic;
Expand All @@ -13,10 +15,9 @@ namespace TickAPI.Tests.Categories.Controllers;
public class CategoryControllerTests
{
[Fact]

public async Task GetCategories_WhenDataIsValid_ShouldReturnOk()
{
//arrange
// Arrange
int pageSize = 20;
int pageNumber = 0;
var categoryServiceMock = new Mock<ICategoryService>();
Expand All @@ -26,14 +27,36 @@ public async Task GetCategories_WhenDataIsValid_ShouldReturnOk()

var sut = new CategoryController(categoryServiceMock.Object);

//act
// Act
var res = await sut.GetCategories(pageSize, pageNumber);

//assert
// Assert
var result = Assert.IsType<ActionResult<PaginatedData<GetCategoryResponseDto>>>(res);
var objectResult = Assert.IsType<OkObjectResult>(result.Result);
Assert.Equal(200, objectResult.StatusCode);
Assert.NotNull(objectResult.Value);
}


[Fact]
public async Task CreateCategory_WhenDataIsValid_ShouldReturnSuccess()
{
// Arrange
const string categoryName = "TestCategory";
var createCategoryDto = new CreateCategoryDto(categoryName);

var categoryServiceMock = new Mock<ICategoryService>();
categoryServiceMock
.Setup(m => m.CreateNewCategoryAsync(categoryName))
.ReturnsAsync(Result<Category>.Success(new Category()));

var sut = new CategoryController(categoryServiceMock.Object);

// Act
var res = await sut.CreateCategory(createCategoryDto);

// Assert
var objectResult = Assert.IsType<OkObjectResult>(res);
Assert.Equal(200, objectResult.StatusCode);
Assert.Equal("category created successfully", objectResult.Value);
}
}
112 changes: 107 additions & 5 deletions TickAPI/TickAPI.Tests/Categories/Services/CategoryServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ namespace TickAPI.Tests.Categories.Services;
public class CategoryServiceTests
{
[Fact]

public async Task GetCategories_WhenDataIsValid_ShouldReturnOk()
public async Task GetCategoriesResponsesAsync_WhenDataIsValid_ShouldReturnOk()
{
//arrange
// Arrange
int pageSize = 10;
int page = 0;
var categoryRepositoryMock = new Mock<ICategoryRepository>();
Expand All @@ -32,11 +31,114 @@ public async Task GetCategories_WhenDataIsValid_ShouldReturnOk()

var sut = new CategoryService(categoryRepositoryMock.Object, paginationServiceMock.Object);

//act
// Act
var res = await sut.GetCategoriesResponsesAsync(pageSize, page);

//assert
// Assert
var result = Assert.IsType<Result<PaginatedData<GetCategoryResponseDto>>>(res);
Assert.True(result.IsSuccess);
}

[Fact]
public async Task GetCategoryByNameAsync_WhenCategoryWithNameIsReturnedFromRepository_ShouldReturnSuccess()
{
// Arrange
const string categoryName = "TestCategory";

var category = new Category()
{
Name = categoryName
};

var categoryRepositoryMock = new Mock<ICategoryRepository>();
categoryRepositoryMock
.Setup(m => m.GetCategoryByNameAsync(categoryName))
.ReturnsAsync(Result<Category>.Success(category));

var paginationServiceMock = new Mock<IPaginationService>();

var sut = new CategoryService(categoryRepositoryMock.Object, paginationServiceMock.Object);

// Act
var res = await sut.GetCategoryByNameAsync(categoryName);

// Assert
Assert.True(res.IsSuccess);
Assert.Equal(category, res.Value);
}

[Fact]
public async Task GetCategoryByNameAsync_WhenCategoryWithNameIsNotReturnedFromRepository_ShouldReturnFailure()
{
// Arrange
const string categoryName = "TestCategory";
const string errorMsg = $"category with name '{categoryName}' not found";
const int statusCode = 404;

var categoryRepositoryMock = new Mock<ICategoryRepository>();
categoryRepositoryMock
.Setup(m => m.GetCategoryByNameAsync(categoryName))
.ReturnsAsync(Result<Category>.Failure(statusCode, errorMsg));

var paginationServiceMock = new Mock<IPaginationService>();

var sut = new CategoryService(categoryRepositoryMock.Object, paginationServiceMock.Object);

// Act
var res = await sut.GetCategoryByNameAsync(categoryName);

// Assert
Assert.True(res.IsError);
Assert.Equal(errorMsg, res.ErrorMsg);
Assert.Equal(statusCode, res.StatusCode);
}

[Fact]
public async Task CreateNewCategoryAsync_WhenCategoryDataIsValid_ShouldReturnNewCategory()
{
// Arrange
const string categoryName = "TestCategory";
const string errorMsg = $"category with name '{categoryName}' not found";
const int statusCode = 404;

var categoryRepositoryMock = new Mock<ICategoryRepository>();
categoryRepositoryMock
.Setup(m => m.GetCategoryByNameAsync(categoryName))
.ReturnsAsync(Result<Category>.Failure(statusCode, errorMsg));

var paginationServiceMock = new Mock<IPaginationService>();

var sut = new CategoryService(categoryRepositoryMock.Object, paginationServiceMock.Object);

// Act
var res = await sut.CreateNewCategoryAsync(categoryName);

// Assert
Assert.True(res.IsSuccess);
Assert.Equal(categoryName, res.Value!.Name);
}

[Fact]
public async Task CreateNewCategoryAsync_WhenWithNotUniqueName_ShouldReturnFailure()
{
// Arrange
const string categoryName = "TestCategory";

var categoryRepositoryMock = new Mock<ICategoryRepository>();
categoryRepositoryMock
.Setup(m => m.GetCategoryByNameAsync(categoryName))
.ReturnsAsync(Result<Category>.Success(new Category()));

var paginationServiceMock = new Mock<IPaginationService>();

var sut = new CategoryService(categoryRepositoryMock.Object, paginationServiceMock.Object);

// Act
var res = await sut.CreateNewCategoryAsync(categoryName);

// Assert
Assert.True(res.IsError);
Assert.Equal(400, res.StatusCode);
Assert.Equal($"category with name '{categoryName}' already exists", res.ErrorMsg);
}
}
2 changes: 1 addition & 1 deletion TickAPI/TickAPI.Tests/Events/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static Event CreateSampleEvent(string name)
EndDate = new DateTime(1970, 1, 2, 0, 0, 0, DateTimeKind.Utc),
MinimumAge = 18,
EventStatus = EventStatus.TicketsAvailable,
Categories = new List<Category> { new Category { CategoryName = "Test" } },
Categories = new List<Category> { new Category { Name = "Test" } },
Address = new Address
{
Country = "United States",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ namespace TickAPI.Categories.Abstractions;
public interface ICategoryRepository
{
public Task<ICollection<Category>> GetCategoriesAsync();
public Task<Result<Category>> GetCategoryByNameAsync(string categoryName);
public Task AddNewCategoryAsync(Category category);
}
2 changes: 2 additions & 0 deletions TickAPI/TickAPI/Categories/Abstractions/ICategoryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ namespace TickAPI.Categories.Abstractions;

public interface ICategoryService
{
public Task<Result<Category>> GetCategoryByNameAsync(string categoryName);
public Task<Result<PaginatedData<GetCategoryResponseDto>>> GetCategoriesResponsesAsync(int pageSize, int page);
public Task<Result<Category>> CreateNewCategoryAsync(string categoryName);
}
19 changes: 15 additions & 4 deletions TickAPI/TickAPI/Categories/Controllers/CategoryController.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
using Microsoft.AspNetCore.Mvc;
using TickAPI.Categories.Abstractions;
using TickAPI.Categories.DTOs;
using TickAPI.Categories.DTOs.Response;
using TickAPI.Common.Auth.Attributes;
using TickAPI.Common.Auth.Enums;
using TickAPI.Common.Pagination.Responses;


namespace TickAPI.Categories.Controllers;

[ApiController]
[Route("api/[controller]")]

public class CategoryController : Controller
{

private readonly ICategoryService _categoryService;

public CategoryController(ICategoryService categoryService)
Expand All @@ -22,14 +21,26 @@ public CategoryController(ICategoryService categoryService)
}

[AuthorizeWithPolicy(AuthPolicies.VerifiedUserPolicy)]
[HttpPost("get-categories")]
[HttpGet("get-categories")]
public async Task<ActionResult<PaginatedData<GetCategoryResponseDto>>> GetCategories([FromQuery] int pageSize, [FromQuery] int page)
{
var res = await _categoryService.GetCategoriesResponsesAsync(pageSize, page);
if (!res.IsSuccess)
if (res.IsError)
{
return StatusCode(StatusCodes.Status500InternalServerError, res.ErrorMsg);
}
return Ok(res.Value);
}

// TODO: Add appropriate policy verification (admin, maybe also organizer?)
[HttpPost("create-category")]
public async Task<ActionResult> CreateCategory([FromBody] CreateCategoryDto request)
{
var newCategoryResult = await _categoryService.CreateNewCategoryAsync(request.Name);

if(newCategoryResult.IsError)
return StatusCode(newCategoryResult.StatusCode, newCategoryResult.ErrorMsg);

return Ok("category created successfully");
}
}
5 changes: 5 additions & 0 deletions TickAPI/TickAPI/Categories/DTOs/CreateCategoryDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace TickAPI.Categories.DTOs;

public record CreateCategoryDto(
string Name
);
2 changes: 1 addition & 1 deletion TickAPI/TickAPI/Categories/Models/Category.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace TickAPI.Categories.Models;
public class Category
{
public Guid Id { get; set; }
public string CategoryName { get; set; }
public string Name { get; set; }
public ICollection<Event> Events { get; set; }
}
18 changes: 18 additions & 0 deletions TickAPI/TickAPI/Categories/Respositories/CategoryRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,22 @@ public async Task<ICollection<Category>> GetCategoriesAsync()
var list = await _tickApiDbContext.Categories.ToListAsync();
return list;
}

public async Task<Result<Category>> GetCategoryByNameAsync(string categoryName)
{
var category = await _tickApiDbContext.Categories.FirstOrDefaultAsync(c => c.Name == categoryName);

if (category == null)
{
return Result<Category>.Failure(StatusCodes.Status404NotFound, $"category with name '{categoryName}' not found");
}

return Result<Category>.Success(category);
}

public async Task AddNewCategoryAsync(Category category)
{
_tickApiDbContext.Categories.Add(category);
await _tickApiDbContext.SaveChangesAsync();
}
}
26 changes: 25 additions & 1 deletion TickAPI/TickAPI/Categories/Services/CategoryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public CategoryService(ICategoryRepository categoryRepository, IPaginationServi
_paginationService = paginationService;
}

public Task<Result<Category>> GetCategoryByNameAsync(string categoryName)
{
return _categoryRepository.GetCategoryByNameAsync(categoryName);
}

public async Task<Result<PaginatedData<GetCategoryResponseDto>>> GetCategoriesResponsesAsync(int pageSize, int page)
{
var categoriesAllResponse = await _categoryRepository.GetCategoriesAsync();
Expand All @@ -27,8 +32,27 @@ public async Task<Result<PaginatedData<GetCategoryResponseDto>>> GetCategoriesRe
return Result<PaginatedData<GetCategoryResponseDto>>.PropagateError(categoriesPaginated);
}

var categoriesResponse = _paginationService.MapData(categoriesPaginated.Value!, (c) => new GetCategoryResponseDto(c.CategoryName));
var categoriesResponse = _paginationService.MapData(categoriesPaginated.Value!, (c) => new GetCategoryResponseDto(c.Name));

return Result<PaginatedData<GetCategoryResponseDto>>.Success(categoriesResponse);
}

public async Task<Result<Category>> CreateNewCategoryAsync(string categoryName)
{
var alreadyExistingResult = await _categoryRepository.GetCategoryByNameAsync(categoryName);

if (alreadyExistingResult.IsSuccess)
{
return Result<Category>.Failure(StatusCodes.Status400BadRequest,
$"category with name '{categoryName}' already exists");
}

var category = new Category()
{
Name = categoryName
};

await _categoryRepository.AddNewCategoryAsync(category);
return Result<Category>.Success(category);
}
}
38 changes: 38 additions & 0 deletions TickAPI/TickAPI/Common/TickApiDbContext/TickApiDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,42 @@ public TickApiDbContext(DbContextOptions<TickApiDbContext> options) : base(optio
public DbSet<TicketType> TicketTypes { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<Category> Categories { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<Category>().HasData(
new Category
{
Id = Guid.Parse("ec3daf69-baa9-4fcd-a674-c09884a57272"),
Name = "Music"
},
new Category
{
Id = Guid.Parse("de89dd76-3b29-43e1-8f4b-5278b1b8bde2"),
Name = "Sports"
},
new Category
{
Id = Guid.Parse("ea58370b-2a17-4770-abea-66399ad69fb8"),
Name = "Conferences"
},
new Category
{
Id = Guid.Parse("4a086d9e-59de-4fd1-a1b2-bd9b5eec797c"),
Name = "Theatre"
},
new Category
{
Id = Guid.Parse("5f8dbe65-30be-453f-8f22-191a11b2977b"),
Name = "Comedy"
},
new Category
{
Id = Guid.Parse("4421327a-4bc8-4706-bec0-666f78ed0c69"),
Name = "Workshops"
}
);
}
}
Loading
Loading