diff --git a/critterfolio/CritterFolio/CritterFolio/Services/DatabaseService.cs b/critterfolio/CritterFolio/CritterFolio/Services/DatabaseService.cs index 07b8c01..838df70 100644 --- a/critterfolio/CritterFolio/CritterFolio/Services/DatabaseService.cs +++ b/critterfolio/CritterFolio/CritterFolio/Services/DatabaseService.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using CritterFolio.DataModels; -using Microsoft.VisualBasic.FileIO; +using System.Reflection; using SQLite; namespace CritterFolio.Services; @@ -32,6 +32,13 @@ public static class DatabaseService } } + public static bool TestDbConnection() + { + return _db != null; + } + + + public static async Task ClearAllTables() { await Init(); @@ -183,4 +190,96 @@ public static class DatabaseService } #endregion + + + + #region Tests + #if DEBUG + + public class ColumnInfo + { + public int Cid { get; set; } + public string Name { get; set; } + public string Type { get; set; } + public int NotNull { get; set; } + public string Dflt_Value { get; set; } + public int Pk { get; set; } + } + + public static async Task VerifySchemaAgainstModel() + { + if (_db is null) + { + Console.WriteLine("Error: Database connection is null."); + return false; + } + + // Automatically determine the table name from the [Table] attribute or the Class name + var modelType = typeof(T); + var tableAttribute = modelType.GetCustomAttribute(); + string tableName = tableAttribute != null ? tableAttribute.Name : modelType.Name; + + Console.WriteLine($"--- Schema Verification: {tableName} (Class: {modelType.Name}) ---"); + + var dbColumns = await _db.QueryAsync($"PRAGMA table_info('{tableName}')"); + var dbColumnNames = dbColumns.Select(c => c.Name.ToLower()).ToList(); + + if (!dbColumns.Any()) + { + Console.WriteLine($"Result: Failure. Table '{tableName}' does not exist in the database."); + return false; + } + + Console.WriteLine($"Database columns for table '{tableName}' ({dbColumns.Count}):"); + foreach (var col in dbColumns) + { + Console.WriteLine($" Column: {col.Name} | Type: {col.Type} | PK: {col.Pk == 1}"); + } + + var modelProperties = modelType + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.GetCustomAttribute() == null) + .ToList(); + + Console.WriteLine($"Model properties for class '{modelType.Name}' ({modelProperties.Count}):"); + foreach (var prop in modelProperties) + { + // Detect if the property has a [Column] attribute name override + var colAttr = prop.GetCustomAttribute(); + string expectedColName = colAttr != null ? colAttr.Name : prop.Name; + Console.WriteLine($" Property: {prop.Name} (Expected Column: {expectedColName}) | Type: {prop.PropertyType.Name}"); + } + + // Match based on the expected column name (either property name or [Column] attribute) + var expectedColumnNames = modelProperties.Select(p => { + var attr = p.GetCustomAttribute(); + return (attr != null ? attr.Name : p.Name).ToLower(); + }).ToList(); + + var missingInDb = expectedColumnNames.Where(p => !dbColumnNames.Contains(p)).ToList(); + var extraInDb = dbColumnNames.Where(c => !expectedColumnNames.Contains(c)).ToList(); + + bool isMatch = !missingInDb.Any(); + + if (missingInDb.Any()) + { + Console.WriteLine($"Result: Failure. Columns missing in database: {string.Join(", ", missingInDb)}"); + } + + if (extraInDb.Any()) + { + Console.WriteLine($"Note: Database contains extra columns: {string.Join(", ", extraInDb)}"); + } + + if (isMatch) + { + Console.WriteLine($"Result: Success. Class '{modelType.Name}' matches table '{tableName}'."); + } + + Console.WriteLine("-------------------------------------------\n"); + return isMatch; + } + + #endif + #endregion } \ No newline at end of file diff --git a/critterfolio/CritterFolio/CritterFolioTests/UnitTest1.cs b/critterfolio/CritterFolio/CritterFolioTests/UnitTest1.cs index 689b72e..a8b11b9 100644 --- a/critterfolio/CritterFolio/CritterFolioTests/UnitTest1.cs +++ b/critterfolio/CritterFolio/CritterFolioTests/UnitTest1.cs @@ -8,20 +8,24 @@ namespace CritterFolioTests; public class Tests { - [SetUp] - public void Setup() + [Test] + public async Task TestDatabaseCreation() { + await DatabaseService.Init(); + + Assert.That(DatabaseService.TestDbConnection(), Is.True); } [Test] - public async Task TestAddingCritter() + public async Task TestDatabaseSchema() { - Assert.That(await DatabaseService.AddCritter(new Critter{Name = "Test Critter"}), Is.True); - } - - [Test] - public async Task TestGettingCritterThatDoesntExist() - { - Assert.That(await DatabaseService.GetCritter(-1), Is.False); + await DatabaseService.Init(); + + Assert.Multiple(async () => + { + Assert.That(await DatabaseService.VerifySchemaAgainstModel(), Is.True); + Assert.That(await DatabaseService.VerifySchemaAgainstModel(), Is.True); + Assert.That(await DatabaseService.VerifySchemaAgainstModel(), Is.True); + }); } } \ No newline at end of file