Commit graph

6 commits

Author SHA1 Message Date
Adam Moussa
cac4384f0d
feat(api): Phase 6 — optimistic concurrency (SHOC contract) + atomic audit staging (#226) 2026-07-14 01:18:30 -04:00
dd058f7170
build(deps): combine open Dependabot updates into one PR
Consolidates the 15 open Dependabot PRs (#195–#209) into a single branch.

Python (lambdas):
- boto3 -> >=1.43.43,<2.0 in suggestions, library-ingest, pdf-generate,
  pdf-extract, aurora-pgvector-init (#203, #205, #206, #208, #204)
- tests: pytest >=9.1.1 (#198, major), pytest-mock >=3.15.1 (#197),
  moto >=5.2.2 (#200), httpx >=0.28.1 (#195)

npm:
- infra: @types/node ^25.9.5 (#196), typescript ~7.0.2 (#199, major)
- web: vitest ^4.1.10 (#202), typescript ~7.0.2 (#207, major)
- shared/api-contracts: typescript ~7.0.2 (#201, major)

Ruby (mobile):
- bundler group: cocoapods 1.17.0, fastlane 2.237.0 + transitive (#209)

Note: TypeScript 5.7 -> 7.0.2 and pytest 8 -> 9.1.1 are major bumps;
relying on CI to validate.
2026-07-08 16:46:43 -04:00
Adam Moussa
5d84399a0d
feat: pricing library — curated priced items feed the RAG corpus (#127)
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.
2026-06-18 12:49:47 -04:00
Adam Moussa
3d050bcf8e
fix(lambdas): SigV4-sign internal API calls and bundle Lambda dependencies (#122)
Some checks are pending
Deploy / Deploy to AWS (push) Waiting to run
The .NET API Lambda Function URL uses authType=AWS_IAM, but the four workload
Lambdas (suggestions, pdf-extract, pdf-generate, library-ingest) sent unsigned
requests with only X-Internal-Api-Key -> every internal call 403s. They also
used bare fromAsset() with no pip bundling -> ImportError at cold start. Both
made the SQS->Lambda->API pipeline non-functional when deployed (v1 pre-flight).

- Add _sign_request_headers (botocore SigV4Auth, service "lambda"); serialize the
  JSON body once and send via httpx content= so the signed payload hash matches
  the bytes sent; preserve X-Internal-Api-Key for the app-layer check. Sign per
  retry attempt to avoid SigV4 timestamp expiry on slow retries.
- Add CDK pip bundling (--platform manylinux2014_aarch64 --only-binary=:all:) to
  all four Lambdas so ARM64 wheels (reportlab, Pillow, pdfplumber) ship.
- Converge _retry_request across all four (fixes possibly-undefined return in
  pdf-extract/pdf-generate).
- Add SigV4 signing regression tests.

Verified: ruff clean, infra tsc clean, aarch64 wheels resolve for all four,
23 pytest pass. GPT-4.1 cross-family review: no BLOCK (FIX + NIT applied).
2026-06-12 17:13:08 -04:00
Adam Moussa
01fe003a6d fix: wire test suites into CI, fix stale tests from Phase 1-2 fixes
- Add web-test job (vitest) and python-test job (pytest) to CI workflow
- dotnet reusable workflow already runs tests by default
- Update InternalApiKeyMiddleware tests for API-C1/API-H1 fixes:
  invalid key now returns 401 (not pass-through), valid key on
  disallowed path returns 403
- Fix suggestions test: include status field for LAM-H4 idempotency guard
- Total: 108 tests (77 .NET, 12 web, 19 Python) all passing
2026-05-27 18:18:44 -04:00
Adam Moussa
d21b1c5edb 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 18:18:44 -04:00