updates for testing

This commit is contained in:
Jacob Dubin
2026-04-14 20:36:07 -05:00
parent 8c58b895f6
commit d1672e1613
8 changed files with 385 additions and 0 deletions

View File

@@ -65,3 +65,8 @@ Useful helper scripts:
- [scripts/cloud/Invoke-LiveJiboPrep.ps1](/OpenJibo/scripts/cloud/Invoke-LiveJiboPrep.ps1)
- [scripts/cloud/Get-WebSocketCaptureSummary.ps1](/OpenJibo/scripts/cloud/Get-WebSocketCaptureSummary.ps1)
- [scripts/cloud/Import-WebSocketCaptureFixture.ps1](/OpenJibo/scripts/cloud/Import-WebSocketCaptureFixture.ps1)
- [scripts/cloud/start-dotnet-with-node-cert.sh](/OpenJibo/scripts/cloud/start-dotnet-with-node-cert.sh)
- [scripts/cloud/invoke-live-jibo-prep.sh](/OpenJibo/scripts/cloud/invoke-live-jibo-prep.sh)
- [scripts/cloud/get-websocket-capture-summary.sh](/OpenJibo/scripts/cloud/get-websocket-capture-summary.sh)
- [scripts/cloud/import-websocket-capture-fixture.py](/OpenJibo/scripts/cloud/import-websocket-capture-fixture.py)
- [live-jibo-test-runbook.md](/OpenJibo/docs/live-jibo-test-runbook.md)

View File

@@ -0,0 +1,134 @@
# Live Jibo .NET Test Runbook
## Goal
Run the first real `Jibo -> .NET OpenJibo cloud` test on the Ubuntu machine using the same working certificate and controlled routing that currently work with the Node server.
This runbook intentionally avoids introducing Azure, new hostnames, or new robot bootstrap changes during the first live test.
## Recommended Approach
Use the existing Ubuntu networking path and certificate material first.
- keep the current controlled Wi-Fi / routing arrangement
- keep the current Jibo-facing hostnames:
- `api.jibo.com`
- `api-socket.jibo.com`
- `neo-hub.jibo.com`
- keep the Node server available as a fallback
- run the `.NET` API with the same cert/key material by converting it to a temporary `.pfx` for Kestrel
## Prerequisites On Ubuntu
Install or confirm these tools:
- `dotnet`
- `openssl`
- `curl`
- `python3`
Optional but useful:
- `pwsh`
`pwsh` is not required anymore for the Ubuntu live test path if you use the bash/python helpers added here.
## Certificate Plan
The Node server currently uses:
- `cert.pem`
- `key.pem`
The `.NET` API can reuse that same material for the test by converting it at startup into a temporary `.pfx`.
If your current cert file already includes the working chain, use it as-is.
If your chain is separate, pass it as `CHAIN_PEM`.
## Step By Step
1. On Ubuntu, stop the Node server if it is currently bound to port `443`.
2. From the repo root, start the `.NET` cloud using the same cert/key:
```bash
./scripts/cloud/start-dotnet-with-node-cert.sh
```
Optional environment overrides:
```bash
CERT_PEM=/path/to/cert.pem \
KEY_PEM=/path/to/key.pem \
CHAIN_PEM=/path/to/chain.pem \
ASPNETCORE_URLS="https://0.0.0.0:443;http://0.0.0.0:24605" \
./scripts/cloud/start-dotnet-with-node-cert.sh
```
3. In another terminal, run the prep checklist:
```bash
./scripts/cloud/invoke-live-jibo-prep.sh
```
4. Verify controlled routing from the Ubuntu environment:
```bash
./scripts/bootstrap/test-openjibo-routing.sh
```
5. Power on Jibo and let it connect using the existing controlled network configuration.
6. Perform the first live checks in this order:
- startup / bootstrap reachability
- one simple chat turn
- one joke turn
7. After the run, summarize the captured websocket telemetry:
```bash
./scripts/cloud/get-websocket-capture-summary.sh
```
8. Inspect exported fixtures under:
- `src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/bin/Debug/net10.0/captures/websocket/fixtures/`
9. Import the best fixture into the checked-in websocket fixture set:
```bash
python3 ./scripts/cloud/import-websocket-capture-fixture.py \
/path/to/exported.flow.json \
neo-hub-real-jibo-first-chat
```
10. Keep notes on:
- whether startup succeeded cleanly
- which websocket paths connected
- whether audio stayed pending or finalized
- whether EOS timing matched expectations
- whether any unexpected message families appeared
## What To Do If The Test Fails
If the robot does not connect or the first turn fails:
1. confirm the `.NET` API is actually bound on `443`
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
4. compare the `.NET` websocket capture output with prior Node logs
5. temporarily switch back to Node to confirm the environment still works
## Not In Scope For This First Test
Do not mix these into the first live run:
- Azure deployment cutover
- new permanent OpenJibo hostnames
- IaC rollout
- new device bootstrap edits beyond the already working setup
Those are valid next steps, but they should follow the first successful `.NET` live capture, not precede it.

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail
HOSTS=(
"https://api.jibo.com/health"
"https://api-socket.jibo.com/"
"https://neo-hub.jibo.com/v1/proactive"
)
for url in "${HOSTS[@]}"; do
if status_code="$(curl --silent --output /dev/null --write-out "%{http_code}" --insecure "${url}")"; then
echo "${url} status=${status_code} success=true"
else
echo "${url} status=000 success=false"
fi
done

View File

@@ -12,3 +12,11 @@ These scripts help exercise the new .NET hosted cloud locally.
Runs a small readiness checklist before the first physical Jibo test against the .NET cloud.
- `Import-WebSocketCaptureFixture.ps1`
Sanitizes an exported websocket capture fixture and copies it into the checked-in websocket fixture set.
- `start-dotnet-with-node-cert.sh`
Starts the .NET API on Linux using the same PEM certificate material already used by the Node server.
- `invoke-live-jibo-prep.sh`
Bash equivalent of the live-run prep checklist for Ubuntu.
- `get-websocket-capture-summary.sh`
Bash summary helper for captured websocket telemetry and exported fixtures.
- `import-websocket-capture-fixture.py`
Cross-platform import/sanitization helper for exported websocket fixtures.

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CAPTURE_DIRECTORY="${1:-${SCRIPT_DIR}/../../src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/bin/Debug/net10.0/captures/websocket}"
if [[ ! -d "${CAPTURE_DIRECTORY}" ]]; then
echo "No websocket capture directory found at ${CAPTURE_DIRECTORY}"
exit 0
fi
shopt -s nullglob
event_files=( "${CAPTURE_DIRECTORY}"/*.events.ndjson )
if [[ ${#event_files[@]} -eq 0 ]]; then
echo "No websocket telemetry event files found in ${CAPTURE_DIRECTORY}"
exit 0
fi
python3 - "$CAPTURE_DIRECTORY" "${event_files[@]}" <<'PY'
import collections
import json
import os
import sys
capture_dir = sys.argv[1]
event_files = sys.argv[2:]
counter = collections.Counter()
for path in event_files:
with open(path, "r", encoding="utf-8") as handle:
for line in handle:
line = line.strip()
if not line:
continue
payload = json.loads(line)
counter[payload.get("EventType", "unknown")] += 1
for key in sorted(counter):
print(f"{key}: {counter[key]}")
fixture_dir = os.path.join(capture_dir, "fixtures")
if os.path.isdir(fixture_dir):
print("")
print("Exported websocket fixtures:")
for name in sorted(os.listdir(fixture_dir)):
if name.endswith(".flow.json"):
print(f" - {name}")
PY

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
import argparse
import json
from pathlib import Path
def redact(value):
if value is None:
return None
if isinstance(value, str):
lowered = value.lower()
if "token" in lowered or "bearer" in lowered or "session" in lowered:
return "[redacted]"
return value
if isinstance(value, list):
return [redact(item) for item in value]
if isinstance(value, dict):
result = {}
for key, item in value.items():
lowered = key.lower()
if "token" in lowered or "authorization" in lowered:
result[key] = "[redacted]"
else:
result[key] = redact(item)
return result
return value
def main():
parser = argparse.ArgumentParser(description="Import and sanitize an exported websocket capture fixture.")
parser.add_argument("source_path")
parser.add_argument("fixture_name")
parser.add_argument(
"--destination-directory",
default=str(Path(__file__).resolve().parents[2] / "src" / "Jibo.Cloud" / "node" / "fixtures" / "websocket"),
)
parser.add_argument("--overwrite", action="store_true")
args = parser.parse_args()
source_path = Path(args.source_path).resolve()
destination_directory = Path(args.destination_directory).resolve()
destination_directory.mkdir(parents=True, exist_ok=True)
destination_path = destination_directory / f"{args.fixture_name}.flow.json"
if destination_path.exists() and not args.overwrite:
raise SystemExit(f"Destination fixture already exists: {destination_path}. Use --overwrite to replace it.")
with source_path.open("r", encoding="utf-8") as handle:
fixture = json.load(handle)
sanitized = redact(fixture)
sanitized["name"] = args.fixture_name
if "session" in sanitized and isinstance(sanitized["session"], dict):
sanitized["session"]["token"] = "[redacted]"
with destination_path.open("w", encoding="utf-8", newline="\n") as handle:
json.dump(sanitized, handle, indent=2)
handle.write("\n")
print("Imported sanitized websocket fixture:")
print(f" - source: {source_path}")
print(f" - destination: {destination_path}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_URL="${BASE_URL:-https://localhost:5001}"
CAPTURE_DIRECTORY="${CAPTURE_DIRECTORY:-${SCRIPT_DIR}/../../src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/bin/Debug/net10.0/captures/websocket}"
EXPECTED_HOSTS=(
"api.jibo.com"
"api-socket.jibo.com"
"neo-hub.jibo.com"
)
echo "OpenJibo live Jibo prep"
echo ""
echo "1. HTTP health check"
curl --silent --show-error --fail "${BASE_URL%/}/health" | python3 -m json.tool
echo ""
echo "2. Expected robot-facing hosts"
for host in "${EXPECTED_HOSTS[@]}"; do
echo " - ${host}"
done
echo ""
echo "3. Capture directory"
mkdir -p "${CAPTURE_DIRECTORY}"
echo " - ${CAPTURE_DIRECTORY}"
echo ""
echo "4. Live-run checklist"
echo " - keep the Ubuntu/Jibo routing setup in place"
echo " - keep the Node server available as a fallback"
echo " - point Jibo at the .NET server using the same controlled network settings"
echo " - perform one startup check, one chat turn, and one joke turn"
echo " - after the run, inspect capture output with scripts/cloud/get-websocket-capture-summary.sh"
echo " - import the best exported fixture with scripts/cloud/import-websocket-capture-fixture.py"

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
API_PROJECT="${REPO_ROOT}/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/Jibo.Cloud.Api.csproj"
CERT_PEM="${CERT_PEM:-${REPO_ROOT}/src/Jibo.Cloud/node/cert.pem}"
KEY_PEM="${KEY_PEM:-${REPO_ROOT}/src/Jibo.Cloud/node/key.pem}"
CHAIN_PEM="${CHAIN_PEM:-}"
PFX_OUT="${PFX_OUT:-${REPO_ROOT}/.tmp/openjibo-dev-cert.pfx}"
PFX_PASSWORD="${PFX_PASSWORD:-openjibo-dev-password}"
ASPNETCORE_URLS="${ASPNETCORE_URLS:-https://0.0.0.0:443;http://0.0.0.0:24605}"
DOTNET_ENVIRONMENT="${DOTNET_ENVIRONMENT:-Development}"
mkdir -p "$(dirname "${PFX_OUT}")"
if [[ ! -f "${CERT_PEM}" ]]; then
echo "Missing CERT_PEM: ${CERT_PEM}" >&2
exit 1
fi
if [[ ! -f "${KEY_PEM}" ]]; then
echo "Missing KEY_PEM: ${KEY_PEM}" >&2
exit 1
fi
OPENSSL_ARGS=(
pkcs12
-export
-out "${PFX_OUT}"
-inkey "${KEY_PEM}"
-in "${CERT_PEM}"
-passout "pass:${PFX_PASSWORD}"
)
if [[ -n "${CHAIN_PEM}" ]]; then
if [[ ! -f "${CHAIN_PEM}" ]]; then
echo "Missing CHAIN_PEM: ${CHAIN_PEM}" >&2
exit 1
fi
OPENSSL_ARGS+=( -certfile "${CHAIN_PEM}" )
fi
echo "Creating PFX for Kestrel"
echo " - cert: ${CERT_PEM}"
echo " - key: ${KEY_PEM}"
if [[ -n "${CHAIN_PEM}" ]]; then
echo " - chain: ${CHAIN_PEM}"
fi
echo " - pfx: ${PFX_OUT}"
openssl "${OPENSSL_ARGS[@]}"
export ASPNETCORE_URLS
export DOTNET_ENVIRONMENT
export ASPNETCORE_Kestrel__Certificates__Default__Path="${PFX_OUT}"
export ASPNETCORE_Kestrel__Certificates__Default__Password="${PFX_PASSWORD}"
echo ""
echo "Starting OpenJibo .NET cloud"
echo " - project: ${API_PROJECT}"
echo " - urls: ${ASPNETCORE_URLS}"
echo " - environment: ${DOTNET_ENVIRONMENT}"
cd "${REPO_ROOT}"
exec dotnet run --project "${API_PROJECT}" --no-launch-profile