more test fixes

This commit is contained in:
Jacob Dubin
2026-04-14 21:38:50 -05:00
parent 0b9e4ee9f3
commit 771919c7bb
9 changed files with 137 additions and 5 deletions

View File

@@ -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

View File

@@ -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`

View File

@@ -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)

View File

@@ -4,6 +4,10 @@
"Enabled": true,
"ExportFixtures": true,
"DirectoryPath": "captures/websocket"
},
"ProtocolTelemetry": {
"Enabled": true,
"DirectoryPath": "captures/http"
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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>();

View File

@@ -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);
}
}

View File

@@ -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";
}