From 6f5b28de9b6f985e64e393cc04dd52ac59c07954 Mon Sep 17 00:00:00 2001 From: Giacomo Castellani Date: Sat, 3 Dec 2022 22:08:06 +0100 Subject: [PATCH 01/13] new resultset/data object --- docs/yamlSchema.json | 159 +++++++++++++++++++++++++++------- src/qest/Models/TestResult.cs | 8 ++ 2 files changed, 134 insertions(+), 33 deletions(-) diff --git a/docs/yamlSchema.json b/docs/yamlSchema.json index 174f089..25e0a6d 100644 --- a/docs/yamlSchema.json +++ b/docs/yamlSchema.json @@ -1,97 +1,164 @@ { "$schema": "http://json-schema.org/draft-04/schema", - "definitions": { "Test": { "properties": { - "name": { "type": "string" }, + "name": { + "type": "string" + }, "variables": { "type": "object", "additionalProperties": true }, - "before": { "$ref": "#/definitions/Scripts" }, + "before": { + "$ref": "#/definitions/Scripts" + }, "steps": { "type": "array", - "items": { "$ref": "#/definitions/TestStep" } + "items": { + "$ref": "#/definitions/TestStep" + } }, - "after": { "$ref": "#/definitions/Scripts" } + "after": { + "$ref": "#/definitions/Scripts" + } }, - "required": ["name", "steps"], + "required": [ + "name", + "steps" + ], "additionalProperties": false }, "TestStep": { "properties": { - "name": { "type": "string" }, - "command": { "$ref": "#/definitions/TestCommand" }, - "results": { "$ref": "#/definitions/ResultGroup" }, + "name": { + "type": "string" + }, + "command": { + "$ref": "#/definitions/TestCommand" + }, + "results": { + "$ref": "#/definitions/ResultGroup" + }, "asserts": { "type": "array", - "items": { "$ref": "#/definitions/Assert" } + "items": { + "$ref": "#/definitions/Assert" + } } }, - "required": ["name", "command", "results"], + "required": [ + "name", + "command", + "results" + ], "additionalProperties": false }, "TestCommand": { "properties": { - "commandText": { "type": "string" }, - "parameters": { "additionalProperties": true } + "commandText": { + "type": "string" + }, + "parameters": { + "additionalProperties": true + } }, - "required": ["commandText"], + "required": [ + "commandText" + ], "additionalProperties": false }, "ResultGroup": { "properties": { "resultSets": { "type": "array", - "items": { "$ref": "#/definitions/ResultSet" } + "items": { + "$ref": "#/definitions/ResultSet" + } }, "outputParameters": { "type": "array", - "items": { "$ref": "#/definitions/OutputParameter" } + "items": { + "$ref": "#/definitions/OutputParameter" + } }, - "returnCode": { "type": "number" } + "returnCode": { + "type": "number" + } }, "additionalProperties": false }, "ResultSet": { "properties": { - "name": { "type": "string" }, - "rowNumber": { "type": "number" }, + "name": { + "type": "string" + }, + "rowNumber": { + "type": "number" + }, "columns": { "type": "array", "items": { "type": "object", "properties": { - "name": { "type": "string" }, - "type": { "$ref": "#/definitions/SqlDbType" } + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/SqlDbType" + } }, - "required": ["name", "type"], + "required": [ + "name", + "type" + ], "additionalProperties": false } + }, + "data": { + "$ref": "#/definitions/DataScript" } }, - "required": ["name", "columns"], + "required": [ + "name", + "columns" + ], "additionalProperties": false }, "OutputParameter": { "type": "object", "properties": { - "name": { "type": "string" }, - "type": { "$ref": "#/definitions/SqlDbType" }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/SqlDbType" + }, "value": {} }, - "required": ["name", "type", "value"], + "required": [ + "name", + "type", + "value" + ], "additionalProperties": false }, "Assert": { "type": "object", "properties": { - "sqlQuery": { "type": "string" }, - "scalarType": { "$ref": "#/definitions/SqlDbType" }, + "sqlQuery": { + "type": "string" + }, + "scalarType": { + "$ref": "#/definitions/SqlDbType" + }, "scalarValue": {} }, - "required": ["sqlQuery", "scalarType", "scalarValue"], + "required": [ + "sqlQuery", + "scalarType", + "scalarValue" + ], "additionalProperties": false }, "Scripts": { @@ -108,14 +175,41 @@ }, "values": { "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "DataScript": { + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/ScriptType" + }, + "values": { + "type": "array", + "items": { + "type": "string" + } + }, + "separator": { + "type": "string" } }, + "required": [ + "type", + "values" + ], "additionalProperties": false }, "ScriptType": { "type": "string", - "enum": ["File", "Inline"] + "enum": [ + "File", + "Inline" + ] }, "SqlDbType": { "type": "string", @@ -138,10 +232,9 @@ ] } }, - "type": "array", "items": { "$ref": "#/definitions/Test" }, "additionalProperties": false -} +} \ No newline at end of file diff --git a/src/qest/Models/TestResult.cs b/src/qest/Models/TestResult.cs index 8e57670..25fb50f 100644 --- a/src/qest/Models/TestResult.cs +++ b/src/qest/Models/TestResult.cs @@ -23,6 +23,7 @@ public class ResultSet public string Name { get; set; } public List Columns { get; set; } public int? RowNumber { get; set; } + public ResultSetData? Data { get; set; } [YamlIgnore] public DataTable? Result { get; set; } @@ -55,4 +56,11 @@ public class Column public qestType Type { get; set; } } + public class ResultSetData + { + public ScriptType Type { get; set; } + public string? Separator { get; set; } + public List Values { get; set; } + } + } From ba588cfbe6525981f0e761785252b18f9c2e1624 Mon Sep 17 00:00:00 2001 From: Giacomo Castellani Date: Sat, 3 Dec 2022 22:08:23 +0100 Subject: [PATCH 02/13] yaml schema included in solution --- src/qest.sln | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qest.sln b/src/qest.sln index 4ead375..3aa7a19 100644 --- a/src/qest.sln +++ b/src/qest.sln @@ -5,6 +5,11 @@ VisualStudioVersion = 17.3.32922.545 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qest", "qest\qest.csproj", "{79EF488B-B5FE-4DEE-807E-51CA359318C9}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{78937758-7B07-465D-A4CA-FE5DB8F29435}" + ProjectSection(SolutionItems) = preProject + ..\docs\yamlSchema.json = ..\docs\yamlSchema.json + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 781c459b569cd11258f36b8f38d52fcc7357bc37 Mon Sep 17 00:00:00 2001 From: Giacomo Castellani Date: Sun, 4 Dec 2022 12:55:26 +0100 Subject: [PATCH 03/13] evaluating data, first draft --- samples/tests/sampleSp.yml | 7 +++- src/qest/Visualizers/ConsoleVisualizer.cs | 44 ++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/samples/tests/sampleSp.yml b/samples/tests/sampleSp.yml index 5285bed..0fa81b3 100644 --- a/samples/tests/sampleSp.yml +++ b/samples/tests/sampleSp.yml @@ -51,6 +51,11 @@ type: Date - name: TimeValue type: Time + data: + type: Inline + values: + - "aaa;True;42" + separator: ";" outputParameters: - name: oldValue type: Int @@ -107,7 +112,7 @@ commandText: dbo.SampleSp parameters: name: "NoMatchData" - newValue: a + newValue: NULL results: returnCode: 0 after: diff --git a/src/qest/Visualizers/ConsoleVisualizer.cs b/src/qest/Visualizers/ConsoleVisualizer.cs index ca3b65a..799a1ce 100644 --- a/src/qest/Visualizers/ConsoleVisualizer.cs +++ b/src/qest/Visualizers/ConsoleVisualizer.cs @@ -158,7 +158,49 @@ private void EvaluateTestStep(TestStep step, Dictionary? variabl } } - LogConsole("OK".EscapeAndAddStyles(okStyle)); + if (expectedResult.Data is not null) + { + + if (expectedResult.Data.Values.Count > currentResult.Rows.Count) + { + LogConsoleError($"Rows: {currentResult.Rows.Count} < {expectedResult.Data.Values.Count}".EscapeAndAddStyles(errorStyle)); + Pass = false; + LogHierarchy.RemoveLast(); + continue; + } + + // check expected values + + for (int i = 0; i < expectedResult.Data.Values.Count; i++) + { + var expectedRow = expectedResult.Data.Values[i].Split(expectedResult.Data.Separator ?? ";"); + var currentRow = currentResult.Rows[i]; + + LogHierarchy.Add($"[{i + 1}]".EscapeMarkup()); + + for (int j = 0; j < expectedRow.Length; j++) + { + var currentValue = currentRow[j]; + var expectedValue = Convert.ChangeType(expectedRow[j], currentValue.GetType()); + + if (!expectedValue.Equals(currentValue)) + { + LogConsoleError($"{expectedResult.Columns[j].Name}: {currentValue} != {expectedValue}".EscapeAndAddStyles(errorStyle)); + Pass = false; + } + else + { + LogConsole($"{expectedResult.Columns[j].Name}: {currentValue}".EscapeAndAddStyles(okStyle), Verbose); + } + } + + LogHierarchy.RemoveLast(); + } + } + + if (Pass) + LogConsole("OK".EscapeAndAddStyles(okStyle)); + LogHierarchy.RemoveLast(); } LogHierarchy.RemoveLast(); From 687e8903653ec137b788e4803bd56939561b988d Mon Sep 17 00:00:00 2001 From: Giacomo Castellani Date: Wed, 7 Dec 2022 17:00:47 +0000 Subject: [PATCH 04/13] comparing data --- src/qest/Visualizers/ConsoleVisualizer.cs | 5 ++- src/qest/Visualizers/TreeVisualizer.cs | 55 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/qest/Visualizers/ConsoleVisualizer.cs b/src/qest/Visualizers/ConsoleVisualizer.cs index 799a1ce..498bf52 100644 --- a/src/qest/Visualizers/ConsoleVisualizer.cs +++ b/src/qest/Visualizers/ConsoleVisualizer.cs @@ -173,10 +173,11 @@ private void EvaluateTestStep(TestStep step, Dictionary? variabl for (int i = 0; i < expectedResult.Data.Values.Count; i++) { - var expectedRow = expectedResult.Data.Values[i].Split(expectedResult.Data.Separator ?? ";"); + var values = expectedResult.Data.Values[i].ReplaceVars(variables); + var expectedRow = values.Split(expectedResult.Data.Separator ?? ";"); var currentRow = currentResult.Rows[i]; - LogHierarchy.Add($"[{i + 1}]".EscapeMarkup()); + LogHierarchy.Add($"{i + 1}".EscapeMarkup()); for (int j = 0; j < expectedRow.Length; j++) { diff --git a/src/qest/Visualizers/TreeVisualizer.cs b/src/qest/Visualizers/TreeVisualizer.cs index 0d7bd18..68f3fdb 100644 --- a/src/qest/Visualizers/TreeVisualizer.cs +++ b/src/qest/Visualizers/TreeVisualizer.cs @@ -154,6 +154,61 @@ private void EvaluateTestStep(TestStep step, Dictionary? variabl } } + if (expectedResult.Data is not null) + { + + if (expectedResult.Data.Values.Count > currentResult.Rows.Count) + { + currentResultNode.AddNode($"Rows: {currentResult.Rows.Count} < {expectedResult.Data.Values.Count}".EscapeAndAddStyles(errorStyle)); + resultSetsNode.AddNode(currentResultNode); + Pass = false; + continue; + } + + // check expected values + + bool dataError = false; + var dataTable = new Table(); + foreach(var column in expectedResult.Columns) + { + dataTable.AddColumn(column.Name); + } + + for (int i = 0; i < expectedResult.Data.Values.Count; i++) + { + var values = expectedResult.Data.Values[i].ReplaceVars(variables); + var expectedRow = values.Split(expectedResult.Data.Separator ?? ";"); + var currentRow = currentResult.Rows[i]; + + List outputRow = new(); + + for (int j = 0; j < expectedRow.Length; j++) + { + var currentValue = currentRow[j]; + var expectedValue = Convert.ChangeType(expectedRow[j], currentValue.GetType()); + + if (!expectedValue.Equals(currentValue)) + { + outputRow.Add($"{currentValue} != {expectedValue}".EscapeAndAddStyles(errorStyle)); + dataError = true; + } + else + outputRow.Add($"{currentValue}".EscapeAndAddStyles(okStyle)); + } + + dataTable.AddRow(outputRow); + } + + currentResultNode.AddNode(dataTable); + + if (dataError) + { + resultSetsNode.AddNode(currentResultNode); + Pass = false; + continue; + } + } + currentResultNode.AddOutputNode(NewMarkupTreeNode("OK".EscapeAndAddStyles(okStyle)), Verbose); resultSetsNode.AddOutputNode(currentResultNode, Verbose); } From 2461d5e396add478890fc5621172a5654f1dd120 Mon Sep 17 00:00:00 2001 From: Giacomo Castellani Date: Wed, 7 Dec 2022 18:45:10 +0100 Subject: [PATCH 05/13] show only compared columns --- src/qest/Visualizers/TreeVisualizer.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/qest/Visualizers/TreeVisualizer.cs b/src/qest/Visualizers/TreeVisualizer.cs index 68f3fdb..4e53de2 100644 --- a/src/qest/Visualizers/TreeVisualizer.cs +++ b/src/qest/Visualizers/TreeVisualizer.cs @@ -169,10 +169,12 @@ private void EvaluateTestStep(TestStep step, Dictionary? variabl bool dataError = false; var dataTable = new Table(); - foreach(var column in expectedResult.Columns) - { - dataTable.AddColumn(column.Name); - } + // foreach(var column in expectedResult.Columns) + // { + // dataTable.AddColumn(column.Name); + + // dataTable.Columns + // } for (int i = 0; i < expectedResult.Data.Values.Count; i++) { @@ -184,6 +186,9 @@ private void EvaluateTestStep(TestStep step, Dictionary? variabl for (int j = 0; j < expectedRow.Length; j++) { + if (i==0) + dataTable.AddColumn(expectedResult.Columns[j].Name); + var currentValue = currentRow[j]; var expectedValue = Convert.ChangeType(expectedRow[j], currentValue.GetType()); @@ -196,7 +201,7 @@ private void EvaluateTestStep(TestStep step, Dictionary? variabl outputRow.Add($"{currentValue}".EscapeAndAddStyles(okStyle)); } - dataTable.AddRow(outputRow); + dataTable.AddRow(outputRow.ToArray()); } currentResultNode.AddNode(dataTable); From 5ffb02858a3dc934469448450a8b4c414c71121e Mon Sep 17 00:00:00 2001 From: Giacomo Castellani Date: Wed, 14 Dec 2022 20:22:57 +0100 Subject: [PATCH 06/13] comparing data from a csv file --- samples/scripts/results.csv | 1 + samples/tests/sampleSp.yml | 57 +++++++++++++++++++++- src/qest/Connectors/MsSqlConnector.cs | 2 +- src/qest/Models/Script.cs | 59 +++++++++++++++++++++-- src/qest/Models/TestResult.cs | 4 +- src/qest/Visualizers/ConsoleVisualizer.cs | 17 +++---- src/qest/Visualizers/TreeVisualizer.cs | 24 +++------ 7 files changed, 126 insertions(+), 38 deletions(-) create mode 100644 samples/scripts/results.csv diff --git a/samples/scripts/results.csv b/samples/scripts/results.csv new file mode 100644 index 0000000..f0ac329 --- /dev/null +++ b/samples/scripts/results.csv @@ -0,0 +1 @@ +{nameVar};True;42;7777;2;1210000000;3,14159265;1,1235813;21;0,00;1985-10-26 09:00:00.000;2015-10-21 07:28:00.0000000;1955-11-12 06:38:00.0000000 +02:00;1900-01-01;09:40:00 diff --git a/samples/tests/sampleSp.yml b/samples/tests/sampleSp.yml index 0fa81b3..d5bece4 100644 --- a/samples/tests/sampleSp.yml +++ b/samples/tests/sampleSp.yml @@ -54,7 +54,7 @@ data: type: Inline values: - - "aaa;True;42" + - "SampleName;True;42" separator: ";" outputParameters: - name: oldValue @@ -65,6 +65,61 @@ - sqlQuery: SELECT COUNT(*) FROM dbo.SampleTable WHERE [IntValue] = {newValueVar} scalarType: Int scalarValue: 1 + - name: Test OK with csv data + command: + commandText: dbo.SampleSp + parameters: + name: "{nameVar}" + newValue: 2 + results: + resultSets: + - name: sampleSpRS1 + rowNumber: 1 + columns: + - name: Name + type: NVarChar + - name: BitValue + type: Bit + - name: TinyIntValue + type: TinyInt + - name: SmallintValue + type: SmallInt + - name: IntValue + type: Int + - name: BigIntValue + type: BigInt + - name: FloatValue + type: Float + - name: RealtValue + type: Real + - name: DecimalValue + type: Decimal + - name: MoneyValue + type: Money + - name: DateTimeValue + type: DateTime + - name: DateTime2Value + type: DateTime2 + - name: DateTimeOffsetValue + type: DateTimeOffset + - name: DateValue + type: Date + - name: TimeValue + type: Time + data: + type: File + values: + - scripts/results.csv + separator: ";" + outputParameters: + - name: oldValue + type: Int + value: 1 + returnCode: 0 + asserts: + - sqlQuery: SELECT COUNT(*) FROM dbo.SampleTable WHERE [IntValue] = {newValueVar} + scalarType: Int + scalarValue: 1 - name: Test KO command: commandText: dbo.SampleSp diff --git a/src/qest/Connectors/MsSqlConnector.cs b/src/qest/Connectors/MsSqlConnector.cs index 41ba390..a37c4bc 100644 --- a/src/qest/Connectors/MsSqlConnector.cs +++ b/src/qest/Connectors/MsSqlConnector.cs @@ -219,7 +219,7 @@ private async Task RunScriptAsync(Scripts scripts) scripts.ActualScripts = new(); foreach (var item in scripts) { - foreach (string innerScript in item.GetValues()) + foreach (string innerScript in item.ReadValue()) { string actualScript = (string)innerScript.ReplaceVarsInParameter(currentTest.Variables); scripts.ActualScripts.Add(actualScript); diff --git a/src/qest/Models/Script.cs b/src/qest/Models/Script.cs index f4b0f0f..4de047b 100644 --- a/src/qest/Models/Script.cs +++ b/src/qest/Models/Script.cs @@ -6,12 +6,19 @@ namespace qest.Models { - public class Script + public class TextArray { public ScriptType Type { get; set; } public List Values { get; set; } - - public IEnumerable GetValues() + + /// + /// Read every item as a whole in the collection + /// + /// A single element of the array + /// + /// + /// + public IEnumerable ReadValue() { if (Values == null) throw new ArgumentNullException(nameof(Values)); @@ -23,6 +30,7 @@ public IEnumerable GetValues() case ScriptType.Inline: yield return value; break; + case ScriptType.File: if (File.Exists(value)) @@ -34,6 +42,49 @@ public IEnumerable GetValues() else throw new FileNotFoundException(null, value); break; + + default: + throw new ArgumentException(nameof(Type)); + } + } + + } + + /// + /// Reads every item in the collection line by line + /// + /// A single line of the collection + /// + /// + /// + public IEnumerable ReadLine() + { + if (Values == null) + throw new ArgumentNullException(nameof(Values)); + + foreach (var value in Values) + { + switch (this.Type) + { + case ScriptType.Inline: + yield return value; + break; + + case ScriptType.File: + + if (File.Exists(value)) + { + using var sr = new StreamReader(value); + while (sr.Peek() >= 0) + { + string data = sr.ReadLine(); + yield return data; + } + } + else + throw new FileNotFoundException(null, value); + break; + default: throw new ArgumentException(nameof(Type)); } @@ -48,7 +99,7 @@ public enum ScriptType File } - public class Scripts : List