From 61f927bd0e1d1fecd9d4749ea904f5bec0ca3054 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 6 Jan 2026 13:30:34 -0500 Subject: [PATCH] feat(tests): add database test infrastructure Add DatabaseTestBase.cs with xUnit Collection for test isolation: - DatabaseTestCollection disables parallel execution - DatabaseTestFixture verifies database connectivity - DatabaseTestBase provides connection and cleanup helpers - InsertTestSearchAsync for creating test data with SearchCriteria - InsertTestSearchWithRawCriteriaAsync for testing invalid JSON scenarios Also adds required packages: Dapper, FluentAssertions, Microsoft.Data.SqlClient --- .../Infrastructure/DatabaseTestBase.cs | 98 +++++++++++++++++++ .../JdeScoping.Database.Tests.csproj | 5 + .../JdeScoping.Database.Tests/Placeholder.cs | 8 -- 3 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 NEW/tests/JdeScoping.Database.Tests/Infrastructure/DatabaseTestBase.cs delete mode 100644 NEW/tests/JdeScoping.Database.Tests/Placeholder.cs diff --git a/NEW/tests/JdeScoping.Database.Tests/Infrastructure/DatabaseTestBase.cs b/NEW/tests/JdeScoping.Database.Tests/Infrastructure/DatabaseTestBase.cs new file mode 100644 index 0000000..29c0d9b --- /dev/null +++ b/NEW/tests/JdeScoping.Database.Tests/Infrastructure/DatabaseTestBase.cs @@ -0,0 +1,98 @@ +using System.Text.Json; +using Dapper; +using JdeScoping.Core.Models.Search; +using Microsoft.Data.SqlClient; + +namespace JdeScoping.Database.Tests.Infrastructure; + +/// +/// xUnit Collection definition to disable parallel execution for database tests. +/// All test classes using [Collection("DatabaseTests")] run sequentially. +/// +[CollectionDefinition("DatabaseTests")] +public class DatabaseTestCollection : ICollectionFixture +{ +} + +/// +/// Shared fixture for database tests. Ensures database is available. +/// +public class DatabaseTestFixture : IAsyncLifetime +{ + /// + /// Connection string for the test database. + /// Uses SA credentials from db_info.md for local development. + /// + public const string ConnectionString = + "Server=localhost,1434;Database=ScopingTool;User Id=sa;Password=ScopingTool_SA_2024Dev;TrustServerCertificate=true"; + + public async Task InitializeAsync() + { + // Verify database is accessible + using var connection = new SqlConnection(ConnectionString); + await connection.OpenAsync(); + } + + public Task DisposeAsync() => Task.CompletedTask; +} + +/// +/// Base class for database function tests. +/// Each test gets its own connection and cleans up created data. +/// +[Collection("DatabaseTests")] +public abstract class DatabaseTestBase : IAsyncLifetime +{ + protected SqlConnection Connection { get; private set; } = null!; + + private readonly List _createdSearchIds = []; + + public async Task InitializeAsync() + { + Connection = new SqlConnection(DatabaseTestFixture.ConnectionString); + await Connection.OpenAsync(); + } + + public async Task DisposeAsync() + { + // Clean up test data + foreach (var id in _createdSearchIds) + { + await Connection.ExecuteAsync("DELETE FROM dbo.Search WHERE ID = @Id", new { Id = id }); + } + + await Connection.DisposeAsync(); + } + + /// + /// Insert a test search with the given criteria and return the ID. + /// + protected async Task InsertTestSearchAsync(SearchCriteria criteria, string userName = "testuser", string name = "Test Search") + { + var criteriaJson = JsonSerializer.Serialize(criteria); + + var id = await Connection.QuerySingleAsync( + @"INSERT INTO dbo.Search (UserName, Name, Status, Criteria) + OUTPUT INSERTED.ID + VALUES (@UserName, @Name, 0, @Criteria)", + new { UserName = userName, Name = name, Criteria = criteriaJson }); + + _createdSearchIds.Add(id); + return id; + } + + /// + /// Insert a test search with raw criteria JSON (for testing invalid JSON scenarios). + /// + protected async Task InsertTestSearchWithRawCriteriaAsync(string? criteriaJson, string userName = "testuser", string name = "Test Search") + { + var id = await Connection.QuerySingleAsync( + @"INSERT INTO dbo.Search (UserName, Name, Status, Criteria) + OUTPUT INSERTED.ID + VALUES (@UserName, @Name, 0, @Criteria)", + new { UserName = userName, Name = name, Criteria = criteriaJson }); + + _createdSearchIds.Add(id); + return id; + } +} diff --git a/NEW/tests/JdeScoping.Database.Tests/JdeScoping.Database.Tests.csproj b/NEW/tests/JdeScoping.Database.Tests/JdeScoping.Database.Tests.csproj index d7ef4c0..8363820 100644 --- a/NEW/tests/JdeScoping.Database.Tests/JdeScoping.Database.Tests.csproj +++ b/NEW/tests/JdeScoping.Database.Tests/JdeScoping.Database.Tests.csproj @@ -5,10 +5,14 @@ enable enable false + true + + + @@ -21,6 +25,7 @@ + diff --git a/NEW/tests/JdeScoping.Database.Tests/Placeholder.cs b/NEW/tests/JdeScoping.Database.Tests/Placeholder.cs deleted file mode 100644 index 8218846..0000000 --- a/NEW/tests/JdeScoping.Database.Tests/Placeholder.cs +++ /dev/null @@ -1,8 +0,0 @@ -// This file exists to ensure the test project compiles. -// Add tests here as needed. -namespace JdeScoping.Database.Tests; - -public class Placeholder -{ - // Tests will be added here -}