using Fleet.Core; using Fleet.Hosting; using Monitor.Protocol; namespace Monitor.Probe.Tests; public sealed class ProbeTests { [Fact] public void ClockEstimator_UsesTheSampleWithTheShortestRoundTrip() { var clock = new ClockEstimator(); Assert.False(clock.HasEstimate); // Offset 1000 with a 50 ms round trip, offset 5000 with a 2 ms round trip (symmetry assumption is far better here) clock.Add(sendUs: 0, agentUs: 25_000 + 1000, receiveUs: 50_000); clock.Add(sendUs: 100_000, agentUs: 101_000 + 5000, receiveUs: 102_000); Assert.True(clock.HasEstimate); Assert.Equal(5000, clock.OffsetUs); // Slow samples do not displace the good one clock.Add(sendUs: 200_000, agentUs: 300_000, receiveUs: 400_000); Assert.Equal(5000, clock.OffsetUs); } private static string NewDirectory() { string path = Path.Combine(Path.GetTempPath(), "probe-tests-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(path); return path; } private static ProbeAgentsResult List(long version, params string[] ids) => new() { Version = version, Agents = [.. ids.Select(id => new AgentEndpoint { AgentId = new NodeId(id), Host = id + ".example", TcpPort = 7443, Spki = "c3BraQ==" })], }; [Fact] public void AgentDirectory_StartsFromBootstrap_IgnoresStaleLists_PersistsChanges_AndReloadsThem() { string directory = NewDirectory(); try { var file = new ProbeFile { ProbeId = "prb_x", Bootstrap = [new BootstrapAgent { AgentId = "agt_boot", Host = "boot.example", TcpPort = 7443, Spki = "c3BraQ==" }] }; var directoryA = new AgentDirectory(new ConfigStore(directory), directory, file, NullNodeLog.Instance); int changes = 0; directoryA.Changed += () => changes++; Assert.Equal(["agt_boot"], directoryA.Agents.Select(a => a.AgentId)); directoryA.Apply(List(5, "agt_1", "agt_2")); Assert.Equal(1, changes); Assert.Equal(["agt_1", "agt_2"], directoryA.Agents.Select(a => a.AgentId)); directoryA.Apply(List(4, "agt_old")); // out-of-order older push directoryA.Apply(List(5, "agt_1", "agt_2")); // same version and content Assert.Equal(1, changes); Assert.Equal(5, directoryA.Version); // The next start uses the saved list, not the bootstrap one var directoryB = new AgentDirectory(new ConfigStore(directory), directory, file, NullNodeLog.Instance); Assert.Equal(["agt_1", "agt_2"], directoryB.Agents.Select(a => a.AgentId)); Assert.Equal(5, directoryB.Version); } finally { Directory.Delete(directory, recursive: true); } } [Fact] public void AgentDirectory_FallsBackToBootstrap_WhenTheSavedFileIsCorrupt() { string directory = NewDirectory(); try { new ConfigStore(directory).Write(AgentDirectory.FileName, "{ not json"u8.ToArray()); var file = new ProbeFile { Bootstrap = [new BootstrapAgent { AgentId = "agt_boot", Host = "h", TcpPort = 1, Spki = "AA==" }] }; var loaded = new AgentDirectory(new ConfigStore(directory), directory, file, NullNodeLog.Instance); Assert.Equal(["agt_boot"], loaded.Agents.Select(a => a.AgentId)); } finally { Directory.Delete(directory, recursive: true); } } }