namespace LibNftables.Tests; public sealed class NftablesClientIntegrationTests { [Fact] public void Validate_InvalidRuleset_ReturnsInvalidResult() { if (!CanCreateClient()) { return; } var client = new NftablesClient(); var request = NftApplyRequest.FromText("this is not valid nft syntax"); NftValidationResult result = client.Validate(request); Assert.False(result.IsValid); Assert.False(string.IsNullOrWhiteSpace(result.Diagnostics)); } [Fact] public async Task ValidateAsync_InvalidRuleset_ReturnsInvalidResult() { if (!CanCreateClient()) { return; } var client = new NftablesClient(); var request = NftApplyRequest.FromText("this is not valid nft syntax"); NftValidationResult result = await client.ValidateAsync(request); Assert.False(result.IsValid); Assert.False(string.IsNullOrWhiteSpace(result.Diagnostics)); } [Fact] public void Apply_InvalidRuleset_ThrowsValidationException() { if (!CanCreateClient()) { return; } var client = new NftablesClient(); var request = NftApplyRequest.FromText("this is not valid nft syntax"); Assert.Throws(() => client.Apply(request)); } [Fact] public void Snapshot_WithInsufficientPrivileges_ThrowsPermissionOrReturnsRuleset() { if (!CanCreateClient()) { return; } var client = new NftablesClient(); try { NftSnapshot snapshot = client.Snapshot(); Assert.False(string.IsNullOrWhiteSpace(snapshot.RulesetText)); } catch (NftPermissionException) { // Expected in unprivileged environments. } } [Fact] [Trait("Category", "Privileged")] public void Snapshot_WithPrivilegedLane_ReturnsRuleset() { if (!CanCreateClient()) { return; } if (!NativeTestSupport.ShouldRunPrivilegedTests()) { return; } var client = new NftablesClient(); NftSnapshot snapshot = client.Snapshot(); Assert.False(string.IsNullOrWhiteSpace(snapshot.RulesetText)); } [Fact] public void ValidateRuleset_WithTypedSetDefinition_ReturnsValidResult() { if (!CanCreateClient()) { return; } var client = new NftablesClient(); var ruleset = new NftRuleset(); var table = new NftTable { Family = NftFamily.Inet, Name = "typed_validation", }; var set = new NftSet { Name = "blocked_ipv4", Type = NftSetType.Ipv4Address, }; set.Elements.Add(NftValue.Address(System.Net.IPAddress.Parse("10.0.0.1"))); set.Elements.Add(NftValue.Address(System.Net.IPAddress.Parse("10.0.0.2"))); table.Sets.Add(set); var chain = new NftChain { Name = "input", Type = NftChainType.Filter, Hook = NftHook.Input, Priority = 0, Policy = NftChainPolicy.Drop, }; chain.Rules.Add(new NftRule { SourceAddressSetName = "blocked_ipv4", TransportProtocol = NftTransportProtocol.Tcp, DestinationPort = NftValue.Port(22), Verdict = NftVerdict.Accept, }); table.Chains.Add(chain); ruleset.Tables.Add(table); NftValidationResult result = client.ValidateRuleset(ruleset); Assert.True(result.IsValid); } [Fact] public void ValidateAndRenderRuleset_WithTypedMapAndRule_ReturnsValidResult() { if (!CanCreateClient()) { return; } var client = new NftablesClient(); var ruleset = new NftRuleset(); var table = new NftTable { Family = NftFamily.Inet, Name = "typed_preview", }; var set = new NftSet { Name = "blocked_ipv4", Type = NftSetType.Ipv4Address, }; set.Elements.Add(NftValue.Address(System.Net.IPAddress.Parse("10.0.0.1"))); table.Sets.Add(set); var map = new NftMap { Name = "service_policy", KeyType = NftMapType.InetService, ValueType = NftMapType.Verdict, }; map.Add(NftValue.Port(80), NftValue.Verdict(NftVerdict.Accept)); table.Maps.Add(map); var chain = new NftChain { Name = "input", Type = NftChainType.Filter, Hook = NftHook.Input, Priority = 0, }; chain.Rules.Add(new NftRule { SourceAddressSetName = "blocked_ipv4", TransportProtocol = NftTransportProtocol.Tcp, DestinationPort = NftValue.Port(22), Verdict = NftVerdict.Accept, }); table.Chains.Add(chain); ruleset.Tables.Add(table); NftRenderedValidationResult result = client.ValidateAndRenderRuleset(ruleset); Assert.True(result.ValidationResult.IsValid); Assert.Contains("add map inet typed_preview service_policy", result.RenderedRulesetText, StringComparison.Ordinal); } private static bool CanCreateClient() { try { _ = new NftablesClient(); return true; } catch (NftException) { return false; } } }