fixes for testing Jibo
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -417,6 +417,7 @@ FodyWeavers.xsd
|
|||||||
**/.dotnet/
|
**/.dotnet/
|
||||||
|
|
||||||
# OpenJibo live-run capture output
|
# OpenJibo live-run capture output
|
||||||
|
captures/
|
||||||
OpenJibo/captures/
|
OpenJibo/captures/
|
||||||
OpenJibo/.tmp/
|
OpenJibo/.tmp/
|
||||||
|
|
||||||
|
|||||||
@@ -78,10 +78,27 @@ OpenJibo/
|
|||||||
|
|
||||||
- port required endpoint and WebSocket behavior from Node to .NET
|
- port required endpoint and WebSocket behavior from Node to .NET
|
||||||
- keep protocol captures and replay fixtures current
|
- keep protocol captures and replay fixtures current
|
||||||
|
- keep HTTP and websocket live-run telemetry writing to the same repo-root capture tree
|
||||||
- harden device bootstrap documentation and scripts
|
- harden device bootstrap documentation and scripts
|
||||||
- map more endpoints and behaviors beyond the current Node coverage
|
- map more endpoints and behaviors beyond the current Node coverage
|
||||||
- stand up the initial `openjibo.com` information site
|
- stand up the initial `openjibo.com` information site
|
||||||
|
|
||||||
|
## Live Test Status
|
||||||
|
|
||||||
|
The first physical `.NET -> Jibo` experiments have now produced useful captures, but not a full wake-and-interact success yet.
|
||||||
|
|
||||||
|
What we have confirmed so far:
|
||||||
|
|
||||||
|
- the robot reaches `.NET` HTTP startup calls on `api.jibo.com`
|
||||||
|
- `.NET` can issue a robot token and accept the `api-socket.jibo.com` websocket
|
||||||
|
- live HTTP and websocket telemetry are now intended to land together under repo-root `captures/`
|
||||||
|
|
||||||
|
What remains unresolved:
|
||||||
|
|
||||||
|
- matching the Node startup cadence closely enough for consistent wake/eye-open behavior
|
||||||
|
- the next post-`api-socket` startup requests and timing seen in successful Node runs
|
||||||
|
- broader live websocket behavior on a real robot beyond the current synthetic parity slice
|
||||||
|
|
||||||
## Important Docs
|
## Important Docs
|
||||||
|
|
||||||
- [Cloud overview](/src/Jibo.Cloud/README.md)
|
- [Cloud overview](/src/Jibo.Cloud/README.md)
|
||||||
|
|||||||
@@ -36,27 +36,31 @@ Move to a second local/staging server or Azure after:
|
|||||||
|
|
||||||
## Telemetry Before Live Runs
|
## Telemetry Before Live Runs
|
||||||
|
|
||||||
The `.NET` cloud now supports structured websocket capture intended for first live runs:
|
The `.NET` cloud now supports structured live capture intended for first robot runs:
|
||||||
|
|
||||||
- event stream written as NDJSON
|
- HTTP request/response event streams written as NDJSON
|
||||||
- per-session fixture export for replay
|
- websocket event streams written as NDJSON
|
||||||
|
- per-session websocket fixture export for replay
|
||||||
- turn metadata including `transID`, buffered audio counts, finalize attempts, and reply types
|
- turn metadata including `transID`, buffered audio counts, finalize attempts, and reply types
|
||||||
|
|
||||||
Default capture location:
|
Default capture location:
|
||||||
|
|
||||||
|
- repo-root `captures/http/`
|
||||||
- repo-root `captures/websocket/`
|
- repo-root `captures/websocket/`
|
||||||
|
|
||||||
Artifacts:
|
Artifacts:
|
||||||
|
|
||||||
|
- `http/*.events.ndjson`
|
||||||
|
- `websocket/*.events.ndjson`
|
||||||
- `*.events.ndjson`
|
- `*.events.ndjson`
|
||||||
- `fixtures/*.flow.json`
|
- `websocket/fixtures/*.flow.json`
|
||||||
|
|
||||||
## Suggested First Hookup Plan
|
## Suggested First Hookup Plan
|
||||||
|
|
||||||
1. Start the `.NET` API on the Ubuntu-backed controlled network using the same robot routing settings currently used for Node.
|
1. Start the `.NET` API on the Ubuntu-backed controlled network using the same robot routing settings currently used for Node.
|
||||||
2. Confirm HTTP bootstrap and websocket acceptance with the existing smoke/routing helpers.
|
2. Confirm HTTP bootstrap and websocket acceptance with the existing smoke/routing helpers.
|
||||||
3. Run one or two controlled listen turns with Jibo.
|
3. Run one or two controlled listen turns with Jibo.
|
||||||
4. Inspect the captured websocket events and exported fixtures.
|
4. Inspect the captured HTTP and websocket events plus exported websocket fixtures.
|
||||||
5. Convert the best captures into sanitized checked-in fixtures and tests.
|
5. Convert the best captures into sanitized checked-in fixtures and tests.
|
||||||
6. Keep Node available to compare any surprising turn behavior before changing infrastructure.
|
6. Keep Node available to compare any surprising turn behavior before changing infrastructure.
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ BASEURL=http://localhost:24605 ./scripts/cloud/invoke-live-jibo-prep.sh
|
|||||||
|
|
||||||
- `captures/websocket/fixtures/`
|
- `captures/websocket/fixtures/`
|
||||||
|
|
||||||
|
Telemetry from the same run should also now be present under:
|
||||||
|
|
||||||
|
- `captures/http/`
|
||||||
|
- `captures/websocket/`
|
||||||
|
|
||||||
9. Import the best fixture into the checked-in websocket fixture set:
|
9. Import the best fixture into the checked-in websocket fixture set:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -133,7 +138,8 @@ If the robot does not connect or the first turn fails:
|
|||||||
2. confirm the cert presented by the `.NET` API matches the currently working Node cert path
|
2. confirm the cert presented by the `.NET` API matches the currently working Node cert path
|
||||||
3. confirm the Ubuntu routing still points Jibo traffic at the same machine
|
3. confirm the Ubuntu routing still points Jibo traffic at the same machine
|
||||||
4. compare the `.NET` websocket capture output with prior Node logs
|
4. compare the `.NET` websocket capture output with prior Node logs
|
||||||
5. temporarily switch back to Node to confirm the environment still works
|
5. compare the `.NET` HTTP capture output with prior Node logs
|
||||||
|
6. temporarily switch back to Node to confirm the environment still works
|
||||||
|
|
||||||
## Not In Scope For This First Test
|
## Not In Scope For This First Test
|
||||||
|
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ Observed from `open-jibo-link.js`:
|
|||||||
| `Loop_*` | `List`, `ListLoops` | medium | initial dispatch implemented |
|
| `Loop_*` | `List`, `ListLoops` | medium | initial dispatch implemented |
|
||||||
| `Robot_*` | `GetRobot`, `UpdateRobot` | medium | initial dispatch implemented |
|
| `Robot_*` | `GetRobot`, `UpdateRobot` | medium | initial dispatch implemented |
|
||||||
| `Update_*` | `ListUpdates`, `ListUpdatesFrom`, `GetUpdateFrom`, `CreateUpdate`, `RemoveUpdate` | medium | list/get scaffolding implemented |
|
| `Update_*` | `ListUpdates`, `ListUpdatesFrom`, `GetUpdateFrom`, `CreateUpdate`, `RemoveUpdate` | medium | list/get scaffolding implemented |
|
||||||
| `Media_20160725` | `List`, `Get`, `Create`, `Remove` | medium | not yet ported |
|
| `Media_20160725` | `List`, `Get`, `Create`, `Remove` | medium | implemented in current parity scaffold |
|
||||||
| `Log_*` | `PutEvents`, `PutEventsAsync`, `PutBinaryAsync`, `PutAsrBinary` | medium | upload endpoints reserved; detailed handling pending |
|
| `Log_*` | `PutEvents`, `PutEventsAsync`, `PutBinaryAsync`, `PutAsrBinary` | medium | async upload metadata and placeholder upload endpoints implemented |
|
||||||
| `Key_*` | `ShouldCreate`, `CreateSymmetricKey`, `GetRequest` | medium | pending |
|
| `Key_*` | `ShouldCreate`, `CreateSymmetricKey`, `GetRequest` | medium | implemented in current parity scaffold |
|
||||||
| `Person_*` | `ListHolidays` | low | pending |
|
| `Person_*` | `ListHolidays` | low | implemented in current parity scaffold |
|
||||||
| `Backup_*` | `List` | low | pending |
|
| `Backup_*` | `List` | low | implemented in current parity scaffold |
|
||||||
|
|
||||||
## WebSocket Flows
|
## WebSocket Flows
|
||||||
|
|
||||||
@@ -101,6 +101,16 @@ That separation is intentional. The synthetic STT path currently exists only to
|
|||||||
| `/upload/log-events` | async log upload target | medium | placeholder endpoint accepted |
|
| `/upload/log-events` | async log upload target | medium | placeholder endpoint accepted |
|
||||||
| `/upload/log-binary` | async binary upload target | medium | placeholder endpoint accepted |
|
| `/upload/log-binary` | async binary upload target | medium | placeholder endpoint accepted |
|
||||||
|
|
||||||
|
## First Live .NET Capture Findings
|
||||||
|
|
||||||
|
The first real `.NET` robot run has confirmed only an early startup slice so far:
|
||||||
|
|
||||||
|
- `api.jibo.com` startup HTTP requests are reaching the `.NET` cloud
|
||||||
|
- `Notification.NewRobotToken` is active in the robot startup sequence
|
||||||
|
- `api-socket.jibo.com/{token}` is being accepted live
|
||||||
|
|
||||||
|
The first live run has not yet shown full startup parity with the working Node server. In particular, the successful Node run continues into additional health/log cadence after token issuance and socket acceptance, while the current `.NET` run has not yet reproduced that full progression consistently.
|
||||||
|
|
||||||
## First Core Revive Slice
|
## First Core Revive Slice
|
||||||
|
|
||||||
The first .NET hosted milestone should fully support:
|
The first .NET hosted milestone should fully support:
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ These scripts help exercise the new .NET hosted cloud locally.
|
|||||||
Summarizes captured websocket telemetry events and exported live-run fixtures from the .NET cloud.
|
Summarizes captured websocket telemetry events and exported live-run fixtures from the .NET cloud.
|
||||||
- repo-root `captures/http/`
|
- repo-root `captures/http/`
|
||||||
Structured HTTP request/response telemetry for live robot startup comparison.
|
Structured HTTP request/response telemetry for live robot startup comparison.
|
||||||
|
- repo-root `captures/websocket/`
|
||||||
|
Structured websocket telemetry plus exported replay fixtures for live robot sessions.
|
||||||
- `Invoke-LiveJiboPrep.ps1`
|
- `Invoke-LiveJiboPrep.ps1`
|
||||||
Runs a small readiness checklist before the first physical Jibo test against the .NET cloud.
|
Runs a small readiness checklist before the first physical Jibo test against the .NET cloud.
|
||||||
- `Import-WebSocketCaptureFixture.ps1`
|
- `Import-WebSocketCaptureFixture.ps1`
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ PFX_PASSWORD="${PFX_PASSWORD:-openjibo-dev-password}"
|
|||||||
ASPNETCORE_URLS="${ASPNETCORE_URLS:-https://0.0.0.0:443;http://0.0.0.0:24605}"
|
ASPNETCORE_URLS="${ASPNETCORE_URLS:-https://0.0.0.0:443;http://0.0.0.0:24605}"
|
||||||
DOTNET_ENVIRONMENT="${DOTNET_ENVIRONMENT:-Development}"
|
DOTNET_ENVIRONMENT="${DOTNET_ENVIRONMENT:-Development}"
|
||||||
CAPTURE_DIRECTORY="${CAPTURE_DIRECTORY:-${REPO_ROOT}/captures/websocket}"
|
CAPTURE_DIRECTORY="${CAPTURE_DIRECTORY:-${REPO_ROOT}/captures/websocket}"
|
||||||
|
PROTOCOL_CAPTURE_DIRECTORY="${PROTOCOL_CAPTURE_DIRECTORY:-${REPO_ROOT}/captures/http}"
|
||||||
|
|
||||||
mkdir -p "$(dirname "${PFX_OUT}")"
|
mkdir -p "$(dirname "${PFX_OUT}")"
|
||||||
mkdir -p "${CAPTURE_DIRECTORY}"
|
mkdir -p "${CAPTURE_DIRECTORY}"
|
||||||
|
mkdir -p "${PROTOCOL_CAPTURE_DIRECTORY}"
|
||||||
|
|
||||||
if [[ ! -f "${CERT_PEM}" ]]; then
|
if [[ ! -f "${CERT_PEM}" ]]; then
|
||||||
echo "Missing CERT_PEM: ${CERT_PEM}" >&2
|
echo "Missing CERT_PEM: ${CERT_PEM}" >&2
|
||||||
@@ -59,13 +61,15 @@ export DOTNET_ENVIRONMENT
|
|||||||
export ASPNETCORE_Kestrel__Certificates__Default__Path="${PFX_OUT}"
|
export ASPNETCORE_Kestrel__Certificates__Default__Path="${PFX_OUT}"
|
||||||
export ASPNETCORE_Kestrel__Certificates__Default__Password="${PFX_PASSWORD}"
|
export ASPNETCORE_Kestrel__Certificates__Default__Password="${PFX_PASSWORD}"
|
||||||
export OpenJibo__Telemetry__DirectoryPath="${CAPTURE_DIRECTORY}"
|
export OpenJibo__Telemetry__DirectoryPath="${CAPTURE_DIRECTORY}"
|
||||||
|
export OpenJibo__ProtocolTelemetry__DirectoryPath="${PROTOCOL_CAPTURE_DIRECTORY}"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Starting OpenJibo .NET cloud"
|
echo "Starting OpenJibo .NET cloud"
|
||||||
echo " - project: ${API_PROJECT}"
|
echo " - project: ${API_PROJECT}"
|
||||||
echo " - urls: ${ASPNETCORE_URLS}"
|
echo " - urls: ${ASPNETCORE_URLS}"
|
||||||
echo " - environment: ${DOTNET_ENVIRONMENT}"
|
echo " - environment: ${DOTNET_ENVIRONMENT}"
|
||||||
echo " - captures: ${CAPTURE_DIRECTORY}"
|
echo " - websocket captures: ${CAPTURE_DIRECTORY}"
|
||||||
|
echo " - http captures: ${PROTOCOL_CAPTURE_DIRECTORY}"
|
||||||
|
|
||||||
cd "${REPO_ROOT}"
|
cd "${REPO_ROOT}"
|
||||||
exec dotnet run --project "${API_PROJECT}" --no-launch-profile
|
exec dotnet run --project "${API_PROJECT}" --no-launch-profile
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ Current websocket scope is still intentionally narrow:
|
|||||||
- `CONTEXT` capture and follow-up turn state
|
- `CONTEXT` capture and follow-up turn state
|
||||||
- `EOS` completion
|
- `EOS` completion
|
||||||
- first skill vertical for joke/chat `SKILL_ACTION` playback
|
- first skill vertical for joke/chat `SKILL_ACTION` playback
|
||||||
|
- repo-root live-run capture support for both `captures/http/` and `captures/websocket/`
|
||||||
|
|
||||||
Not yet covered:
|
Not yet covered:
|
||||||
|
|
||||||
@@ -85,3 +86,17 @@ Not yet covered:
|
|||||||
- upstream Nimbus or broader skill lifecycle behavior
|
- upstream Nimbus or broader skill lifecycle behavior
|
||||||
- animation / expression command families
|
- animation / expression command families
|
||||||
- ESML feature parity beyond the narrow synthetic playback payloads used in the current scaffold
|
- ESML feature parity beyond the narrow synthetic playback payloads used in the current scaffold
|
||||||
|
|
||||||
|
## Live Capture Status
|
||||||
|
|
||||||
|
The first real `.NET` robot test has confirmed:
|
||||||
|
|
||||||
|
- startup HTTP traffic reaches the `.NET` cloud
|
||||||
|
- `Notification.NewRobotToken` is in the active startup path
|
||||||
|
- `api-socket.jibo.com` connections are being accepted live
|
||||||
|
|
||||||
|
It has not yet confirmed:
|
||||||
|
|
||||||
|
- full startup parity with the successful Node run cadence
|
||||||
|
- consistent eye-open / wake completion on the robot
|
||||||
|
- the later health/log upload sequence currently seen in the working Node run
|
||||||
|
|||||||
@@ -278,14 +278,15 @@ public sealed class JiboCloudProtocolService(ICloudStateStore stateStore)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body = envelope.TryParseBody();
|
var body = envelope.TryParseBody();
|
||||||
var deviceId = envelope.DeviceId
|
var deviceId = !string.IsNullOrWhiteSpace(envelope.DeviceId)
|
||||||
?? ReadString(body, "deviceId")
|
? envelope.DeviceId!
|
||||||
?? ReadString(body, "serial_number")
|
: ReadString(body, "deviceId")
|
||||||
?? ReadString(body, "serialNumber")
|
?? ReadString(body, "serial_number")
|
||||||
?? ReadString(body, "cpuid")
|
?? ReadString(body, "serialNumber")
|
||||||
?? ReadString(body, "cpuId")
|
?? ReadString(body, "cpuid")
|
||||||
?? ReadString(body, "robotId")
|
?? ReadString(body, "cpuId")
|
||||||
?? "unknown-device";
|
?? ReadString(body, "robotId")
|
||||||
|
?? "unknown-device";
|
||||||
|
|
||||||
stateStore.GetOrCreateDevice(deviceId, envelope.FirmwareVersion, envelope.ApplicationVersion);
|
stateStore.GetOrCreateDevice(deviceId, envelope.FirmwareVersion, envelope.ApplicationVersion);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Jibo.Cloud.Tests")]
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
namespace Jibo.Cloud.Infrastructure.Telemetry;
|
||||||
|
|
||||||
|
internal static class CapturePathResolver
|
||||||
|
{
|
||||||
|
public static string Resolve(string configuredDirectoryPath, string currentDirectory, string appBaseDirectory)
|
||||||
|
{
|
||||||
|
if (Path.IsPathRooted(configuredDirectoryPath))
|
||||||
|
{
|
||||||
|
return Path.GetFullPath(configuredDirectoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var repoRoot = FindOpenJiboRepoRoot(currentDirectory) ?? FindOpenJiboRepoRoot(appBaseDirectory);
|
||||||
|
var baseDirectory = repoRoot ?? currentDirectory;
|
||||||
|
return Path.GetFullPath(configuredDirectoryPath, baseDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? FindOpenJiboRepoRoot(string? startPath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(startPath))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var directory = new DirectoryInfo(Path.GetFullPath(startPath));
|
||||||
|
if (!directory.Exists && directory.Parent is not null)
|
||||||
|
{
|
||||||
|
directory = directory.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (directory is not null)
|
||||||
|
{
|
||||||
|
if (File.Exists(Path.Combine(directory.FullName, "OpenJibo.slnx")))
|
||||||
|
{
|
||||||
|
return directory.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
directory = directory.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,10 @@ public sealed class FileProtocolTelemetrySink(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var directory = Path.GetFullPath(options.Value.DirectoryPath, AppContext.BaseDirectory);
|
var directory = CapturePathResolver.Resolve(
|
||||||
|
options.Value.DirectoryPath,
|
||||||
|
Directory.GetCurrentDirectory(),
|
||||||
|
AppContext.BaseDirectory);
|
||||||
Directory.CreateDirectory(directory);
|
Directory.CreateDirectory(directory);
|
||||||
var filePath = Path.Combine(directory, $"{DateTimeOffset.UtcNow:yyyyMMdd}.events.ndjson");
|
var filePath = Path.Combine(directory, $"{DateTimeOffset.UtcNow:yyyyMMdd}.events.ndjson");
|
||||||
|
|
||||||
|
|||||||
@@ -227,7 +227,10 @@ public sealed class FileWebSocketTelemetrySink(
|
|||||||
|
|
||||||
private string GetBaseDirectory()
|
private string GetBaseDirectory()
|
||||||
{
|
{
|
||||||
return Path.GetFullPath(options.Value.DirectoryPath, AppContext.BaseDirectory);
|
return CapturePathResolver.Resolve(
|
||||||
|
options.Value.DirectoryPath,
|
||||||
|
Directory.GetCurrentDirectory(),
|
||||||
|
AppContext.BaseDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string BuildFixtureName(CloudSession session, CapturedWebSocketFixtureBuilder fixture)
|
private static string BuildFixtureName(CloudSession session, CapturedWebSocketFixtureBuilder fixture)
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using Jibo.Cloud.Domain.Models;
|
||||||
|
using Jibo.Cloud.Infrastructure.Telemetry;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Jibo.Cloud.Tests.Protocol;
|
||||||
|
|
||||||
|
public sealed class FileProtocolTelemetrySinkTests : IDisposable
|
||||||
|
{
|
||||||
|
private readonly string _workspaceRoot;
|
||||||
|
private readonly string _repoRoot;
|
||||||
|
private readonly string _appBaseDirectory;
|
||||||
|
|
||||||
|
public FileProtocolTelemetrySinkTests()
|
||||||
|
{
|
||||||
|
_workspaceRoot = Path.Combine(Path.GetTempPath(), "OpenJibo.ProtocolTelemetry.Tests", Guid.NewGuid().ToString("N"));
|
||||||
|
_repoRoot = Path.Combine(_workspaceRoot, "OpenJibo");
|
||||||
|
_appBaseDirectory = Path.Combine(_repoRoot, "src", "Jibo.Cloud", "dotnet", "src", "Jibo.Cloud.Api", "bin", "Debug", "net10.0");
|
||||||
|
|
||||||
|
Directory.CreateDirectory(_repoRoot);
|
||||||
|
Directory.CreateDirectory(_appBaseDirectory);
|
||||||
|
File.WriteAllText(Path.Combine(_repoRoot, "OpenJibo.slnx"), string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RecordAsync_ResolvesRelativePathAgainstOpenJiboRepoRoot()
|
||||||
|
{
|
||||||
|
var captureDirectory = CapturePathResolver.Resolve("captures/http", _repoRoot, _appBaseDirectory);
|
||||||
|
var sink = new FileProtocolTelemetrySink(
|
||||||
|
NullLogger<FileProtocolTelemetrySink>.Instance,
|
||||||
|
Options.Create(new ProtocolTelemetryOptions
|
||||||
|
{
|
||||||
|
Enabled = true,
|
||||||
|
DirectoryPath = captureDirectory
|
||||||
|
}));
|
||||||
|
|
||||||
|
var envelope = new ProtocolEnvelope
|
||||||
|
{
|
||||||
|
HostName = "api.jibo.com",
|
||||||
|
Method = "POST",
|
||||||
|
Path = "/",
|
||||||
|
ServicePrefix = "Notification_20150505",
|
||||||
|
Operation = "NewRobotToken",
|
||||||
|
BodyText = """{"deviceId":"robot-123"}"""
|
||||||
|
};
|
||||||
|
|
||||||
|
await sink.RecordAsync(envelope, ProtocolDispatchResult.Ok(new { token = "token-robot-123" }));
|
||||||
|
|
||||||
|
var captureFile = Directory.GetFiles(captureDirectory, "*.events.ndjson").Single();
|
||||||
|
var contents = await File.ReadAllTextAsync(captureFile);
|
||||||
|
|
||||||
|
Assert.Contains("Notification_20150505", contents);
|
||||||
|
Assert.DoesNotContain(Path.Combine("bin", "Debug"), captureFile, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(_workspaceRoot))
|
||||||
|
{
|
||||||
|
Directory.Delete(_workspaceRoot, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ public sealed class JiboCloudProtocolServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task NewRobotToken_UsesBodyDeviceId()
|
public async Task NewRobotToken_UsesBodyDeviceId_WhenHeaderDeviceIdIsEmpty()
|
||||||
{
|
{
|
||||||
var result = await _service.DispatchAsync(new ProtocolEnvelope
|
var result = await _service.DispatchAsync(new ProtocolEnvelope
|
||||||
{
|
{
|
||||||
@@ -36,6 +36,7 @@ public sealed class JiboCloudProtocolServiceTests
|
|||||||
Method = "POST",
|
Method = "POST",
|
||||||
ServicePrefix = "Notification_20160715",
|
ServicePrefix = "Notification_20160715",
|
||||||
Operation = "NewRobotToken",
|
Operation = "NewRobotToken",
|
||||||
|
DeviceId = string.Empty,
|
||||||
BodyText = """{"deviceId":"robot-123"}"""
|
BodyText = """{"deviceId":"robot-123"}"""
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ namespace Jibo.Cloud.Tests.WebSockets;
|
|||||||
public sealed class FileWebSocketTelemetrySinkTests : IDisposable
|
public sealed class FileWebSocketTelemetrySinkTests : IDisposable
|
||||||
{
|
{
|
||||||
private readonly string _directoryPath;
|
private readonly string _directoryPath;
|
||||||
|
private readonly string _repoRoot;
|
||||||
|
private readonly string _appBaseDirectory;
|
||||||
|
|
||||||
public FileWebSocketTelemetrySinkTests()
|
public FileWebSocketTelemetrySinkTests()
|
||||||
{
|
{
|
||||||
_directoryPath = Path.Combine(Path.GetTempPath(), "OpenJibo.Tests", Guid.NewGuid().ToString("N"));
|
_directoryPath = Path.Combine(Path.GetTempPath(), "OpenJibo.Tests", Guid.NewGuid().ToString("N"));
|
||||||
|
_repoRoot = Path.Combine(_directoryPath, "OpenJibo");
|
||||||
|
_appBaseDirectory = Path.Combine(_repoRoot, "src", "Jibo.Cloud", "dotnet", "src", "Jibo.Cloud.Api", "bin", "Debug", "net10.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -61,6 +65,48 @@ public sealed class FileWebSocketTelemetrySinkTests : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RecordsFixtureUsingRepoRootForRelativePaths()
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(_repoRoot);
|
||||||
|
Directory.CreateDirectory(_appBaseDirectory);
|
||||||
|
File.WriteAllText(Path.Combine(_repoRoot, "OpenJibo.slnx"), string.Empty);
|
||||||
|
var captureDirectory = CapturePathResolver.Resolve("captures/websocket", _repoRoot, _appBaseDirectory);
|
||||||
|
|
||||||
|
var sink = new FileWebSocketTelemetrySink(
|
||||||
|
NullLogger<FileWebSocketTelemetrySink>.Instance,
|
||||||
|
Options.Create(new WebSocketTelemetryOptions
|
||||||
|
{
|
||||||
|
Enabled = true,
|
||||||
|
ExportFixtures = true,
|
||||||
|
DirectoryPath = captureDirectory
|
||||||
|
}));
|
||||||
|
|
||||||
|
var envelope = new WebSocketMessageEnvelope
|
||||||
|
{
|
||||||
|
ConnectionId = "conn-relative",
|
||||||
|
HostName = "neo-hub.jibo.com",
|
||||||
|
Path = "/listen",
|
||||||
|
Kind = "neo-hub-listen",
|
||||||
|
Token = "token-relative",
|
||||||
|
Text = """{"type":"LISTEN","transID":"trans-relative","data":{"text":"hello"}}"""
|
||||||
|
};
|
||||||
|
var session = new CloudSession
|
||||||
|
{
|
||||||
|
Token = "token-relative",
|
||||||
|
HostName = "neo-hub.jibo.com",
|
||||||
|
Path = "/listen"
|
||||||
|
};
|
||||||
|
session.TurnState.TransId = "trans-relative";
|
||||||
|
|
||||||
|
await sink.RecordConnectionOpenedAsync(envelope, session);
|
||||||
|
await sink.RecordOutboundAsync(envelope, session, [new WebSocketReply { Text = """{"type":"LISTEN"}""" }]);
|
||||||
|
await sink.RecordConnectionClosedAsync(envelope, session, "test");
|
||||||
|
|
||||||
|
var fixtureDirectory = Path.Combine(captureDirectory, "fixtures");
|
||||||
|
Assert.Single(Directory.GetFiles(fixtureDirectory, "*.flow.json"));
|
||||||
|
}
|
||||||
|
|
||||||
private FileWebSocketTelemetrySink CreateSink()
|
private FileWebSocketTelemetrySink CreateSink()
|
||||||
{
|
{
|
||||||
return new FileWebSocketTelemetrySink(
|
return new FileWebSocketTelemetrySink(
|
||||||
|
|||||||
Reference in New Issue
Block a user