197 lines
7.1 KiB
C#
197 lines
7.1 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Paca.Mobile.Core;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests.Mobile;
|
|
|
|
/// <summary>
|
|
/// Gate 120 ~ 125: 원클릭 LAN 자동 발견 및 비콘 핸드셰이크 기계적 검증
|
|
/// </summary>
|
|
public class MobileLanAutoDiscoveryGateTests
|
|
{
|
|
[Fact]
|
|
public void Gate120_BeaconMessage_Serialization_And_Parsing()
|
|
{
|
|
var msg = LanDiscoveryMessage.CreatePong(
|
|
deviceName: "Desktop-Encep",
|
|
endpoint: "192.168.0.10:52100",
|
|
pairingMode: true,
|
|
fingerprint: "PACA-TEST-FP"
|
|
);
|
|
|
|
var encoded = msg.ToPayload();
|
|
Assert.StartsWith("PACA_DISCOVERY|PONG|", encoded);
|
|
|
|
var parsed = LanDiscoveryMessage.Parse(encoded);
|
|
Assert.NotNull(parsed);
|
|
Assert.Equal(LanDiscoveryMessageType.Pong, parsed.Type);
|
|
Assert.Equal("Desktop-Encep", parsed.DeviceName);
|
|
Assert.Equal("192.168.0.10:52100", parsed.Endpoint);
|
|
Assert.True(parsed.IsPairingAllowed);
|
|
Assert.Equal("PACA-TEST-FP", parsed.Fingerprint);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate121_PingMessage_Creation()
|
|
{
|
|
var ping = LanDiscoveryMessage.CreatePing(clientDeviceName: "Galaxy-S24");
|
|
var payload = ping.ToPayload();
|
|
|
|
Assert.Equal("PACA_DISCOVERY|PING|Galaxy-S24", payload);
|
|
|
|
var parsed = LanDiscoveryMessage.Parse(payload);
|
|
Assert.NotNull(parsed);
|
|
Assert.Equal(LanDiscoveryMessageType.Ping, parsed.Type);
|
|
Assert.Equal("Galaxy-S24", parsed.DeviceName);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate122_Invalid_Or_Malformed_Beacon_Handled_Safely()
|
|
{
|
|
Assert.Null(LanDiscoveryMessage.Parse(null!));
|
|
Assert.Null(LanDiscoveryMessage.Parse(""));
|
|
Assert.Null(LanDiscoveryMessage.Parse("RANDOM_PACKET|TEST"));
|
|
Assert.Null(LanDiscoveryMessage.Parse("PACA_DISCOVERY|UNKNOWN"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate123_LanDiscoveredDevice_Formatting()
|
|
{
|
|
var device = new DiscoveredLanDevice(
|
|
DeviceName: "My-Studio-PC",
|
|
Endpoint: "192.168.1.50:52100",
|
|
IsPairingAllowed: true,
|
|
Fingerprint: "FP123",
|
|
LastSeen: DateTimeOffset.UtcNow
|
|
);
|
|
|
|
Assert.Equal("http://192.168.1.50:52100", device.HttpBaseUrl);
|
|
Assert.True(device.IsAlive(TimeSpan.FromSeconds(5)));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Gate124_Mock_Prober_Discovers_Responding_Host()
|
|
{
|
|
var prober = new LanDiscoveryProber();
|
|
var fakePong = LanDiscoveryMessage.CreatePong("Encep-PC", "192.168.0.2:52100", true, "FP-001");
|
|
|
|
// 시뮬레이션 패킷 수신 처리
|
|
prober.HandleReceivedPayload(fakePong.ToPayload(), "192.168.0.2");
|
|
|
|
var devices = prober.GetActiveDevices();
|
|
Assert.Single(devices);
|
|
Assert.Equal("Encep-PC", devices[0].DeviceName);
|
|
Assert.Equal("192.168.0.2:52100", devices[0].Endpoint);
|
|
Assert.True(devices[0].IsPairingAllowed);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate125_Prober_Removes_Stale_Devices()
|
|
{
|
|
var prober = new LanDiscoveryProber();
|
|
var pong = LanDiscoveryMessage.CreatePong("Old-PC", "192.168.0.99:52100", false, "FP-OLD");
|
|
|
|
// 10초 전 시점으로 등록
|
|
prober.AddOrUpdate(new DiscoveredLanDevice(
|
|
DeviceName: pong.DeviceName,
|
|
Endpoint: pong.Endpoint,
|
|
IsPairingAllowed: pong.IsPairingAllowed,
|
|
Fingerprint: pong.Fingerprint,
|
|
LastSeen: DateTimeOffset.UtcNow.AddSeconds(-15)
|
|
));
|
|
|
|
prober.PruneStale(maxAge: TimeSpan.FromSeconds(10));
|
|
var devices = prober.GetActiveDevices();
|
|
Assert.Empty(devices);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Gate126_LanBeaconResponder_Responds_To_Ping_Correctly()
|
|
{
|
|
using var responder = new Paca.Server.Discovery.LanBeaconResponder(
|
|
deviceName: "Desktop-Workstation",
|
|
httpPort: 52100,
|
|
pairingAllowed: true,
|
|
fingerprint: "DESK-FP-999"
|
|
);
|
|
|
|
// 로컬 비콘 프로필 검증
|
|
var pong = responder.BuildPongMessage("192.168.1.20");
|
|
Assert.NotNull(pong);
|
|
Assert.Equal("Desktop-Workstation", pong.DeviceName);
|
|
Assert.Equal("192.168.1.20:52100", pong.Endpoint);
|
|
Assert.True(pong.IsPairingAllowed);
|
|
Assert.Equal("DESK-FP-999", pong.Fingerprint);
|
|
|
|
// PING 패킷 처리 검증
|
|
var pingPayload = LanDiscoveryMessage.CreatePing("Pixel-9").ToPayload();
|
|
var replyPayload = responder.ProcessIncomingPayload(pingPayload, "192.168.1.20");
|
|
Assert.NotNull(replyPayload);
|
|
|
|
var parsed = LanDiscoveryMessage.Parse(replyPayload);
|
|
Assert.NotNull(parsed);
|
|
Assert.Equal(LanDiscoveryMessageType.Pong, parsed.Type);
|
|
Assert.Equal("Desktop-Workstation", parsed.DeviceName);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate127_LanBeaconResponder_Does_Not_Echo_SenderIp_As_Server_Endpoint_Audit()
|
|
{
|
|
using var responder = new Paca.Server.Discovery.LanBeaconResponder(
|
|
deviceName: "Desktop-Studio",
|
|
httpPort: 5000,
|
|
pairingAllowed: true,
|
|
fingerprint: "DESK-FP-1"
|
|
);
|
|
|
|
// 모바일 클라이언트 IP가 192.168.1.88인 경우
|
|
var pingPayload = LanDiscoveryMessage.CreatePing("Galaxy-Phone").ToPayload();
|
|
var replyPayload = responder.ProcessIncomingPayload(pingPayload, "192.168.1.88");
|
|
Assert.NotNull(replyPayload);
|
|
|
|
var parsed = LanDiscoveryMessage.Parse(replyPayload);
|
|
Assert.NotNull(parsed);
|
|
|
|
// PONG 엔드포인트는 송신자(모바일)의 IP(192.168.1.88)가 아니라 0.0.0.0 또는 서버 바인딩이어야 함
|
|
Assert.NotEqual("192.168.1.88:5000", parsed.Endpoint);
|
|
Assert.Equal("0.0.0.0:5000", parsed.Endpoint);
|
|
|
|
// 모바일 프로버가 수신했을 때, 패킷을 보낸 데스크톱 IP(예: 192.168.1.10)로 올바르게 복원되는지 검증
|
|
var prober = new LanDiscoveryProber();
|
|
prober.HandleReceivedPayload(replyPayload, "192.168.1.10");
|
|
|
|
var devices = prober.GetActiveDevices();
|
|
Assert.Single(devices);
|
|
Assert.Equal("192.168.1.10:5000", devices[0].Endpoint);
|
|
Assert.Equal("http://192.168.1.10:5000", devices[0].HttpBaseUrl);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate128_LanDiscoveryProber_Resolves_0_0_0_0_And_ColonPort_To_RemoteIp_Audit()
|
|
{
|
|
var prober = new LanDiscoveryProber();
|
|
|
|
// 1) 0.0.0.0:5000 형태
|
|
var pongZero = LanDiscoveryMessage.CreatePong("PC-A", "0.0.0.0:5000", true, "FP-A");
|
|
prober.HandleReceivedPayload(pongZero.ToPayload(), "10.0.0.5");
|
|
|
|
// 2) :5000 형태 (IP 생략 콜론 포트)
|
|
var pongColon = LanDiscoveryMessage.CreatePong("PC-B", ":52100", true, "FP-B");
|
|
prober.HandleReceivedPayload(pongColon.ToPayload(), "10.0.0.6");
|
|
|
|
var devices = prober.GetActiveDevices();
|
|
Assert.Equal(2, devices.Count);
|
|
|
|
var devA = devices.First(d => d.DeviceName == "PC-A");
|
|
Assert.Equal("10.0.0.5:5000", devA.Endpoint);
|
|
|
|
var devB = devices.First(d => d.DeviceName == "PC-B");
|
|
Assert.Equal("10.0.0.6:52100", devB.Endpoint);
|
|
}
|
|
}
|
|
|