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