From 5323e1977c52dabff7240805645ad7e6918d4c12 Mon Sep 17 00:00:00 2001 From: Rafal Mielowski Date: Thu, 26 Feb 2026 18:20:30 +0100 Subject: [PATCH] Add emulator handler and tests for ForwardRequest - Implement ForwardRequestHandler with MockBackendResponse store - Add ForwardRequestStore for queuing mock backend responses - Add MockBackendResponse record (StatusCode, StatusReason, Headers, Body) - Register ForwardRequestStore in GatewayContext and TestDocumentExtensions - Sequential responses via Returns() and default via ReturnsDefault() - Handler applies mock response to context (status, headers, body) - Add ForwardRequestTests with 6 test cases - Sections: Backend Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/EmulatorPolicyChecklist.md | 2 +- .../Document/TestDocumentExtensions.cs | 3 + .../Emulator/Data/ForwardRequestStore.cs | 25 +++++ .../Emulator/Data/MockBackendResponse.cs | 12 +++ .../Policies/ForwardRequestHandler.cs | 26 +++++- src/Testing/GatewayContext.cs | 1 + .../Emulator/Policies/ForwardRequestTests.cs | 93 ++++++++++++++++++- 7 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 src/Testing/Emulator/Data/ForwardRequestStore.cs create mode 100644 src/Testing/Emulator/Data/MockBackendResponse.cs diff --git a/docs/EmulatorPolicyChecklist.md b/docs/EmulatorPolicyChecklist.md index f9faf9cd..f80ba9aa 100644 --- a/docs/EmulatorPolicyChecklist.md +++ b/docs/EmulatorPolicyChecklist.md @@ -50,7 +50,7 @@ Track progress of emulator policy handler implementation. Each policy needs a ha | ⬜ | CacheStore | CacheStoreHandler.cs | Store interaction | `emulator/cache-store` | | ⬜ | Cors | CorsHandler.cs | Context mutation | `emulator/cors` | | ⬜ | EmitMetric | EmitMetricHandler.cs | No-op + callbacks | `emulator/emit-metric` | -| ⬜ | ForwardRequest | ForwardRequestHandler.cs | External service mock | `emulator/forward-request` | +| ✅ | ForwardRequest | ForwardRequestHandler.cs | External service mock | `emulator/forward-request` | | ⬜ | JsonP | JsonPHandler.cs | Context mutation | `emulator/jsonp` | | ⬜ | JsonToXml | JsonToXmlHandle.cs | No-op + callbacks | `emulator/json-to-xml` | | ⬜ | LlmEmitTokenMetric | LlmEmitTokenMetricHandler.cs | No-op + callbacks | `emulator/llm-emit-token-metric` | diff --git a/src/Testing/Document/TestDocumentExtensions.cs b/src/Testing/Document/TestDocumentExtensions.cs index 6a91eb81..464e56fa 100644 --- a/src/Testing/Document/TestDocumentExtensions.cs +++ b/src/Testing/Document/TestDocumentExtensions.cs @@ -31,4 +31,7 @@ public static ResponseExampleStore SetupResponseExampleStore(this TestDocument d public static LoggerStore SetupLoggerStore(this TestDocument document) => document.Context.LoggerStore; + + public static ForwardRequestStore SetupForwardRequest(this TestDocument document) => + document.Context.ForwardRequestStore; } \ No newline at end of file diff --git a/src/Testing/Emulator/Data/ForwardRequestStore.cs b/src/Testing/Emulator/Data/ForwardRequestStore.cs new file mode 100644 index 00000000..1523ae3b --- /dev/null +++ b/src/Testing/Emulator/Data/ForwardRequestStore.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Azure.ApiManagement.PolicyToolkit.Testing.Emulator.Data; + +public class ForwardRequestStore +{ + private readonly Queue _responses = new(); + private MockBackendResponse? _default; + + public ForwardRequestStore Returns(MockBackendResponse response) + { + _responses.Enqueue(response); + return this; + } + + public ForwardRequestStore ReturnsDefault(MockBackendResponse response) + { + _default = response; + return this; + } + + internal MockBackendResponse? GetNext() => + _responses.Count > 0 ? _responses.Dequeue() : _default; +} diff --git a/src/Testing/Emulator/Data/MockBackendResponse.cs b/src/Testing/Emulator/Data/MockBackendResponse.cs new file mode 100644 index 00000000..6734939b --- /dev/null +++ b/src/Testing/Emulator/Data/MockBackendResponse.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Azure.ApiManagement.PolicyToolkit.Testing.Emulator.Data; + +public record MockBackendResponse +{ + public int StatusCode { get; init; } = 200; + public string StatusReason { get; init; } = "OK"; + public Dictionary Headers { get; init; } = new(); + public string? Body { get; init; } +} diff --git a/src/Testing/Emulator/Policies/ForwardRequestHandler.cs b/src/Testing/Emulator/Policies/ForwardRequestHandler.cs index dbb0a455..71cbdc33 100644 --- a/src/Testing/Emulator/Policies/ForwardRequestHandler.cs +++ b/src/Testing/Emulator/Policies/ForwardRequestHandler.cs @@ -1,7 +1,8 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.Azure.ApiManagement.PolicyToolkit.Authoring; +using Microsoft.Azure.ApiManagement.PolicyToolkit.Testing.Expressions; namespace Microsoft.Azure.ApiManagement.PolicyToolkit.Testing.Emulator.Policies; @@ -12,6 +13,27 @@ internal class ForwardRequestHandler : PolicyHandlerOptionalParam + var callbackExecuted = false; + test.SetupBackend().ForwardRequest().WithCallback((context, _) => { - executedCallback = true; + callbackExecuted = true; + context.Response.StatusCode = 418; }); test.RunBackend(); - executedCallback.Should().BeTrue(); + callbackExecuted.Should().BeTrue(); + test.Context.Response.StatusCode.Should().Be(418); } } \ No newline at end of file