test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
using FluentAssertions;
|
|
|
|
|
using ProposalSystem.Application.DTOs;
|
|
|
|
|
using ProposalSystem.Application.Validators;
|
|
|
|
|
using ProposalSystem.Domain.Entities;
|
|
|
|
|
using Xunit;
|
|
|
|
|
|
|
|
|
|
namespace ProposalSystem.Tests.Validators;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Tests for CreateProposalValidator — validates request payloads before
|
|
|
|
|
/// they hit the service layer.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class CreateProposalValidatorTests
|
|
|
|
|
{
|
|
|
|
|
private readonly CreateProposalValidator _sut = new();
|
|
|
|
|
|
|
|
|
|
[Fact(DisplayName = "Valid proposal request passes validation")]
|
|
|
|
|
public void ValidRequest_Passes()
|
|
|
|
|
{
|
|
|
|
|
var request = new CreateProposalRequest(
|
|
|
|
|
WorkOrderNumber: "WO-001",
|
2026-05-27 17:36:36 -04:00
|
|
|
PoNumber: null,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
CustomerName: "Acme Corp",
|
|
|
|
|
CustomerAddress: "123 Main St, Suite 100",
|
|
|
|
|
ScopeOfWork: "Replace HVAC system in building B",
|
|
|
|
|
ServiceCategory: ServiceCategory.HVAC,
|
|
|
|
|
Priority: Priority.Standard,
|
|
|
|
|
Notes: "Initial assessment complete"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeTrue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory(DisplayName = "Empty required fields fail validation")]
|
|
|
|
|
[InlineData("", "Customer", "Address", "Scope")]
|
|
|
|
|
[InlineData("WO-001", "", "Address", "Scope")]
|
|
|
|
|
[InlineData("WO-001", "Customer", "", "Scope")]
|
|
|
|
|
[InlineData("WO-001", "Customer", "Address", "")]
|
|
|
|
|
public void EmptyRequiredFields_Fail(string wo, string name, string address, string scope)
|
|
|
|
|
{
|
2026-05-27 17:36:36 -04:00
|
|
|
var request = new CreateProposalRequest(wo, null, name, address, scope,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
ServiceCategory.General, Priority.Standard, null);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact(DisplayName = "WorkOrderNumber exceeding 50 chars fails")]
|
|
|
|
|
public void WorkOrderNumber_TooLong_Fails()
|
|
|
|
|
{
|
|
|
|
|
var request = new CreateProposalRequest(
|
|
|
|
|
WorkOrderNumber: new string('X', 51),
|
2026-05-27 17:36:36 -04:00
|
|
|
PoNumber: null,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
CustomerName: "Customer",
|
|
|
|
|
CustomerAddress: "Address",
|
|
|
|
|
ScopeOfWork: "Scope",
|
|
|
|
|
ServiceCategory: ServiceCategory.General,
|
|
|
|
|
Priority: Priority.Standard,
|
|
|
|
|
Notes: null
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
|
|
|
result.Errors.Should().Contain(e => e.PropertyName == "WorkOrderNumber");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact(DisplayName = "CustomerName exceeding 200 chars fails")]
|
|
|
|
|
public void CustomerName_TooLong_Fails()
|
|
|
|
|
{
|
|
|
|
|
var request = new CreateProposalRequest(
|
|
|
|
|
WorkOrderNumber: "WO-001",
|
2026-05-27 17:36:36 -04:00
|
|
|
PoNumber: null,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
CustomerName: new string('X', 201),
|
|
|
|
|
CustomerAddress: "Address",
|
|
|
|
|
ScopeOfWork: "Scope",
|
|
|
|
|
ServiceCategory: ServiceCategory.General,
|
|
|
|
|
Priority: Priority.Standard,
|
|
|
|
|
Notes: null
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
|
|
|
result.Errors.Should().Contain(e => e.PropertyName == "CustomerName");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact(DisplayName = "ScopeOfWork exceeding 10000 chars fails")]
|
|
|
|
|
public void ScopeOfWork_TooLong_Fails()
|
|
|
|
|
{
|
|
|
|
|
var request = new CreateProposalRequest(
|
|
|
|
|
WorkOrderNumber: "WO-001",
|
2026-05-27 17:36:36 -04:00
|
|
|
PoNumber: null,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
CustomerName: "Customer",
|
|
|
|
|
CustomerAddress: "Address",
|
|
|
|
|
ScopeOfWork: new string('X', 10001),
|
|
|
|
|
ServiceCategory: ServiceCategory.General,
|
|
|
|
|
Priority: Priority.Standard,
|
|
|
|
|
Notes: null
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
|
|
|
result.Errors.Should().Contain(e => e.PropertyName == "ScopeOfWork");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact(DisplayName = "Notes exceeding 5000 chars fails")]
|
|
|
|
|
public void Notes_TooLong_Fails()
|
|
|
|
|
{
|
|
|
|
|
var request = new CreateProposalRequest(
|
|
|
|
|
WorkOrderNumber: "WO-001",
|
2026-05-27 17:36:36 -04:00
|
|
|
PoNumber: null,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
CustomerName: "Customer",
|
|
|
|
|
CustomerAddress: "Address",
|
|
|
|
|
ScopeOfWork: "Scope",
|
|
|
|
|
ServiceCategory: ServiceCategory.General,
|
|
|
|
|
Priority: Priority.Standard,
|
|
|
|
|
Notes: new string('X', 5001)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
|
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Notes");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact(DisplayName = "Null notes are valid")]
|
|
|
|
|
public void NullNotes_Passes()
|
|
|
|
|
{
|
|
|
|
|
var request = new CreateProposalRequest(
|
|
|
|
|
WorkOrderNumber: "WO-001",
|
2026-05-27 17:36:36 -04:00
|
|
|
PoNumber: null,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
CustomerName: "Customer",
|
|
|
|
|
CustomerAddress: "Address",
|
|
|
|
|
ScopeOfWork: "Scope",
|
|
|
|
|
ServiceCategory: ServiceCategory.General,
|
|
|
|
|
Priority: Priority.Standard,
|
|
|
|
|
Notes: null
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeTrue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact(DisplayName = "Invalid ServiceCategory enum value fails")]
|
|
|
|
|
public void InvalidServiceCategory_Fails()
|
|
|
|
|
{
|
|
|
|
|
var request = new CreateProposalRequest(
|
|
|
|
|
WorkOrderNumber: "WO-001",
|
2026-05-27 17:36:36 -04:00
|
|
|
PoNumber: null,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
CustomerName: "Customer",
|
|
|
|
|
CustomerAddress: "Address",
|
|
|
|
|
ScopeOfWork: "Scope",
|
|
|
|
|
ServiceCategory: (ServiceCategory)999,
|
|
|
|
|
Priority: Priority.Standard,
|
|
|
|
|
Notes: null
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
|
|
|
result.Errors.Should().Contain(e => e.PropertyName == "ServiceCategory");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact(DisplayName = "Invalid Priority enum value fails")]
|
|
|
|
|
public void InvalidPriority_Fails()
|
|
|
|
|
{
|
|
|
|
|
var request = new CreateProposalRequest(
|
|
|
|
|
WorkOrderNumber: "WO-001",
|
2026-05-27 17:36:36 -04:00
|
|
|
PoNumber: null,
|
test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests
QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification
QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement
QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)
QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling
QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers
Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 17:31:18 -04:00
|
|
|
CustomerName: "Customer",
|
|
|
|
|
CustomerAddress: "Address",
|
|
|
|
|
ScopeOfWork: "Scope",
|
|
|
|
|
ServiceCategory: ServiceCategory.General,
|
|
|
|
|
Priority: (Priority)999,
|
|
|
|
|
Notes: null
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _sut.Validate(request);
|
|
|
|
|
|
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
|
|
|
result.Errors.Should().Contain(e => e.PropertyName == "Priority");
|
|
|
|
|
}
|
|
|
|
|
}
|