- InstallProduct now extracts into the real C:\Games (the launcher's GAMES_DIR) so deployed products land where their catalog launch entries point; uninstall removes the real product folder. Tests pass an isolated games root through the VirtualLauncher ctor. - New "Actually launch apps (real processes)" toggle (off by default = simulated PIDs): LaunchApp starts the entry's exe exactly like the Agent (same start info, same registered-but-not-installed error), Kill*/ Uninstall/Wipe terminate the real processes (kill before folder delete), and self-exited apps are pruned from GetLaunchedApps/FullUpdate. Real processes die with the machine: power off, reboot, or closing vPOD. The Agent's autoRestart watchdog is deliberately not emulated. - Pod Power and Mimicking Game groups swapped (game left, power right). - Provisioning round-trip test now skips as inconclusive when a running TeslaConsole holds UDP 53291 instead of failing the suite; two new tests cover real launch/kill (ping.exe) and the missing-exe error path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
95 lines
3.7 KiB
C#
95 lines
3.7 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using Tesla;
|
|
using VPod;
|
|
using Xunit;
|
|
|
|
namespace TeslaConsole.DiffTests
|
|
{
|
|
/// <summary>
|
|
/// End-to-end loopback exercise of vPOD's pod-side SecureConfig provisioning
|
|
/// against the REAL console-side implementation (Tesla.PodConfigurationServer,
|
|
/// the exact code the console runs behind Manage Site's "Configure" button):
|
|
/// RQST beacon reception, the AES "RPLY" config broadcast, and the OFB+RSA
|
|
/// session-key exchange must all interoperate.
|
|
///
|
|
/// Uses the protocol's fixed ports (UDP 53291/53292, TCP 53292) and real UDP
|
|
/// broadcasts on loopback — do not run while a console or another vPOD is
|
|
/// provisioning on this machine.
|
|
/// </summary>
|
|
public class VPodProvisioningTests
|
|
{
|
|
[Fact]
|
|
public void Console_Provisions_VPod_And_Both_Hold_The_Same_Session_Key()
|
|
{
|
|
byte[] mac = PodProvisioning.MacForHost(7);
|
|
var provisioning = new PodProvisioning(mac);
|
|
|
|
byte[] beaconMac = null;
|
|
string beaconRequestId = null;
|
|
using (var beaconSeen = new ManualResetEventSlim())
|
|
using (var podProvisioned = new ManualResetEventSlim())
|
|
{
|
|
byte[] podKey = null;
|
|
provisioning.ConfigReceived += _ => { };
|
|
provisioning.Provisioned += key =>
|
|
{
|
|
podKey = key;
|
|
podProvisioned.Set();
|
|
};
|
|
|
|
// The console side: listens on UDP 53291 for RQST beacons; the
|
|
// delegate fires on vPOD's first beacon (sent at Start).
|
|
PodConfigurationServer consoleServer;
|
|
try
|
|
{
|
|
consoleServer = new PodConfigurationServer(53291, 53292, (m, id) =>
|
|
{
|
|
beaconMac = m;
|
|
beaconRequestId = id;
|
|
beaconSeen.Set();
|
|
});
|
|
}
|
|
catch (System.Net.Sockets.SocketException)
|
|
{
|
|
// UDP 53291 already bound — a real TeslaConsole is running on
|
|
// this machine. The protocol ports are fixed, so the test can't
|
|
// run; treat as inconclusive rather than failing the suite.
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
provisioning.Start();
|
|
Assert.True(beaconSeen.Wait(TimeSpan.FromSeconds(15)),
|
|
"console never received vPOD's RQST beacon");
|
|
Assert.Equal(mac, beaconMac);
|
|
Assert.Equal(provisioning.RequestId, beaconRequestId);
|
|
|
|
// The console side of Configure: broadcast the AES-encrypted
|
|
// network config and run the RSA key exchange against the pod.
|
|
byte[] consoleKey = consoleServer.SendEncryptionKey(
|
|
provisioning.Passphrase,
|
|
IPAddress.Loopback,
|
|
IPAddress.Parse("255.255.255.0"),
|
|
IPAddress.Any,
|
|
IPAddress.Any,
|
|
"vpod-test",
|
|
TimeSpan.FromSeconds(30));
|
|
|
|
Assert.True(podProvisioned.Wait(TimeSpan.FromSeconds(15)),
|
|
"vPOD never completed provisioning");
|
|
Assert.NotNull(consoleKey);
|
|
Assert.Equal(32, consoleKey.Length);
|
|
Assert.Equal(consoleKey, podKey);
|
|
}
|
|
finally
|
|
{
|
|
provisioning.Stop();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|