finish off sprint advisories and sprints
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// RuntimeObservationTests.cs
|
||||
// Sprint: SPRINT_20260122_038_Scanner_ebpf_probe_type
|
||||
// Task: EBPF-001 - Add ProbeType field to RuntimeObservation
|
||||
// Description: Unit tests for RuntimeObservation and EbpfProbeType
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using StellaOps.RuntimeInstrumentation.Tetragon;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.RuntimeInstrumentation.Tetragon.Tests;
|
||||
|
||||
public class RuntimeObservationTests
|
||||
{
|
||||
[Fact]
|
||||
public void RuntimeObservation_WithoutProbeType_CreatesValidInstance()
|
||||
{
|
||||
// Arrange & Act - backward compatibility: ProbeType is optional
|
||||
var observation = new RuntimeObservation
|
||||
{
|
||||
ObservedAt = DateTimeOffset.UtcNow,
|
||||
SourceType = RuntimeObservationSourceType.Tetragon,
|
||||
ContainerId = "container123"
|
||||
};
|
||||
|
||||
// Assert
|
||||
Assert.Null(observation.ProbeType);
|
||||
Assert.Null(observation.FunctionName);
|
||||
Assert.Null(observation.FunctionAddress);
|
||||
Assert.Equal(RuntimeObservationSourceType.Tetragon, observation.SourceType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RuntimeObservation_WithProbeType_SetsFieldCorrectly()
|
||||
{
|
||||
// Arrange & Act
|
||||
var observation = new RuntimeObservation
|
||||
{
|
||||
ObservedAt = DateTimeOffset.UtcNow,
|
||||
SourceType = RuntimeObservationSourceType.Tetragon,
|
||||
ProbeType = EbpfProbeType.Uprobe,
|
||||
FunctionName = "SSL_connect",
|
||||
FunctionAddress = 0x7f1234567890
|
||||
};
|
||||
|
||||
// Assert
|
||||
Assert.Equal(EbpfProbeType.Uprobe, observation.ProbeType);
|
||||
Assert.Equal("SSL_connect", observation.FunctionName);
|
||||
Assert.Equal(0x7f1234567890, observation.FunctionAddress);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(EbpfProbeType.Kprobe)]
|
||||
[InlineData(EbpfProbeType.Kretprobe)]
|
||||
[InlineData(EbpfProbeType.Uprobe)]
|
||||
[InlineData(EbpfProbeType.Uretprobe)]
|
||||
[InlineData(EbpfProbeType.Tracepoint)]
|
||||
[InlineData(EbpfProbeType.Usdt)]
|
||||
[InlineData(EbpfProbeType.Fentry)]
|
||||
[InlineData(EbpfProbeType.Fexit)]
|
||||
public void RuntimeObservation_WithAllProbeTypes_Succeeds(EbpfProbeType probeType)
|
||||
{
|
||||
// Arrange & Act
|
||||
var observation = new RuntimeObservation
|
||||
{
|
||||
ObservedAt = DateTimeOffset.UtcNow,
|
||||
SourceType = RuntimeObservationSourceType.Tetragon,
|
||||
ProbeType = probeType
|
||||
};
|
||||
|
||||
// Assert
|
||||
Assert.Equal(probeType, observation.ProbeType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RuntimeObservation_RecordEquality_WorksWithNewFields()
|
||||
{
|
||||
// Arrange
|
||||
var timestamp = DateTimeOffset.UtcNow;
|
||||
|
||||
var obs1 = new RuntimeObservation
|
||||
{
|
||||
ObservedAt = timestamp,
|
||||
SourceType = RuntimeObservationSourceType.Tetragon,
|
||||
ProbeType = EbpfProbeType.Uprobe,
|
||||
FunctionName = "SSL_connect",
|
||||
FunctionAddress = 0x12345678
|
||||
};
|
||||
|
||||
var obs2 = new RuntimeObservation
|
||||
{
|
||||
ObservedAt = timestamp,
|
||||
SourceType = RuntimeObservationSourceType.Tetragon,
|
||||
ProbeType = EbpfProbeType.Uprobe,
|
||||
FunctionName = "SSL_connect",
|
||||
FunctionAddress = 0x12345678
|
||||
};
|
||||
|
||||
// Assert - records with same values are equal
|
||||
Assert.Equal(obs1, obs2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RuntimeObservation_RecordInequality_WithDifferentProbeType()
|
||||
{
|
||||
// Arrange
|
||||
var timestamp = DateTimeOffset.UtcNow;
|
||||
|
||||
var obs1 = new RuntimeObservation
|
||||
{
|
||||
ObservedAt = timestamp,
|
||||
SourceType = RuntimeObservationSourceType.Tetragon,
|
||||
ProbeType = EbpfProbeType.Uprobe
|
||||
};
|
||||
|
||||
var obs2 = new RuntimeObservation
|
||||
{
|
||||
ObservedAt = timestamp,
|
||||
SourceType = RuntimeObservationSourceType.Tetragon,
|
||||
ProbeType = EbpfProbeType.Uretprobe
|
||||
};
|
||||
|
||||
// Assert - different probe types are not equal
|
||||
Assert.NotEqual(obs1, obs2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RuntimeObservation_WithAllFields_CreatesCompleteInstance()
|
||||
{
|
||||
// Arrange
|
||||
var timestamp = DateTimeOffset.UtcNow;
|
||||
var observationId = Guid.NewGuid().ToString();
|
||||
|
||||
// Act
|
||||
var observation = new RuntimeObservation
|
||||
{
|
||||
ObservedAt = timestamp,
|
||||
ObservationCount = 5,
|
||||
StackSampleHash = "sha256:abc123",
|
||||
ProcessId = 12345,
|
||||
ContainerId = "container123",
|
||||
PodName = "my-pod",
|
||||
Namespace = "production",
|
||||
SourceType = RuntimeObservationSourceType.Tetragon,
|
||||
ObservationId = observationId,
|
||||
ProbeType = EbpfProbeType.Uprobe,
|
||||
FunctionName = "crypto_encrypt",
|
||||
FunctionAddress = 0x7f1234567890
|
||||
};
|
||||
|
||||
// Assert - all fields set correctly
|
||||
Assert.Equal(timestamp, observation.ObservedAt);
|
||||
Assert.Equal(5, observation.ObservationCount);
|
||||
Assert.Equal("sha256:abc123", observation.StackSampleHash);
|
||||
Assert.Equal(12345, observation.ProcessId);
|
||||
Assert.Equal("container123", observation.ContainerId);
|
||||
Assert.Equal("my-pod", observation.PodName);
|
||||
Assert.Equal("production", observation.Namespace);
|
||||
Assert.Equal(RuntimeObservationSourceType.Tetragon, observation.SourceType);
|
||||
Assert.Equal(observationId, observation.ObservationId);
|
||||
Assert.Equal(EbpfProbeType.Uprobe, observation.ProbeType);
|
||||
Assert.Equal("crypto_encrypt", observation.FunctionName);
|
||||
Assert.Equal(0x7f1234567890, observation.FunctionAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public class EbpfProbeTypeTests
|
||||
{
|
||||
[Fact]
|
||||
public void EbpfProbeType_HasAllExpectedValues()
|
||||
{
|
||||
// Assert - enum has all expected probe types
|
||||
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Kprobe));
|
||||
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Kretprobe));
|
||||
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Uprobe));
|
||||
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Uretprobe));
|
||||
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Tracepoint));
|
||||
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Usdt));
|
||||
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Fentry));
|
||||
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Fexit));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EbpfProbeType_EnumCount_Is8()
|
||||
{
|
||||
// Assert - exactly 8 probe types as specified in EBPF-001
|
||||
var values = Enum.GetValues<EbpfProbeType>();
|
||||
Assert.Equal(8, values.Length);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Kprobe", EbpfProbeType.Kprobe)]
|
||||
[InlineData("Kretprobe", EbpfProbeType.Kretprobe)]
|
||||
[InlineData("Uprobe", EbpfProbeType.Uprobe)]
|
||||
[InlineData("Uretprobe", EbpfProbeType.Uretprobe)]
|
||||
[InlineData("Tracepoint", EbpfProbeType.Tracepoint)]
|
||||
[InlineData("Usdt", EbpfProbeType.Usdt)]
|
||||
[InlineData("Fentry", EbpfProbeType.Fentry)]
|
||||
[InlineData("Fexit", EbpfProbeType.Fexit)]
|
||||
public void EbpfProbeType_ParsesFromString(string name, EbpfProbeType expected)
|
||||
{
|
||||
// Act
|
||||
var parsed = Enum.Parse<EbpfProbeType>(name);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, parsed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// TetragonEventAdapterProbeTypeTests.cs
|
||||
// Sprint: SPRINT_20260122_038_Scanner_ebpf_probe_type
|
||||
// Task: EBPF-002 - Update Tetragon event parser to populate ProbeType
|
||||
// Description: Integration tests for Tetragon event parser probe type mapping
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using Moq;
|
||||
using StellaOps.RuntimeInstrumentation.Tetragon;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.RuntimeInstrumentation.Tetragon.Tests;
|
||||
|
||||
public class TetragonEventAdapterProbeTypeTests
|
||||
{
|
||||
private readonly Mock<ISymbolResolver> _mockSymbolResolver;
|
||||
private readonly Mock<IHotSymbolIndex> _mockHotSymbolIndex;
|
||||
private readonly Mock<ILogger<TetragonEventAdapter>> _mockLogger;
|
||||
private readonly TetragonEventAdapter _adapter;
|
||||
|
||||
public TetragonEventAdapterProbeTypeTests()
|
||||
{
|
||||
_mockSymbolResolver = new Mock<ISymbolResolver>();
|
||||
_mockHotSymbolIndex = new Mock<IHotSymbolIndex>();
|
||||
_mockLogger = new Mock<ILogger<TetragonEventAdapter>>();
|
||||
|
||||
var options = new TestOptions<TetragonAdapterOptions>(new TetragonAdapterOptions
|
||||
{
|
||||
UpdateHotSymbolIndex = false // Disable for these tests
|
||||
});
|
||||
|
||||
_adapter = new TetragonEventAdapter(
|
||||
_mockSymbolResolver.Object,
|
||||
_mockHotSymbolIndex.Object,
|
||||
options,
|
||||
_mockLogger.Object);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TetragonEventType.Kprobe, EbpfProbeType.Kprobe)]
|
||||
[InlineData(TetragonEventType.Kretprobe, EbpfProbeType.Kretprobe)]
|
||||
[InlineData(TetragonEventType.Uprobe, EbpfProbeType.Uprobe)]
|
||||
[InlineData(TetragonEventType.Uretprobe, EbpfProbeType.Uretprobe)]
|
||||
[InlineData(TetragonEventType.Tracepoint, EbpfProbeType.Tracepoint)]
|
||||
[InlineData(TetragonEventType.Usdt, EbpfProbeType.Usdt)]
|
||||
[InlineData(TetragonEventType.Fentry, EbpfProbeType.Fentry)]
|
||||
[InlineData(TetragonEventType.Fexit, EbpfProbeType.Fexit)]
|
||||
public async Task AdaptAsync_WithEbpfProbeType_MapsCorrectly(
|
||||
TetragonEventType tetragonType,
|
||||
EbpfProbeType expectedProbeType)
|
||||
{
|
||||
// Arrange
|
||||
var tetragonEvent = CreateTestEvent(tetragonType, "test_function", 0x12345678);
|
||||
|
||||
// Act
|
||||
var result = await _adapter.AdaptAsync(tetragonEvent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(expectedProbeType, result.ProbeType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TetragonEventType.ProcessExec)]
|
||||
[InlineData(TetragonEventType.ProcessExit)]
|
||||
public async Task AdaptAsync_WithProcessEvent_ReturnsNullProbeType(TetragonEventType tetragonType)
|
||||
{
|
||||
// Arrange
|
||||
var tetragonEvent = CreateTestEvent(tetragonType, null, null);
|
||||
|
||||
// Act
|
||||
var result = await _adapter.AdaptAsync(tetragonEvent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Null(result.ProbeType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdaptAsync_WithFunctionAddress_PopulatesFunctionAddress()
|
||||
{
|
||||
// Arrange
|
||||
const ulong expectedAddress = 0x7f1234567890;
|
||||
var tetragonEvent = CreateTestEvent(TetragonEventType.Uprobe, "SSL_connect", expectedAddress);
|
||||
|
||||
// Act
|
||||
var result = await _adapter.AdaptAsync(tetragonEvent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(expectedAddress, result.FunctionAddress);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdaptAsync_WithoutStackTrace_HasNullFunctionAddress()
|
||||
{
|
||||
// Arrange
|
||||
var tetragonEvent = new TetragonEvent
|
||||
{
|
||||
Type = TetragonEventType.Uprobe,
|
||||
Time = DateTimeOffset.UtcNow,
|
||||
FunctionName = "SSL_connect",
|
||||
Process = new TetragonProcess { Pid = 1234 },
|
||||
StackTrace = null // No stack trace
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _adapter.AdaptAsync(tetragonEvent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Null(result.FunctionAddress);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TetragonEventType.Kprobe, RuntimeEventSource.Syscall)]
|
||||
[InlineData(TetragonEventType.Kretprobe, RuntimeEventSource.Syscall)]
|
||||
[InlineData(TetragonEventType.Fentry, RuntimeEventSource.Syscall)]
|
||||
[InlineData(TetragonEventType.Fexit, RuntimeEventSource.Syscall)]
|
||||
[InlineData(TetragonEventType.Uprobe, RuntimeEventSource.LibraryLoad)]
|
||||
[InlineData(TetragonEventType.Uretprobe, RuntimeEventSource.LibraryLoad)]
|
||||
[InlineData(TetragonEventType.Tracepoint, RuntimeEventSource.Tracepoint)]
|
||||
[InlineData(TetragonEventType.Usdt, RuntimeEventSource.Tracepoint)]
|
||||
public async Task AdaptAsync_MapsSourceCorrectly(
|
||||
TetragonEventType tetragonType,
|
||||
RuntimeEventSource expectedSource)
|
||||
{
|
||||
// Arrange
|
||||
var tetragonEvent = CreateTestEvent(tetragonType, "test_func", 0x12345678);
|
||||
|
||||
// Act
|
||||
var result = await _adapter.AdaptAsync(tetragonEvent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(expectedSource, result.Source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdaptAsync_WithFullContext_PopulatesAllFields()
|
||||
{
|
||||
// Arrange
|
||||
var timestamp = DateTimeOffset.UtcNow;
|
||||
var tetragonEvent = new TetragonEvent
|
||||
{
|
||||
Type = TetragonEventType.Uprobe,
|
||||
Time = timestamp,
|
||||
FunctionName = "SSL_connect",
|
||||
Process = new TetragonProcess
|
||||
{
|
||||
Pid = 1234,
|
||||
Tid = 5678,
|
||||
Binary = "/usr/lib/libssl.so",
|
||||
Docker = "container123",
|
||||
Pod = new TetragonPod
|
||||
{
|
||||
Name = "my-pod",
|
||||
Namespace = "production",
|
||||
Container = new TetragonContainer { Id = "container123", Name = "app" }
|
||||
}
|
||||
},
|
||||
StackTrace = new TetragonStackTrace
|
||||
{
|
||||
Frames = new List<TetragonStackFrame>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Address = 0x7f1234567890,
|
||||
Offset = 0x100,
|
||||
Module = "libssl.so",
|
||||
Symbol = "SSL_connect",
|
||||
Flags = StackFrameFlags.User
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_mockSymbolResolver
|
||||
.Setup(r => r.ResolveAsync(It.IsAny<ulong>(), It.IsAny<string?>(), It.IsAny<string?>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ResolvedSymbol
|
||||
{
|
||||
Name = "SSL_connect",
|
||||
DemangledName = "SSL_connect",
|
||||
Confidence = 1.0
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await _adapter.AdaptAsync(tetragonEvent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(EbpfProbeType.Uprobe, result.ProbeType);
|
||||
Assert.Equal(0x7f1234567890ul, result.FunctionAddress);
|
||||
Assert.Equal("SSL_connect", result.SyscallName);
|
||||
Assert.Equal(1234, result.ProcessId);
|
||||
Assert.Equal(5678, result.ThreadId);
|
||||
Assert.Equal("container123", result.ContainerId);
|
||||
Assert.Equal("my-pod", result.PodName);
|
||||
Assert.Equal("production", result.Namespace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdaptAsync_NullEvent_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = await _adapter.AdaptAsync(null!);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
private static TetragonEvent CreateTestEvent(
|
||||
TetragonEventType type,
|
||||
string? functionName,
|
||||
ulong? address)
|
||||
{
|
||||
return new TetragonEvent
|
||||
{
|
||||
Type = type,
|
||||
Time = DateTimeOffset.UtcNow,
|
||||
FunctionName = functionName,
|
||||
Process = new TetragonProcess
|
||||
{
|
||||
Pid = 1234,
|
||||
Tid = 5678,
|
||||
Binary = "/usr/bin/test"
|
||||
},
|
||||
StackTrace = address.HasValue
|
||||
? new TetragonStackTrace
|
||||
{
|
||||
Frames = new List<TetragonStackFrame>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Address = address.Value,
|
||||
Offset = 0x100,
|
||||
Module = "test",
|
||||
Symbol = functionName,
|
||||
Flags = StackFrameFlags.User
|
||||
}
|
||||
}
|
||||
}
|
||||
: null
|
||||
};
|
||||
}
|
||||
|
||||
// Helper class for options
|
||||
private sealed class TestOptions<T> : IOptions<T> where T : class
|
||||
{
|
||||
public TestOptions(T value) => Value = value;
|
||||
public T Value { get; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user