Skip to content

[Prototype] Task 2/6 via Kestrel-hosted fake-API controllers (compare vs #129, #130)#131

Draft
elahmed-microsoft wants to merge 11 commits into
mainfrom
users/elahmed/mock-testserver-prototype
Draft

[Prototype] Task 2/6 via Kestrel-hosted fake-API controllers (compare vs #129, #130)#131
elahmed-microsoft wants to merge 11 commits into
mainfrom
users/elahmed/mock-testserver-prototype

Conversation

@elahmed-microsoft

@elahmed-microsoft elahmed-microsoft commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Purpose: comparison prototype (do not merge as-is)

A third implementation of Task 2/6, for side-by-side comparison with #129 (WireMock.Net) and #130 (in-memory HttpMessageHandler). This implements Anthony's suggestion: host controllers that present themselves as the Ingestion/XFUS APIs and point the client's service-URI config at them. Same 7 integration tests, all passing.

Approach: Kestrel-hosted fake-API app (first-party)

A small ASP.NET Core app with controllers mirroring the routes the client calls, hosted on a random loopback port via Kestrel; the real client is pointed at it via IngestionConfig:BaseAddress.

  • IngestionController / XfusController — present the products/... and api/v2/assets/... route spaces and delegate to per-test scenario stores.
  • IngestionScenarioStore / XfusScenarioStore — same fluent stub API as the other prototypes (StubGetProduct, StubNoDeltaUploadSuccess, retry/polling, errors); records requests for assertions.
  • MockServerTestHost — boots the fake app, points the client at it, keeps FakeAccessTokenProvider.

A note on TestServer vs Kestrel

Anthony said "TestServer + localhost+port". TestServer itself is in-memory (no real port) and needs the Microsoft.AspNetCore.TestHost package. To honor the literal "localhost+port + config" wording and keep real-socket fidelity and avoid any NuGet package, I hosted the same fake app on Kestrel using only <FrameworkReference Include="Microsoft.AspNetCore.App" /> (ships with the SDK). Swapping to in-memory TestServer is a trivial change if preferred.

Only Ingestion needs the config override; XFUS is reached via the uploadDomain returned in the fake package response.

Three-way comparison

#129 WireMock #130 HttpMessageHandler #131 this (fake-API app)
First-party No Yes Yes
New NuGet packages ~140 transitive 0 0 (FrameworkReference only)
CI feed provisioning Required (blocking #129) None None
restore --locked-mode needs feed promotion passes passes
Real socket exercised Yes No (in-memory) Yes (loopback Kestrel)
"Realistic fake service" (routes/controllers) partial No Yes
Code to maintain least (DSL provided) small most (a fake app)

Where it lands

This is the most faithful and highest-fidelity first-party option: it's a real HTTP server with real routing/controllers, exercises the client's real sockets (matching WireMock), and needs no third-party package or feed provisioning (matching #130). The cost is the most code — a small fake-API app to maintain.

Verification

  • dotnet build clean; dotnet restore --locked-mode passes with no new packages (the exact step failing on Task 2/6: Mock HTTP server infrastructure for Ingestion API and XFUS #129).
  • The same 7 integration tests pass. A mutation check (breaking the XFUS fake) confirmed the upload test fails inside the real upload state machine — i.e. requests genuinely traverse real HTTP to the fake app.

Recommendation

Pick based on priorities: #131 if you want a realistic, first-party fake service with real-socket fidelity (most code); #130 if you want the lightest first-party option (no real socket); #129 if the built-in scenario DSL is worth the third-party dependency + feed overhead.

Add the WireMock.Net package reference and lock-file entries; it provides the in-process HTTP mock server used to build the Ingestion and XFUS fixtures in later phases. The package is saved to the private feed and is publicly readable, so CI restores it without authentication.
Add IngestionMockServer, a reusable WireMock.Net-backed fake of the Partner Center Ingestion API with fluent stubs for the endpoints PackageUploader uses (GetProduct, GetBranches, CreatePackage, package processing poll, package configuration, CreateSubmission, submission poll) and configurable success/error/retry/polling scenarios. Polling and retry use WireMock stateful scenarios with self-looping terminal states.
Add XfusMockServer, a reusable WireMock.Net-backed fake of the XFUS upload service serving the three-step chunked upload (initialize, block payload PUT, continue) rooted at /api/v2/assets/. Omits directUploadParameters.sasUri so the client uses the proxy PUT path, and emits the upload status as a number to match the client serializer. Provides a no-delta success convenience plus initialize/block/continue primitives and stateful continue-progression and block-retry scenarios.
…se 3)

Add MockServerTestHost, which owns the Ingestion and XFUS WireMock fakes and composes the real IPackageUploaderService pointed at them (Ingestion via IngestionConfig:BaseAddress, XFUS via the upload domain returned in the package response), keeping FakeAccessTokenProvider. Extend IngestionMockServer.StubCreatePackage to embed the XFUS upload info, and add IntegrationTestBase.CreateMockServerHost().
Remove the superseded in-process PackageUploaderTestHost and MockHttpMessageHandler, migrate the smoke test to CreateMockServerHost() (real HTTP to the WireMock Ingestion fake), and drop the CreateHost overload. MockServerTestHost is now the single canonical integration test host.
…Task 2/6 Phase 4)

Add IngestionApiTests (success, NotFound, transient-error retry, branches), UploadFlowTests (full no-delta upload tying the Ingestion and XFUS fakes together), and PublishFlowTests (submission create + poll to Published). Enhance IngestionMockServer with submission pendingUpdateInfo (mapper NRE guard), package-config market group, StubProcessPackage, and a flights stub; add fast retry config to MockServerTestHost.
- IngestionMockServer: emit null (not an empty object) for absent uploadInfo so the client's Guid mapping doesn't throw on a null XfusId; short-circuit StubRetryThenSuccess(failures<=0) to an unconditional success instead of an unmatched 404.
- XfusMockServer: short-circuit StubBlockUploadRetryThenSuccess(failures<=0); document the single-asset-per-server assumption of StubContinueProgression.
- MockServerTestHost: dispose the WireMock servers if composition throws, to avoid leaking listeners.
- UploadFlowTests: assert a block payload PUT actually reached XFUS rather than any request.
…pp (controllers)

Replace WireMock.Net with a first-party fake-API ASP.NET Core app: IngestionController and XfusController mirror the routes the client calls and delegate to per-test scenario stores; the host boots them on a random loopback port via Kestrel and points the real client at it (real sockets, real SocketsHttpHandler). Uses only the Microsoft.AspNetCore.App shared framework (FrameworkReference) -- zero NuGet packages, so locked-mode restore needs no feed provisioning. Same 7 integration tests pass.
@elahmed-microsoft elahmed-microsoft changed the title probe [Prototype] Task 2/6 via Kestrel-hosted fake-API controllers (compare vs #129, #130) Jun 30, 2026
- MockServerTestHost: dispose the Kestrel app if composition throws after StartAsync, to avoid leaking a listener.
- FakeApiControllerBase: record binary (octet-stream) request bodies as a size marker instead of a lossy UTF-8 text read.
- IngestionScenarioStore: include marketGroupPackages in the package-configuration paged list so the already-initialized path is exercised; default StubProcessPackage state to Processed and document that it (not the poll) drives the returned state.
- UploadFlowTests: assert the package processing poll GET was issued so the polling stub is load-bearing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant