more test fixes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
param(
|
||||
[string]$CaptureDirectory = "..\..\src\Jibo.Cloud\dotnet\src\Jibo.Cloud.Api\bin\Debug\net10.0\captures\websocket"
|
||||
[string]$CaptureDirectory = "..\..\captures\websocket"
|
||||
)
|
||||
|
||||
$resolvedDirectory = Resolve-Path -LiteralPath $CaptureDirectory -ErrorAction Stop
|
||||
|
||||
@@ -8,6 +8,8 @@ These scripts help exercise the new .NET hosted cloud locally.
|
||||
Replays a sanitized HTTP fixture against a running local instance.
|
||||
- `Get-WebSocketCaptureSummary.ps1`
|
||||
Summarizes captured websocket telemetry events and exported live-run fixtures from the .NET cloud.
|
||||
- repo-root `captures/http/`
|
||||
Structured HTTP request/response telemetry for live robot startup comparison.
|
||||
- `Invoke-LiveJiboPrep.ps1`
|
||||
Runs a small readiness checklist before the first physical Jibo test against the .NET cloud.
|
||||
- `Import-WebSocketCaptureFixture.ps1`
|
||||
|
||||
@@ -21,12 +21,30 @@ app.Use(async (context, next) =>
|
||||
return;
|
||||
}
|
||||
|
||||
var kind = ResolveSocketKind(context.Request.Host.Host, context.Request.Path);
|
||||
var token = ResolveToken(context.Request);
|
||||
if (kind == "unknown")
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind == "api-socket" && string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((kind is "neo-hub-listen" or "neo-hub-proactive") && string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return;
|
||||
}
|
||||
|
||||
var webSocketService = context.RequestServices.GetRequiredService<JiboWebSocketService>();
|
||||
var telemetrySink = context.RequestServices.GetRequiredService<IWebSocketTelemetrySink>();
|
||||
using var socket = await context.WebSockets.AcceptWebSocketAsync();
|
||||
|
||||
var kind = ResolveSocketKind(context.Request.Host.Host, context.Request.Path);
|
||||
var token = ResolveToken(context.Request);
|
||||
var openEnvelope = new WebSocketMessageEnvelope
|
||||
{
|
||||
ConnectionId = Guid.NewGuid().ToString("N"),
|
||||
@@ -89,10 +107,11 @@ app.Use(async (context, next) =>
|
||||
|
||||
app.MapGet("/health", () => Results.Json(new { ok = true, service = "OpenJibo Cloud Api" }));
|
||||
|
||||
app.MapMethods("/{**path}", ["GET", "POST", "PUT"], async (HttpContext context, JiboCloudProtocolService service, CancellationToken cancellationToken) =>
|
||||
app.MapMethods("/{**path}", ["GET", "POST", "PUT"], async (HttpContext context, JiboCloudProtocolService service, IProtocolTelemetrySink telemetrySink, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var envelope = await BuildEnvelopeAsync(context, cancellationToken);
|
||||
var result = await service.DispatchAsync(envelope, cancellationToken);
|
||||
await telemetrySink.RecordAsync(envelope, result, cancellationToken);
|
||||
|
||||
context.Response.StatusCode = result.StatusCode;
|
||||
context.Response.ContentType = result.ContentType;
|
||||
@@ -174,7 +193,14 @@ static string ResolveSocketKind(string host, PathString path)
|
||||
return "neo-hub-listen";
|
||||
}
|
||||
|
||||
return "openjibo";
|
||||
if (host.Equals("openjibo.com", StringComparison.OrdinalIgnoreCase) ||
|
||||
host.Equals("openjibo.ai", StringComparison.OrdinalIgnoreCase) ||
|
||||
host.Equals("localhost", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "openjibo";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static string? ResolveToken(HttpRequest request)
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
"Enabled": true,
|
||||
"ExportFixtures": true,
|
||||
"DirectoryPath": "captures/websocket"
|
||||
},
|
||||
"ProtocolTelemetry": {
|
||||
"Enabled": true,
|
||||
"DirectoryPath": "captures/http"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Jibo.Cloud.Domain.Models;
|
||||
|
||||
namespace Jibo.Cloud.Application.Abstractions;
|
||||
|
||||
public interface IProtocolTelemetrySink
|
||||
{
|
||||
Task RecordAsync(ProtocolEnvelope envelope, ProtocolDispatchResult result, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Jibo.Cloud.Application.Abstractions;
|
||||
using Jibo.Cloud.Domain.Models;
|
||||
|
||||
namespace Jibo.Cloud.Application.Services;
|
||||
|
||||
public sealed class NullProtocolTelemetrySink : IProtocolTelemetrySink
|
||||
{
|
||||
public Task RecordAsync(ProtocolEnvelope envelope, ProtocolDispatchResult result, CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||
}
|
||||
@@ -15,6 +15,7 @@ public static class ServiceCollectionExtensions
|
||||
if (configuration is not null)
|
||||
{
|
||||
services.Configure<WebSocketTelemetryOptions>(configuration.GetSection("OpenJibo:Telemetry"));
|
||||
services.Configure<ProtocolTelemetryOptions>(configuration.GetSection("OpenJibo:ProtocolTelemetry"));
|
||||
}
|
||||
|
||||
services.AddSingleton<ICloudStateStore, InMemoryCloudStateStore>();
|
||||
@@ -22,6 +23,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddSingleton<ISttStrategy, SyntheticBufferedAudioSttStrategy>();
|
||||
services.AddSingleton<ISttStrategySelector, DefaultSttStrategySelector>();
|
||||
services.AddSingleton<IWebSocketTelemetrySink, FileWebSocketTelemetrySink>();
|
||||
services.AddSingleton<IProtocolTelemetrySink, FileProtocolTelemetrySink>();
|
||||
services.AddSingleton<ProtocolToTurnContextMapper>();
|
||||
services.AddSingleton<ResponsePlanToSocketMessagesMapper>();
|
||||
services.AddSingleton<WebSocketTurnFinalizationService>();
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Text.Json;
|
||||
using Jibo.Cloud.Application.Abstractions;
|
||||
using Jibo.Cloud.Domain.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Jibo.Cloud.Infrastructure.Telemetry;
|
||||
|
||||
public sealed class FileProtocolTelemetrySink(
|
||||
ILogger<FileProtocolTelemetrySink> logger,
|
||||
IOptions<ProtocolTelemetryOptions> options) : IProtocolTelemetrySink
|
||||
{
|
||||
private readonly SemaphoreSlim _writeLock = new(1, 1);
|
||||
|
||||
public async Task RecordAsync(ProtocolEnvelope envelope, ProtocolDispatchResult result, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!options.Value.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var directory = Path.GetFullPath(options.Value.DirectoryPath, AppContext.BaseDirectory);
|
||||
Directory.CreateDirectory(directory);
|
||||
var filePath = Path.Combine(directory, $"{DateTimeOffset.UtcNow:yyyyMMdd}.events.ndjson");
|
||||
|
||||
var payload = new
|
||||
{
|
||||
capturedUtc = DateTimeOffset.UtcNow,
|
||||
request = new
|
||||
{
|
||||
envelope.RequestId,
|
||||
envelope.Transport,
|
||||
envelope.Method,
|
||||
envelope.HostName,
|
||||
envelope.Path,
|
||||
envelope.ServicePrefix,
|
||||
envelope.Operation,
|
||||
envelope.DeviceId,
|
||||
envelope.CorrelationId,
|
||||
envelope.FirmwareVersion,
|
||||
envelope.ApplicationVersion,
|
||||
envelope.Headers,
|
||||
envelope.BodyText
|
||||
},
|
||||
response = new
|
||||
{
|
||||
result.StatusCode,
|
||||
result.ContentType,
|
||||
result.Headers,
|
||||
result.BodyText
|
||||
}
|
||||
};
|
||||
|
||||
var line = JsonSerializer.Serialize(payload) + Environment.NewLine;
|
||||
|
||||
await _writeLock.WaitAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
await File.AppendAllTextAsync(filePath, line, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_writeLock.Release();
|
||||
}
|
||||
|
||||
logger.LogInformation(
|
||||
"HTTP telemetry {Method} {Host}{Path} target={Target} status={StatusCode}",
|
||||
envelope.Method,
|
||||
envelope.HostName,
|
||||
envelope.Path,
|
||||
$"{envelope.ServicePrefix}.{envelope.Operation}".Trim('.'),
|
||||
result.StatusCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Jibo.Cloud.Infrastructure.Telemetry;
|
||||
|
||||
public sealed class ProtocolTelemetryOptions
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public string DirectoryPath { get; set; } = "captures/http";
|
||||
}
|
||||
Reference in New Issue
Block a user