mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-31 14:43:40 +02:00
--scenario
This commit is contained in:
@@ -20,8 +20,7 @@ public static class Program
|
|||||||
Console.WriteLine("Choose an example to run:");
|
Console.WriteLine("Choose an example to run:");
|
||||||
Console.WriteLine("1. Echo Server");
|
Console.WriteLine("1. Echo Server");
|
||||||
Console.WriteLine("2. Custom Message Handler");
|
Console.WriteLine("2. Custom Message Handler");
|
||||||
Console.WriteLine("3. ...");
|
Console.WriteLine("3. Broadcast");
|
||||||
Console.WriteLine("4. Scenario/State Machine");
|
|
||||||
Console.WriteLine("5. WebSocket Proxy");
|
Console.WriteLine("5. WebSocket Proxy");
|
||||||
Console.WriteLine("6. Multiple WebSocket Endpoints");
|
Console.WriteLine("6. Multiple WebSocket Endpoints");
|
||||||
Console.WriteLine("7. All Examples (runs all endpoints)");
|
Console.WriteLine("7. All Examples (runs all endpoints)");
|
||||||
@@ -41,9 +40,6 @@ public static class Program
|
|||||||
case "3":
|
case "3":
|
||||||
await RunBroadcastExample();
|
await RunBroadcastExample();
|
||||||
break;
|
break;
|
||||||
case "4":
|
|
||||||
await RunScenarioExample();
|
|
||||||
break;
|
|
||||||
case "5":
|
case "5":
|
||||||
await RunProxyExample();
|
await RunProxyExample();
|
||||||
break;
|
break;
|
||||||
@@ -232,115 +228,6 @@ public static class Program
|
|||||||
server.Stop();
|
server.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Example 4: Scenario/State Machine
|
|
||||||
/// Demonstrates state transitions during WebSocket session
|
|
||||||
/// </summary>
|
|
||||||
private static async Task RunScenarioExample()
|
|
||||||
{
|
|
||||||
Console.WriteLine("\n=== Scenario/State Machine Example ===");
|
|
||||||
Console.WriteLine("Starting WebSocket server with scenario support...\n");
|
|
||||||
|
|
||||||
var server = WireMockServer.Start(new WireMockServerSettings
|
|
||||||
{
|
|
||||||
Port = 9091,
|
|
||||||
Logger = new WireMockConsoleLogger()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initial state: Waiting for players
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/game")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.InScenario("GameSession")
|
|
||||||
.WillSetStateTo("Lobby")
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.WithMessageHandler(async (msg, ctx) =>
|
|
||||||
{
|
|
||||||
await ctx.SendAsync("Welcome to the game lobby! Type 'ready' to start or 'quit' to leave.");
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Lobby state: Waiting for ready
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/game")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.InScenario("GameSession")
|
|
||||||
.WhenStateIs("Lobby")
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.WithMessageHandler(async (msg, ctx) =>
|
|
||||||
{
|
|
||||||
var text = msg.Text?.ToLower() ?? string.Empty;
|
|
||||||
|
|
||||||
if (text == "ready")
|
|
||||||
{
|
|
||||||
ctx.SetScenarioState("Playing");
|
|
||||||
await ctx.SendAsync("Game started! Type 'attack' to attack, 'defend' to defend, or 'quit' to exit.");
|
|
||||||
}
|
|
||||||
else if (text == "quit")
|
|
||||||
{
|
|
||||||
await ctx.SendAsync("You left the lobby. Goodbye!");
|
|
||||||
await ctx.CloseAsync(WebSocketCloseStatus.NormalClosure, "Player quit");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await ctx.SendAsync("In lobby. Type 'ready' to start or 'quit' to leave.");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Playing state: Game is active
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/game")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.InScenario("GameSession")
|
|
||||||
.WhenStateIs("Playing")
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.WithMessageHandler(async (msg, ctx) =>
|
|
||||||
{
|
|
||||||
var text = msg.Text?.ToLower() ?? string.Empty;
|
|
||||||
|
|
||||||
if (text == "attack")
|
|
||||||
{
|
|
||||||
await ctx.SendAsync("You attacked! Critical hit! 💥");
|
|
||||||
}
|
|
||||||
else if (text == "defend")
|
|
||||||
{
|
|
||||||
await ctx.SendAsync("You defended! Shield up! 🛡️");
|
|
||||||
}
|
|
||||||
else if (text == "quit")
|
|
||||||
{
|
|
||||||
ctx.SetScenarioState("GameOver");
|
|
||||||
await ctx.SendAsync("Game over! Thanks for playing.");
|
|
||||||
await ctx.CloseAsync(WebSocketCloseStatus.NormalClosure, "Game ended");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await ctx.SendAsync("Unknown action. Type 'attack', 'defend', or 'quit'.");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
Console.WriteLine($"Game server listening at: {server.Urls[0]}/ws/game");
|
|
||||||
Console.WriteLine("\nConnect and follow the game flow:");
|
|
||||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/game");
|
|
||||||
Console.WriteLine("\nGame flow: Lobby -> Type 'ready' -> Playing -> Type 'attack'/'defend' -> Type 'quit'");
|
|
||||||
Console.WriteLine("\nPress any key to stop server...");
|
|
||||||
Console.ReadKey();
|
|
||||||
server.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Example 5: WebSocket Proxy
|
/// Example 5: WebSocket Proxy
|
||||||
/// Proxies WebSocket connections to another server
|
/// Proxies WebSocket connections to another server
|
||||||
@@ -547,9 +434,6 @@ public static class Program
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Game scenario endpoint
|
|
||||||
SetupGameScenario(server);
|
|
||||||
|
|
||||||
// Time endpoint
|
// Time endpoint
|
||||||
server
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create()
|
||||||
@@ -566,45 +450,6 @@ public static class Program
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetupGameScenario(WireMockServer server)
|
|
||||||
{
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/game")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.InScenario("GameSession")
|
|
||||||
.WillSetStateTo("Lobby")
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.WithMessageHandler(async (msg, ctx) =>
|
|
||||||
{
|
|
||||||
await ctx.SendAsync("Welcome! Type 'ready' to start.");
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/game")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.InScenario("GameSession")
|
|
||||||
.WhenStateIs("Lobby")
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.WithMessageHandler(async (msg, ctx) =>
|
|
||||||
{
|
|
||||||
if (msg.Text?.ToLower() == "ready")
|
|
||||||
{
|
|
||||||
ctx.SetScenarioState("Playing");
|
|
||||||
await ctx.SendAsync("Game started!");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods for testing
|
// Helper methods for testing
|
||||||
private static async Task TestWebSocketEcho(string baseUrl)
|
private static async Task TestWebSocketEcho(string baseUrl)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -78,12 +78,6 @@ internal class WebSocketResponseProvider(WebSocketBuilder builder) : IResponsePr
|
|||||||
guidUtils
|
guidUtils
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update scenario state following the same pattern as WireMockMiddleware
|
|
||||||
if (mapping.Scenario != null)
|
|
||||||
{
|
|
||||||
wsContext.UpdateScenarioState();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to registry if broadcast is enabled
|
// Add to registry if broadcast is enabled
|
||||||
registry?.AddConnection(wsContext);
|
registry?.AddConnection(wsContext);
|
||||||
|
|
||||||
|
|||||||
@@ -101,45 +101,6 @@ public class WireMockWebSocketContext : IWebSocketContext
|
|||||||
LogWebSocketMessage(WebSocketMessageDirection.Send, WebSocketMessageType.Close, $"CloseStatus: {closeStatus}, Description: {statusDescription}", null);
|
LogWebSocketMessage(WebSocketMessageDirection.Send, WebSocketMessageType.Close, $"CloseStatus: {closeStatus}, Description: {statusDescription}", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void SetScenarioState(string nextState)
|
|
||||||
{
|
|
||||||
SetScenarioState(nextState, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void SetScenarioState(string nextState, string? description)
|
|
||||||
{
|
|
||||||
if (Mapping.Scenario == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the same logic as WireMockMiddleware
|
|
||||||
if (Options.Scenarios.TryGetValue(Mapping.Scenario, out var scenarioState))
|
|
||||||
{
|
|
||||||
// Directly set the next state (bypass counter logic for manual WebSocket state changes)
|
|
||||||
scenarioState.NextState = nextState;
|
|
||||||
scenarioState.Started = true;
|
|
||||||
scenarioState.Finished = nextState == null;
|
|
||||||
|
|
||||||
// Reset counter when manually setting state
|
|
||||||
scenarioState.Counter = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create new scenario state if it doesn't exist
|
|
||||||
Options.Scenarios.TryAdd(Mapping.Scenario, new ScenarioState
|
|
||||||
{
|
|
||||||
Name = Mapping.Scenario,
|
|
||||||
NextState = nextState,
|
|
||||||
Started = true,
|
|
||||||
Finished = nextState == null,
|
|
||||||
Counter = 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task BroadcastTextAsync(string text, CancellationToken cancellationToken = default)
|
public async Task BroadcastTextAsync(string text, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
@@ -149,40 +110,6 @@ public class WireMockWebSocketContext : IWebSocketContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update scenario state following the same pattern as WireMockMiddleware.UpdateScenarioState
|
|
||||||
/// This is called automatically when the WebSocket connection is established.
|
|
||||||
/// </summary>
|
|
||||||
internal void UpdateScenarioState()
|
|
||||||
{
|
|
||||||
if (Mapping.Scenario == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure scenario exists
|
|
||||||
if (!Options.Scenarios.TryGetValue(Mapping.Scenario, out var scenario))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Follow exact same logic as WireMockMiddleware.UpdateScenarioState
|
|
||||||
// Increase the number of times this state has been executed
|
|
||||||
scenario.Counter++;
|
|
||||||
|
|
||||||
// Only if the number of times this state is executed equals the required StateTimes,
|
|
||||||
// proceed to next state and reset the counter to 0
|
|
||||||
if (scenario.Counter == (Mapping.TimesInSameState ?? 1))
|
|
||||||
{
|
|
||||||
scenario.NextState = Mapping.NextState;
|
|
||||||
scenario.Counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Else just update Started and Finished
|
|
||||||
scenario.Started = true;
|
|
||||||
scenario.Finished = Mapping.NextState == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void LogWebSocketMessage(
|
internal void LogWebSocketMessage(
|
||||||
WebSocketMessageDirection direction,
|
WebSocketMessageDirection direction,
|
||||||
WebSocketMessageType messageType,
|
WebSocketMessageType messageType,
|
||||||
|
|||||||
@@ -50,21 +50,6 @@ public interface IWebSocketContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription);
|
Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manually set the scenario state. This bypasses the counter logic and directly sets the next state.
|
|
||||||
/// Use this for programmatic state changes during WebSocket sessions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nextState">The next state to transition to</param>
|
|
||||||
void SetScenarioState(string nextState);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manually set the scenario state with description. This bypasses the counter logic and directly sets the next state.
|
|
||||||
/// Use this for programmatic state changes during WebSocket sessions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nextState">The next state to transition to</param>
|
|
||||||
/// <param name="description">Optional description for logging</param>
|
|
||||||
void SetScenarioState(string nextState, string? description);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Broadcast text message to all connections in this mapping
|
/// Broadcast text message to all connections in this mapping
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user