mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-09-30 20:33:14 +00:00
Some checks are pending
Deploy / Deploy to AWS (push) Waiting to run
Adds a managed pricing library so admins can seed/curate reference priced items
directly, instead of the corpus being populated only by ingesting Sent proposals.
v1 PR5.
API:
- PricingLibraryItem entity + migration; /api/pricing-library CRUD (admin), with
GET {id} reachable by internal Lambda callers (admins role via internal key).
- Create/update publish an ADDITIVE library-ingest SQS job {pricingLibraryItemId},
wrapped so a publish failure never rolls back the save.
Lambda (library-ingest):
- Additive event-shape branch: pricingLibraryItemId -> fetch item, format markdown,
upload to pricing-library/{category}/{id}.md, trigger KB sync. The existing
proposalId path is byte-for-byte unchanged. Explicit error when neither id present;
warns when both present.
Web:
- Pricing Library management page (/admin/pricing-library): list / create / edit / delete.
GPT-4.1 cross-review on the event-shape change: no BLOCK (neither/both-id handling
applied). Verified: api 159 tests; web tsc + 26 tests; lambdas ruff + 37 pytest.
121 lines
3.4 KiB
C#
121 lines
3.4 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 UpdatePricingLibraryItemValidator.
|
|
/// </summary>
|
|
public class UpdatePricingLibraryItemValidatorTests
|
|
{
|
|
private readonly UpdatePricingLibraryItemValidator _sut = new();
|
|
|
|
[Fact(DisplayName = "PR5: Valid update request passes")]
|
|
public void ValidRequest_Passes()
|
|
{
|
|
var request = new UpdatePricingLibraryItemRequest(
|
|
Title: "Updated Title",
|
|
Description: "Updated description",
|
|
ServiceCategory: ServiceCategory.Electrical,
|
|
Unit: "hr",
|
|
UnitPrice: 125.50m,
|
|
Keywords: "electrical wiring"
|
|
);
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
result.IsValid.Should().BeTrue();
|
|
}
|
|
|
|
[Fact(DisplayName = "PR5: All-null update request passes (no changes)")]
|
|
public void AllNullRequest_Passes()
|
|
{
|
|
var request = new UpdatePricingLibraryItemRequest(
|
|
Title: null,
|
|
Description: null,
|
|
ServiceCategory: null,
|
|
Unit: null,
|
|
UnitPrice: null,
|
|
Keywords: null
|
|
);
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
result.IsValid.Should().BeTrue();
|
|
}
|
|
|
|
[Fact(DisplayName = "PR5: Empty title when provided fails")]
|
|
public void EmptyTitle_Fails()
|
|
{
|
|
var request = new UpdatePricingLibraryItemRequest(
|
|
Title: "",
|
|
Description: null,
|
|
ServiceCategory: null,
|
|
Unit: null,
|
|
UnitPrice: null,
|
|
Keywords: 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 UpdatePricingLibraryItemRequest(
|
|
Title: new string('X', 201),
|
|
Description: null,
|
|
ServiceCategory: null,
|
|
Unit: null,
|
|
UnitPrice: null,
|
|
Keywords: null
|
|
);
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Title");
|
|
}
|
|
|
|
[Fact(DisplayName = "PR5: Negative UnitPrice fails")]
|
|
public void NegativeUnitPrice_Fails()
|
|
{
|
|
var request = new UpdatePricingLibraryItemRequest(
|
|
Title: null,
|
|
Description: null,
|
|
ServiceCategory: null,
|
|
Unit: null,
|
|
UnitPrice: -5.00m,
|
|
Keywords: null
|
|
);
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
result.Errors.Should().Contain(e => e.PropertyName == "UnitPrice");
|
|
}
|
|
|
|
[Fact(DisplayName = "PR5: Invalid ServiceCategory enum fails")]
|
|
public void InvalidServiceCategory_Fails()
|
|
{
|
|
var request = new UpdatePricingLibraryItemRequest(
|
|
Title: null,
|
|
Description: null,
|
|
ServiceCategory: (ServiceCategory)999,
|
|
Unit: null,
|
|
UnitPrice: null,
|
|
Keywords: null
|
|
);
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
result.Errors.Should().Contain(e => e.PropertyName == "ServiceCategory");
|
|
}
|
|
}
|