84 lines
5.4 KiB
C#
84 lines
5.4 KiB
C#
using System.Net;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using Microsoft.AspNetCore.TestHost;
|
|
using WxAgent.Service;
|
|
using Xunit;
|
|
|
|
namespace WxAgent.Service.Tests;
|
|
|
|
public sealed class ServiceTests
|
|
{
|
|
private sealed class Backend : IAgentBackend
|
|
{
|
|
public IReadOnlyList<AgentCapability> Capabilities =>
|
|
[
|
|
new("agent-status", true, true, true, false, false, "read", false, 30, null, []),
|
|
new("accounts-list", true, true, true, false, false, "read", false, 30, null, []),
|
|
new("sessions-list", true, true, true, false, false, "read", false, 30, null, []),
|
|
new("messages-read", true, true, true, true, false, "read", false, 30, null, []),
|
|
new("contacts-list", true, true, true, false, false, "read", false, 30, null, []),
|
|
new("listener-events", true, true, true, true, false, "read", false, 30, null, [])
|
|
];
|
|
public Task<object> StatusAsync(CancellationToken cancellationToken) => Task.FromResult<object>(new { online = true, wechatAvailable = false });
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RealRoutesAndMcpShareCredentialsAndRejectRevokedBrowserSession()
|
|
{
|
|
var directory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(directory);
|
|
var token = new string('A', 43);
|
|
var options = new ServiceOptions { CredentialFile = Path.Combine(directory, "credentials.json"), DataDirectory = directory };
|
|
void Rotate(string value) => File.WriteAllText(options.CredentialFile,
|
|
JsonSerializer.Serialize(new[] { new ServiceCredential("alice", ServiceOptions.HashToken(value), ["read"], []) }));
|
|
Rotate(token);
|
|
await using var app = ServiceHost.Build(options, new Backend(), b => b.WebHost.UseTestServer());
|
|
try
|
|
{
|
|
await app.StartAsync();
|
|
using var client = app.GetTestClient();
|
|
client.BaseAddress = new Uri("http://localhost:5088");
|
|
Assert.Equal(HttpStatusCode.Unauthorized, (await client.GetAsync("/api/v1/status")).StatusCode);
|
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
|
Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/api/v1/status")).StatusCode);
|
|
client.DefaultRequestHeaders.Add("Origin", "http://evil.invalid");
|
|
Assert.Equal(HttpStatusCode.Forbidden, (await client.GetAsync("/api/v1/status")).StatusCode);
|
|
client.DefaultRequestHeaders.Remove("Origin");
|
|
client.DefaultRequestHeaders.Host = "evil.invalid";
|
|
Assert.Equal(HttpStatusCode.Forbidden, (await client.GetAsync("/api/v1/status")).StatusCode);
|
|
client.DefaultRequestHeaders.Host = null;
|
|
client.DefaultRequestHeaders.Accept.ParseAdd("application/json");
|
|
client.DefaultRequestHeaders.Accept.ParseAdd("text/event-stream");
|
|
using var initialize = await client.PostAsJsonAsync("/mcp", new { jsonrpc = "2.0", id = 1, method = "initialize", @params = new { protocolVersion = "2025-11-25", capabilities = new { }, clientInfo = new { name = "wxagent-integration", version = "1" } } });
|
|
Assert.Equal(HttpStatusCode.OK, initialize.StatusCode);
|
|
using var list = await client.PostAsJsonAsync("/mcp", new { jsonrpc = "2.0", id = 2, method = "tools/list" });
|
|
var schema = await list.Content.ReadAsStringAsync();
|
|
Assert.Equal(HttpStatusCode.OK, list.StatusCode);
|
|
Assert.Contains("agent_status", schema);
|
|
Assert.Contains("inputSchema", schema);
|
|
using var call = await client.PostAsJsonAsync("/mcp", new { jsonrpc = "2.0", id = 3, method = "tools/call", @params = new { name = "agent_status", arguments = new { } } });
|
|
Assert.Equal(HttpStatusCode.OK, call.StatusCode);
|
|
Assert.Contains("wechatAvailable", await call.Content.ReadAsStringAsync());
|
|
client.DefaultRequestHeaders.Authorization = null;
|
|
client.DefaultRequestHeaders.Add("Origin", "http://localhost:5088");
|
|
using var login = await client.PostAsJsonAsync("/api/v1/login", new { token });
|
|
Assert.Equal(HttpStatusCode.OK, login.StatusCode);
|
|
var cookie = login.Headers.GetValues("Set-Cookie").Single().Split(';')[0];
|
|
var result = await login.Content.ReadFromJsonAsync<JsonElement>();
|
|
client.DefaultRequestHeaders.Add("Cookie", cookie);
|
|
Assert.Equal(HttpStatusCode.Forbidden, (await client.PostAsync("/api/v1/logout", null)).StatusCode);
|
|
Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/api/v1/status")).StatusCode);
|
|
Rotate(new string('B', 43));
|
|
Assert.Equal(HttpStatusCode.Unauthorized, (await client.GetAsync("/api/v1/status")).StatusCode);
|
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
|
Assert.Equal(HttpStatusCode.Unauthorized, (await client.PostAsJsonAsync("/mcp", new { jsonrpc = "2.0", id = 4, method = "tools/list" })).StatusCode);
|
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", new string('B', 43));
|
|
Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/api/v1/status")).StatusCode);
|
|
}
|
|
finally { await app.StopAsync(); Directory.Delete(directory, true); }
|
|
}
|
|
}
|