mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-09-30 21:43:14 +00:00
240 lines
7.1 KiB
C#
240 lines
7.1 KiB
C#
|
|
using FluentAssertions;
|
||
|
|
using ProposalSystem.Application.DTOs;
|
||
|
|
using ProposalSystem.Application.Validators;
|
||
|
|
using ProposalSystem.Domain.Entities;
|
||
|
|
using Xunit;
|
||
|
|
|
||
|
|
namespace ProposalSystem.Tests.Validators;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// PR5: Tests for CreatePricingLibraryItemValidator.
|
||
|
|
/// </summary>
|
||
|
|
public class CreatePricingLibraryItemValidatorTests
|
||
|
|
{
|
||
|
|
private readonly CreatePricingLibraryItemValidator _sut = new();
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Valid pricing library item passes validation")]
|
||
|
|
public void ValidRequest_Passes()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "HVAC Filter Replacement",
|
||
|
|
Description: "Standard 20x25x1 MERV-13 filter replacement",
|
||
|
|
ServiceCategory: ServiceCategory.HVAC,
|
||
|
|
Unit: "ea",
|
||
|
|
UnitPrice: 45.00m,
|
||
|
|
Keywords: "filter HVAC maintenance preventive",
|
||
|
|
Source: "Manual"
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeTrue();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Empty title fails validation")]
|
||
|
|
public void EmptyTitle_Fails()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.General,
|
||
|
|
Unit: null,
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeFalse();
|
||
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Title");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Title exceeding 200 chars fails")]
|
||
|
|
public void Title_TooLong_Fails()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: new string('X', 201),
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.General,
|
||
|
|
Unit: null,
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeFalse();
|
||
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Title");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Description exceeding 5000 chars fails")]
|
||
|
|
public void Description_TooLong_Fails()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Valid Title",
|
||
|
|
Description: new string('X', 5001),
|
||
|
|
ServiceCategory: ServiceCategory.General,
|
||
|
|
Unit: null,
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeFalse();
|
||
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Description");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Negative UnitPrice fails")]
|
||
|
|
public void NegativeUnitPrice_Fails()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Valid Title",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.Plumbing,
|
||
|
|
Unit: "hr",
|
||
|
|
UnitPrice: -10.00m,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeFalse();
|
||
|
|
result.Errors.Should().Contain(e => e.PropertyName == "UnitPrice");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Zero UnitPrice is valid")]
|
||
|
|
public void ZeroUnitPrice_Passes()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Complimentary Inspection",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.General,
|
||
|
|
Unit: "ea",
|
||
|
|
UnitPrice: 0m,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeTrue();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Null UnitPrice is valid")]
|
||
|
|
public void NullUnitPrice_Passes()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Variable Price Item",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.General,
|
||
|
|
Unit: null,
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeTrue();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Keywords exceeding 2000 chars fails")]
|
||
|
|
public void Keywords_TooLong_Fails()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Valid Title",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.General,
|
||
|
|
Unit: null,
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: new string('X', 2001),
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeFalse();
|
||
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Keywords");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Invalid ServiceCategory enum value fails")]
|
||
|
|
public void InvalidServiceCategory_Fails()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Valid Title",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: (ServiceCategory)999,
|
||
|
|
Unit: null,
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeFalse();
|
||
|
|
result.Errors.Should().Contain(e => e.PropertyName == "ServiceCategory");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Unit exceeding 50 chars fails")]
|
||
|
|
public void Unit_TooLong_Fails()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Valid Title",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.General,
|
||
|
|
Unit: new string('X', 51),
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeFalse();
|
||
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Unit");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Source exceeding 100 chars fails")]
|
||
|
|
public void Source_TooLong_Fails()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Valid Title",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.General,
|
||
|
|
Unit: null,
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: null,
|
||
|
|
Source: new string('X', 101)
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeFalse();
|
||
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Source");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact(DisplayName = "PR5: Minimal valid request (title + category only) passes")]
|
||
|
|
public void MinimalValidRequest_Passes()
|
||
|
|
{
|
||
|
|
var request = new CreatePricingLibraryItemRequest(
|
||
|
|
Title: "Basic Item",
|
||
|
|
Description: null,
|
||
|
|
ServiceCategory: ServiceCategory.Other,
|
||
|
|
Unit: null,
|
||
|
|
UnitPrice: null,
|
||
|
|
Keywords: null,
|
||
|
|
Source: null
|
||
|
|
);
|
||
|
|
|
||
|
|
var result = _sut.Validate(request);
|
||
|
|
|
||
|
|
result.IsValid.Should().BeTrue();
|
||
|
|
}
|
||
|
|
}
|