From 4682520d3c3cd1bddc483400e1a1a7ed26c75c47 Mon Sep 17 00:00:00 2001 From: javiertuya <10879637+javiertuya@users.noreply.github.com> Date: Fri, 5 Jan 2024 15:36:05 +0100 Subject: [PATCH] Review ad fix EF SqlServer example --- .github/workflows/test.yml | 7 +- .../Giis.Qacover.Driver/StatementAdapter.N.cs | 2 +- .../Ef2EventListener.N.cs | 5 -- net/QACoverTestEf/QACoverTestEf.csproj | 6 +- .../TestSqlserverEvaluationEF.N.cs | 16 ----- .../TestEvaluationEf.N.cs | 70 ++++++++++++------- .../Test4giis.Qacoverapp.Ef/AppSimpleEf.N.cs | 12 ++-- .../Test4giis.Qacoverapp.Ef/EfModel.N.cs | 38 +++++++--- 8 files changed, 86 insertions(+), 70 deletions(-) delete mode 100644 net/QACoverTestEf/Test4giis.Qacover.Ef.Sqlserver/TestSqlserverEvaluationEF.N.cs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67617e0..669ec71 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -255,14 +255,13 @@ jobs: # run: dotnet nuget add source https://apiint.nugettest.org/v3/index.json -n int.nugettest.org - name: Run test - ADO.NET run: dotnet test --logger "trx;LogFileName=../../reports/qacover-report.trx" QACoverTest/QACoverTest.csproj - #- name: Run test - Entity Framework - # run: dotnet test --logger "trx;LogFileName=../../reports/qacover-report-ef.trx" QACoverTestEf/QACoverTestEf.csproj + - name: Run test - Entity Framework + run: dotnet test --logger "trx;LogFileName=../../reports/qacover-report-ef.trx" QACoverTestEf/QACoverTestEf.csproj - name: Junit html report if: always() uses: javiertuya/junit-report-action@v1.2.0 with: - #net-trx-report: "net/reports/qacover-report.trx,net/reports/qacover-report-ef.trx" - net-trx-report: "net/reports/qacover-report.trx" + net-trx-report: "net/reports/qacover-report.trx,net/reports/qacover-report-ef.trx" net-surefire-folder: "net/target/surefire-reports" surefire-files: "net/target/surefire-reports/TEST-*.xml" report-dir: net/target/site diff --git a/net/QACover/Giis.Qacover.Driver/StatementAdapter.N.cs b/net/QACover/Giis.Qacover.Driver/StatementAdapter.N.cs index 9d0b556..458c83d 100644 --- a/net/QACover/Giis.Qacover.Driver/StatementAdapter.N.cs +++ b/net/QACover/Giis.Qacover.Driver/StatementAdapter.N.cs @@ -81,7 +81,7 @@ public override bool HasRows(string sql) // que tienen como tipo interno un string). Esto causa que otros metodos que // utilicen GetSqlWithValues para hacer logs puedan no mostrar correctamente la sql. cmd.CommandText = sql; - if (this.nativeParams != null) + if (cmd is SqlCommand && this.nativeParams != null) AddParameters(cmd, this.nativeParams); else if (this.parameters != null && this.parameters.Size() > 0) cmd.CommandText = GetSqlWithValues(sql); diff --git a/net/QACoverEf2spy/Giis.Qacover.Ef2driver/Ef2EventListener.N.cs b/net/QACoverEf2spy/Giis.Qacover.Ef2driver/Ef2EventListener.N.cs index 742c9be..b76a686 100644 --- a/net/QACoverEf2spy/Giis.Qacover.Ef2driver/Ef2EventListener.N.cs +++ b/net/QACoverEf2spy/Giis.Qacover.Ef2driver/Ef2EventListener.N.cs @@ -5,13 +5,8 @@ using System.Data.Common; //Prueba de concepto de un interceptor EF Core -//(sutituye al anterior que usaba un componente no nativo Z.EntityFramework...) //Este interceptor se anyade en OnConfiguring del contexto y se puede deshabilitar //para no capturar determinadas queries, p.e. cuando se crean datos de prueba - -//TODO revisar resto de metodos a interceptar (p.e. escalares) -//TODO Revisar que se interceptan consultas adicionales como las de obtener metadatos -//posibilidad crear configuracion adicional para omitir la evaluacion de reglas (similar a include/exclude) namespace Giis.Qacover.Ef2driver { public class Ef2EventListener : DbCommandInterceptor diff --git a/net/QACoverTestEf/QACoverTestEf.csproj b/net/QACoverTestEf/QACoverTestEf.csproj index c29fde6..0042e2d 100644 --- a/net/QACoverTestEf/QACoverTestEf.csproj +++ b/net/QACoverTestEf/QACoverTestEf.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -14,8 +14,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/net/QACoverTestEf/Test4giis.Qacover.Ef.Sqlserver/TestSqlserverEvaluationEF.N.cs b/net/QACoverTestEf/Test4giis.Qacover.Ef.Sqlserver/TestSqlserverEvaluationEF.N.cs deleted file mode 100644 index c819181..0000000 --- a/net/QACoverTestEf/Test4giis.Qacover.Ef.Sqlserver/TestSqlserverEvaluationEF.N.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Giis.Qacover.Model; - -using NUnit.Framework; - -namespace Test4giis.Qacover.Ef.Sqlserver -{ - /// Particularidades especificas de SQL Server (boolean y date) - public class TestSqlserverEvaluationEf : TestEvaluationEf - { - //override la variabilidad para indicar el uso de este sgbd, heredando todos los tests - protected override Variability GetVariant() - { - return new Variability("sqlserver"); - } - } -} diff --git a/net/QACoverTestEf/Test4giis.Qacover.Ef/TestEvaluationEf.N.cs b/net/QACoverTestEf/Test4giis.Qacover.Ef/TestEvaluationEf.N.cs index 232553a..040c6a3 100644 --- a/net/QACoverTestEf/Test4giis.Qacover.Ef/TestEvaluationEf.N.cs +++ b/net/QACoverTestEf/Test4giis.Qacover.Ef/TestEvaluationEf.N.cs @@ -1,29 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; +using Giis.Qacover.Model; +using Microsoft.EntityFrameworkCore; using NUnit.Framework; using NUnit.Framework.Legacy; using Test4giis.Qacoverapp.Ef; namespace Test4giis.Qacover.Ef { + /** + * This test requires the same SQLServer database that the one used in ADO.NET tests, + * the setup of test data and query execution are done using Entity Framework + * thorugh the mock app defined in AppSimpleEf + */ public class TestEvaluationEf : Base { + protected override Variability GetVariant() + { + return new Variability("sqlserver"); + } + [SetUp] public void SetUpTestData() { using (EfModel db = new EfModel()) { + // Deactivate the interceptor to forget the setup queries + // (although in this case queries are only updates) db.DisableInterceptor(); //evita evaluar estas queries que no son de la aplicacion - db.Database.EnsureCreated(); - //ojo, estamos usando el contexto interceptado, no pasa nada si son sentencias de actualizacion - //pero habria que permitir crear un contexto sin interceptar - //este si hace lectura - db.SimpleEntitys.RemoveRange(from c in db.SimpleEntitys select c); + // Setup a fresh table to hold the test data + try + { + db.Database.ExecuteSqlRaw("drop table TestEfTable"); + } + catch (Microsoft.Data.SqlClient.SqlException) { } + db.Database.ExecuteSqlRaw("CREATE TABLE TestEfTable (Id integer NOT NULL CONSTRAINT PK_TestEfTable PRIMARY KEY , Num INTEGER NOT NULL, Txt varchar(32) NULL)"); + //db.TestEfTable.RemoveRange(from c in db.TestEfTable select c); - db.Add(new SimpleEfEntity { Id = 1, Num = 0, Text = "abc" }); - db.Add(new SimpleEfEntity { Id = 2, Num = 99, Text = "xyz" }); - db.Add(new SimpleEfEntity { Id = 3, Num = 0, Text = null }); + // Setup test data for all tests to cover some rules and do not cover oters + db.Add(new TestEfEntity { Id = 1, Num = 0, Txt = "abc" }); + db.Add(new TestEfEntity { Id = 2, Num = 99, Txt = "xyz" }); + db.Add(new TestEfEntity { Id = 3, Num = 0, Txt = null }); db.SaveChanges(); } } @@ -32,21 +48,21 @@ public void SetUpTestData() public virtual void TestEvalEfNoParameters() { AppSimpleEf app = new AppSimpleEf(); - List pojo = app.QueryEfNoParams(); + List pojo = app.QueryEfNoParams(); ClassicAssert.AreEqual(1, pojo.Count); ClassicAssert.AreEqual(2, pojo[0].Id); ClassicAssert.AreEqual(99, pojo[0].Num); - ClassicAssert.AreEqual("xyz", pojo[0].Text); + ClassicAssert.AreEqual("xyz", pojo[0].Txt); //compara eliminando las comillas dobles que inserta EntityFramework en tablas y columnas - String efSql= "SELECT s.Id, s.Num, s.Text FROM SimpleEntitys AS s WHERE (s.Text = 'xyz') AND (s.Num = 99) ORDER BY s.Id"; - AssertEvalResults(efSql, + string efSql= "SELECT [t].[Id], [t].[Num], [t].[Txt] FROM [TestEfTable] AS [t] WHERE ([t].[Txt] = 'xyz') AND ([t].[Num] = 99) ORDER BY [t].[Id]"; + AssertEvalResults(efSql, string.Empty, string.Empty, - "COVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE ((s.Text = 'xyz')) AND ((s.Num = 99))\n" - + "UNCOVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE NOT((s.Text = 'xyz')) AND ((s.Num = 99))\n" - + "UNCOVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE (s.Text IS NULL) AND ((s.Num = 99))\n" - + "UNCOVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE (s.Num = 100) AND ((s.Text = 'xyz'))\n" - + "COVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE (s.Num = 99) AND ((s.Text = 'xyz'))\n" - + "UNCOVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE (s.Num = 98) AND ((s.Text = 'xyz'))", + "COVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE (([t].[Txt] = 'xyz')) AND (([t].[Num] = 99))\n" + + "UNCOVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE NOT(([t].[Txt] = 'xyz')) AND (([t].[Num] = 99))\n" + + "UNCOVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE ([t].[Txt] IS NULL) AND (([t].[Num] = 99))\n" + + "UNCOVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE ([t].[Num] = 100) AND (([t].[Txt] = 'xyz'))\n" + + "COVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE ([t].[Num] = 99) AND (([t].[Txt] = 'xyz'))\n" + + "UNCOVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE ([t].[Num] = 98) AND (([t].[Txt] = 'xyz'))", "{}", true, false); } [Test()] @@ -54,13 +70,13 @@ public virtual void TestEvalEfParameters() { options.SetFpcServiceOptions("noboundaries"); AppSimpleEf app = new AppSimpleEf(); - List pojo = app.QueryEfParams(99, "xyz"); - string efSql = "SELECT s.Id, s.Num, s.Text FROM SimpleEntitys AS s WHERE (s.Text = @__param1_0) AND (s.Num > @__param2_1) ORDER BY s.Id"; + List pojo = app.QueryEfParams(99, "xyz"); + string efSql = "SELECT [t].[Id], [t].[Num], [t].[Txt] FROM [TestEfTable] AS [t] WHERE ([t].[Txt] = @__param1_0) AND ([t].[Num] > @__param2_1) ORDER BY [t].[Id]"; AssertEvalResults(efSql, string.Empty, string.Empty, - "UNCOVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE ((s.Text = 'xyz')) AND ((s.Num > 99))\n" - + "UNCOVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE NOT((s.Text = 'xyz')) AND ((s.Num > 99))\n" - + "UNCOVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE (s.Text IS NULL) AND ((s.Num > 99))\n" - + "COVERED SELECT s.Id , s.Num , s.Text FROM SimpleEntitys AS s WHERE NOT((s.Num > 99)) AND ((s.Text = 'xyz'))", + "UNCOVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE (([t].[Txt] = 'xyz')) AND (([t].[Num] > 99))\n" + + "UNCOVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE NOT(([t].[Txt] = 'xyz')) AND (([t].[Num] > 99))\n" + + "UNCOVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE ([t].[Txt] IS NULL) AND (([t].[Num] > 99))\n" + + "COVERED SELECT [t].[Id] , [t].[Num] , [t].[Txt] FROM [TestEfTable] AS [t] WHERE NOT(([t].[Num] > 99)) AND (([t].[Txt] = 'xyz'))", "{@__param1_0='xyz', @__param2_1=99}", true, false); } } diff --git a/net/QACoverTestEf/Test4giis.Qacoverapp.Ef/AppSimpleEf.N.cs b/net/QACoverTestEf/Test4giis.Qacoverapp.Ef/AppSimpleEf.N.cs index 85bf2d9..3382452 100644 --- a/net/QACoverTestEf/Test4giis.Qacoverapp.Ef/AppSimpleEf.N.cs +++ b/net/QACoverTestEf/Test4giis.Qacoverapp.Ef/AppSimpleEf.N.cs @@ -12,23 +12,23 @@ public AppSimpleEf() db.Database.EnsureCreated(); } } - public List QueryEfNoParams() + public List QueryEfNoParams() { //String sql = "select id,num,text from test where text=? and num>?"; using (var db = new EfModel()) { - return db.SimpleEntitys.Where(b => b.Text == "xyz").Where(b => b.Num == 99) - .OrderBy(b => b.Id).ToList(); + return db.TestEfTable.Where(b => b.Txt == "xyz").Where(b => b.Num == 99) + .OrderBy(b => b.Id).ToList(); } } - public List QueryEfParams(int param2, string param1) + public List QueryEfParams(int param2, string param1) { //String sql = "select id,num,text from test where text=? and num>?"; using (var db = new EfModel()) { - return db.SimpleEntitys.Where(b => b.Text == param1).Where(b => b.Num > param2) - .OrderBy(b => b.Id).ToList(); + return db.TestEfTable.Where(b => b.Txt == param1).Where(b => b.Num > param2) + .OrderBy(b => b.Id).ToList(); } } diff --git a/net/QACoverTestEf/Test4giis.Qacoverapp.Ef/EfModel.N.cs b/net/QACoverTestEf/Test4giis.Qacoverapp.Ef/EfModel.N.cs index 1c658fa..a733a76 100644 --- a/net/QACoverTestEf/Test4giis.Qacoverapp.Ef/EfModel.N.cs +++ b/net/QACoverTestEf/Test4giis.Qacoverapp.Ef/EfModel.N.cs @@ -1,26 +1,48 @@ -using System; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Giis.Qacover.Ef2driver; using Giis.Portable.Util; +using Giis.Tdrules.Store.Rdb; +using System.Linq; namespace Test4giis.Qacoverapp.Ef { public class EfModel : Ef2InterceptorContext { - public DbSet SimpleEntitys { get; set; } + public DbSet TestEfTable { get; set; } + + // Connection string uses the same configuration parameters that are used for ADO.NET tests + private string GetConnectionString() + { + string SetupPath = FileUtil.GetPath(Parameters.GetProjectRoot(), "..", "setup"); + string DatabaseProperties = FileUtil.GetPath(SetupPath, "database.properties"); + string url = new JdbcProperties().GetProp(DatabaseProperties, "qacover.netcore.qacoverdb.sqlserver.url"); + string user = new JdbcProperties().GetProp(DatabaseProperties, "qacover.netcore.qacoverdb.sqlserver.user"); + string EnvironmentProperties = FileUtil.GetPath(SetupPath, "environment.properties"); + string password = new JdbcProperties().GetEnvVar(EnvironmentProperties, "TEST_" + "sqlserver".ToUpper() + "_PWD"); + return url + ";UID=" + user + ";PWD=" + password; + } protected override void OnConfiguring(DbContextOptionsBuilder options) { - base.OnConfiguring(options); //para que se instale el interceptor - options.UseSqlite("Data Source=" + FileUtil.GetPath(Parameters.GetProjectRoot(), "TestEf.db")); + base.OnConfiguring(options); // base class (Ef2InterceptorContext) will install the interceptor + options.UseSqlServer(GetConnectionString()); } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // Disable generated unicode literals + foreach (var property in modelBuilder.Model.GetEntityTypes() + .SelectMany(e => e.GetProperties().Where(p => p.ClrType == typeof(string)))) + { + property.SetIsUnicode(false); + } + } } - public class SimpleEfEntity + public class TestEfEntity { public int Id { get; set; } - public Int16 Num { get; set; } - public string Text { get; set; } + public int Num { get; set; } + public string Txt { get; set; } } }