Files
TeslaSuite/Console/tests/TeslaConsole.DiffTests/VPodProvisioningTests.cs
T
CydandClaude Fable 5 d30ce8bdbe vPOD: real C:\Games installs, optional real app launching, power-group swap
- 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>
2026-07-10 10:46:55 -05:00

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