openclaw-windows-node/tests/OpenClaw.Shared.Tests/WebBridgeMessageTests.cs
github-actions[bot] c7630fa008
Some checks failed
Build and Test / test (push) Has been cancelled
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled
Build and Test / build (win-arm64) (push) Has been cancelled
Build and Test / build (win-x64) (push) Has been cancelled
Build and Test / build-msix (ARM64, win-arm64) (push) Has been cancelled
Build and Test / build-msix (x64, win-x64) (push) Has been cancelled
Build and Test / build-extension (arm64) (push) Has been cancelled
Build and Test / build-extension (x64) (push) Has been cancelled
Build and Test / release (push) Has been cancelled
[Repo Assist] feat: wire WebView2 bidirectional nativeΓåöSPA bridge in WebChatWindow
Adds the WebView2 native-to-SPA bridge and hardens it with origin validation, dispatcher marshaling for native posts, closed-window guards, sanitized bridge logging, and validated payload JSON.\n\nValidated locally with build.ps1, Shared tests, and Tray tests; GitHub CI is green.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-27 20:25:28 -07:00

152 lines
5.1 KiB
C#

using Xunit;
using OpenClaw.Shared;
namespace OpenClaw.Shared.Tests;
public class WebBridgeMessageTests
{
// ── TryParse ─────────────────────────────────────────────────────────
[Fact]
public void TryParse_ValidTypeOnly_ReturnsMsgWithNullPayload()
{
var msg = WebBridgeMessage.TryParse("""{"type":"ready","payload":{}}""");
Assert.NotNull(msg);
Assert.Equal("ready", msg!.Type);
}
[Fact]
public void TryParse_ValidWithStringPayload_ReturnsMsgWithPayload()
{
var msg = WebBridgeMessage.TryParse("""{"type":"draft-text","payload":{"text":"hello"}}""");
Assert.NotNull(msg);
Assert.Equal("draft-text", msg!.Type);
Assert.Equal("""{"text":"hello"}""", msg.PayloadJson);
}
[Fact]
public void TryParse_MissingTypeField_ReturnsNull()
{
var msg = WebBridgeMessage.TryParse("""{"payload":{}}""");
Assert.Null(msg);
}
[Fact]
public void TryParse_EmptyTypeValue_ReturnsNull()
{
var msg = WebBridgeMessage.TryParse("""{"type":"","payload":{}}""");
Assert.Null(msg);
}
[Fact]
public void TryParse_NullOrEmptyInput_ReturnsNull()
{
Assert.Null(WebBridgeMessage.TryParse(null));
Assert.Null(WebBridgeMessage.TryParse(""));
Assert.Null(WebBridgeMessage.TryParse(" "));
}
[Fact]
public void TryParse_InvalidJson_ReturnsNull()
{
Assert.Null(WebBridgeMessage.TryParse("not-json"));
Assert.Null(WebBridgeMessage.TryParse("{bad json"));
}
[Fact]
public void TryParse_TypeIsNotString_ReturnsNull()
{
var msg = WebBridgeMessage.TryParse("""{"type":42,"payload":{}}""");
Assert.Null(msg);
}
[Fact]
public void TryParse_NullPayload_IgnoresPayload()
{
var msg = WebBridgeMessage.TryParse("""{"type":"voice-start","payload":null}""");
Assert.NotNull(msg);
Assert.Equal("voice-start", msg!.Type);
Assert.Null(msg.PayloadJson);
}
// ── ToJson ───────────────────────────────────────────────────────────
[Fact]
public void ToJson_NoPayload_EmitsEmptyObject()
{
var msg = new WebBridgeMessage(WebBridgeMessage.TypeRecordingStart);
var json = msg.ToJson();
Assert.Contains("\"type\":\"recording-start\"", json);
Assert.Contains("\"payload\":{}", json);
}
[Fact]
public void ToJson_WithAnonymousPayload_SerializesPayload()
{
var msg = new WebBridgeMessage(WebBridgeMessage.TypeDraftText);
var json = msg.ToJson(new { text = "hello world" });
Assert.Contains("\"type\":\"draft-text\"", json);
Assert.Contains("\"text\":\"hello world\"", json);
}
[Fact]
public void ToJson_WithStoredPayloadJson_EmbeddedVerbatim()
{
var msg = new WebBridgeMessage(WebBridgeMessage.TypeDraftText, """{"text":"hi"}""");
var json = msg.ToJson();
Assert.Contains("\"payload\":{\"text\":\"hi\"}", json);
}
[Fact]
public void Constructor_InvalidStoredPayloadJson_Throws()
{
Assert.Throws<ArgumentException>(() =>
new WebBridgeMessage(WebBridgeMessage.TypeDraftText, "{bad json"));
}
[Fact]
public void Constructor_BlankStoredPayloadJson_TreatedAsNoPayload()
{
var msg = new WebBridgeMessage(WebBridgeMessage.TypeReady, " ");
Assert.Null(msg.PayloadJson);
Assert.Contains("\"payload\":{}", msg.ToJson());
}
[Fact]
public void ToJson_PassedPayloadOverridesStoredPayloadJson()
{
var msg = new WebBridgeMessage(WebBridgeMessage.TypeDraftText, """{"text":"old"}""");
var json = msg.ToJson(new { text = "new" });
Assert.Contains("\"text\":\"new\"", json);
Assert.DoesNotContain("old", json);
}
// ── round-trip ───────────────────────────────────────────────────────
[Theory]
[InlineData(WebBridgeMessage.TypeRecordingStart)]
[InlineData(WebBridgeMessage.TypeRecordingStop)]
[InlineData(WebBridgeMessage.TypeVoiceStart)]
[InlineData(WebBridgeMessage.TypeVoiceStop)]
[InlineData(WebBridgeMessage.TypeReady)]
public void RoundTrip_WellKnownTypes_PreserveType(string type)
{
var original = new WebBridgeMessage(type);
var json = original.ToJson();
var parsed = WebBridgeMessage.TryParse(json);
Assert.NotNull(parsed);
Assert.Equal(type, parsed!.Type);
}
[Fact]
public void RoundTrip_DraftText_PreservesPayload()
{
var original = new WebBridgeMessage(WebBridgeMessage.TypeDraftText);
var json = original.ToJson(new { text = "round trip" });
var parsed = WebBridgeMessage.TryParse(json);
Assert.NotNull(parsed);
Assert.Equal(WebBridgeMessage.TypeDraftText, parsed!.Type);
Assert.Contains("round trip", parsed.PayloadJson ?? "");
}
}