Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions .config/dotnet-tools.json

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/build-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@
<ProjectReference Include="..\..\src\LightQueryProfiler.Shared\LightQueryProfiler.Shared.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="TestFiles\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
100 changes: 100 additions & 0 deletions tests/LightQueryProfiler.Shared.UnitTests/README.md
Original file line number Diff line number Diff line change
@@ -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
```
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

namespace LightQueryProfiler.Shared.UnitTests.Services
{
/// <summary>
/// Integration tests that require a local SQL Server instance.
/// These tests are skipped in CI environments.
/// </summary>
public class ProfilerServiceUnitTests
{
private readonly IApplicationDbContext _applicationDbContext;
Expand All @@ -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<ProfilerEvent>? events;
Expand All @@ -47,6 +53,7 @@ public async Task GetLastEventsAsync()


[Fact]
[Trait("Category", "Integration")]
public void StopProfiling()
{
_profilerService.StopProfiling(sessionName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
105 changes: 105 additions & 0 deletions tests/LightQueryProfiler.Shared.UnitTests/docs/INTEGRATION_TESTS.md
Original file line number Diff line number Diff line change
@@ -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
Loading