# Epic 3: NetSuite Mock Server — Design Spec

## Business Justification & Problem Statement

We cannot develop the SuiteX Orchestrator or the Sync Workers against the live NetSuite API because we will instantly exhaust our production/sandbox governance limits, blocking other business operations. Furthermore, we cannot easily force NetSuite to throw specific errors (like 429s or 503s) to test our retry logic.

## Proposed Architectural Solution

We will build a **standalone Node.js (NestJS) Mock Server** that mimics the NetSuite RESTlet endpoints exactly. For the MVP, it will only support the **Project** and **Project Task** domains.

It will be deployed as a serverless container on **Google Cloud Run** and utilize a persistent **Cloud SQL (MySQL 8.*)** database to maintain state. This ensures the mock environment remains available for the SuiteX poller without losing data during container scale-to-zero events, while providing middleware to artificially simulate NetSuite's latency and governance throttling.

## Explicit Functional Requirements

### A. The RESTlet Endpoints

Create mock endpoints for `POST /mock-netsuite/restlet/project` and `POST /mock-netsuite/restlet/project-task`. These endpoints must accept and validate the **Epic 1 Canonical JSON** payloads.

### B. State Persistence

Save incoming mocked records to a local database, automatically generating a mock NetSuite `internalId` and a `lastModifiedDate` timestamp upon creation.

### C. The Poller Endpoint

Create a mock endpoint that accepts cursor-based pagination queries (`lastModifiedDate` + `lastInternalId`) and returns mocked records, simulating NetSuite's 1,000-result search limit.

### D. The Chaos Middleware (Governance)

Implement a NestJS Interceptor that allows developers to toggle artificial constraints:

- Introduce **200ms–2000ms** of artificial latency.
- Hard-drop requests with a **429 Too Many Requests** if more than 5 concurrent connections are detected.

## Testable Acceptance Criteria (Definition of Done)

### Scenario 1: Valid Payload Acceptance (The Happy Path)

- **Given** a formatted JSON payload representing a record change (e.g., Project) that utilizes strict booleans (`true`/`false`), ISO8601 UTC formatted dates, and alphanumerically sorted multiselect arrays.
- **When** the payload is processed by the Canonical JSON Schema validator.
- **Then** the schema validation passes with zero errors, confirming the payload is safely formatted for the `events.raw` Pub/Sub topic.

### Scenario 2: Normalization Rejection (The NetSuite Quirk Trap)

- **Given** an inbound payload generated by a NetSuite User Event script that contains raw, un-normalized NetSuite data types (e.g., a checkbox as the string `"T"` or a localized date like `"11/15/2025"`).
- **When** the payload is evaluated by the Canonical JSON Schema validator.
- **Then** the schema validator explicitly throws a type/format error, rejects the payload as a "Permanent error," and routes it to the Dead Letter Queue to prevent data corruption.

### Scenario 3: Attribution Metadata Enforcement (The Infinite Loop Trap)

- **Given** a SuiteX outbound change event or a Legacy API "Shadow Event".
- **When** the event envelope is generated and evaluated by the validator.
- **Then** the schema validation strictly requires and confirms that `sourceSystem` is set to `suitex` and a valid `writeId` UUID is present, ensuring the orchestrator can track idempotency and prevent circular loops.

## List of Deliverables

| Deliverable | Description |
|-------------|-------------|
| **Cloud Run Deployment** | A containerized NestJS application deployed to Google Cloud Run, configured to scale to zero when idle to minimize costs. |
| **Cloud SQL Persistence** | A MySQL 8.* database instance provisioned on Cloud SQL, connected securely via Cloud Run VPC Serverless Access, featuring the MockRecord entity schema to guarantee durable state persistence. |
| **Mock RESTlet Endpoint (Write)** | A fully functional POST endpoint that accepts Epic 1 JSON schemas, enforces validation rules, and checks for idempotency using the `writeId`. |
| **Mock Search Endpoint (Read)** | A fully functional POST endpoint that accepts cursor queries and returns paginated, chronologically sorted results limited to 1,000 records per response. |
| **The Chaos Interceptor (Middleware)** | A configurable NestJS Interceptor/Guard that reliably injects artificial latency and generates 429 Too Many Requests responses when concurrency thresholds are breached. |
