diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
deleted file mode 100644
index 5bd5ffb..0000000
--- a/.config/dotnet-tools.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "version": 1,
- "isRoot": true,
- "tools": {
- "electronnet.cli": {
- "version": "23.6.1",
- "commands": [
- "electronize"
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml
index 15ad937..030bc08 100644
--- a/.github/workflows/build-ci.yml
+++ b/.github/workflows/build-ci.yml
@@ -20,5 +20,5 @@ jobs:
run: dotnet restore
- name: Build
run: dotnet build --no-restore
-# - name: Test
-# run: dotnet test --no-build --verbosity normal
+ - name: Test
+ run: dotnet test --no-build --verbosity normal --filter "Category!=Integration"
diff --git a/tests/LightQueryProfiler.Shared.UnitTests/LightQueryProfiler.Shared.UnitTests.csproj b/tests/LightQueryProfiler.Shared.UnitTests/LightQueryProfiler.Shared.UnitTests.csproj
index e89b3b4..96d8d9d 100644
--- a/tests/LightQueryProfiler.Shared.UnitTests/LightQueryProfiler.Shared.UnitTests.csproj
+++ b/tests/LightQueryProfiler.Shared.UnitTests/LightQueryProfiler.Shared.UnitTests.csproj
@@ -27,4 +27,10 @@
+
+
+ PreserveNewest
+
+
+
diff --git a/tests/LightQueryProfiler.Shared.UnitTests/README.md b/tests/LightQueryProfiler.Shared.UnitTests/README.md
new file mode 100644
index 0000000..2d86845
--- /dev/null
+++ b/tests/LightQueryProfiler.Shared.UnitTests/README.md
@@ -0,0 +1,100 @@
+# LightQueryProfiler.Shared.UnitTests
+
+This project contains unit and integration tests for the LightQueryProfiler.Shared library.
+
+## Test Structure
+
+```
+LightQueryProfiler.Shared.UnitTests/
+├── Enums/ # Tests for enumerations
+├── Factories/ # Tests for factory classes
+├── Models/ # Tests for data models
+├── Services/ # Tests for service classes
+├── TestFiles/ # Test data files
+└── docs/ # Documentation
+```
+
+## Running Tests
+
+### All Tests (Local Development)
+
+```bash
+dotnet test
+```
+
+### Unit Tests Only (CI/CD)
+
+```bash
+dotnet test --filter "Category!=Integration"
+```
+
+### Integration Tests Only
+
+```bash
+dotnet test --filter "Category=Integration"
+```
+
+## Test Categories
+
+### Unit Tests (66 tests)
+- No external dependencies required
+- Run automatically in CI/CD pipeline
+- Fast execution
+
+### Integration Tests (3 tests)
+- Require local SQL Server instance
+- Only run locally by developers
+- Marked with `[Trait("Category", "Integration")]`
+- Skipped in GitHub Actions CI/CD
+
+## Integration Test Requirements
+
+Integration tests require:
+- SQL Server running on `localhost`
+- Windows Authentication enabled
+- Permissions to create Extended Events sessions
+- Access to `master` database
+
+For more details, see [Integration Tests Documentation](docs/INTEGRATION_TESTS.md)
+
+## Test Framework
+
+- **Framework**: xUnit v3
+- **Mocking**: Moq
+- **Target Framework**: .NET 10.0
+
+## CI/CD Integration
+
+The GitHub Actions workflow automatically excludes integration tests using:
+```bash
+dotnet test --no-build --verbosity normal --filter "Category!=Integration"
+```
+
+This ensures that CI builds don't fail due to missing SQL Server instances in the build environment.
+
+## Best Practices
+
+1. **Keep tests independent**: Each test should be able to run in isolation
+2. **Use meaningful names**: Follow the pattern `MethodName_WhenCondition_ExpectedResult`
+3. **Arrange-Act-Assert**: Structure tests using the AAA pattern
+4. **Mark integration tests**: Use `[Trait("Category", "Integration")]` for tests requiring external resources
+5. **Avoid test interdependencies**: Tests should not rely on execution order
+
+## Adding New Tests
+
+When adding new tests:
+
+1. Place tests in the appropriate folder based on the class being tested
+2. Mirror the source code structure
+3. Use `[Fact]` for single test cases
+4. Use `[Theory]` with `[InlineData]` for parameterized tests
+5. Add `[Trait("Category", "Integration")]` if the test requires external resources
+
+## Code Coverage
+
+To generate code coverage locally:
+
+```bash
+dotnet tool install -g dotnet-coverage
+dotnet-coverage collect -f cobertura -o coverage.cobertura.xml dotnet test
+```
diff --git a/tests/LightQueryProfiler.Shared.UnitTests/Services/ProfilerEventUniqueKeyIntegrationTests.cs b/tests/LightQueryProfiler.Shared.UnitTests/Services/ProfilerEventUniqueKeyIntegrationTests.cs
index 7eaf6b9..27c44a1 100644
--- a/tests/LightQueryProfiler.Shared.UnitTests/Services/ProfilerEventUniqueKeyIntegrationTests.cs
+++ b/tests/LightQueryProfiler.Shared.UnitTests/Services/ProfilerEventUniqueKeyIntegrationTests.cs
@@ -16,7 +16,7 @@ public ProfilerEventUniqueKeyIntegrationTests()
public void ParseRealXml_AllEventsHaveUniqueKeys()
{
// Arrange
- string sourceFile = "..\\..\\..\\TestFiles\\RingBufferTarget.xml";
+ string sourceFile = Path.Combine("TestFiles", "RingBufferTarget.xml");
if (!File.Exists(sourceFile))
{
Assert.Fail("Test file not found: " + sourceFile);
@@ -38,7 +38,7 @@ public void ParseRealXml_AllEventsHaveUniqueKeys()
public void ParseRealXml_AllEventsHaveEventSequence()
{
// Arrange
- string sourceFile = "..\\..\\..\\TestFiles\\RingBufferTarget.xml";
+ string sourceFile = Path.Combine("TestFiles", "RingBufferTarget.xml");
if (!File.Exists(sourceFile))
{
Assert.Fail("Test file not found: " + sourceFile);
@@ -60,7 +60,7 @@ public void ParseRealXml_AllEventsHaveEventSequence()
public void ParseRealXml_EventKeysAreSequenceBased()
{
// Arrange
- string sourceFile = "..\\..\\..\\TestFiles\\RingBufferTarget.xml";
+ string sourceFile = Path.Combine("TestFiles", "RingBufferTarget.xml");
if (!File.Exists(sourceFile))
{
Assert.Fail("Test file not found: " + sourceFile);
@@ -81,7 +81,7 @@ public void ParseRealXml_EventKeysAreSequenceBased()
public void ParseRealXml_DifferentEventTypesWithSameSequenceDoNotCollide()
{
// Arrange
- string sourceFile = "..\\..\\..\\TestFiles\\RingBufferTarget.xml";
+ string sourceFile = Path.Combine("TestFiles", "RingBufferTarget.xml");
if (!File.Exists(sourceFile))
{
Assert.Fail("Test file not found: " + sourceFile);
@@ -110,7 +110,7 @@ public void ParseRealXml_DifferentEventTypesWithSameSequenceDoNotCollide()
public void ParseRealXml_EventSequenceValuesAreMonotonicallyIncreasing()
{
// Arrange
- string sourceFile = "..\\..\\..\\TestFiles\\RingBufferTarget.xml";
+ string sourceFile = Path.Combine("TestFiles", "RingBufferTarget.xml");
if (!File.Exists(sourceFile))
{
Assert.Fail("Test file not found: " + sourceFile);
@@ -137,7 +137,7 @@ public void ParseRealXml_EventSequenceValuesAreMonotonicallyIncreasing()
public void ParseRealXml_EventKeysMatchExpectedFormat()
{
// Arrange
- string sourceFile = "..\\..\\..\\TestFiles\\RingBufferTarget.xml";
+ string sourceFile = Path.Combine("TestFiles", "RingBufferTarget.xml");
if (!File.Exists(sourceFile))
{
Assert.Fail("Test file not found: " + sourceFile);
diff --git a/tests/LightQueryProfiler.Shared.UnitTests/Services/ProfilerServiceUnitTests.cs b/tests/LightQueryProfiler.Shared.UnitTests/Services/ProfilerServiceUnitTests.cs
index 2711d59..bf8bb60 100644
--- a/tests/LightQueryProfiler.Shared.UnitTests/Services/ProfilerServiceUnitTests.cs
+++ b/tests/LightQueryProfiler.Shared.UnitTests/Services/ProfilerServiceUnitTests.cs
@@ -7,6 +7,10 @@
namespace LightQueryProfiler.Shared.UnitTests.Services
{
+ ///
+ /// Integration tests that require a local SQL Server instance.
+ /// These tests are skipped in CI environments.
+ ///
public class ProfilerServiceUnitTests
{
private readonly IApplicationDbContext _applicationDbContext;
@@ -27,12 +31,14 @@ public ProfilerServiceUnitTests()
}
[Fact]
+ [Trait("Category", "Integration")]
public void StartProfiling()
{
_profilerService.StartProfiling(sessionName, _baseProfilerSessionTemplate);
}
[Fact]
+ [Trait("Category", "Integration")]
public async Task GetLastEventsAsync()
{
List? events;
@@ -47,6 +53,7 @@ public async Task GetLastEventsAsync()
[Fact]
+ [Trait("Category", "Integration")]
public void StopProfiling()
{
_profilerService.StopProfiling(sessionName);
diff --git a/tests/LightQueryProfiler.Shared.UnitTests/Services/XEventServiceUnitTests.cs b/tests/LightQueryProfiler.Shared.UnitTests/Services/XEventServiceUnitTests.cs
index a47c998..4a8579a 100644
--- a/tests/LightQueryProfiler.Shared.UnitTests/Services/XEventServiceUnitTests.cs
+++ b/tests/LightQueryProfiler.Shared.UnitTests/Services/XEventServiceUnitTests.cs
@@ -15,7 +15,7 @@ public XEventServiceUnitTests()
[Fact]
public void Parse()
{
- string sourceFile = "..\\..\\..\\TestFiles\\RingBufferTarget.xml";
+ string sourceFile = Path.Combine("TestFiles", "RingBufferTarget.xml");
if (File.Exists(sourceFile) == false)
{
throw new Exception("File not found");
diff --git a/tests/LightQueryProfiler.Shared.UnitTests/docs/INTEGRATION_TESTS.md b/tests/LightQueryProfiler.Shared.UnitTests/docs/INTEGRATION_TESTS.md
new file mode 100644
index 0000000..a27043c
--- /dev/null
+++ b/tests/LightQueryProfiler.Shared.UnitTests/docs/INTEGRATION_TESTS.md
@@ -0,0 +1,105 @@
+# Integration Tests Documentation
+
+## Overview
+
+This project contains both unit tests and integration tests. Integration tests require a local SQL Server instance and are marked with the `[Trait("Category", "Integration")]` attribute.
+
+## Test Categories
+
+### Unit Tests
+- **Location**: All test files in `LightQueryProfiler.Shared.UnitTests`
+- **Requirements**: No external dependencies
+- **Execution**: Run automatically in CI/CD pipelines
+
+### Integration Tests
+- **Location**: `Services/ProfilerServiceUnitTests.cs`
+- **Requirements**: Local SQL Server instance running on `localhost`
+- **Execution**: Only run locally by developers
+
+## Running Tests
+
+### Running All Tests (Local Development)
+
+To run all tests including integration tests:
+
+```bash
+dotnet test
+```
+
+### Running Only Unit Tests (CI/CD)
+
+To run only unit tests (excluding integration tests):
+
+```bash
+dotnet test --filter "Category!=Integration"
+```
+
+This is the default behavior in the GitHub Actions CI/CD pipeline.
+
+### Running Only Integration Tests
+
+To run only integration tests:
+
+```bash
+dotnet test --filter "Category=Integration"
+```
+
+## Integration Test Requirements
+
+The integration tests in `ProfilerServiceUnitTests.cs` require:
+
+1. **SQL Server**: A local SQL Server instance accessible at `localhost`
+2. **Connection String**: `Server=localhost;Database=master;Trusted_Connection=True;TrustServerCertificate=True;`
+3. **Permissions**: The Windows user running the tests must have permissions to:
+ - Create and manage Extended Events sessions
+ - Access the `master` database
+
+## CI/CD Behavior
+
+The GitHub Actions workflow (`.github/workflows/build-ci.yml`) automatically excludes integration tests using the filter `--filter "Category!=Integration"`. This prevents CI builds from failing due to missing SQL Server instances in the GitHub-hosted runners.
+
+## Adding New Integration Tests
+
+When adding new tests that require database connectivity or other external resources:
+
+1. Add the `[Trait("Category", "Integration")]` attribute to the test method
+2. Document any specific requirements in this file
+3. Ensure the test can be skipped without breaking the CI/CD pipeline
+
+### Example
+
+```csharp
+[Fact]
+[Trait("Category", "Integration")]
+public async Task MyNewTest_RequiresDatabase()
+{
+ // Test implementation that requires SQL Server
+}
+```
+
+## Troubleshooting
+
+### Integration Tests Fail Locally
+
+If integration tests fail on your local machine:
+
+1. Verify SQL Server is running: `sqlcmd -S localhost -E -Q "SELECT @@VERSION"`
+2. Check Windows Authentication is enabled
+3. Verify your user has necessary permissions
+4. Check the connection string in `ProfilerServiceUnitTests.cs`
+
+### Tests Run Slowly
+
+Integration tests are slower than unit tests because they:
+- Connect to a real database
+- Create and manage Extended Events sessions
+- Process real event data
+
+This is expected behavior.
+
+## Best Practices
+
+1. **Keep integration tests separate**: Don't mix database operations in unit tests
+2. **Use traits consistently**: Always use `[Trait("Category", "Integration")]` for tests requiring external resources
+3. **Document dependencies**: Update this file when adding new integration test requirements
+4. **Local testing**: Run integration tests locally before committing changes to database-related code
\ No newline at end of file