UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

1,551 lines (1,287 loc) 102 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.aspnetXUnitTemplate = void 0; exports.aspnetXUnitTemplate = { id: 'aspnet-xunit', name: 'aspnet-xunit', displayName: 'ASP.NET Core with xUnit Testing', description: 'Enterprise .NET API with comprehensive xUnit testing suite', language: 'csharp', framework: 'aspnet-xunit', version: '1.0.0', tags: ['aspnet', 'xunit', 'testing', 'moq', 'fluentassertions'], port: 5000, dependencies: {}, features: ['authentication', 'database', 'validation', 'logging', 'testing'], files: { // Main project file '{{serviceName}}.csproj': `<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" /> <PackageReference Include="AutoMapper" Version="12.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> <PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" /> <PackageReference Include="Serilog.AspNetCore" Version="8.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" /> <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" /> </ItemGroup> </Project>`, // Test project file '{{serviceName}}.Tests.csproj': `<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <IsPackable>false</IsPackable> <IsTestProject>true</IsTestProject> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="coverlet.collector" Version="6.0.0"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="FluentAssertions" Version="6.12.0" /> <PackageReference Include="Moq" Version="4.20.69" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" /> <PackageReference Include="AutoFixture" Version="4.18.0" /> <PackageReference Include="AutoFixture.Xunit2" Version="4.18.0" /> <PackageReference Include="AutoFixture.AutoMoq" Version="4.18.0" /> <PackageReference Include="Testcontainers.MsSql" Version="3.6.0" /> <PackageReference Include="Bogus" Version="34.0.2" /> </ItemGroup> <ItemGroup> <ProjectReference Include="../{{serviceName}}.csproj" /> </ItemGroup> </Project>`, // Program.cs 'Program.cs': `using {{serviceName}}.Data; using {{serviceName}}.Services; using {{serviceName}}.Models; using {{serviceName}}.DTOs; using {{serviceName}}.Profiles; using {{serviceName}}.Validators; using Microsoft.EntityFrameworkCore; using AutoMapper; using FluentValidation; using Serilog; using Microsoft.OpenApi.Models; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; var builder = WebApplication.CreateBuilder(args); // Configure Serilog Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(builder.Configuration) .Enrich.FromLogContext() .WriteTo.Console() .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); builder.Host.UseSerilog(); // Add services to the container builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); // Configure Entity Framework builder.Services.AddDbContext<ApplicationDbContext>(options => { if (builder.Environment.IsEnvironment("Testing")) { options.UseInMemoryDatabase("TestDb"); } else { options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")); } }); // Configure AutoMapper builder.Services.AddAutoMapper(typeof(UserProfile), typeof(ProductProfile), typeof(OrderProfile)); // Configure FluentValidation builder.Services.AddValidatorsFromAssemblyContaining<CreateUserDtoValidator>(); // Configure Swagger/OpenAPI builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "{{serviceName}} API", Version = "v1", Description = "Enterprise API with comprehensive xUnit testing", Contact = new OpenApiContact { Name = "{{serviceName}} Team", Email = "team@{{serviceName}}.com" } }); // Include XML comments var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); if (File.Exists(xmlPath)) { c.IncludeXmlComments(xmlPath); } // Configure JWT authentication in Swagger c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, Scheme = "Bearer" }); c.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }, Scheme = "oauth2", Name = "Bearer", In = ParameterLocation.Header, }, new List<string>() } }); }); // Configure JWT Authentication var jwtSettings = builder.Configuration.GetSection("JwtSettings"); var secretKey = jwtSettings["SecretKey"]; builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtSettings["Issuer"], ValidAudience = jwtSettings["Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey!)) }; }); // Register application services builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddScoped<IProductService, ProductService>(); builder.Services.AddScoped<IOrderService, OrderService>(); builder.Services.AddScoped<IAuthService, AuthService>(); var app = builder.Build(); // Configure the HTTP request pipeline if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); // Ensure database is created (skip in testing environment) if (!app.Environment.IsEnvironment("Testing")) { using var scope = app.Services.CreateScope(); var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); context.Database.EnsureCreated(); } app.Run(); // Make the implicit Program class public for testing public partial class Program { }`, // Models/User.cs 'Models/User.cs': `using System.ComponentModel.DataAnnotations; namespace {{serviceName}}.Models; public class User { public int Id { get; set; } [Required] [StringLength(100)] public string Name { get; set; } = string.Empty; [Required] [EmailAddress] [StringLength(255)] public string Email { get; set; } = string.Empty; [Required] public string PasswordHash { get; set; } = string.Empty; [StringLength(20)] public string? PhoneNumber { get; set; } public DateTime DateOfBirth { get; set; } [StringLength(500)] public string? Address { get; set; } [StringLength(100)] public string? City { get; set; } [StringLength(100)] public string? Country { get; set; } public bool IsActive { get; set; } = true; public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? UpdatedAt { get; set; } // Navigation properties public virtual ICollection<Order> Orders { get; set; } = new List<Order>(); }`, // Models/Product.cs 'Models/Product.cs': `using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace {{serviceName}}.Models; public class Product { public int Id { get; set; } [Required] [StringLength(200)] public string Name { get; set; } = string.Empty; [StringLength(1000)] public string? Description { get; set; } [Required] [Column(TypeName = "decimal(18,2)")] public decimal Price { get; set; } [StringLength(100)] public string? Category { get; set; } [StringLength(50)] public string? Brand { get; set; } public int StockQuantity { get; set; } [StringLength(500)] public string? ImageUrl { get; set; } public bool IsActive { get; set; } = true; public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? UpdatedAt { get; set; } // Navigation properties public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>(); }`, // Models/Order.cs 'Models/Order.cs': `using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace {{serviceName}}.Models; public class Order { public int Id { get; set; } [Required] public int UserId { get; set; } [Required] [StringLength(50)] public string OrderNumber { get; set; } = string.Empty; [Required] [Column(TypeName = "decimal(18,2)")] public decimal TotalAmount { get; set; } [Required] [StringLength(50)] public string Status { get; set; } = "Pending"; [StringLength(500)] public string? ShippingAddress { get; set; } [StringLength(500)] public string? Notes { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? UpdatedAt { get; set; } // Navigation properties public virtual User User { get; set; } = null!; public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>(); }`, // Models/OrderItem.cs 'Models/OrderItem.cs': `using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace {{serviceName}}.Models; public class OrderItem { public int Id { get; set; } [Required] public int OrderId { get; set; } [Required] public int ProductId { get; set; } [Required] public int Quantity { get; set; } [Required] [Column(TypeName = "decimal(18,2)")] public decimal UnitPrice { get; set; } [Required] [Column(TypeName = "decimal(18,2)")] public decimal TotalPrice { get; set; } // Navigation properties public virtual Order Order { get; set; } = null!; public virtual Product Product { get; set; } = null!; }`, // DTOs/UserDtos.cs 'DTOs/UserDtos.cs': `using System.ComponentModel.DataAnnotations; namespace {{serviceName}}.DTOs; public class UserDto { public int Id { get; set; } public string Name { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; public string? PhoneNumber { get; set; } public DateTime DateOfBirth { get; set; } public string? Address { get; set; } public string? City { get; set; } public string? Country { get; set; } public bool IsActive { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } } public class CreateUserDto { [Required] [StringLength(100)] public string Name { get; set; } = string.Empty; [Required] [EmailAddress] [StringLength(255)] public string Email { get; set; } = string.Empty; [Required] [StringLength(100, MinimumLength = 6)] public string Password { get; set; } = string.Empty; [StringLength(20)] public string? PhoneNumber { get; set; } public DateTime DateOfBirth { get; set; } [StringLength(500)] public string? Address { get; set; } [StringLength(100)] public string? City { get; set; } [StringLength(100)] public string? Country { get; set; } } public class UpdateUserDto { [StringLength(100)] public string? Name { get; set; } [EmailAddress] [StringLength(255)] public string? Email { get; set; } [StringLength(20)] public string? PhoneNumber { get; set; } public DateTime? DateOfBirth { get; set; } [StringLength(500)] public string? Address { get; set; } [StringLength(100)] public string? City { get; set; } [StringLength(100)] public string? Country { get; set; } public bool? IsActive { get; set; } } public class OrderDto { public int Id { get; set; } public int UserId { get; set; } public string OrderNumber { get; set; } = string.Empty; public decimal TotalAmount { get; set; } public string Status { get; set; } = string.Empty; public string? ShippingAddress { get; set; } public string? Notes { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } public UserDto? User { get; set; } public List<OrderItemDto> OrderItems { get; set; } = new(); } public class OrderItemDto { public int Id { get; set; } public int OrderId { get; set; } public int ProductId { get; set; } public int Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal TotalPrice { get; set; } public ProductDto? Product { get; set; } }`, // DTOs/ProductDtos.cs 'DTOs/ProductDtos.cs': `using System.ComponentModel.DataAnnotations; namespace {{serviceName}}.DTOs; public class ProductDto { public int Id { get; set; } public string Name { get; set; } = string.Empty; public string? Description { get; set; } public decimal Price { get; set; } public string? Category { get; set; } public string? Brand { get; set; } public int StockQuantity { get; set; } public string? ImageUrl { get; set; } public bool IsActive { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } } public class CreateProductDto { [Required] [StringLength(200)] public string Name { get; set; } = string.Empty; [StringLength(1000)] public string? Description { get; set; } [Required] [Range(0.01, double.MaxValue)] public decimal Price { get; set; } [StringLength(100)] public string? Category { get; set; } [StringLength(50)] public string? Brand { get; set; } [Required] [Range(0, int.MaxValue)] public int StockQuantity { get; set; } [StringLength(500)] public string? ImageUrl { get; set; } } public class UpdateProductDto { [StringLength(200)] public string? Name { get; set; } [StringLength(1000)] public string? Description { get; set; } [Range(0.01, double.MaxValue)] public decimal? Price { get; set; } [StringLength(100)] public string? Category { get; set; } [StringLength(50)] public string? Brand { get; set; } [Range(0, int.MaxValue)] public int? StockQuantity { get; set; } [StringLength(500)] public string? ImageUrl { get; set; } public bool? IsActive { get; set; } }`, // Profiles/UserProfile.cs 'Profiles/UserProfile.cs': `using AutoMapper; using {{serviceName}}.Models; using {{serviceName}}.DTOs; namespace {{serviceName}}.Profiles; public class UserProfile : Profile { public UserProfile() { CreateMap<User, UserDto>(); CreateMap<CreateUserDto, User>() .ForMember(dest => dest.Id, opt => opt.Ignore()) .ForMember(dest => dest.PasswordHash, opt => opt.Ignore()) .ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true)) .ForMember(dest => dest.CreatedAt, opt => opt.MapFrom(src => DateTime.UtcNow)) .ForMember(dest => dest.UpdatedAt, opt => opt.Ignore()) .ForMember(dest => dest.Orders, opt => opt.Ignore()); CreateMap<UpdateUserDto, User>() .ForMember(dest => dest.Id, opt => opt.Ignore()) .ForMember(dest => dest.PasswordHash, opt => opt.Ignore()) .ForMember(dest => dest.CreatedAt, opt => opt.Ignore()) .ForMember(dest => dest.UpdatedAt, opt => opt.MapFrom(src => DateTime.UtcNow)) .ForMember(dest => dest.Orders, opt => opt.Ignore()) .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null)); } }`, // Profiles/ProductProfile.cs 'Profiles/ProductProfile.cs': `using AutoMapper; using {{serviceName}}.Models; using {{serviceName}}.DTOs; namespace {{serviceName}}.Profiles; public class ProductProfile : Profile { public ProductProfile() { CreateMap<Product, ProductDto>(); CreateMap<CreateProductDto, Product>() .ForMember(dest => dest.Id, opt => opt.Ignore()) .ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true)) .ForMember(dest => dest.CreatedAt, opt => opt.MapFrom(src => DateTime.UtcNow)) .ForMember(dest => dest.UpdatedAt, opt => opt.Ignore()) .ForMember(dest => dest.OrderItems, opt => opt.Ignore()); CreateMap<UpdateProductDto, Product>() .ForMember(dest => dest.Id, opt => opt.Ignore()) .ForMember(dest => dest.CreatedAt, opt => opt.Ignore()) .ForMember(dest => dest.UpdatedAt, opt => opt.MapFrom(src => DateTime.UtcNow)) .ForMember(dest => dest.OrderItems, opt => opt.Ignore()) .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null)); } }`, // Profiles/OrderProfile.cs 'Profiles/OrderProfile.cs': `using AutoMapper; using {{serviceName}}.Models; using {{serviceName}}.DTOs; namespace {{serviceName}}.Profiles; public class OrderProfile : Profile { public OrderProfile() { CreateMap<Order, OrderDto>(); CreateMap<OrderItem, OrderItemDto>(); } }`, // Validators/CreateUserDtoValidator.cs 'Validators/CreateUserDtoValidator.cs': `using FluentValidation; using {{serviceName}}.DTOs; namespace {{serviceName}}.Validators; public class CreateUserDtoValidator : AbstractValidator<CreateUserDto> { public CreateUserDtoValidator() { RuleFor(x => x.Name) .NotEmpty() .Length(2, 100); RuleFor(x => x.Email) .NotEmpty() .EmailAddress() .MaximumLength(255); RuleFor(x => x.Password) .NotEmpty() .MinimumLength(6) .MaximumLength(100) .Matches(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)") .WithMessage("Password must contain at least one lowercase letter, one uppercase letter, and one digit"); RuleFor(x => x.PhoneNumber) .MaximumLength(20) .When(x => !string.IsNullOrEmpty(x.PhoneNumber)); RuleFor(x => x.DateOfBirth) .LessThan(DateTime.Now.AddYears(-13)) .WithMessage("User must be at least 13 years old"); } }`, // Data/ApplicationDbContext.cs 'Data/ApplicationDbContext.cs': `using Microsoft.EntityFrameworkCore; using {{serviceName}}.Models; namespace {{serviceName}}.Data; public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<User> Users { get; set; } public DbSet<Product> Products { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderItem> OrderItems { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // User configuration modelBuilder.Entity<User>(entity => { entity.HasKey(e => e.Id); entity.HasIndex(e => e.Email).IsUnique(); entity.Property(e => e.Name).IsRequired().HasMaxLength(100); entity.Property(e => e.Email).IsRequired().HasMaxLength(255); entity.Property(e => e.PasswordHash).IsRequired(); }); // Product configuration modelBuilder.Entity<Product>(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Name).IsRequired().HasMaxLength(200); entity.Property(e => e.Price).HasColumnType("decimal(18,2)"); }); // Order configuration modelBuilder.Entity<Order>(entity => { entity.HasKey(e => e.Id); entity.HasIndex(e => e.OrderNumber).IsUnique(); entity.Property(e => e.OrderNumber).IsRequired().HasMaxLength(50); entity.Property(e => e.TotalAmount).HasColumnType("decimal(18,2)"); entity.Property(e => e.Status).IsRequired().HasMaxLength(50); entity.HasOne(e => e.User) .WithMany(u => u.Orders) .HasForeignKey(e => e.UserId) .OnDelete(DeleteBehavior.Restrict); }); // OrderItem configuration modelBuilder.Entity<OrderItem>(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.UnitPrice).HasColumnType("decimal(18,2)"); entity.Property(e => e.TotalPrice).HasColumnType("decimal(18,2)"); entity.HasOne(e => e.Order) .WithMany(o => o.OrderItems) .HasForeignKey(e => e.OrderId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(e => e.Product) .WithMany(p => p.OrderItems) .HasForeignKey(e => e.ProductId) .OnDelete(DeleteBehavior.Restrict); }); } }`, // Services/IUserService.cs 'Services/IUserService.cs': `using {{serviceName}}.DTOs; namespace {{serviceName}}.Services; public interface IUserService { Task<UserDto?> GetByIdAsync(int id); Task<UserDto?> GetByEmailAsync(string email); Task<IEnumerable<UserDto>> GetAllAsync(); Task<UserDto> CreateAsync(CreateUserDto createUserDto); Task<UserDto?> UpdateAsync(int id, UpdateUserDto updateUserDto); Task<bool> DeleteAsync(int id); Task<bool> ExistsAsync(int id); Task<bool> EmailExistsAsync(string email); }`, // Services/UserService.cs 'Services/UserService.cs': `using Microsoft.EntityFrameworkCore; using AutoMapper; using {{serviceName}}.Data; using {{serviceName}}.Models; using {{serviceName}}.DTOs; using BCrypt.Net; namespace {{serviceName}}.Services; public class UserService : IUserService { private readonly ApplicationDbContext _context; private readonly IMapper _mapper; private readonly ILogger<UserService> _logger; public UserService(ApplicationDbContext context, IMapper mapper, ILogger<UserService> logger) { _context = context; _mapper = mapper; _logger = logger; } public async Task<UserDto?> GetByIdAsync(int id) { _logger.LogInformation("Getting user by ID: {UserId}", id); var user = await _context.Users .AsNoTracking() .FirstOrDefaultAsync(u => u.Id == id); return user != null ? _mapper.Map<UserDto>(user) : null; } public async Task<UserDto?> GetByEmailAsync(string email) { _logger.LogInformation("Getting user by email: {Email}", email); var user = await _context.Users .AsNoTracking() .FirstOrDefaultAsync(u => u.Email == email); return user != null ? _mapper.Map<UserDto>(user) : null; } public async Task<IEnumerable<UserDto>> GetAllAsync() { _logger.LogInformation("Getting all users"); var users = await _context.Users .AsNoTracking() .OrderBy(u => u.Name) .ToListAsync(); return _mapper.Map<IEnumerable<UserDto>>(users); } public async Task<UserDto> CreateAsync(CreateUserDto createUserDto) { _logger.LogInformation("Creating new user with email: {Email}", createUserDto.Email); if (await EmailExistsAsync(createUserDto.Email)) { throw new InvalidOperationException($"User with email {createUserDto.Email} already exists"); } var user = _mapper.Map<User>(createUserDto); user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(createUserDto.Password); _context.Users.Add(user); await _context.SaveChangesAsync(); _logger.LogInformation("User created successfully with ID: {UserId}", user.Id); return _mapper.Map<UserDto>(user); } public async Task<UserDto?> UpdateAsync(int id, UpdateUserDto updateUserDto) { _logger.LogInformation("Updating user with ID: {UserId}", id); var user = await _context.Users.FindAsync(id); if (user == null) { _logger.LogWarning("User not found with ID: {UserId}", id); return null; } if (!string.IsNullOrEmpty(updateUserDto.Email) && updateUserDto.Email != user.Email && await EmailExistsAsync(updateUserDto.Email)) { throw new InvalidOperationException($"User with email {updateUserDto.Email} already exists"); } _mapper.Map(updateUserDto, user); user.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); _logger.LogInformation("User updated successfully with ID: {UserId}", id); return _mapper.Map<UserDto>(user); } public async Task<bool> DeleteAsync(int id) { _logger.LogInformation("Deleting user with ID: {UserId}", id); var user = await _context.Users.FindAsync(id); if (user == null) { _logger.LogWarning("User not found with ID: {UserId}", id); return false; } _context.Users.Remove(user); await _context.SaveChangesAsync(); _logger.LogInformation("User deleted successfully with ID: {UserId}", id); return true; } public async Task<bool> ExistsAsync(int id) { return await _context.Users.AnyAsync(u => u.Id == id); } public async Task<bool> EmailExistsAsync(string email) { return await _context.Users.AnyAsync(u => u.Email == email); } }`, // Services/IProductService.cs 'Services/IProductService.cs': `using {{serviceName}}.DTOs; namespace {{serviceName}}.Services; public interface IProductService { Task<ProductDto?> GetByIdAsync(int id); Task<IEnumerable<ProductDto>> GetAllAsync(); Task<IEnumerable<ProductDto>> GetByCategoryAsync(string category); Task<ProductDto> CreateAsync(CreateProductDto createProductDto); Task<ProductDto?> UpdateAsync(int id, UpdateProductDto updateProductDto); Task<bool> DeleteAsync(int id); Task<bool> ExistsAsync(int id); }`, // Services/ProductService.cs 'Services/ProductService.cs': `using Microsoft.EntityFrameworkCore; using AutoMapper; using {{serviceName}}.Data; using {{serviceName}}.Models; using {{serviceName}}.DTOs; namespace {{serviceName}}.Services; public class ProductService : IProductService { private readonly ApplicationDbContext _context; private readonly IMapper _mapper; private readonly ILogger<ProductService> _logger; public ProductService(ApplicationDbContext context, IMapper mapper, ILogger<ProductService> logger) { _context = context; _mapper = mapper; _logger = logger; } public async Task<ProductDto?> GetByIdAsync(int id) { var product = await _context.Products .AsNoTracking() .FirstOrDefaultAsync(p => p.Id == id); return product != null ? _mapper.Map<ProductDto>(product) : null; } public async Task<IEnumerable<ProductDto>> GetAllAsync() { var products = await _context.Products .AsNoTracking() .OrderBy(p => p.Name) .ToListAsync(); return _mapper.Map<IEnumerable<ProductDto>>(products); } public async Task<IEnumerable<ProductDto>> GetByCategoryAsync(string category) { var products = await _context.Products .AsNoTracking() .Where(p => p.Category == category) .OrderBy(p => p.Name) .ToListAsync(); return _mapper.Map<IEnumerable<ProductDto>>(products); } public async Task<ProductDto> CreateAsync(CreateProductDto createProductDto) { var product = _mapper.Map<Product>(createProductDto); _context.Products.Add(product); await _context.SaveChangesAsync(); return _mapper.Map<ProductDto>(product); } public async Task<ProductDto?> UpdateAsync(int id, UpdateProductDto updateProductDto) { var product = await _context.Products.FindAsync(id); if (product == null) { return null; } _mapper.Map(updateProductDto, product); product.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); return _mapper.Map<ProductDto>(product); } public async Task<bool> DeleteAsync(int id) { var product = await _context.Products.FindAsync(id); if (product == null) { return false; } _context.Products.Remove(product); await _context.SaveChangesAsync(); return true; } public async Task<bool> ExistsAsync(int id) { return await _context.Products.AnyAsync(p => p.Id == id); } }`, // Services/IOrderService.cs (stub) 'Services/IOrderService.cs': `namespace {{serviceName}}.Services; public interface IOrderService { // Add order service methods here }`, // Services/OrderService.cs (stub) 'Services/OrderService.cs': `namespace {{serviceName}}.Services; public class OrderService : IOrderService { // Add order service implementation here }`, // Services/IAuthService.cs (stub) 'Services/IAuthService.cs': `namespace {{serviceName}}.Services; public interface IAuthService { Task<string> AuthenticateAsync(string email, string password); }`, // Services/AuthService.cs (stub) 'Services/AuthService.cs': `namespace {{serviceName}}.Services; public class AuthService : IAuthService { public Task<string> AuthenticateAsync(string email, string password) { // Add authentication logic here throw new NotImplementedException(); } }`, // Controllers/UsersController.cs 'Controllers/UsersController.cs': `using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using {{serviceName}}.Services; using {{serviceName}}.DTOs; namespace {{serviceName}}.Controllers; [ApiController] [Route("api/[controller]")] public class UsersController : ControllerBase { private readonly IUserService _userService; private readonly ILogger<UsersController> _logger; public UsersController(IUserService userService, ILogger<UsersController> logger) { _userService = userService; _logger = logger; } [HttpGet] public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers() { try { var users = await _userService.GetAllAsync(); return Ok(users); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while getting users"); return StatusCode(500, "An error occurred while processing your request"); } } [HttpGet("{id}")] public async Task<ActionResult<UserDto>> GetUser(int id) { try { var user = await _userService.GetByIdAsync(id); if (user == null) { return NotFound($"User with ID {id} not found"); } return Ok(user); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while getting user {UserId}", id); return StatusCode(500, "An error occurred while processing your request"); } } [HttpPost] public async Task<ActionResult<UserDto>> CreateUser([FromBody] CreateUserDto createUserDto) { try { var user = await _userService.CreateAsync(createUserDto); return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while creating user"); return StatusCode(500, "An error occurred while processing your request"); } } [HttpPut("{id}")] public async Task<ActionResult<UserDto>> UpdateUser(int id, [FromBody] UpdateUserDto updateUserDto) { try { var user = await _userService.UpdateAsync(id, updateUserDto); if (user == null) { return NotFound($"User with ID {id} not found"); } return Ok(user); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while updating user {UserId}", id); return StatusCode(500, "An error occurred while processing your request"); } } [HttpDelete("{id}")] public async Task<IActionResult> DeleteUser(int id) { try { var result = await _userService.DeleteAsync(id); if (!result) { return NotFound($"User with ID {id} not found"); } return NoContent(); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while deleting user {UserId}", id); return StatusCode(500, "An error occurred while processing your request"); } } }`, // Controllers/ProductsController.cs 'Controllers/ProductsController.cs': `using Microsoft.AspNetCore.Mvc; using {{serviceName}}.Services; using {{serviceName}}.DTOs; namespace {{serviceName}}.Controllers; [ApiController] [Route("api/[controller]")] public class ProductsController : ControllerBase { private readonly IProductService _productService; private readonly ILogger<ProductsController> _logger; public ProductsController(IProductService productService, ILogger<ProductsController> logger) { _productService = productService; _logger = logger; } [HttpGet] public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts() { try { var products = await _productService.GetAllAsync(); return Ok(products); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while getting products"); return StatusCode(500, "An error occurred while processing your request"); } } [HttpGet("category/{category}")] public async Task<ActionResult<IEnumerable<ProductDto>>> GetProductsByCategory(string category) { try { var products = await _productService.GetByCategoryAsync(category); return Ok(products); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while getting products by category {Category}", category); return StatusCode(500, "An error occurred while processing your request"); } } [HttpGet("{id}")] public async Task<ActionResult<ProductDto>> GetProduct(int id) { try { var product = await _productService.GetByIdAsync(id); if (product == null) { return NotFound($"Product with ID {id} not found"); } return Ok(product); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while getting product {ProductId}", id); return StatusCode(500, "An error occurred while processing your request"); } } [HttpPost] public async Task<ActionResult<ProductDto>> CreateProduct([FromBody] CreateProductDto createProductDto) { try { var product = await _productService.CreateAsync(createProductDto); return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while creating product"); return StatusCode(500, "An error occurred while processing your request"); } } [HttpPut("{id}")] public async Task<ActionResult<ProductDto>> UpdateProduct(int id, [FromBody] UpdateProductDto updateProductDto) { try { var product = await _productService.UpdateAsync(id, updateProductDto); if (product == null) { return NotFound($"Product with ID {id} not found"); } return Ok(product); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while updating product {ProductId}", id); return StatusCode(500, "An error occurred while processing your request"); } } [HttpDelete("{id}")] public async Task<IActionResult> DeleteProduct(int id) { try { var result = await _productService.DeleteAsync(id); if (!result) { return NotFound($"Product with ID {id} not found"); } return NoContent(); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while deleting product {ProductId}", id); return StatusCode(500, "An error occurred while processing your request"); } } }`, // Tests/TestFixtures/DatabaseFixture.cs 'Tests/TestFixtures/DatabaseFixture.cs': `using Microsoft.EntityFrameworkCore; using {{serviceName}}.Data; namespace {{serviceName}}.Tests.TestFixtures; public class DatabaseFixture : IDisposable { public ApplicationDbContext Context { get; private set; } public DatabaseFixture() { var options = new DbContextOptionsBuilder<ApplicationDbContext>() .UseInMemoryDatabase($"TestDb_{Guid.NewGuid()}") .Options; Context = new ApplicationDbContext(options); Context.Database.EnsureCreated(); } public void Dispose() { Context.Dispose(); } }`, // Tests/TestFixtures/TestDataGenerator.cs 'Tests/TestFixtures/TestDataGenerator.cs': `using Bogus; using {{serviceName}}.Models; using {{serviceName}}.DTOs; namespace {{serviceName}}.Tests.TestFixtures; public static class TestDataGenerator { public static Faker<User> UserFaker => new Faker<User>() .RuleFor(u => u.Id, f => f.Random.Int(1, 1000)) .RuleFor(u => u.Name, f => f.Person.FullName) .RuleFor(u => u.Email, f => f.Person.Email) .RuleFor(u => u.PasswordHash, f => BCrypt.Net.BCrypt.HashPassword("TestPassword123")) .RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber()) .RuleFor(u => u.DateOfBirth, f => f.Person.DateOfBirth.AddYears(-f.Random.Int(18, 65))) .RuleFor(u => u.Address, f => f.Address.FullAddress()) .RuleFor(u => u.City, f => f.Address.City()) .RuleFor(u => u.Country, f => f.Address.Country()) .RuleFor(u => u.IsActive, f => f.Random.Bool(0.9f)) .RuleFor(u => u.CreatedAt, f => f.Date.Past()) .RuleFor(u => u.UpdatedAt, f => f.Date.Recent()); public static Faker<CreateUserDto> CreateUserDtoFaker => new Faker<CreateUserDto>() .RuleFor(u => u.Name, f => f.Person.FullName) .RuleFor(u => u.Email, f => f.Person.Email) .RuleFor(u => u.Password, f => "TestPassword123") .RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber()) .RuleFor(u => u.DateOfBirth, f => f.Person.DateOfBirth.AddYears(-f.Random.Int(18, 65))) .RuleFor(u => u.Address, f => f.Address.FullAddress()) .RuleFor(u => u.City, f => f.Address.City()) .RuleFor(u => u.Country, f => f.Address.Country()); public static Faker<UpdateUserDto> UpdateUserDtoFaker => new Faker<UpdateUserDto>() .RuleFor(u => u.Name, f => f.Person.FullName) .RuleFor(u => u.Email, f => f.Person.Email) .RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber()) .RuleFor(u => u.DateOfBirth, f => f.Person.DateOfBirth.AddYears(-f.Random.Int(18, 65))) .RuleFor(u => u.Address, f => f.Address.FullAddress()) .RuleFor(u => u.City, f => f.Address.City()) .RuleFor(u => u.Country, f => f.Address.Country()) .RuleFor(u => u.IsActive, f => f.Random.Bool(0.9f)); public static Faker<Product> ProductFaker => new Faker<Product>() .RuleFor(p => p.Id, f => f.Random.Int(1, 1000)) .RuleFor(p => p.Name, f => f.Commerce.ProductName()) .RuleFor(p => p.Description, f => f.Commerce.ProductDescription()) .RuleFor(p => p.Price, f => f.Random.Decimal(1, 1000)) .RuleFor(p => p.Category, f => f.Commerce.Categories(1)[0]) .RuleFor(p => p.Brand, f => f.Company.CompanyName()) .RuleFor(p => p.StockQuantity, f => f.Random.Int(0, 100)) .RuleFor(p => p.ImageUrl, f => f.Image.PicsumUrl()) .RuleFor(p => p.IsActive, f => f.Random.Bool(0.9f)) .RuleFor(p => p.CreatedAt, f => f.Date.Past()) .RuleFor(p => p.UpdatedAt, f => f.Date.Recent()); public static Faker<CreateProductDto> CreateProductDtoFaker => new Faker<CreateProductDto>() .RuleFor(p => p.Name, f => f.Commerce.ProductName()) .RuleFor(p => p.Description, f => f.Commerce.ProductDescription()) .RuleFor(p => p.Price, f => f.Random.Decimal(1, 1000)) .RuleFor(p => p.Category, f => f.Commerce.Categories(1)[0]) .RuleFor(p => p.Brand, f => f.Company.CompanyName()) .RuleFor(p => p.StockQuantity, f => f.Random.Int(0, 100)) .RuleFor(p => p.ImageUrl, f => f.Image.PicsumUrl()); public static Faker<UpdateProductDto> UpdateProductDtoFaker => new Faker<UpdateProductDto>() .RuleFor(p => p.Name, f => f.Commerce.ProductName()) .RuleFor(p => p.Description, f => f.Commerce.ProductDescription()) .RuleFor(p => p.Price, f => f.Random.Decimal(1, 1000)) .RuleFor(p => p.Category, f => f.Commerce.Categories(1)[0]) .RuleFor(p => p.Brand, f => f.Company.CompanyName()) .RuleFor(p => p.StockQuantity, f => f.Random.Int(0, 100)) .RuleFor(p => p.ImageUrl, f => f.Image.PicsumUrl()) .RuleFor(p => p.IsActive, f => f.Random.Bool(0.9f)); }`, // Tests/Services/UserServiceTests.cs 'Tests/Services/UserServiceTests.cs': `using Xunit; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Moq; using AutoMapper; using {{serviceName}}.Services; using {{serviceName}}.Data; using {{serviceName}}.Models; using {{serviceName}}.DTOs; using {{serviceName}}.Profiles; using {{serviceName}}.Tests.TestFixtures; namespace {{serviceName}}.Tests.Services; public class UserServiceTests : IClassFixture<DatabaseFixture> { private readonly DatabaseFixture _fixture; private readonly Mock<ILogger<UserService>> _loggerMock; private readonly IMapper _mapper; private readonly UserService _userService; public UserServiceTests(DatabaseFixture fixture) { _fixture = fixture; _loggerMock = new Mock<ILogger<UserService>>(); var config = new MapperConfiguration(cfg => cfg.AddProfile<UserProfile>()); _mapper = config.CreateMapper(); _userService = new UserService(_fixture.Context, _mapper, _loggerMock.Object); } [Fact] public async Task GetByIdAsync_WithValidId_ShouldReturnUserDto() { // Arrange var user = TestDataGenerator.UserFaker.Generate(); _fixture.Context.Users.Add(user); await _fixture.Context.SaveChangesAsync(); // Act var result = await _userService.GetByIdAsync(user.Id); // Assert result.Should().NotBeNull(); result!.Id.Should().Be(user.Id); result.Name.Should().Be(user.Name); result.Email.Should().Be(user.Email); } [Fact] public async Task GetByIdAsync_WithInvalidId_ShouldReturnNull() { // Arrange var invalidId = 99999; // Act var result = await _userService.GetByIdAsync(invalidId); // Assert result.Should().BeNull(); } [Fact] public async Task GetByEmailAsync_WithValidEmail_ShouldReturnUserDto() { // Arrange var user = TestDataGenerator.UserFaker.Generate(); _fixture.Context.Users.Add(user); await _fixture.Context.SaveChangesAsync(); // Act var result = await _userService.GetByEmailAsync(user.Email); // Assert result.Should().NotBeNull(); result!.Email.Should().Be(user.Email); result.Name.Should().Be(user.Name); } [Fact] public async Task GetByEmailAsync_WithInvalidEmail_ShouldReturnNull() { // Arrange var invalidEmail = "nonexistent@example.com"; // Act var result = await _userService.GetByEmailAsync(invalidEmail); // Assert result.Should().BeNull(); } [Fact] public async Task GetAllAsync_ShouldReturnAllUsers() { // Arrange var users = TestDataGenerator.UserFaker.Generate(3); _fixture.Context.Users.AddRange(users); await _fixture.Context.SaveChangesAsync(); // Act var result = await _userService.GetAllAsync(); // Assert result.Should().NotBeEmpty(); result.Should().HaveCountGreaterOrEqualTo(3); } [Fact] public async Task CreateAsync_WithValidData_ShouldCreateUser() { // Arrange var createUserDto = TestDataGenerator.CreateUserDtoFaker.Generate(); // Act var result = await _userService.CreateAsync(createUserDto); // Assert result.Should().NotBeNull(); result.Name.Should().Be(createUserDto.Name); result.Email.Should().Be(createUserDto.Email); result.Id.Should().BeGreaterThan(0); // Verify user exists in database var userInDb = await _fixture.Context.Users.FindAsync(result.Id); userInDb.Should().NotBeNull(); userInDb!.PasswordHash.Should().NotBeEmpty(); BCrypt.Net.BCrypt.Verify(createUserDto.Password, userInDb.PasswordHash).Should().BeTrue(); } [Fact] public async Task CreateAsync_WithExistingEmail_ShouldThrowException() { // Arrange var existingUser = TestDataGenerator.UserFaker.Generate(); _fixture.Context.Users.Add(existingUser); await _fixture.Context.SaveChangesAsync(); var createUserDto = TestDataGenerator.CreateUserDtoFaker.Generate(); createUserDto.Email = existingUser.Email; // Act & Assert await _userService.Invoking(s => s.CreateAsync(createUserDto)) .Should().ThrowAsync<InvalidOperationException>() .WithMessage($"User with email {existingUser.Email} already exists"); } [Fact] public async Task UpdateAsync_WithValidData_ShouldUpdateUser() { // Arrange var user = TestDataGenerator.UserFaker.Generate(); _fixture.Context.Users.Add(user); await _fixture.Context.SaveChangesAsync(); var updateUserDto = TestDataGenerator.UpdateUserDtoFaker.Generate(); // Act var result = await _userService.Update