From a3ee0a9cfd1b07d39725798efa466995ed908e20 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 9 Nov 2021 15:03:41 -0500 Subject: [PATCH 001/119] initial commit. --- Source/Run_Methods.sql | 11 +++++++ Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql | 12 ++++++++ Tests/AnnotationNoTransactionTests.class.sql | 30 ++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql create mode 100644 Tests/AnnotationNoTransactionTests.class.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index b67d36bf9..a8058a2f0 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -59,6 +59,8 @@ BEGIN TRUNCATE TABLE tSQLt.CaptureOutputLog; CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); + --TODO:NoTran + ---- CREATE #NoTran IF EXISTS (SELECT 1 FROM sys.extended_properties WHERE name = N'SetFakeViewOnTrigger') BEGIN @@ -85,6 +87,10 @@ BEGIN SET @Result = 'Success'; + --TODO:NoTran + ---- Move AnnotationProcessing to here? + ---- Save NoTransaction Status in variable!!! + ---- Do not start transaction? BEGIN TRAN; SAVE TRAN @TranName; @@ -103,6 +109,8 @@ BEGIN BEGIN IF (@SetUp IS NOT NULL) EXEC @SetUp; EXEC (@Cmd); + --TODO:NoTran + ----EXEC @CleanUp --Probably further down both, TestClassName.CleanUp and provided as a parameter to the annotation? IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) BEGIN SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; @@ -220,6 +228,9 @@ BEGIN SET @Msg = ERROR_MESSAGE(); END CATCH + --TODO:NoTran + ---- Compare @@Trancount, throw up arms if it doesn't match + --TODO:NoTran BEGIN TRY ROLLBACK TRAN @TranName; END TRY diff --git a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql new file mode 100644 index 000000000..253c44875 --- /dev/null +++ b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql @@ -0,0 +1,12 @@ +IF OBJECT_ID('tSQLt.[@tSQLt:NoTransaction]') IS NOT NULL DROP FUNCTION tSQLt.[@tSQLt:NoTransaction]; +GO +---Build+ +GO +CREATE FUNCTION tSQLt.[@tSQLt:NoTransaction]() +RETURNS TABLE +AS +RETURN + SELECT NULL AS AnnotationCmd; +GO +---Build- +GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql new file mode 100644 index 000000000..6d82be20e --- /dev/null +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -0,0 +1,30 @@ +EXEC tSQLt.NewTestClass 'AnnotationNoTransactionTests'; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test runs test without transaction] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS INSERT INTO #TranCount VALUES(''I'',@@TRANCOUNT);; + '); + + SELECT 'B' Id, @@TRANCOUNT TranCount + INTO #TranCount; + + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + + INSERT INTO #TranCount VALUES('A',@@TRANCOUNT); + + SELECT Id, TranCount-MIN(TranCount)OVER() AdjustedTrancount + INTO #Actual + FROM #TranCount; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES('B',0),('I',0),('A',0); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO From 6c641e987a8b0a59c391b0fd7dd280dd45a81403 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 9 Nov 2021 17:28:18 -0500 Subject: [PATCH 002/119] check in quick! --- Source/Run_Methods.sql | 36 +++++++++++++------ ...tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql | 7 +++- Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql | 2 +- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index a8058a2f0..2481176a9 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -59,8 +59,7 @@ BEGIN TRUNCATE TABLE tSQLt.CaptureOutputLog; CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); - --TODO:NoTran - ---- CREATE #NoTran + CREATE TABLE #NoTransaction(X INT); IF EXISTS (SELECT 1 FROM sys.extended_properties WHERE name = N'SetFakeViewOnTrigger') BEGIN @@ -70,7 +69,7 @@ BEGIN SELECT @Cmd = 'EXEC ' + @TestName; - SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), --tSQLt.Private_GetCleanSchemaName('', @TestName), + SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), @TestObjectId = OBJECT_ID(@TestName); @@ -91,21 +90,30 @@ BEGIN ---- Move AnnotationProcessing to here? ---- Save NoTransaction Status in variable!!! ---- Do not start transaction? - BEGIN TRAN; - SAVE TRAN @TranName; + DECLARE @SkipTestFlag BIT = 0; + DECLARE @NoTransactionFlag BIT = 0; + BEGIN TRY + + EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; + SET @SkipTestFlag = CASE WHEN EXISTS(SELECT 1 FROM #SkipTest) THEN 1 ELSE 0 END; + SET @NoTransactionFlag = CASE WHEN EXISTS(SELECT 1 FROM #NoTransaction) THEN 1 ELSE 0 END; + + IF(@NoTransactionFlag = 0) + BEGIN + BEGIN TRAN; + SAVE TRAN @TranName; + END; - SET @PreExecTrancount = @@TRANCOUNT; + SET @PreExecTrancount = @@TRANCOUNT; - TRUNCATE TABLE tSQLt.TestMessage; + TRUNCATE TABLE tSQLt.TestMessage; - BEGIN TRY - EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; DECLARE @TmpMsg NVARCHAR(MAX); DECLARE @TestEndTime DATETIME2; SET @TestEndTime = NULL; BEGIN TRY - IF(NOT EXISTS(SELECT 1 FROM #SkipTest)) + IF(@SkipTestFlag = 0) BEGIN IF (@SetUp IS NOT NULL) EXEC @SetUp; EXEC (@Cmd); @@ -232,7 +240,10 @@ BEGIN ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran BEGIN TRY + IF(@NoTransactionFlag = 0) + BEGIN ROLLBACK TRAN @TranName; + END; END TRY BEGIN CATCH DECLARE @PostExecTrancount INT; @@ -271,7 +282,10 @@ BEGIN 'Error', 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; END; - COMMIT; + IF(@NoTransactionFlag = 0) + BEGIN + COMMIT; + END; IF(@Verbose = 1) BEGIN diff --git a/Source/tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql b/Source/tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql index 15bd9bdef..0f970159b 100644 --- a/Source/tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql +++ b/Source/tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql @@ -7,7 +7,12 @@ RETURNS TABLE AS RETURN SELECT AF.* - FROM (SELECT PSSV.Major FROM tSQLt.Private_SqlVersion() AS PSV CROSS APPLY tSQLt.Private_SplitSqlVersion(PSV.ProductVersion) AS PSSV) AV + FROM + ( + SELECT PSSV.Major + FROM tSQLt.Private_SqlVersion() AS PSV + CROSS APPLY tSQLt.Private_SplitSqlVersion(PSV.ProductVersion) AS PSSV + ) AV CROSS APPLY tSQLt.[@tSQLt:SkipTest]('Minimum required version is '+ CAST(@MinVersion AS NVARCHAR(MAX))+ ', but current version is '+ diff --git a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql index 253c44875..e6b6c10a0 100644 --- a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql +++ b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql @@ -6,7 +6,7 @@ CREATE FUNCTION tSQLt.[@tSQLt:NoTransaction]() RETURNS TABLE AS RETURN - SELECT NULL AS AnnotationCmd; + SELECT 'INSERT INTO #NoTransaction DEFAULT VALUES;' AS AnnotationCmd; GO ---Build- GO From 1030bf90aaefc408b4a9a93b8453873fb6fdb205 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 9 Nov 2021 23:02:05 -0500 Subject: [PATCH 003/119] BITs are hard. Transactions are complicated. But the build is fixed. --- Source/BuildOrder.txt | 1 + Source/Run_Methods.sql | 6 +++-- Source/Source.ssmssqlproj | 21 +++++++--------- Tests/AnnotationNoTransactionTests.class.sql | 10 ++++++++ Tests/Tests.ssmssqlproj | 25 ++++++++++++-------- 5 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index e70ba5f24..457cebdda 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -105,6 +105,7 @@ tSQLt.(at)tSQLt_SkipTest.sfn.sql tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql tSQLt.(at)tSQLt_MaxSqlMajorVersion.sfn.sql tSQLt.(at)tSQLt_RunOnlyOnHostPlatform.sfn.sql +tSQLt.(at)tSQLt_NoTransaction.sfn.sql tSQLt.RemoveExternalAccessKey.ssp.sql tSQLt.InstallExternalAccessKey.ssp.sql tSQLt.Private_InstallationInfo.sfn.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 2481176a9..37eb25204 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -92,6 +92,7 @@ BEGIN ---- Do not start transaction? DECLARE @SkipTestFlag BIT = 0; DECLARE @NoTransactionFlag BIT = 0; + DECLARE @TransactionStartedFlag BIT = 0; BEGIN TRY EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; @@ -101,6 +102,7 @@ BEGIN IF(@NoTransactionFlag = 0) BEGIN BEGIN TRAN; + SET @TransactionStartedFlag = 1; SAVE TRAN @TranName; END; @@ -240,7 +242,7 @@ BEGIN ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran BEGIN TRY - IF(@NoTransactionFlag = 0) + IF(@TransactionStartedFlag = 1) BEGIN ROLLBACK TRAN @TranName; END; @@ -282,7 +284,7 @@ BEGIN 'Error', 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; END; - IF(@NoTransactionFlag = 0) + IF(@TransactionStartedFlag = 1) BEGIN COMMIT; END; diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 9bf6e3537..83e2fcd47 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -18,9 +18,6 @@ ExecutePrepareServer.sql - - - Run_Methods.sql @@ -30,11 +27,11 @@ tSQLt.(at)tSQLt_MaxSqlMajorVersion.sfn.sql - - - tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql + + tSQLt.(at)tSQLt_NoTransaction.sfn.sql + @@ -42,9 +39,9 @@ tSQLt.(at)tSQLt_RunOnlyOnHostPlatform.sfn.sql - - - + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa + localhost,41433 + tSQLt_sa tSQLt.(at)tSQLt_SkipTest.sfn.sql @@ -426,9 +423,9 @@ tSQLt.Private_PrepareFakeFunctionOutputTable.ssp.sql - - - + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa + localhost,41433 + tSQLt_sa tSQLt.Private_ProcessTestAnnotations.ssp.sql diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 6d82be20e..b2e93894a 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -28,3 +28,13 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS IN END; GO + +/*-- TODO + +-- transaction opened during test +-- transaction commited during test +-- test skipped? +-- cleanup execution tables +-- + +--*/ \ No newline at end of file diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index c3a19eb54..44bcdc618 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -2,7 +2,6 @@ - @@ -12,22 +11,28 @@ AnnotationHostPlatformTests.class.sql + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa + localhost,41433 + tSQLt_sa + AnnotationNoTransactionTests.class.sql + - - - + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa + localhost,41433 + tSQLt_sa AnnotationSkipTestTests.class.sql - - - + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa + localhost,41433 + tSQLt_sa AnnotationSqlServerVersionTests.class.sql - - - + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa + localhost,41433 + tSQLt_sa AnnotationsTests.class.sql From 87b03d89739f32e84982e525e8c5c27137c042b2 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 10 Nov 2021 10:06:36 -0500 Subject: [PATCH 004/119] Updated SpyProcedure to mark the SpyProcedureLog table as a temp object; updating Private_MarktSQLtTempObject to not create an extended property for the original object if no name is provided. --- Source/Run_Methods.sql | 6 +--- Source/tSQLt.SpyProcedure.ssp.sql | 1 + Tests/AnnotationNoTransactionTests.class.sql | 4 ++- ...Private_MarktSQLtTempObjectTests.class.sql | 18 ++++++++++++ Tests/SpyProcedureTests.class.sql | 28 +++++++++++++++++-- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 37eb25204..fb9a8da94 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -86,10 +86,6 @@ BEGIN SET @Result = 'Success'; - --TODO:NoTran - ---- Move AnnotationProcessing to here? - ---- Save NoTransaction Status in variable!!! - ---- Do not start transaction? DECLARE @SkipTestFlag BIT = 0; DECLARE @NoTransactionFlag BIT = 0; DECLARE @TransactionStartedFlag BIT = 0; @@ -120,7 +116,7 @@ BEGIN IF (@SetUp IS NOT NULL) EXEC @SetUp; EXEC (@Cmd); --TODO:NoTran - ----EXEC @CleanUp --Probably further down both, TestClassName.CleanUp and provided as a parameter to the annotation? + ----EXEC @CleanUp --Probably further down, called ".CleanUp" IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) BEGIN SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; diff --git a/Source/tSQLt.SpyProcedure.ssp.sql b/Source/tSQLt.SpyProcedure.ssp.sql index 4740b47f9..4f14acd35 100644 --- a/Source/tSQLt.SpyProcedure.ssp.sql +++ b/Source/tSQLt.SpyProcedure.ssp.sql @@ -34,6 +34,7 @@ BEGIN EXEC(@CreateProcedureStatement); EXEC tSQLt.Private_MarktSQLtTempObject @ProcedureName, N'PROCEDURE', @NewNameOfOriginalObject; + EXEC tSQLt.Private_MarktSQLtTempObject @LogTableName, N'TABLE', NULL; RETURN 0; END; diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index b2e93894a..62e78df43 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -35,6 +35,8 @@ GO -- transaction commited during test -- test skipped? -- cleanup execution tables --- +-- SpyProcedureLog table needs to be marked with IsTempObject in extended properties +-- named cleanup (needs to execute even if there's an error during test execution) +-- confirm pre and post transaction counts match --*/ \ No newline at end of file diff --git a/Tests/Private_MarktSQLtTempObjectTests.class.sql b/Tests/Private_MarktSQLtTempObjectTests.class.sql index 205caa035..386228dc0 100644 --- a/Tests/Private_MarktSQLtTempObjectTests.class.sql +++ b/Tests/Private_MarktSQLtTempObjectTests.class.sql @@ -110,6 +110,24 @@ BEGIN INSERT INTO #Expected VALUES('BIT'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +CREATE PROCEDURE Private_MarktSQLtTempObjectTests.[test doesn't set tSQLt.Private_TestDouble_OrgObjectName if @NewNameOfOriginalObject is NULL] +AS +BEGIN + CREATE TABLE Private_MarktSQLtTempObjectTests.TempTable1(i INT NOT NULL); + EXEC tSQLt.Private_MarktSQLtTempObject + @ObjectName = 'Private_MarktSQLtTempObjectTests.TempTable1', + @ObjectType = N'TABLE', + @NewNameOfOriginalObject = NULL; + + SELECT * + INTO #Actual + FROM sys.extended_properties AS EP + WHERE EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.major_id = OBJECT_ID('Private_MarktSQLtTempObjectTests.TempTable1') + AND EP.name = 'tSQLt.Private_TestDouble_OrgObjectName'; + EXEC tSQLt.AssertEmptyTable '#Actual'; END; GO \ No newline at end of file diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index 12d830d50..74279d1ed 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -755,12 +755,12 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -CREATE PROC SpyProcedureTests.[test SpyProcedure calls tSQLt.Private_MarktSQLtTempObject on new object] +CREATE PROC SpyProcedureTests.[test SpyProcedure calls tSQLt.Private_MarktSQLtTempObject on new objects] AS BEGIN DECLARE @OriginalObjectId INT = OBJECT_ID('tSQLt.Private_MarktSQLtTempObject'); - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_MarktSQLtTempObject'; + EXEC tSQLt.SpyProcedure @ProcedureName = '[tSQLt].[Private_MarktSQLtTempObject]'; --We are testing that SpyProcedure calls this ^^ after spying this ^^, so this line serves as prep and action. SELECT ObjectName, ObjectType, NewNameOfOriginalObject @@ -769,11 +769,33 @@ BEGIN SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('tSQLt.Private_MarktSQLtTempObject', N'PROCEDURE', OBJECT_NAME(@OriginalObjectId)); + VALUES('[tSQLt].[Private_MarktSQLtTempObject]', N'PROCEDURE', OBJECT_NAME(@OriginalObjectId)), + ('[tSQLt].[Private_MarktSQLtTempObject_SpyProcedureLog]', N'TABLE', NULL); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +CREATE PROC SpyProcedureTests.[test new SpyProcedureLog table is marked as tSQLt.IsTempObject] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 AS RETURN;'); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1'; + + SELECT name, value + INTO #Actual + FROM sys.extended_properties + WHERE class_desc = 'OBJECT_OR_COLUMN' + AND major_id = OBJECT_ID('SpyProcedureTests.TempProcedure1_SpyProcedureLog') + AND name = 'tSQLt.IsTempObject'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('tSQLt.IsTempObject', 1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO From 29b8350e7a52e9575dad676b384139952c686699 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 10 Nov 2021 18:31:37 -0500 Subject: [PATCH 005/119] Debugging code to Run_Methods.sql; Made sure that if @NewNameOfOriginalObject NULL, we don't set the extended property tSQLt.Private_TestDouble_OrgObjectName; Messed around with transactions by writing a very good test, but it didn't actually work. --- Source/Run_Methods.sql | 26 +++++++++++-- .../tSQLt.Private_MarktSQLtTempObject.ssp.sql | 30 +++++++++------ Tests/AnnotationNoTransactionTests.class.sql | 33 ++++++++++++++++- ...Private_MarktSQLtTempObjectTests.class.sql | 37 +++++++++++++++++++ Tests/UndoTestDoublesTests.class.sql | 4 ++ 5 files changed, 113 insertions(+), 17 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index fb9a8da94..4db5981b1 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -49,7 +49,7 @@ BEGIN DECLARE @Result NVARCHAR(MAX); DECLARE @TranName CHAR(32); EXEC tSQLt.GetNewTranName @TranName OUT; DECLARE @TestResultId INT; - DECLARE @PreExecTrancount INT; + DECLARE @PreExecTrancount INT = NULL; DECLARE @TestObjectId INT; DECLARE @VerboseMsg NVARCHAR(MAX); @@ -103,7 +103,12 @@ BEGIN END; SET @PreExecTrancount = @@TRANCOUNT; - + --SELECT 1 X, @SkipTestFlag SkipTestFlag, + -- @NoTransactionFlag NoTransactionFlag, + -- @TransactionStartedFlag TransactionStartedFlag, + -- @PreExecTrancount PreExecTrancount, + -- @@TRANCOUNT Trancount, + -- @TestName TestName; TRUNCATE TABLE tSQLt.TestMessage; @@ -115,6 +120,7 @@ BEGIN BEGIN IF (@SetUp IS NOT NULL) EXEC @SetUp; EXEC (@Cmd); + --TODO:NoTran ----EXEC @CleanUp --Probably further down, called ".CleanUp" IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) @@ -233,13 +239,25 @@ BEGIN SET @Result = 'Error'; SET @Msg = ERROR_MESSAGE(); END CATCH - + --SELECT 2 X, @SkipTestFlag SkipTestFlag, + -- @NoTransactionFlag NoTransactionFlag, + -- @TransactionStartedFlag TransactionStartedFlag, + -- @PreExecTrancount PreExecTrancount, + -- @@TRANCOUNT Trancount, + -- @TestName TestName; --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran BEGIN TRY IF(@TransactionStartedFlag = 1) - BEGIN + BEGIN + --SELECT 3 X, @SkipTestFlag SkipTestFlag, + -- @NoTransactionFlag NoTransactionFlag, + -- @TransactionStartedFlag TransactionStartedFlag, + -- @PreExecTrancount PreExecTrancount, + -- @@TRANCOUNT Trancount, + -- @TestName TestName; + ROLLBACK TRAN @TranName; END; END TRY diff --git a/Source/tSQLt.Private_MarktSQLtTempObject.ssp.sql b/Source/tSQLt.Private_MarktSQLtTempObject.ssp.sql index f05a2af81..fa12ef231 100644 --- a/Source/tSQLt.Private_MarktSQLtTempObject.ssp.sql +++ b/Source/tSQLt.Private_MarktSQLtTempObject.ssp.sql @@ -4,7 +4,7 @@ GO CREATE PROCEDURE tSQLt.Private_MarktSQLtTempObject @ObjectName NVARCHAR(MAX), @ObjectType NVARCHAR(MAX), - @NewNameOfOriginalObject NVARCHAR(4000) + @NewNameOfOriginalObject NVARCHAR(4000) = NULL AS BEGIN DECLARE @UnquotedSchemaName NVARCHAR(MAX); @@ -26,11 +26,14 @@ BEGIN @level0type = N'SCHEMA', @level0name = @UnquotedSchemaName, @level1type = @ObjectType, @level1name = @UnquotedObjectName; - EXEC sys.sp_addextendedproperty - @name = N'tSQLt.Private_TestDouble_OrgObjectName', - @value = @NewNameOfOriginalObject, - @level0type = N'SCHEMA', @level0name = @UnquotedSchemaName, - @level1type = @ObjectType, @level1name = @UnquotedObjectName; + IF(@NewNameOfOriginalObject IS NOT NULL) + BEGIN + EXEC sys.sp_addextendedproperty + @name = N'tSQLt.Private_TestDouble_OrgObjectName', + @value = @NewNameOfOriginalObject, + @level0type = N'SCHEMA', @level0name = @UnquotedSchemaName, + @level1type = @ObjectType, @level1name = @UnquotedObjectName; + END; END; ELSE BEGIN @@ -41,12 +44,15 @@ BEGIN @level1type = N'TABLE', @level1name = @UnquotedParentName, @level2type = @ObjectType, @level2name = @UnquotedObjectName; - EXEC sys.sp_addextendedproperty - @name = N'tSQLt.Private_TestDouble_OrgObjectName', - @value = @NewNameOfOriginalObject, - @level0type = N'SCHEMA', @level0name = @UnquotedSchemaName, - @level1type = N'TABLE', @level1name = @UnquotedParentName, - @level2type = @ObjectType, @level2name = @UnquotedObjectName; + IF(@NewNameOfOriginalObject IS NOT NULL) + BEGIN + EXEC sys.sp_addextendedproperty + @name = N'tSQLt.Private_TestDouble_OrgObjectName', + @value = @NewNameOfOriginalObject, + @level0type = N'SCHEMA', @level0name = @UnquotedSchemaName, + @level1type = N'TABLE', @level1name = @UnquotedParentName, + @level2type = @ObjectType, @level2name = @UnquotedObjectName; + END; END; END; ---Build- diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 62e78df43..00e7797d9 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -28,15 +28,46 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS IN END; GO +--[@tSQLt:SkipTest]('TODO: needs other tests first') +CREATE PROCEDURE AnnotationNoTransactionTests.[test produces meaningful error when pre and post transactions counts don't match] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN; + '); + + --EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; + + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; +END; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test succeeding test gets correct entry in TestResults table] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RETURN; + '); + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + SELECT * FROM tSQLt.TestResult AS TR + EXEC tSQLt.Fail 'TODO: testname, TranName, Result'; +END; +GO /*-- TODO -- transaction opened during test -- transaction commited during test -- test skipped? +-- inner-transaction-free test succeeds +-- inner-transaction-free test fails +-- inner-transaction-free test errors -- cleanup execution tables --- SpyProcedureLog table needs to be marked with IsTempObject in extended properties -- named cleanup (needs to execute even if there's an error during test execution) -- confirm pre and post transaction counts match +-- [test produces meaningful error when pre and post transactions counts don't match] --*/ \ No newline at end of file diff --git a/Tests/Private_MarktSQLtTempObjectTests.class.sql b/Tests/Private_MarktSQLtTempObjectTests.class.sql index 386228dc0..83b04387a 100644 --- a/Tests/Private_MarktSQLtTempObjectTests.class.sql +++ b/Tests/Private_MarktSQLtTempObjectTests.class.sql @@ -130,4 +130,41 @@ BEGIN EXEC tSQLt.AssertEmptyTable '#Actual'; END; +GO +CREATE PROCEDURE Private_MarktSQLtTempObjectTests.[test doesn't set tSQLt.Private_TestDouble_OrgObjectName if @NewNameOfOriginalObject is NULL for child objects] +AS +BEGIN + CREATE TABLE Private_MarktSQLtTempObjectTests.TempTable1(i INT NOT NULL CONSTRAINT TempConstraint1 PRIMARY KEY); + EXEC tSQLt.Private_MarktSQLtTempObject + @ObjectName = 'Private_MarktSQLtTempObjectTests.TempConstraint1', + @ObjectType = N'CONSTRAINT', + @NewNameOfOriginalObject = NULL; + + SELECT * + INTO #Actual + FROM sys.extended_properties AS EP + WHERE EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.major_id = OBJECT_ID('Private_MarktSQLtTempObjectTests.TempConstraint1') + AND EP.name = 'tSQLt.Private_TestDouble_OrgObjectName'; + + EXEC tSQLt.AssertEmptyTable '#Actual'; +END; +GO +CREATE PROCEDURE Private_MarktSQLtTempObjectTests.[test defaults to not setting tSQLt.Private_TestDouble_OrgObjectName] +AS +BEGIN + CREATE TABLE Private_MarktSQLtTempObjectTests.TempTable1(i INT NOT NULL); + EXEC tSQLt.Private_MarktSQLtTempObject + @ObjectName = 'Private_MarktSQLtTempObjectTests.TempTable1', + @ObjectType = N'TABLE'; + + SELECT * + INTO #Actual + FROM sys.extended_properties AS EP + WHERE EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.major_id = OBJECT_ID('Private_MarktSQLtTempObjectTests.TempTable1') + AND EP.name = 'tSQLt.Private_TestDouble_OrgObjectName'; + + EXEC tSQLt.AssertEmptyTable '#Actual'; +END; GO \ No newline at end of file diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index 81cdac500..6ecd6337c 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -474,6 +474,10 @@ BEGIN EXEC tSQLt.RemoveObject @ObjectName='UndoTestDoublesTests.aSimpleTable'; EXEC ('CREATE PROCEDURE UndoTestDoublesTests.aSimpleTable AS PRINT ''Who came up with that name?'';'); EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_Print'; + EXEC sys.sp_dropextendedproperty + @name = N'tSQLt.IsTempObject', + @level0type = N'SCHEMA', @level0name = 'tSQLt', + @level1type = N'TABLE', @level1name = 'Private_Print_SpyProcedureLog'; EXEC tSQLt.UndoTestDoubles @Force=1; From f5fa012206054fc164843d7527cbf3e0caf51639 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 10 Nov 2021 21:58:40 -0500 Subject: [PATCH 006/119] Added a named primary key constraint to the tSQLt.TestResult table; Discussion in progress on TranName and how we deal with NoTransaction tests; Wrote a test. Yay. --- Source/Run_Methods.sql | 4 ++- Source/tSQLt.class.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 28 ++++++++++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 4db5981b1..07d9ef561 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -47,7 +47,7 @@ BEGIN DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; DECLARE @Result NVARCHAR(MAX); - DECLARE @TranName CHAR(32); EXEC tSQLt.GetNewTranName @TranName OUT; + DECLARE @TranName CHAR(32) = NULL; DECLARE @TestResultId INT; DECLARE @PreExecTrancount INT = NULL; DECLARE @TestObjectId INT; @@ -97,6 +97,8 @@ BEGIN IF(@NoTransactionFlag = 0) BEGIN + EXEC tSQLt.GetNewTranName @TranName OUT; + UPDATE we still need to save the TranName as something somewhere. BEGIN TRAN; SET @TransactionStartedFlag = 1; SAVE TRAN @TranName; diff --git a/Source/tSQLt.class.sql b/Source/tSQLt.class.sql index 3e6b92874..914764f43 100644 --- a/Source/tSQLt.class.sql +++ b/Source/tSQLt.class.sql @@ -1,7 +1,7 @@ ---Build+ GO CREATE TABLE tSQLt.TestResult( - Id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, + Id INT IDENTITY(1,1) CONSTRAINT [PK:tSQLt.TestResult] PRIMARY KEY CLUSTERED, Class NVARCHAR(MAX) NOT NULL, TestCase NVARCHAR(MAX) NOT NULL, Name AS (QUOTENAME(Class) + '.' + QUOTENAME(TestCase)), diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 00e7797d9..f0b31c024 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -53,8 +53,32 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RE '); EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; - SELECT * FROM tSQLt.TestResult AS TR - EXEC tSQLt.Fail 'TODO: testname, TranName, Result'; + SELECT Result INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + VALUES('Success'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test transaction name is NULL in TestResults table] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RETURN; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + SELECT TranName INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + VALUES(NULL); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO /*-- TODO From 29b925a3bdba9e1d27ae475f348625bf4dfdb2ef Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 11 Nov 2021 11:35:49 -0500 Subject: [PATCH 007/119] Make TranName nullable in the tSQLt.TestResult table; made some tests pass to test the whole NULL thing; Created a failing test. Build continues to be broken. --- Source/Run_Methods.sql | 2 +- Source/tSQLt.class.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 43 ++++++++++++++++++-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 07d9ef561..4cf7c2b0e 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -98,7 +98,7 @@ BEGIN IF(@NoTransactionFlag = 0) BEGIN EXEC tSQLt.GetNewTranName @TranName OUT; - UPDATE we still need to save the TranName as something somewhere. + UPDATE tSQLt.TestResult SET TranName = @TranName WHERE Id = @TestResultId; BEGIN TRAN; SET @TransactionStartedFlag = 1; SAVE TRAN @TranName; diff --git a/Source/tSQLt.class.sql b/Source/tSQLt.class.sql index 914764f43..890c867d9 100644 --- a/Source/tSQLt.class.sql +++ b/Source/tSQLt.class.sql @@ -5,7 +5,7 @@ CREATE TABLE tSQLt.TestResult( Class NVARCHAR(MAX) NOT NULL, TestCase NVARCHAR(MAX) NOT NULL, Name AS (QUOTENAME(Class) + '.' + QUOTENAME(TestCase)), - TranName NVARCHAR(MAX) NOT NULL, + TranName NVARCHAR(MAX) NULL, Result NVARCHAR(MAX) NULL, Msg NVARCHAR(MAX) NULL, TestStartTime DATETIME2 NOT NULL CONSTRAINT [DF:TestResult(TestStartTime)] DEFAULT SYSDATETIME(), diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index f0b31c024..f527ed4f4 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -43,6 +43,38 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BE EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; END; GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test transaction name is NULL in TestResults table] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RETURN; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + SELECT TranName INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + VALUES(NULL); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test if not NoTransaction TranName is valued in TestResults table] +AS +BEGIN + DECLARE @ActualTranName NVARCHAR(MAX); + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE MyInnerTests.[test should execute inside of transaction] AS RETURN;'); + + EXEC tSQLt.Run 'MyInnerTests.[test should execute inside of transaction]'; + SET @ActualTranName = (SELECT TranName FROM tSQLt.TestResult); + + EXEC tSQLt.AssertLike @ExpectedPattern = 'tSQLtTran%', @Actual = @ActualTranName; +END; +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test succeeding test gets correct entry in TestResults table] AS BEGIN @@ -62,22 +94,23 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RE EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test transaction name is NULL in TestResults table] +CREATE PROCEDURE AnnotationNoTransactionTests.[test failing test gets correct entry in TestResults table] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:NoTransaction]() -CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RETURN; +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS EXEC tSQLt.Fail ''Some Obscure Reason''; '); + EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; - SELECT TranName INTO #Actual FROM tSQLt.TestResult AS TR; + SELECT Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES(NULL); + VALUES('Success','Some Obscure Reason'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO @@ -93,5 +126,7 @@ GO -- named cleanup (needs to execute even if there's an error during test execution) -- confirm pre and post transaction counts match -- [test produces meaningful error when pre and post transactions counts don't match] +-- we still need to save the TranName as something somewhere. +-- settings need to be preserved (e.g. SummaryError) --*/ \ No newline at end of file From 0378bbeba3109b5c7449e0c6e2f786192e357de5 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 11 Nov 2021 11:37:30 -0500 Subject: [PATCH 008/119] Save the noodling around transaction names. --- Experiments/TestingTransactionNames.sql | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Experiments/TestingTransactionNames.sql diff --git a/Experiments/TestingTransactionNames.sql b/Experiments/TestingTransactionNames.sql new file mode 100644 index 000000000..85958a011 --- /dev/null +++ b/Experiments/TestingTransactionNames.sql @@ -0,0 +1,21 @@ +IF(XACT_STATE()<>0)ROLLBACK +GO +DROP TABLE IF EXISTS #Actual +GO +DECLARE @TranName NVARCHAR(32) = NULL; +SELECT 1,@@TRANCOUNT +SELECT 1 X,@@TRANCOUNT TC INTO #Actual +BEGIN TRAN; +SELECT 2,@@TRANCOUNT +INSERT INTO #Actual VALUES(2,@@TRANCOUNT); +SAVE TRAN @TranName; +SELECT 3,@@TRANCOUNT +SELECT * FROM sys.dm_tran_current_transaction AS DTCT JOIN sys.dm_tran_session_transactions +AS DTAT ON DTAT.transaction_id = DTCT.transaction_id +INSERT INTO #Actual VALUES(3,@@TRANCOUNT); +SELECT * FROM #Actual AS A +ROLLBACK TRAN @TranName; +SELECT 4,@@TRANCOUNT +INSERT INTO #Actual VALUES(4,@@TRANCOUNT); +COMMIT; +SELECT * FROM #Actual AS A From 8b3b9a442ea5bd69ad44ca0cec7e1a701bb770e7 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 11 Nov 2021 14:47:17 -0500 Subject: [PATCH 009/119] just in case running with no transactions kills my laptop. --- Tests/AnnotationNoTransactionTests.class.sql | 46 ++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index f527ed4f4..e4d3932a2 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -110,17 +110,57 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS EX SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('Success','Some Obscure Reason'); + VALUES('Failure','Some Obscure Reason'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test recoverable erroring test gets correct entry in TestResults table] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RAISERROR (''Some Obscure Recoverable Error'', 16, 10); + '); + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + SELECT Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + VALUES('Error','Some Obscure Recoverable Error[16,10]{MyInnerTests.test should execute outside of transaction,3}'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +--[@tSQLt:SkipTest]('Only works if we run the test without a transaction and ...e doesn''t feel comfortable doing that yet.') +CREATE PROCEDURE AnnotationNoTransactionTests.[test unrecoverable erroring test gets correct entry in TestResults table] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS SELECT CAST(''Some obscure string'' AS INT); + '); + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + SELECT Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + VALUES('Error','Some Obscure Recoverable Error[16,10]{MyInnerTests.test should execute outside of transaction,3}'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO + /*-- TODO -- transaction opened during test -- transaction commited during test -- test skipped? --- inner-transaction-free test succeeds --- inner-transaction-free test fails -- inner-transaction-free test errors -- cleanup execution tables -- named cleanup (needs to execute even if there's an error during test execution) From 4d3ff1619a33698022d105763720fab1e6e74f87 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 11 Nov 2021 21:31:10 -0500 Subject: [PATCH 010/119] Debugging Run_Methods.sql; Saving some transaction noodling; Remove unused table, tSQLt.Private_ExpectException; testing skiptest with notransaction and it works (we think). --- Experiments/Experiments.ssmssqlproj | 4 +++- Source/Run_Methods.sql | 24 +++++++++++++++++--- Source/tSQLt.class.sql | 2 -- Tests/AnnotationNoTransactionTests.class.sql | 13 +++++++---- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/Experiments/Experiments.ssmssqlproj b/Experiments/Experiments.ssmssqlproj index 4bf7c06d0..262ebcdec 100644 --- a/Experiments/Experiments.ssmssqlproj +++ b/Experiments/Experiments.ssmssqlproj @@ -2,7 +2,6 @@ - @@ -102,6 +101,9 @@ SQLCmdTest.1.sql + + TestingTransactionNames.sql + diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 4cf7c2b0e..85d42a0be 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -110,11 +110,14 @@ BEGIN -- @TransactionStartedFlag TransactionStartedFlag, -- @PreExecTrancount PreExecTrancount, -- @@TRANCOUNT Trancount, - -- @TestName TestName; + -- @TestName TestName, + -- @Result Result, + -- @Msg Msg; TRUNCATE TABLE tSQLt.TestMessage; + DECLARE @TmpMsg NVARCHAR(MAX); DECLARE @TestEndTime DATETIME2; SET @TestEndTime = NULL; BEGIN TRY @@ -246,7 +249,9 @@ BEGIN -- @TransactionStartedFlag TransactionStartedFlag, -- @PreExecTrancount PreExecTrancount, -- @@TRANCOUNT Trancount, - -- @TestName TestName; + -- @TestName TestName, + -- @Result Result, + -- @Msg Msg; --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran @@ -258,7 +263,9 @@ BEGIN -- @TransactionStartedFlag TransactionStartedFlag, -- @PreExecTrancount PreExecTrancount, -- @@TRANCOUNT Trancount, - -- @TestName TestName; + -- @TestName TestName, + -- @Result Result, + -- @Msg Msg; ROLLBACK TRAN @TranName; END; @@ -277,6 +284,17 @@ BEGIN END; END CATCH; + --SELECT 4 X, @SkipTestFlag SkipTestFlag, + -- @NoTransactionFlag NoTransactionFlag, + -- @TransactionStartedFlag TransactionStartedFlag, + -- @PreExecTrancount PreExecTrancount, + -- @@TRANCOUNT Trancount, + -- @TestName TestName, + -- @Result Result, + -- @Msg Msg; + + + If(@Result NOT IN ('Success','Skipped')) BEGIN SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; diff --git a/Source/tSQLt.class.sql b/Source/tSQLt.class.sql index 890c867d9..5789e3b07 100644 --- a/Source/tSQLt.class.sql +++ b/Source/tSQLt.class.sql @@ -22,8 +22,6 @@ CREATE TABLE tSQLt.Run_LastExecution( LoginTime DATETIME ); GO -CREATE TABLE tSQLt.Private_ExpectException(i INT); -GO CREATE PROCEDURE tSQLt.Private_Print @Message NVARCHAR(MAX), @Severity INT = 0 diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index e4d3932a2..2d8643c2c 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -134,24 +134,25 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RA EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO ---[@tSQLt:SkipTest]('Only works if we run the test without a transaction and ...e doesn''t feel comfortable doing that yet.') -CREATE PROCEDURE AnnotationNoTransactionTests.[test unrecoverable erroring test gets correct entry in TestResults table] +--[@tSQLt:NoTransaction]() +--[@tSQLt:SkipTest]('TODO: needs other tests first') +CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:NoTransaction]() -CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS SELECT CAST(''Some obscure string'' AS INT); +CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT CAST(''Some obscure string'' AS INT); '); EXEC tSQLt.SetSummaryError 0; - EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; SELECT Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('Error','Some Obscure Recoverable Error[16,10]{MyInnerTests.test should execute outside of transaction,3}'); + VALUES('Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO @@ -168,5 +169,7 @@ GO -- [test produces meaningful error when pre and post transactions counts don't match] -- we still need to save the TranName as something somewhere. -- settings need to be preserved (e.g. SummaryError) +-- Ctrl+9 is broken with NoTransaction +-- preserve content of all tSQLt.% tables --*/ \ No newline at end of file From 8f031dd6b0417c42548456913c8fd21e669ed92e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 11 Nov 2021 21:59:21 -0500 Subject: [PATCH 011/119] oh my gosh, collations were almost a thing, but they aren't so now tSQLt.TestMessage --> #TestMessage. --- Source/Run_Methods.sql | 7 ++----- Source/tSQLt.Fail.ssp.sql | 2 +- Source/tSQLt.class.sql | 4 ---- Tests/FailTests.class.sql | 2 +- Tests/tSQLt_test.class.sql | 4 ++-- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 85d42a0be..1e13ee430 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -57,6 +57,7 @@ BEGIN SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); TRUNCATE TABLE tSQLt.CaptureOutputLog; + CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); CREATE TABLE #NoTransaction(X INT); @@ -114,10 +115,6 @@ BEGIN -- @Result Result, -- @Msg Msg; - TRUNCATE TABLE tSQLt.TestMessage; - - - DECLARE @TmpMsg NVARCHAR(MAX); DECLARE @TestEndTime DATETIME2; SET @TestEndTime = NULL; BEGIN TRY @@ -149,7 +146,7 @@ BEGIN SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' BEGIN - SELECT @Msg = Msg FROM tSQLt.TestMessage; + SELECT @Msg = Msg FROM #TestMessage; SET @Result = 'Failure'; END ELSE diff --git a/Source/tSQLt.Fail.ssp.sql b/Source/tSQLt.Fail.ssp.sql index 6153c1e42..8eb493704 100644 --- a/Source/tSQLt.Fail.ssp.sql +++ b/Source/tSQLt.Fail.ssp.sql @@ -37,7 +37,7 @@ BEGIN SAVE TRAN @TranName; END; - INSERT INTO tSQLt.TestMessage(Msg) + INSERT INTO #TestMessage(Msg) SELECT COALESCE(@Message0, '!NULL!') + COALESCE(@Message1, '!NULL!') + COALESCE(@Message2, '!NULL!') diff --git a/Source/tSQLt.class.sql b/Source/tSQLt.class.sql index 5789e3b07..da00c4035 100644 --- a/Source/tSQLt.class.sql +++ b/Source/tSQLt.class.sql @@ -12,10 +12,6 @@ CREATE TABLE tSQLt.TestResult( TestEndTime DATETIME2 NULL ); GO -CREATE TABLE tSQLt.TestMessage( - Msg NVARCHAR(MAX) -); -GO CREATE TABLE tSQLt.Run_LastExecution( TestName NVARCHAR(MAX), SessionId INT, diff --git a/Tests/FailTests.class.sql b/Tests/FailTests.class.sql index 28aa139d3..3f5bbb828 100644 --- a/Tests/FailTests.class.sql +++ b/Tests/FailTests.class.sql @@ -93,7 +93,7 @@ BEGIN DECLARE @TestResultMessage NVARCHAR(MAX); SELECT @TestResultMessage = Msg - FROM tSQLt.TestMessage; + FROM #TestMessage; DECLARE @ExpectedMessage NVARCHAR(MAX); SET @ExpectedMessage = '%Not really a failure - just seeing that fail works%'+CHAR(13)+CHAR(10)+'Warning: Uncommitable transaction detected!%' diff --git a/Tests/tSQLt_test.class.sql b/Tests/tSQLt_test.class.sql index 31fa05afd..26acc7c20 100644 --- a/Tests/tSQLt_test.class.sql +++ b/Tests/tSQLt_test.class.sql @@ -1310,7 +1310,7 @@ BEGIN SELECT '{' + Msg + '}' AS BracedMsg INTO #actual - FROM tSQLt.TestMessage; + FROM #TestMessage; SELECT TOP(0) * INTO #expected @@ -1333,7 +1333,7 @@ BEGIN SELECT '{' + Msg + '}' AS BracedMsg INTO #actual - FROM tSQLt.TestMessage; + FROM #TestMessage; SELECT TOP(0) * INTO #expected From 589346e4a68d2631a19ddcb837f4fa9b51cb0d88 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 12 Nov 2021 21:49:45 -0500 Subject: [PATCH 012/119] Removed Private_NullCellTable and related tests --- Source/BuildOrder.txt | 1 - ..._CreateResultTableForCompareTables.ssp.sql | 6 +- Source/tSQLt.Private_Init.ssp.sql | 2 - Source/tSQLt.Private_NullCellTable.tbl.sql | 21 --- Tests/AssertEqualsTableTests.class.sql | 8 +- Tests/Private_InitTests.class.sql | 39 ------ Tests/Private_NullCellTableTests.class.sql | 131 ------------------ 7 files changed, 5 insertions(+), 203 deletions(-) delete mode 100644 Source/tSQLt.Private_NullCellTable.tbl.sql delete mode 100644 Tests/Private_NullCellTableTests.class.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 457cebdda..730c1a746 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -78,7 +78,6 @@ tSQLt.Private_CreateResultTableForCompareTables.ssp.sql tSQLt.Private_ValidateThatAllDataTypesInTableAreSupported.ssp.sql tSQLt.Private_CompareTablesFailIfUnequalRowsExists.ssp.sql tSQLt.Private_CompareTables.ssp.sql -tSQLt.Private_NullCellTable.tbl.sql tSQLt.AssertObjectExists.ssp.sql tSQLt.AssertObjectDoesNotExist.ssp.sql tSQLt.AssertEqualsString.ssp.sql diff --git a/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql b/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql index 17055733f..f57d3ce18 100644 --- a/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql +++ b/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql @@ -10,10 +10,8 @@ AS BEGIN DECLARE @Cmd NVARCHAR(MAX); SET @Cmd = ' - SELECT ''='' AS ' + @ResultColumn + ', Expected.* INTO ' + @ResultTable + ' - FROM tSQLt.Private_NullCellTable N - LEFT JOIN ' + @BaseTable + ' AS Expected ON N.I <> N.I - TRUNCATE TABLE ' + @ResultTable + ';' --Need to insert an actual row to prevent IDENTITY property from transfering (IDENTITY_COL can't be NULLable); + SELECT TOP(0) ''>'' AS ' + @ResultColumn + ', Expected.* INTO ' + @ResultTable + ' + FROM ' + @BaseTable + ' AS Expected RIGHT JOIN ' + @BaseTable + ' AS X ON 1=0; ' EXEC(@Cmd); END GO diff --git a/Source/tSQLt.Private_Init.ssp.sql b/Source/tSQLt.Private_Init.ssp.sql index ddf1efb2c..d3bb1ce99 100644 --- a/Source/tSQLt.Private_Init.ssp.sql +++ b/Source/tSQLt.Private_Init.ssp.sql @@ -7,8 +7,6 @@ AS BEGIN EXEC tSQLt.Private_CleanTestResult; - UPDATE tSQLt.Private_NullCellTable SET I=I WHERE 'Forcing trigger execution.' != ''; - DECLARE @enable BIT; SET @enable = 1; DECLARE @version_match BIT;SET @version_match = 0; BEGIN TRY diff --git a/Source/tSQLt.Private_NullCellTable.tbl.sql b/Source/tSQLt.Private_NullCellTable.tbl.sql deleted file mode 100644 index 8aea4a562..000000000 --- a/Source/tSQLt.Private_NullCellTable.tbl.sql +++ /dev/null @@ -1,21 +0,0 @@ -IF OBJECT_ID('tSQLt.Private_NullCellTable_PreventTruncate') IS NOT NULL DROP TABLE tSQLt.Private_NullCellTable_PreventTruncate; -IF OBJECT_ID('tSQLt.Private_NullCellTable') IS NOT NULL DROP TABLE tSQLt.Private_NullCellTable; -GO ----Build+ -GO -CREATE TABLE tSQLt.Private_NullCellTable( - I INT CONSTRAINT[U:tSQLt.Private_NullCellTable] UNIQUE CLUSTERED -); -GO - -INSERT INTO tSQLt.Private_NullCellTable (I) VALUES (NULL); -GO - -CREATE TRIGGER tSQLt.Private_NullCellTable_StopModifications ON tSQLt.Private_NullCellTable INSTEAD OF DELETE, INSERT, UPDATE -AS -BEGIN - IF EXISTS (SELECT 1 FROM tSQLt.Private_NullCellTable) RETURN; - INSERT INTO tSQLt.Private_NullCellTable VALUES (NULL); -END; -GO - diff --git a/Tests/AssertEqualsTableTests.class.sql b/Tests/AssertEqualsTableTests.class.sql index daff47a8f..6b30c988f 100644 --- a/Tests/AssertEqualsTableTests.class.sql +++ b/Tests/AssertEqualsTableTests.class.sql @@ -468,11 +468,10 @@ GO CREATE PROCEDURE AssertEqualsTableTests.[test considers NULL values identical] AS BEGIN - SELECT * - INTO AssertEqualsTableTests.NullCellTableCopy - FROM tSQLt.Private_NullCellTable; + SELECT NULL [aNULLColumn] INTO #Actual; + SELECT NULL [aNULLColumn] INTO #Expected; - EXEC tSQLt.AssertEqualsTable 'tSQLt.Private_NullCellTable', 'AssertEqualsTableTests.NullCellTableCopy'; + EXEC tSQLt.AssertEqualsTable #Expected, #Actual; END; GO @@ -705,7 +704,6 @@ BEGIN EXEC tSQLt.AssertEqualsTable 'expected', 'actual'; END; GO - CREATE PROC AssertEqualsTableTests.test_AssertEqualsTable_works_with_expected_having_identity_column AS BEGIN diff --git a/Tests/Private_InitTests.class.sql b/Tests/Private_InitTests.class.sql index 1b21021bf..662db364b 100644 --- a/Tests/Private_InitTests.class.sql +++ b/Tests/Private_InitTests.class.sql @@ -39,42 +39,3 @@ RETURNS TABLE AS RETURN SELECT '1234' Version, '1234' ClrVersion,CAST(98.76 AS NUMERIC(10,2)) SqlVersion, CAST(98.76 AS NUMERIC(10,2)) InstalledOnSqlVersion, NULL SqlBuild, CAST(NULL AS NVARCHAR(MAX)) SqlEdition; GO -CREATE PROCEDURE Private_InitTests.[test Private_Init causes a NULL row to be inserted in tSQLt.Private_NullCellTable if empty] -AS -BEGIN - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.EnableExternalAccess'; - EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Info', @FakeFunctionName = 'Private_InitTests.[matching versions]'; - - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NullCellTable'; - EXEC tSQLt.ApplyTrigger @TableName = 'tSQLt.Private_NullCellTable', @TriggerName= 'tSQLt.Private_NullCellTable_StopModifications'; - - EXEC tSQLt.Private_Init; - - SELECT * INTO #Actual FROM tSQLt.Private_NullCellTable; - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - INSERT INTO #Expected VALUES (NULL); - - EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; -END; -GO - -CREATE PROCEDURE Private_InitTests.[test Private_Init does not insert a second row into tSQLt.Private_NullCellTable when called repeatedly] -AS -BEGIN - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.EnableExternalAccess'; - EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Info', @FakeFunctionName = 'Private_InitTests.[matching versions]'; - - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NullCellTable'; - EXEC tSQLt.ApplyTrigger @TableName = 'tSQLt.Private_NullCellTable', @TriggerName= 'tSQLt.Private_NullCellTable_StopModifications'; - - EXEC tSQLt.Private_Init; - EXEC tSQLt.Private_Init; - - SELECT * INTO #Actual FROM tSQLt.Private_NullCellTable; - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - - INSERT INTO #Expected VALUES (NULL); - - EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; -END; -GO \ No newline at end of file diff --git a/Tests/Private_NullCellTableTests.class.sql b/Tests/Private_NullCellTableTests.class.sql deleted file mode 100644 index e04a125ac..000000000 --- a/Tests/Private_NullCellTableTests.class.sql +++ /dev/null @@ -1,131 +0,0 @@ - EXEC tSQLt.NewTestClass 'Private_NullCellTableTests'; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test table contains a single null cell] - AS - BEGIN - CREATE TABLE Private_NullCellTableTests.Expected (I INT); - INSERT INTO Private_NullCellTableTests.Expected(I) VALUES (NULL); - - EXEC tSQLt.AssertEqualsTable 'Private_NullCellTableTests.Expected', 'tSQLt.Private_NullCellTable'; - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[AssertStatementPerformsNoDataChangeToTable] - @Statement NVARCHAR(MAX) - AS - BEGIN - CREATE TABLE Private_NullCellTableTests.Expected (I INT); - INSERT INTO Private_NullCellTableTests.Expected(I) VALUES (NULL); - - BEGIN TRY - EXEC @Statement; - END TRY - BEGIN CATCH - -- Left intentionally empty - END CATCH; - - EXEC tSQLt.AssertEqualsTable 'Private_NullCellTableTests.Expected', 'tSQLt.Private_NullCellTable'; - - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test cannot insert second NULL row] - AS - BEGIN - EXEC Private_NullCellTableTests.[AssertStatementPerformsNoDataChangeToTable] 'INSERT INTO tSQLt.Private_NullCellTable (I) VALUES (NULL);'; - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test cannot insert a non-NULL row] - AS - BEGIN - EXEC Private_NullCellTableTests.[AssertStatementPerformsNoDataChangeToTable] 'INSERT INTO tSQLt.Private_NullCellTable (I) VALUES (5);'; - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test cannot delete row] - AS - BEGIN - EXEC Private_NullCellTableTests.[AssertStatementPerformsNoDataChangeToTable] 'DELETE FROM tSQLt.Private_NullCellTable;'; - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test cannot update row] - AS - BEGIN - EXEC Private_NullCellTableTests.[AssertStatementPerformsNoDataChangeToTable] 'UPDATE tSQLt.Private_NullCellTable SET I = 13;'; - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test can insert a row if the table is empty] - AS - BEGIN - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NullCellTable'; - EXEC tSQLt.ApplyTrigger @TableName = 'tSQLt.Private_NullCellTable', @TriggerName= 'tSQLt.Private_NullCellTable_StopModifications'; - - INSERT INTO tSQLt.Private_NullCellTable VALUES (NULL); - - SELECT * INTO #Actual FROM tSQLt.Private_NullCellTable; - - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - - INSERT INTO #Expected VALUES (NULL); - - EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test any insert will insert NULL row if table is empty] - AS - BEGIN - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NullCellTable'; - EXEC tSQLt.ApplyTrigger @TableName = 'tSQLt.Private_NullCellTable', @TriggerName= 'tSQLt.Private_NullCellTable_StopModifications'; - - INSERT INTO tSQLt.Private_NullCellTable VALUES (10),(11),(12); - - SELECT * INTO #Actual FROM tSQLt.Private_NullCellTable; - - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - - INSERT INTO #Expected VALUES (NULL); - - EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test any update will insert NULL row if table is empty] - AS - BEGIN - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NullCellTable'; - EXEC tSQLt.ApplyTrigger @TableName = 'tSQLt.Private_NullCellTable', @TriggerName= 'tSQLt.Private_NullCellTable_StopModifications'; - - UPDATE tSQLt.Private_NullCellTable SET I = I WHERE 1=0; - - SELECT * INTO #Actual FROM tSQLt.Private_NullCellTable; - - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - - INSERT INTO #Expected VALUES (NULL); - - EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; - END; - GO - - CREATE PROCEDURE Private_NullCellTableTests.[test any delete will insert NULL row if table is empty] - AS - BEGIN - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NullCellTable'; - EXEC tSQLt.ApplyTrigger @TableName = 'tSQLt.Private_NullCellTable', @TriggerName= 'tSQLt.Private_NullCellTable_StopModifications'; - - DELETE tSQLt.Private_NullCellTable WHERE 1=0; - - SELECT * INTO #Actual FROM tSQLt.Private_NullCellTable; - - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - - INSERT INTO #Expected VALUES (NULL); - - EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; - END; - GO From aff36d11fbfa4533419d371617c156c45ea2d4aa Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 12 Nov 2021 22:03:16 -0500 Subject: [PATCH 013/119] Removed tSQLt.AssertEqualsTableSchema_Actual and tSQLt.AssertEqualsTableSchema_Expected tables and replaced them with temp tables. --- Source/BuildOrder.txt | 1 - Source/tSQLt.AssertEqualsTableSchema.ssp.sql | 10 +++---- .../tSQLt.AssertEqualsTableSchema.tables.sql | 28 ------------------- 3 files changed, 5 insertions(+), 34 deletions(-) delete mode 100644 Source/tSQLt.AssertEqualsTableSchema.tables.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 730c1a746..b50eeaf84 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -93,7 +93,6 @@ tSQLt.Private_ValidateObjectsCompatibleWithFakeFunction.ssp.sql tSQLt.Private_CreateFakeFunction.ssp.sql tSQLt.FakeFunction.ssp.sql tSQLt.RenameClass.ssp.sql -tSQLt.AssertEqualsTableSchema.tables.sql tSQLt.AssertEqualsTableSchema.ssp.sql tSQLt.AssertStringTable.udt.sql tSQLt.AssertStringIn.ssp.sql diff --git a/Source/tSQLt.AssertEqualsTableSchema.ssp.sql b/Source/tSQLt.AssertEqualsTableSchema.ssp.sql index bf1807dcb..b41c60c62 100644 --- a/Source/tSQLt.AssertEqualsTableSchema.ssp.sql +++ b/Source/tSQLt.AssertEqualsTableSchema.ssp.sql @@ -8,9 +8,8 @@ CREATE PROCEDURE tSQLt.AssertEqualsTableSchema @Message NVARCHAR(MAX) = NULL AS BEGIN - INSERT INTO tSQLt.Private_AssertEqualsTableSchema_Expected([RANK(column_id)],name,system_type_id,user_type_id,max_length,precision,scale,collation_name,is_nullable) SELECT - RANK()OVER(ORDER BY C.column_id), + RANK()OVER(ORDER BY C.column_id) [RANK(column_id)], C.name, CAST(C.system_type_id AS NVARCHAR(MAX))+QUOTENAME(TS.name) system_type_id, CAST(C.user_type_id AS NVARCHAR(MAX))+CASE WHEN TU.system_type_id<> TU.user_type_id THEN QUOTENAME(SCHEMA_NAME(TU.schema_id))+'.' ELSE '' END + QUOTENAME(TU.name) user_type_id, @@ -19,15 +18,15 @@ BEGIN C.scale, C.collation_name, C.is_nullable + INTO #Expected FROM sys.columns AS C JOIN sys.types AS TS ON C.system_type_id = TS.user_type_id JOIN sys.types AS TU ON C.user_type_id = TU.user_type_id WHERE C.object_id = OBJECT_ID(@Expected); - INSERT INTO tSQLt.Private_AssertEqualsTableSchema_Actual([RANK(column_id)],name,system_type_id,user_type_id,max_length,precision,scale,collation_name,is_nullable) SELECT - RANK()OVER(ORDER BY C.column_id), + RANK()OVER(ORDER BY C.column_id) [RANK(column_id)], C.name, CAST(C.system_type_id AS NVARCHAR(MAX))+QUOTENAME(TS.name) system_type_id, CAST(C.user_type_id AS NVARCHAR(MAX))+CASE WHEN TU.system_type_id<> TU.user_type_id THEN QUOTENAME(SCHEMA_NAME(TU.schema_id))+'.' ELSE '' END + QUOTENAME(TU.name) user_type_id, @@ -36,6 +35,7 @@ BEGIN C.scale, C.collation_name, C.is_nullable + INTO #Actual FROM sys.columns AS C JOIN sys.types AS TS ON C.system_type_id = TS.user_type_id @@ -43,6 +43,6 @@ BEGIN ON C.user_type_id = TU.user_type_id WHERE C.object_id = OBJECT_ID(@Actual); - EXEC tSQLt.AssertEqualsTable 'tSQLt.Private_AssertEqualsTableSchema_Expected','tSQLt.Private_AssertEqualsTableSchema_Actual',@Message=@Message,@FailMsg='Unexpected/missing column(s)'; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual',@Message=@Message,@FailMsg='Unexpected/missing column(s)'; END; GO \ No newline at end of file diff --git a/Source/tSQLt.AssertEqualsTableSchema.tables.sql b/Source/tSQLt.AssertEqualsTableSchema.tables.sql deleted file mode 100644 index 58962af25..000000000 --- a/Source/tSQLt.AssertEqualsTableSchema.tables.sql +++ /dev/null @@ -1,28 +0,0 @@ -IF OBJECT_ID('tSQLt.Private_AssertEqualsTableSchema_Expected') IS NOT NULL DROP TABLE tSQLt.Private_AssertEqualsTableSchema_Expected; -IF OBJECT_ID('tSQLt.Private_AssertEqualsTableSchema_Actual') IS NOT NULL DROP TABLE tSQLt.Private_AssertEqualsTableSchema_Actual; -GO ----Build+ -GO -CREATE TABLE [tSQLt].[Private_AssertEqualsTableSchema_Actual] -( - name NVARCHAR(256) NULL, - [RANK(column_id)] INT NULL, - system_type_id NVARCHAR(MAX) NULL, - user_type_id NVARCHAR(MAX) NULL, - max_length SMALLINT NULL, - precision TINYINT NULL, - scale TINYINT NULL, - collation_name NVARCHAR(256) NULL, - is_nullable BIT NULL, - is_identity BIT NULL -); -GO -EXEC(' - SET NOCOUNT ON; - SELECT TOP(0) * - INTO tSQLt.Private_AssertEqualsTableSchema_Expected - FROM tSQLt.Private_AssertEqualsTableSchema_Actual AS AETSA; -'); -GO ----Build- -GO \ No newline at end of file From 99c6919ee78e6d21ab8cbd0fabbdd92671c3c869 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 12 Nov 2021 22:57:39 -0500 Subject: [PATCH 014/119] Clean up more NULLCellTable stuff in project files; added collation experiment --- Experiments/CollationTests.sql | 37 +++++++++++++++++++++++++++++ Experiments/Experiments.ssmssqlproj | 10 ++++++++ Source/Source.ssmssqlproj | 34 +++++++++++++------------- Tests/Tests.ssmssqlproj | 31 ++++++++++-------------- 4 files changed, 76 insertions(+), 36 deletions(-) create mode 100644 Experiments/CollationTests.sql diff --git a/Experiments/CollationTests.sql b/Experiments/CollationTests.sql new file mode 100644 index 000000000..57f615de3 --- /dev/null +++ b/Experiments/CollationTests.sql @@ -0,0 +1,37 @@ +/*-- +RELEVANT LINKS +https://www.red-gate.com/simple-talk/databases/sql-server/t-sql-programming-sql-server/questions-sql-server-collations-shy-ask/ +https://docs.microsoft.com/en-us/sql/t-sql/statements/windows-collation-name-transact-sql?view=sql-server-ver15 +--*/ + +SELECT DATABASEPROPERTYEX('tempdb','Collation') [tempdb collation], + DATABASEPROPERTYEX(DB_NAME(),'Collation') [database collation] +GO + +DROP TABLE IF EXISTS dbo.atable; +DROP TABLE IF EXISTS tempdb..#Actual; +SELECT + 'Hello World!' COLLATE SQL_Polish_CP1250_CI_AS c1, + 'Hello World!' COLLATE SQL_Latin1_General_CP437_BIN c2, + 'Hello World!' COLLATE Albanian_BIN2 c3 + INTO dbo.atable +SELECT * INTO #Actual FROM dbo.atable + +SELECT 'dbo.atable' [table],* FROM sys.columns WHERE object_id = OBJECT_ID('dbo.atable') + +SELECT '#Actual',* FROM tempdb.sys.columns WHERE object_id = OBJECT_ID('tempdb..#Actual'); + +GO + +DROP TABLE IF EXISTS tempdb..#E; +CREATE TABLE #E( + C1 NVARCHAR(MAX) COLLATE DATABASE_DEFAULT, + C2 NVARCHAR(MAX) +); + +SELECT '#E',* FROM tempdb.sys.columns WHERE object_id = OBJECT_ID('tempdb..#E'); +GO +SELECT * +FROM #E E1 +JOIN #E E2 +ON E2.C2 = E1.C1 \ No newline at end of file diff --git a/Experiments/Experiments.ssmssqlproj b/Experiments/Experiments.ssmssqlproj index 262ebcdec..73eb07cae 100644 --- a/Experiments/Experiments.ssmssqlproj +++ b/Experiments/Experiments.ssmssqlproj @@ -2,6 +2,7 @@ + @@ -35,6 +36,12 @@ CertificateTest.sql + + + + + CollationTests.sql + @@ -102,6 +109,9 @@ SQLCmdTest.1.sql + + + TestingTransactionNames.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 83e2fcd47..5aac71856 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -2,6 +2,7 @@ + @@ -18,6 +19,9 @@ ExecutePrepareServer.sql + + + Run_Methods.sql @@ -27,9 +31,15 @@ tSQLt.(at)tSQLt_MaxSqlMajorVersion.sfn.sql + + + tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql + + + tSQLt.(at)tSQLt_NoTransaction.sfn.sql @@ -39,9 +49,9 @@ tSQLt.(at)tSQLt_RunOnlyOnHostPlatform.sfn.sql - 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa - localhost,41433 - tSQLt_sa + + + tSQLt.(at)tSQLt_SkipTest.sfn.sql @@ -74,12 +84,6 @@ tSQLt.AssertEqualsTableSchema.ssp.sql - - - - - tSQLt.AssertEqualsTableSchema.tables.sql - @@ -410,12 +414,6 @@ tSQLt.Private_NewTestClassList.tbl.sql - - - - - tSQLt.Private_NullCellTable.tbl.sql - @@ -423,9 +421,9 @@ tSQLt.Private_PrepareFakeFunctionOutputTable.ssp.sql - 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa - localhost,41433 - tSQLt_sa + + + tSQLt.Private_ProcessTestAnnotations.ssp.sql diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 44bcdc618..669bad234 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -2,6 +2,7 @@ + @@ -12,27 +13,27 @@ AnnotationHostPlatformTests.class.sql - 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa - localhost,41433 - tSQLt_sa + + + AnnotationNoTransactionTests.class.sql - 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa - localhost,41433 - tSQLt_sa + + + AnnotationSkipTestTests.class.sql - 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa - localhost,41433 - tSQLt_sa + + + AnnotationSqlServerVersionTests.class.sql - 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:localhost,41433:False:tSQLt_sa - localhost,41433 - tSQLt_sa + + + AnnotationsTests.class.sql @@ -227,12 +228,6 @@ Private_MarktSQLtTempObjectTests.class.sql - - - - - Private_NullCellTableTests.class.sql - From fa08ee4e39b3487208fe7478767defedb5151436 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 13 Nov 2021 13:38:28 -0500 Subject: [PATCH 015/119] Added more ExploratoryTests but in the new file and then deleted the old file because #consolidation. --- Tests/AssertEqualsTableSchemaTests.class.sql | 19 +++- Tests/ExploratoryTests.class.sql | 23 ----- Tests/Tests.ssmssqlproj | 12 +-- Tests/_ExploratoryTests.class.sql | 99 ++++++++++++++++++++ 4 files changed, 123 insertions(+), 30 deletions(-) delete mode 100644 Tests/ExploratoryTests.class.sql create mode 100644 Tests/_ExploratoryTests.class.sql diff --git a/Tests/AssertEqualsTableSchemaTests.class.sql b/Tests/AssertEqualsTableSchemaTests.class.sql index 71d79aac5..b7c95049b 100644 --- a/Tests/AssertEqualsTableSchemaTests.class.sql +++ b/Tests/AssertEqualsTableSchemaTests.class.sql @@ -190,7 +190,7 @@ BEGIN EXEC tSQLt_testutil.assertFailCalled @Command, 'AssertEqualsTableSchema did not call Fail'; END; GO -CREATE PROCEDURE AssertEqualsTableSchemaTests.[test fail if 2nd table has Column with different colation order] +CREATE PROCEDURE AssertEqualsTableSchemaTests.[test fail if 2nd table has Column with different collation order] AS BEGIN CREATE TABLE AssertEqualsTableSchemaTests.Tbl1( @@ -288,6 +288,23 @@ BEGIN EXEC tSQLt.AssertEqualsTableSchema @Expected = 'AssertEqualsTableSchemaTests.Tbl1', @Actual = 'AssertEqualsTableSchemaTests.Tbl2'; END; GO +--[@tSQLt:SkipTest]('Not currently supported. See Issue xxxxx') +CREATE PROCEDURE AssertEqualsTableSchemaTests.[test calls fail if tables are #temporary and their schema does not match] +AS +BEGIN + CREATE TABLE #Actual( + Id INT PRIMARY KEY, + NoKey INT NULL + ); + CREATE TABLE #Expected( + Id BIT PRIMARY KEY, + NoKey NVARCHAR(MAX) NULL + ); + DECLARE @Command VARCHAR(MAX) = 'EXEC tSQLt.AssertEqualsTableSchema @Expected = ''#Expected'', @Actual = ''#Actual'';'; + EXEC tSQLt_testutil.assertFailCalled @Command, 'AssertEqualsTableSchema did not call Fail'; + +END; +GO /* SELECT diff --git a/Tests/ExploratoryTests.class.sql b/Tests/ExploratoryTests.class.sql deleted file mode 100644 index 33b95af93..000000000 --- a/Tests/ExploratoryTests.class.sql +++ /dev/null @@ -1,23 +0,0 @@ -EXEC tSQLt.NewTestClass 'ExploratoryTests'; -GO -CREATE PROCEDURE ExploratoryTests.[test NULL can be CAST into any datatype] -AS -BEGIN - DECLARE @cmd NVARCHAR(MAX) = - ( - SELECT ',CAST(NULL AS '+QUOTENAME(SCHEMA_NAME(schema_id))+'.'+QUOTENAME(name)+')['+CAST(user_type_id AS NVARCHAR(MAX))+']' - FROM sys.types - WHERE is_user_defined = 0 - FOR XML PATH(''),TYPE - ).value('.','NVARCHAR(MAX)'); - SET @cmd = STUFF(@cmd,1,1,''); - SET @cmd = 'SELECT TOP(0) '+@cmd+' INTO ExploratoryTests.DataTypeTestTable;' - EXEC(@cmd); - SELECT * - INTO #Actual - FROM sys.columns - WHERE object_id = OBJECT_ID('ExploratoryTests.DataTypeTestTable') - AND name <> user_type_id - EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; -END; -GO diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 669bad234..7389346c7 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -6,6 +6,12 @@ + + + + + _ExploratoryTests.class.sql + @@ -138,12 +144,6 @@ ExpectNoExceptionTests.class.sql - - - - - ExploratoryTests.class.sql - diff --git a/Tests/_ExploratoryTests.class.sql b/Tests/_ExploratoryTests.class.sql new file mode 100644 index 000000000..514042c23 --- /dev/null +++ b/Tests/_ExploratoryTests.class.sql @@ -0,0 +1,99 @@ +EXEC tSQLt.NewTestClass '_ExploratoryTests'; +GO +CREATE PROCEDURE [_ExploratoryTests].[test NULL can be CAST into any datatype] +AS +BEGIN + DECLARE @cmd NVARCHAR(MAX) = + ( + SELECT ',CAST(NULL AS '+QUOTENAME(SCHEMA_NAME(schema_id))+'.'+QUOTENAME(name)+')['+CAST(user_type_id AS NVARCHAR(MAX))+']' + FROM sys.types + WHERE is_user_defined = 0 + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'); + SET @cmd = STUFF(@cmd,1,1,''); + SET @cmd = 'SELECT TOP(0) '+@cmd+' INTO [_ExploratoryTests].DataTypeTestTable;' + EXEC(@cmd); + SELECT * + INTO #Actual + FROM sys.columns + WHERE object_id = OBJECT_ID('[_ExploratoryTests].DataTypeTestTable') + AND name <> user_type_id + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO + +CREATE PROCEDURE [_ExploratoryTests].[test MSSQL preserves COLLATION when using SELECT INTO] +AS +BEGIN + SELECT + 'Hello World!' COLLATE SQL_Polish_CP1250_CI_AS c1, + 'Hello World!' COLLATE SQL_Latin1_General_CP437_BIN c2, + 'Hello World!' COLLATE Albanian_BIN2 c3 + INTO [_ExploratoryTests].Table1 + + SELECT * INTO [_ExploratoryTests].Table2 FROM [_ExploratoryTests].Table1 + + EXEC tSQLt.AssertEqualsTableSchema @Expected = '[_ExploratoryTests].Table1', @Actual = '[_ExploratoryTests].Table2'; +END; +GO +CREATE PROCEDURE [_ExploratoryTests].[test MSSQL preserves COLLATION when using SELECT INTO across databases] +AS +BEGIN + SELECT + 'Hello World!' COLLATE SQL_Polish_CP1250_CI_AS c1, + 'Hello World!' COLLATE SQL_Latin1_General_CP437_BIN c2, + 'Hello World!' COLLATE Albanian_BIN2 c3 + INTO [_ExploratoryTests].Table1 + + SELECT * INTO tempdb.dbo.Table2 FROM [_ExploratoryTests].Table1 + + SELECT + C.name COLLATE DATABASE_DEFAULT name, + C.collation_name COLLATE DATABASE_DEFAULT collation_name + INTO #Expected + FROM sys.columns C + WHERE C.object_id = OBJECT_ID('[_ExploratoryTests].Table1'); + + SELECT + C.name COLLATE DATABASE_DEFAULT name, + C.collation_name COLLATE DATABASE_DEFAULT collation_name + INTO #Actual + FROM tempdb.sys.columns C + WHERE C.object_id = OBJECT_ID('tempdb.dbo.Table2'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +CREATE PROCEDURE [_ExploratoryTests].[test MSSQL creates INTO table before processing] +AS +BEGIN + SELECT 1 X INTO #Test; + DECLARE @NotExpected INT = OBJECT_ID('tempdb..#Test'); + DECLARE @Actual INT; + + EXEC sys.sp_executesql N' + SELECT * INTO #Test FROM tempdb.sys.objects C WHERE C.object_id = OBJECT_ID(''tempdb..#Test''); + EXEC sys.sp_executesql N''SELECT @Actual = object_id FROM #Test;'',N''@Actual INT OUTPUT'',@Actual OUT;', + N'@Actual INT OUTPUT', + @Actual OUT; + + EXEC tSQLt.AssertNotEquals @Expected = @NotExpected, @Actual = @Actual; +END; +GO +CREATE PROCEDURE [_ExploratoryTests].[test MSSQL creates INTO table after compiling] +AS +BEGIN + SELECT 1 X INTO #Test; + DECLARE @NotExpected INT = OBJECT_ID('tempdb..#Test'); + DECLARE @Actual INT; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'Invalid column name ''object_id''.'; + EXEC sys.sp_executesql N' + SELECT * INTO #Test FROM tempdb.sys.objects C WHERE C.object_id = OBJECT_ID(''tempdb..#Test''); + SELECT @Actual = object_id FROM #Test;', + N'@Actual INT OUTPUT', + @Actual OUT; + +END; +GO From 5824281e84363374437d96a193f3f555aded4838 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 13 Nov 2021 17:33:25 -0500 Subject: [PATCH 016/119] Started tSQLt.Private_CleanUp and related tests --- Source/Run_Methods.sql | 6 +- Tests/AnnotationNoTransactionTests.class.sql | 73 +++++++++++++++++++- Tests/tSQLt.Private_CleanUp.ssp.sql | 10 +++ 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 Tests/tSQLt.Private_CleanUp.ssp.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 1e13ee430..df1a6c9c7 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -281,6 +281,7 @@ BEGIN END; END CATCH; + --SELECT 4 X, @SkipTestFlag SkipTestFlag, -- @NoTransactionFlag NoTransactionFlag, -- @TransactionStartedFlag TransactionStartedFlag, @@ -289,8 +290,9 @@ BEGIN -- @TestName TestName, -- @Result Result, -- @Msg Msg; - - + DECLARE @CleanUpErrorMsg NVARCHAR(MAX); + EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; + SET @Msg = @Msg + ' ' + @CleanUpErrorMsg; If(@Result NOT IN ('Success','Skipped')) BEGIN diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 2d8643c2c..3fb6ae276 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -156,15 +156,84 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test calls tSQLt.Private_CleanUp] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test1] AS RETURN; + '); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT FullTestName INTO #Actual FROM tSQLt.Private_CleanUp_SpyProcedureLog; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + VALUES('[MyInnerTests].[test1]'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test message returned by tSQLt.Private_CleanUp is appended to tSQLt.TestResult.Msg] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test1] AS PRINT 1/0; + '); + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'SET @ErrorMsg = '''';'; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + DECLARE @Actual NVARCHAR(MAX) = (SELECT Msg FROM tSQLt.TestResult); + + EXEC tSQLt.AssertLike @ExpectedPattern = '% ', @Actual = @Actual; +END; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test message returned by tSQLt.Private_CleanUp is called before the test result message is printed] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test1] AS RAISERROR('''',16,10); + '); + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'SET @ErrorMsg = '''';'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_Print'; + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + DECLARE @Actual NVARCHAR(MAX) = (SELECT Message FROM tSQLt.Private_Print_SpyProcedureLog WHERE Message LIKE '%%'); + + EXEC tSQLt.AssertLike @ExpectedPattern = '% ', @Actual = @Actual; +END; +GO /*-- TODO +-- CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) +---- there will be three clean up methods, executed in the following order +---- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter +---- 2. User defined clean up for a test class as specified by [].CleanUp +---- 3. tSQLt.Private_CleanUp +---- test for execution in the correct place in Private_RunTest +---- test errors in any are captured and cause the test to Error +---- If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. +---- appropriate error messages are appended to the test msg +---- tSQLt.Private_CleanUp Tests +----- Tables --> SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('tSQLt'); +----- tSQLt.UndoTestDoubles + -- transaction opened during test -- transaction commited during test -- test skipped? -- inner-transaction-free test errors --- cleanup execution tables --- named cleanup (needs to execute even if there's an error during test execution) -- confirm pre and post transaction counts match -- [test produces meaningful error when pre and post transactions counts don't match] -- we still need to save the TranName as something somewhere. diff --git a/Tests/tSQLt.Private_CleanUp.ssp.sql b/Tests/tSQLt.Private_CleanUp.ssp.sql new file mode 100644 index 000000000..b10308b58 --- /dev/null +++ b/Tests/tSQLt.Private_CleanUp.ssp.sql @@ -0,0 +1,10 @@ +IF OBJECT_ID('tSQLt.Private_CleanUp') IS NOT NULL DROP PROCEDURE tSQLt.Private_CleanUp; +GO +CREATE PROCEDURE tSQLt.Private_CleanUp + @FullTestName NVARCHAR(MAX), + @ErrorMsg NVARCHAR(MAX) OUTPUT +AS +BEGIN + RETURN; +END; +GO \ No newline at end of file From 6042196b1d91e39e0b45ce235b1dc82102dde3bc Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 14 Nov 2021 14:29:07 -0500 Subject: [PATCH 017/119] Fixed Run_Methods issue which was breaking the build, added _ExploratoryTests_SA tests and started working on the code and tests for Private_CleanUp --- Source/BuildOrder.txt | 3 +- Source/Run_Methods.sql | 55 ++++++------------- Source/Source.ssmssqlproj | 6 ++ .../tSQLt.Private_CleanUp.ssp.sql | 6 +- Tests.SA/Tests.SA.ssmssqlproj | 6 ++ Tests.SA/_ExploratoryTests_SA.class.sql | 31 +++++++++++ Tests/AnnotationNoTransactionTests.class.sql | 52 +++++++++++++++++- Tests/Private_CleanUpTests.class.sql | 25 +++++++++ Tests/Tests.ssmssqlproj | 6 ++ Tests/_ExploratoryTests.class.sql | 29 ---------- 10 files changed, 148 insertions(+), 71 deletions(-) rename {Tests => Source}/tSQLt.Private_CleanUp.ssp.sql (88%) create mode 100644 Tests.SA/_ExploratoryTests_SA.class.sql create mode 100644 Tests/Private_CleanUpTests.class.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index b50eeaf84..064fe37b4 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -52,6 +52,7 @@ tSQLt.FriendlySQLServerVersion.sfn.sql tSQLt.Info.sfn.sql tSQLt.Private_Init.ssp.sql tSQLt.Private_HostPlatform.svw.sql +tSQLt.Private_CleanUp.ssp.sql Run_Methods.sql tSQLt.Private_SysTypes.svw.sql tSQLt.Private_GetFullTypeName.sfn.sql @@ -60,6 +61,7 @@ tSQLt.Private_ScriptIndex.sfn.sql tSQLt.Private_RemoveSchemaBinding.ssp.sql tSQLt.Private_RemoveSchemaBoundReferences.ssp.sql tSQLt.Private_GetForeignKeyDefinition.sfn.sql +tSQLt.Private_MarktSQLtTempObject.ssp.sql ApplyConstraint_Methods.sql tSQLt.Private_ValidateFakeTableParameters.ssp.sql tSQLt.Private_GetDataTypeOrComputedColumnDefinition.sfn.sql @@ -68,7 +70,6 @@ tSQLt.Private_GetDefaultConstraintDefinition.sfn.sql tSQLt.Private_GetUniqueConstraintDefinition.sfn.sql tSQLt.Private_CreateFakeTableStatement.sfn.sql tSQLt.Private_CreateFakeOfTable.ssp.sql -tSQLt.Private_MarktSQLtTempObject.ssp.sql tSQLt.FakeTable.ssp.sql tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql tSQLt.Private_CreateProcedureSpy.ssp.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index df1a6c9c7..51373d5e3 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -106,14 +106,6 @@ BEGIN END; SET @PreExecTrancount = @@TRANCOUNT; - --SELECT 1 X, @SkipTestFlag SkipTestFlag, - -- @NoTransactionFlag NoTransactionFlag, - -- @TransactionStartedFlag TransactionStartedFlag, - -- @PreExecTrancount PreExecTrancount, - -- @@TRANCOUNT Trancount, - -- @TestName TestName, - -- @Result Result, - -- @Msg Msg; DECLARE @TmpMsg NVARCHAR(MAX); DECLARE @TestEndTime DATETIME2; SET @TestEndTime = NULL; @@ -241,29 +233,13 @@ BEGIN SET @Result = 'Error'; SET @Msg = ERROR_MESSAGE(); END CATCH - --SELECT 2 X, @SkipTestFlag SkipTestFlag, - -- @NoTransactionFlag NoTransactionFlag, - -- @TransactionStartedFlag TransactionStartedFlag, - -- @PreExecTrancount PreExecTrancount, - -- @@TRANCOUNT Trancount, - -- @TestName TestName, - -- @Result Result, - -- @Msg Msg; + --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran BEGIN TRY IF(@TransactionStartedFlag = 1) BEGIN - --SELECT 3 X, @SkipTestFlag SkipTestFlag, - -- @NoTransactionFlag NoTransactionFlag, - -- @TransactionStartedFlag TransactionStartedFlag, - -- @PreExecTrancount PreExecTrancount, - -- @@TRANCOUNT Trancount, - -- @TestName TestName, - -- @Result Result, - -- @Msg Msg; - ROLLBACK TRAN @TranName; END; END TRY @@ -281,18 +257,12 @@ BEGIN END; END CATCH; - - --SELECT 4 X, @SkipTestFlag SkipTestFlag, - -- @NoTransactionFlag NoTransactionFlag, - -- @TransactionStartedFlag TransactionStartedFlag, - -- @PreExecTrancount PreExecTrancount, - -- @@TRANCOUNT Trancount, - -- @TestName TestName, - -- @Result Result, - -- @Msg Msg; - DECLARE @CleanUpErrorMsg NVARCHAR(MAX); - EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; - SET @Msg = @Msg + ' ' + @CleanUpErrorMsg; + IF (@NoTransactionFlag = 1) + BEGIN + DECLARE @CleanUpErrorMsg NVARCHAR(MAX); + EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; + SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); + END; If(@Result NOT IN ('Success','Skipped')) BEGIN @@ -906,3 +876,14 @@ BEGIN END GO --Build- + + + + --SELECT 3 X, @SkipTestFlag SkipTestFlag, + -- @NoTransactionFlag NoTransactionFlag, + -- @TransactionStartedFlag TransactionStartedFlag, + -- @PreExecTrancount PreExecTrancount, + -- @@TRANCOUNT Trancount, + -- @TestName TestName, + -- @Result Result, + -- @Msg Msg; diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 5aac71856..0b51cb080 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -216,6 +216,12 @@ tSQLt.Private_CleanTestResult.ssp.sql + + + + + tSQLt.Private_CleanUp.ssp.sql + diff --git a/Tests/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql similarity index 88% rename from Tests/tSQLt.Private_CleanUp.ssp.sql rename to Source/tSQLt.Private_CleanUp.ssp.sql index b10308b58..73d2d6333 100644 --- a/Tests/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -1,5 +1,7 @@ IF OBJECT_ID('tSQLt.Private_CleanUp') IS NOT NULL DROP PROCEDURE tSQLt.Private_CleanUp; GO +---Build+ +GO CREATE PROCEDURE tSQLt.Private_CleanUp @FullTestName NVARCHAR(MAX), @ErrorMsg NVARCHAR(MAX) OUTPUT @@ -7,4 +9,6 @@ AS BEGIN RETURN; END; -GO \ No newline at end of file +GO +---Build- +GO diff --git a/Tests.SA/Tests.SA.ssmssqlproj b/Tests.SA/Tests.SA.ssmssqlproj index 2dfb28eb7..ac9b70c20 100644 --- a/Tests.SA/Tests.SA.ssmssqlproj +++ b/Tests.SA/Tests.SA.ssmssqlproj @@ -6,6 +6,12 @@ + + + + + _ExploratoryTests_SA.class.sql + diff --git a/Tests.SA/_ExploratoryTests_SA.class.sql b/Tests.SA/_ExploratoryTests_SA.class.sql new file mode 100644 index 000000000..cd352483e --- /dev/null +++ b/Tests.SA/_ExploratoryTests_SA.class.sql @@ -0,0 +1,31 @@ +EXEC tSQLt.NewTestClass '_ExploratoryTests_SA'; +GO +CREATE PROCEDURE [_ExploratoryTests_SA].[test MSSQL preserves COLLATION when using SELECT INTO across databases] +AS +BEGIN + SELECT + 'Hello World!' COLLATE SQL_Polish_CP1250_CI_AS c1, + 'Hello World!' COLLATE SQL_Latin1_General_CP437_BIN c2, + 'Hello World!' COLLATE Albanian_BIN2 c3 + INTO [_ExploratoryTests_SA].Table1 + + SELECT * INTO tempdb.dbo.Table2 FROM [_ExploratoryTests_SA].Table1 + + SELECT + C.name COLLATE DATABASE_DEFAULT name, + C.collation_name COLLATE DATABASE_DEFAULT collation_name + INTO #Expected + FROM sys.columns C + WHERE C.object_id = OBJECT_ID('[_ExploratoryTests_SA].Table1'); + + SELECT + C.name COLLATE DATABASE_DEFAULT name, + C.collation_name COLLATE DATABASE_DEFAULT collation_name + INTO #Actual + FROM tempdb.sys.columns C + WHERE C.object_id = OBJECT_ID('tempdb.dbo.Table2'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 3fb6ae276..be4433909 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -164,8 +164,9 @@ BEGIN --[@'+'tSQLt:NoTransaction]() CREATE PROCEDURE MyInnerTests.[test1] AS RETURN; '); - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; + + EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.Run 'MyInnerTests.[test1]'; SELECT FullTestName INTO #Actual FROM tSQLt.Private_CleanUp_SpyProcedureLog; @@ -177,6 +178,51 @@ CREATE PROCEDURE MyInnerTests.[test1] AS RETURN; EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and succeeding] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE MyInnerTests.[test1] AS RETURN;'); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT * INTO #Actual FROM tSQLt.Private_CleanUp_SpyProcedureLog; + + EXEC tSQLt.AssertEmptyTable '#Actual'; +END; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and failing] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE MyInnerTests.[test1] AS EXEC tSQLt.Fail;'); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT * INTO #Actual FROM tSQLt.Private_CleanUp_SpyProcedureLog; + + EXEC tSQLt.AssertEmptyTable '#Actual'; +END; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and erroring] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE MyInnerTests.[test1] AS RAISERROR(''X'',16,10);'); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT * INTO #Actual FROM tSQLt.Private_CleanUp_SpyProcedureLog; + + EXEC tSQLt.AssertEmptyTable '#Actual'; +END; +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test message returned by tSQLt.Private_CleanUp is appended to tSQLt.TestResult.Msg] AS BEGIN @@ -222,8 +268,8 @@ GO ---- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter ---- 2. User defined clean up for a test class as specified by [].CleanUp ---- 3. tSQLt.Private_CleanUp ----- test for execution in the correct place in Private_RunTest ----- test errors in any are captured and cause the test to Error +---- test for execution in the correct place in Private_RunTest, after the outer-most test execution try catch +---- Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error ---- If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. ---- appropriate error messages are appended to the test msg ---- tSQLt.Private_CleanUp Tests diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql new file mode 100644 index 000000000..0bb7b88f9 --- /dev/null +++ b/Tests/Private_CleanUpTests.class.sql @@ -0,0 +1,25 @@ +EXEC tSQLt.NewTestClass 'Private_CleanUpTests'; +GO +CREATE PROCEDURE Private_CleanUpTests.[test ] +AS +BEGIN + +END; +GO + + +/*-- TODO + +-- CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) +---- there will be three clean up methods, executed in the following order +---- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter +---- 2. User defined clean up for a test class as specified by [].CleanUp +---- 3. tSQLt.Private_CleanUp +---- Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error +---- If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. +---- appropriate error messages are appended to the test msg +---- tSQLt.Private_CleanUp Tests +----- Tables --> SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('tSQLt'); +----- tSQLt.UndoTestDoubles + +--*/ \ No newline at end of file diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 7389346c7..be9041632 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -186,6 +186,12 @@ NewTestClassTests.class.sql + + + + + Private_CleanUpTests.class.sql + diff --git a/Tests/_ExploratoryTests.class.sql b/Tests/_ExploratoryTests.class.sql index 514042c23..81c58ac48 100644 --- a/Tests/_ExploratoryTests.class.sql +++ b/Tests/_ExploratoryTests.class.sql @@ -36,35 +36,6 @@ BEGIN EXEC tSQLt.AssertEqualsTableSchema @Expected = '[_ExploratoryTests].Table1', @Actual = '[_ExploratoryTests].Table2'; END; GO -CREATE PROCEDURE [_ExploratoryTests].[test MSSQL preserves COLLATION when using SELECT INTO across databases] -AS -BEGIN - SELECT - 'Hello World!' COLLATE SQL_Polish_CP1250_CI_AS c1, - 'Hello World!' COLLATE SQL_Latin1_General_CP437_BIN c2, - 'Hello World!' COLLATE Albanian_BIN2 c3 - INTO [_ExploratoryTests].Table1 - - SELECT * INTO tempdb.dbo.Table2 FROM [_ExploratoryTests].Table1 - - SELECT - C.name COLLATE DATABASE_DEFAULT name, - C.collation_name COLLATE DATABASE_DEFAULT collation_name - INTO #Expected - FROM sys.columns C - WHERE C.object_id = OBJECT_ID('[_ExploratoryTests].Table1'); - - SELECT - C.name COLLATE DATABASE_DEFAULT name, - C.collation_name COLLATE DATABASE_DEFAULT collation_name - INTO #Actual - FROM tempdb.sys.columns C - WHERE C.object_id = OBJECT_ID('tempdb.dbo.Table2'); - - EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; - -END; -GO CREATE PROCEDURE [_ExploratoryTests].[test MSSQL creates INTO table before processing] AS BEGIN From d26c918f317a7094374e40a06dc9dfa38da10fa9 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 14 Nov 2021 17:57:00 -0500 Subject: [PATCH 018/119] Added Private_ResettSQLtTablesTests.class.sql, started writing tests for Private_CleanUp --- Source/BuildOrder.txt | 1 + Source/tSQLt.Private_CleanUp.ssp.sql | 3 ++- Source/tSQLt.Private_ResettSQLtTables.ssp.sql | 10 ++++++++ Tests/Private_CleanUpTests.class.sql | 25 ++++++++++++++++++- Tests/Private_ResettSQLtTablesTests.class.sql | 15 +++++++++++ 5 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 Source/tSQLt.Private_ResettSQLtTables.ssp.sql create mode 100644 Tests/Private_ResettSQLtTablesTests.class.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 064fe37b4..74add8749 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -52,6 +52,7 @@ tSQLt.FriendlySQLServerVersion.sfn.sql tSQLt.Info.sfn.sql tSQLt.Private_Init.ssp.sql tSQLt.Private_HostPlatform.svw.sql +tSQLt.Private_ResettSQLtTables.ssp.sql tSQLt.Private_CleanUp.ssp.sql Run_Methods.sql tSQLt.Private_SysTypes.svw.sql diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index 73d2d6333..c124aab61 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -7,7 +7,8 @@ CREATE PROCEDURE tSQLt.Private_CleanUp @ErrorMsg NVARCHAR(MAX) OUTPUT AS BEGIN - RETURN; + EXEC tSQLt.UndoTestDoubles @Force = 0; + EXEC tSQLt.Private_ResettSQLtTables; END; GO ---Build- diff --git a/Source/tSQLt.Private_ResettSQLtTables.ssp.sql b/Source/tSQLt.Private_ResettSQLtTables.ssp.sql new file mode 100644 index 000000000..d804cb1a5 --- /dev/null +++ b/Source/tSQLt.Private_ResettSQLtTables.ssp.sql @@ -0,0 +1,10 @@ +IF OBJECT_ID('tSQLt.Private_ResettSQLtTables') IS NOT NULL DROP PROCEDURE tSQLt.Private_ResettSQLtTables; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_ResettSQLtTables +AS +BEGIN + RETURN; +END; +GO diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql index 0bb7b88f9..0aa27ceb6 100644 --- a/Tests/Private_CleanUpTests.class.sql +++ b/Tests/Private_CleanUpTests.class.sql @@ -1,9 +1,32 @@ EXEC tSQLt.NewTestClass 'Private_CleanUpTests'; GO -CREATE PROCEDURE Private_CleanUpTests.[test ] +CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.UndoTestDoubles] AS BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @ErrorMsg = NULL; + + SELECT _id_, Force INTO #Actual FROM tSQLt.UndoTestDoubles_SpyProcedureLog; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,0); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.Private_ResettSQLtTables] +AS +BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTables'; + + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @ErrorMsg = NULL; + + SELECT _id_ INTO #Actual FROM tSQLt.Private_ResettSQLtTables_SpyProcedureLog; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO diff --git a/Tests/Private_ResettSQLtTablesTests.class.sql b/Tests/Private_ResettSQLtTablesTests.class.sql new file mode 100644 index 000000000..6503eeeed --- /dev/null +++ b/Tests/Private_ResettSQLtTablesTests.class.sql @@ -0,0 +1,15 @@ +EXEC tSQLt.NewTestClass 'Private_ResettSQLtTablesTests'; +GO +CREATE PROCEDURE Private_ResettSQLtTablesTests.[test TODO] +AS +BEGIN + EXEC tSQLt.Fail; +END; +GO + +/*-- +TODO + +- Tables --> SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('tSQLt'); + +--*/ \ No newline at end of file From 66f8674967a98932396abc65c6de17283d40e24e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 14 Nov 2021 17:57:00 -0500 Subject: [PATCH 019/119] Added Private_ResettSQLtTablesTests.class.sql, started writing tests for Private_CleanUp, we also need the ssmssqlproj files so that the files show up in SSMS (deprecated). --- Source/BuildOrder.txt | 1 + Source/Source.ssmssqlproj | 6 +++++ Source/tSQLt.Private_CleanUp.ssp.sql | 3 ++- Source/tSQLt.Private_ResettSQLtTables.ssp.sql | 10 ++++++++ Tests/Private_CleanUpTests.class.sql | 25 ++++++++++++++++++- Tests/Private_ResettSQLtTablesTests.class.sql | 15 +++++++++++ Tests/Tests.ssmssqlproj | 6 +++++ 7 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 Source/tSQLt.Private_ResettSQLtTables.ssp.sql create mode 100644 Tests/Private_ResettSQLtTablesTests.class.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 064fe37b4..74add8749 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -52,6 +52,7 @@ tSQLt.FriendlySQLServerVersion.sfn.sql tSQLt.Info.sfn.sql tSQLt.Private_Init.ssp.sql tSQLt.Private_HostPlatform.svw.sql +tSQLt.Private_ResettSQLtTables.ssp.sql tSQLt.Private_CleanUp.ssp.sql Run_Methods.sql tSQLt.Private_SysTypes.svw.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 0b51cb080..a80af00d7 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -480,6 +480,12 @@ tSQLt.Private_ResetNewTestClassList.ssp.sql + + + + + tSQLt.Private_ResettSQLtTables.ssp.sql + diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index 73d2d6333..c124aab61 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -7,7 +7,8 @@ CREATE PROCEDURE tSQLt.Private_CleanUp @ErrorMsg NVARCHAR(MAX) OUTPUT AS BEGIN - RETURN; + EXEC tSQLt.UndoTestDoubles @Force = 0; + EXEC tSQLt.Private_ResettSQLtTables; END; GO ---Build- diff --git a/Source/tSQLt.Private_ResettSQLtTables.ssp.sql b/Source/tSQLt.Private_ResettSQLtTables.ssp.sql new file mode 100644 index 000000000..d804cb1a5 --- /dev/null +++ b/Source/tSQLt.Private_ResettSQLtTables.ssp.sql @@ -0,0 +1,10 @@ +IF OBJECT_ID('tSQLt.Private_ResettSQLtTables') IS NOT NULL DROP PROCEDURE tSQLt.Private_ResettSQLtTables; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_ResettSQLtTables +AS +BEGIN + RETURN; +END; +GO diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql index 0bb7b88f9..0aa27ceb6 100644 --- a/Tests/Private_CleanUpTests.class.sql +++ b/Tests/Private_CleanUpTests.class.sql @@ -1,9 +1,32 @@ EXEC tSQLt.NewTestClass 'Private_CleanUpTests'; GO -CREATE PROCEDURE Private_CleanUpTests.[test ] +CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.UndoTestDoubles] AS BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @ErrorMsg = NULL; + + SELECT _id_, Force INTO #Actual FROM tSQLt.UndoTestDoubles_SpyProcedureLog; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,0); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.Private_ResettSQLtTables] +AS +BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTables'; + + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @ErrorMsg = NULL; + + SELECT _id_ INTO #Actual FROM tSQLt.Private_ResettSQLtTables_SpyProcedureLog; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO diff --git a/Tests/Private_ResettSQLtTablesTests.class.sql b/Tests/Private_ResettSQLtTablesTests.class.sql new file mode 100644 index 000000000..6503eeeed --- /dev/null +++ b/Tests/Private_ResettSQLtTablesTests.class.sql @@ -0,0 +1,15 @@ +EXEC tSQLt.NewTestClass 'Private_ResettSQLtTablesTests'; +GO +CREATE PROCEDURE Private_ResettSQLtTablesTests.[test TODO] +AS +BEGIN + EXEC tSQLt.Fail; +END; +GO + +/*-- +TODO + +- Tables --> SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('tSQLt'); + +--*/ \ No newline at end of file diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index be9041632..4079ca26c 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -264,6 +264,12 @@ Private_ResetNewTestClassListTests.class.sql + + + + + Private_ResettSQLtTablesTests.class.sql + From c18e3d539f088cbb437e99973bfebad5b6568226 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 14 Nov 2021 21:55:31 -0500 Subject: [PATCH 020/119] Continue development on handling tSQLt tables during NoTransaction tests. --- Source/BuildOrder.txt | 2 + Source/Source.ssmssqlproj | 12 +++ Source/tSQLt.Private_ResettSQLtTable.ssp.sql | 12 +++ ...QLt.Private_ResettSQLtTableAction.view.sql | 16 ++++ Source/tSQLt.Private_ResettSQLtTables.ssp.sql | 10 ++- ...ivate_ResettSQLtTableActionTests.class.sql | 31 +++++++ Tests/Private_ResettSQLtTablesTests.class.sql | 83 ++++++++++++++++++- Tests/Tests.ssmssqlproj | 6 ++ 8 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 Source/tSQLt.Private_ResettSQLtTable.ssp.sql create mode 100644 Source/tSQLt.Private_ResettSQLtTableAction.view.sql create mode 100644 Tests/Private_ResettSQLtTableActionTests.class.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 74add8749..1fb3c406d 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -52,6 +52,8 @@ tSQLt.FriendlySQLServerVersion.sfn.sql tSQLt.Info.sfn.sql tSQLt.Private_Init.ssp.sql tSQLt.Private_HostPlatform.svw.sql +tSQLt.Private_ResettSQLtTableAction.view.sql +tSQLt.Private_ResettSQLtTable.ssp.sql tSQLt.Private_ResettSQLtTables.ssp.sql tSQLt.Private_CleanUp.ssp.sql Run_Methods.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index a80af00d7..7f5c7ec59 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -480,6 +480,18 @@ tSQLt.Private_ResetNewTestClassList.ssp.sql + + + + + tSQLt.Private_ResettSQLtTable.ssp.sql + + + + + + tSQLt.Private_ResettSQLtTableAction.view.sql + diff --git a/Source/tSQLt.Private_ResettSQLtTable.ssp.sql b/Source/tSQLt.Private_ResettSQLtTable.ssp.sql new file mode 100644 index 000000000..7f14ca97c --- /dev/null +++ b/Source/tSQLt.Private_ResettSQLtTable.ssp.sql @@ -0,0 +1,12 @@ +IF OBJECT_ID('tSQLt.Private_ResettSQLtTable') IS NOT NULL DROP PROCEDURE tSQLt.Private_ResettSQLtTable; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_ResettSQLtTable +@FullTableName NVARCHAR(MAX), +@Action NVARCHAR(MAX) +AS +BEGIN + RETURN; +END; +GO diff --git a/Source/tSQLt.Private_ResettSQLtTableAction.view.sql b/Source/tSQLt.Private_ResettSQLtTableAction.view.sql new file mode 100644 index 000000000..9ae6aa2e3 --- /dev/null +++ b/Source/tSQLt.Private_ResettSQLtTableAction.view.sql @@ -0,0 +1,16 @@ +IF OBJECT_ID('tSQLt.Private_ResettSQLtTableAction') IS NOT NULL DROP VIEW tSQLt.Private_ResettSQLtTableAction; +GO +---Build+ +GO +CREATE VIEW tSQLt.Private_ResettSQLtTableAction +AS +SELECT * + FROM( + VALUES('[tSQLt].[Private_NewTestClassList]','Restore'), + ('[tSQLt].[Run_LastExecution]','Restore'), + ('[tSQLt].[Private_Configurations]','Restore'), + ('[tSQLt].[CaptureOutputLog]','Ignore'), + ('[tSQLt].[Private_RenamedObjectLog]','Ignore'), + ('[tSQLt].[TestResult]','Ignore') + )X(Name, Action); +GO diff --git a/Source/tSQLt.Private_ResettSQLtTables.ssp.sql b/Source/tSQLt.Private_ResettSQLtTables.ssp.sql index d804cb1a5..8f63da72b 100644 --- a/Source/tSQLt.Private_ResettSQLtTables.ssp.sql +++ b/Source/tSQLt.Private_ResettSQLtTables.ssp.sql @@ -5,6 +5,12 @@ GO CREATE PROCEDURE tSQLt.Private_ResettSQLtTables AS BEGIN - RETURN; + DECLARE @cmd NVARCHAR(MAX) = ( + SELECT 'EXEC tSQLt.Private_ResettSQLtTable @FullTableName = '''+X.Name+''', @Action = '''+X.Action+''';' + FROM tSQLt.Private_ResettSQLtTableAction X + WHERE X.Action = 'Restore' + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'); + EXEC(@cmd); END; -GO +GO \ No newline at end of file diff --git a/Tests/Private_ResettSQLtTableActionTests.class.sql b/Tests/Private_ResettSQLtTableActionTests.class.sql new file mode 100644 index 000000000..d14713198 --- /dev/null +++ b/Tests/Private_ResettSQLtTableActionTests.class.sql @@ -0,0 +1,31 @@ +EXEC tSQLt.NewTestClass 'Private_ResettSQLtTableActionTests'; +GO +CREATE PROCEDURE Private_ResettSQLtTableActionTests.[test contains all tSQLt tables] +AS +BEGIN + SELECT Name INTO #Actual FROM tSQLt.Private_ResettSQLtTableAction; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + SELECT QUOTENAME(SCHEMA_NAME(t.schema_id))+'.'+QUOTENAME(t.name) + FROM sys.tables t WHERE schema_id = SCHEMA_ID('tSQLt') AND name NOT LIKE ('%SpyProcedureLog'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +CREATE PROCEDURE Private_ResettSQLtTableActionTests.[test has the correct actions for all tSQLt tables] +AS +BEGIN + SELECT Name, Action INTO #Actual FROM tSQLt.Private_ResettSQLtTableAction; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + SELECT '[tSQLt].[Private_NewTestClassList]','Restore' UNION ALL + SELECT '[tSQLt].[Run_LastExecution]', 'Restore' UNION ALL + SELECT '[tSQLt].[Private_Configurations]', 'Restore' UNION ALL + SELECT '[tSQLt].[CaptureOutputLog]', 'Ignore' UNION ALL + SELECT '[tSQLt].[Private_RenamedObjectLog]','Ignore' UNION ALL + SELECT '[tSQLt].[TestResult]', 'Ignore'; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO \ No newline at end of file diff --git a/Tests/Private_ResettSQLtTablesTests.class.sql b/Tests/Private_ResettSQLtTablesTests.class.sql index 6503eeeed..c37051097 100644 --- a/Tests/Private_ResettSQLtTablesTests.class.sql +++ b/Tests/Private_ResettSQLtTablesTests.class.sql @@ -1,15 +1,94 @@ EXEC tSQLt.NewTestClass 'Private_ResettSQLtTablesTests'; GO -CREATE PROCEDURE Private_ResettSQLtTablesTests.[test TODO] +CREATE PROCEDURE Private_ResettSQLtTablesTests.[test does not call tSQLt.Private_ResettSQLtTable if tSQLt.Private_ResettSQLtTableAction is empty] AS BEGIN - EXEC tSQLt.Fail; + --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_ResettSQLtTableAction'; + + EXEC tSQLt.Private_ResettSQLtTables; + + EXEC tSQLt.AssertEmptyTable @TableName = 'tSQLt.Private_ResettSQLtTable_SpyProcedureLog'; + +END; +GO +CREATE PROCEDURE Private_ResettSQLtTablesTests.[test calls tSQLt.Private_ResettSQLtTable if tSQLt.Private_ResettSQLtTableAction contains a table] +AS +BEGIN + --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_ResettSQLtTableAction'; + EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl1'',''Restore'');') + + EXEC tSQLt.Private_ResettSQLtTables; + + SELECT FullTableName INTO #Actual FROM tSQLt.Private_ResettSQLtTable_SpyProcedureLog; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES('tbl1'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +CREATE PROCEDURE Private_ResettSQLtTablesTests.[test calls tSQLt.Private_ResettSQLtTable for each table in tSQLt.Private_ResettSQLtTableAction] +AS +BEGIN + --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_ResettSQLtTableAction'; + EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl1'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl2'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl3'',''Restore'');') + + EXEC tSQLt.Private_ResettSQLtTables; + + SELECT FullTableName INTO #Actual FROM tSQLt.Private_ResettSQLtTable_SpyProcedureLog; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES('tbl1'),('tbl2'),('tbl3'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +CREATE PROCEDURE Private_ResettSQLtTablesTests.[test calls tSQLt.Private_ResettSQLtTable only for 'Restore' tables] +AS +BEGIN + --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_ResettSQLtTableAction'; + EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl1'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl2'',''Ignore'');') + EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl3'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl4'',''Other'');') + + EXEC tSQLt.Private_ResettSQLtTables @save=0; + + SELECT FullTableName INTO #Actual FROM tSQLt.Private_ResettSQLtTable_SpyProcedureLog; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES('tbl1'),('tbl3'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO + + + /*-- TODO - Tables --> SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('tSQLt'); +Action - Table Name +Restore - Private_NewTestClassList +Restore - Run_LastExecution +Restore - Private_Configurations +Ignore - CaptureOutputLog +Ignore - Private_RenamedObjectLog +Ignore - TestResult --*/ \ No newline at end of file diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 4079ca26c..23b3779eb 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -264,6 +264,12 @@ Private_ResetNewTestClassListTests.class.sql + + + + + Private_ResettSQLtTableActionTests.class.sql + From 0971675301aa405eaee8784ed1c656114da36440 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 14 Nov 2021 22:08:49 -0500 Subject: [PATCH 021/119] Wild renaming from Private_ResettSQLtTableAction to Private_NoTransactionTableAction and Private_ResettSQLtTable(s) to Private_NoTransactionHandleTable(s). --- Source/BuildOrder.txt | 6 +- Source/Source.ssmssqlproj | 12 ++-- Source/tSQLt.Private_CleanUp.ssp.sql | 2 +- ...t.Private_NoTransactionHandleTable.ssp.sql | 12 ++++ ....Private_NoTransactionHandleTables.ssp.sql | 16 +++++ ...Private_NoTransactionTableAction.view.sql} | 4 +- Source/tSQLt.Private_ResettSQLtTable.ssp.sql | 12 ---- Source/tSQLt.Private_ResettSQLtTables.ssp.sql | 16 ----- Tests/Private_CleanUpTests.class.sql | 6 +- ...ivate_ResettSQLtTableActionTests.class.sql | 10 ++-- Tests/Private_ResettSQLtTablesTests.class.sql | 58 +++++++++---------- Tests/Tests.ssmssqlproj | 8 +-- 12 files changed, 81 insertions(+), 81 deletions(-) create mode 100644 Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql create mode 100644 Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql rename Source/{tSQLt.Private_ResettSQLtTableAction.view.sql => tSQLt.Private_NoTransactionTableAction.view.sql} (69%) delete mode 100644 Source/tSQLt.Private_ResettSQLtTable.ssp.sql delete mode 100644 Source/tSQLt.Private_ResettSQLtTables.ssp.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 1fb3c406d..0e6070e25 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -52,9 +52,9 @@ tSQLt.FriendlySQLServerVersion.sfn.sql tSQLt.Info.sfn.sql tSQLt.Private_Init.ssp.sql tSQLt.Private_HostPlatform.svw.sql -tSQLt.Private_ResettSQLtTableAction.view.sql -tSQLt.Private_ResettSQLtTable.ssp.sql -tSQLt.Private_ResettSQLtTables.ssp.sql +tSQLt.Private_NoTransactionTableAction.view.sql +tSQLt.Private_NoTransactionHandleTable.ssp.sql +tSQLt.Private_NoTransactionHandleTables.ssp.sql tSQLt.Private_CleanUp.ssp.sql Run_Methods.sql tSQLt.Private_SysTypes.svw.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 7f5c7ec59..7a65591e8 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -480,23 +480,23 @@ tSQLt.Private_ResetNewTestClassList.ssp.sql - + - tSQLt.Private_ResettSQLtTable.ssp.sql + tSQLt.Private_NoTransactionHandleTable.ssp.sql - + - tSQLt.Private_ResettSQLtTableAction.view.sql + tSQLt.Private_NoTransactionTableAction.view.sql - + - tSQLt.Private_ResettSQLtTables.ssp.sql + tSQLt.Private_NoTransactionHandleTables.ssp.sql diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index c124aab61..bbc1ce95a 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -8,7 +8,7 @@ CREATE PROCEDURE tSQLt.Private_CleanUp AS BEGIN EXEC tSQLt.UndoTestDoubles @Force = 0; - EXEC tSQLt.Private_ResettSQLtTables; + EXEC tSQLt.Private_NoTransactionHandleTables; END; GO ---Build- diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql new file mode 100644 index 000000000..7060a23cd --- /dev/null +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -0,0 +1,12 @@ +IF OBJECT_ID('tSQLt.Private_NoTransactionHandleTable') IS NOT NULL DROP PROCEDURE tSQLt.Private_NoTransactionHandleTable; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTable +@FullTableName NVARCHAR(MAX), +@Action NVARCHAR(MAX) +AS +BEGIN + RETURN; +END; +GO diff --git a/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql new file mode 100644 index 000000000..feda033e1 --- /dev/null +++ b/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql @@ -0,0 +1,16 @@ +IF OBJECT_ID('tSQLt.Private_NoTransactionHandleTables') IS NOT NULL DROP PROCEDURE tSQLt.Private_NoTransactionHandleTables; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTables +AS +BEGIN + DECLARE @cmd NVARCHAR(MAX) = ( + SELECT 'EXEC tSQLt.Private_NoTransactionHandleTable @FullTableName = '''+X.Name+''', @Action = '''+X.Action+''';' + FROM tSQLt.Private_NoTransactionTableAction X + WHERE X.Action = 'Restore' + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'); + EXEC(@cmd); +END; +GO \ No newline at end of file diff --git a/Source/tSQLt.Private_ResettSQLtTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql similarity index 69% rename from Source/tSQLt.Private_ResettSQLtTableAction.view.sql rename to Source/tSQLt.Private_NoTransactionTableAction.view.sql index 9ae6aa2e3..6432022b3 100644 --- a/Source/tSQLt.Private_ResettSQLtTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -1,8 +1,8 @@ -IF OBJECT_ID('tSQLt.Private_ResettSQLtTableAction') IS NOT NULL DROP VIEW tSQLt.Private_ResettSQLtTableAction; +IF OBJECT_ID('tSQLt.Private_NoTransactionTableAction') IS NOT NULL DROP VIEW tSQLt.Private_NoTransactionTableAction; GO ---Build+ GO -CREATE VIEW tSQLt.Private_ResettSQLtTableAction +CREATE VIEW tSQLt.Private_NoTransactionTableAction AS SELECT * FROM( diff --git a/Source/tSQLt.Private_ResettSQLtTable.ssp.sql b/Source/tSQLt.Private_ResettSQLtTable.ssp.sql deleted file mode 100644 index 7f14ca97c..000000000 --- a/Source/tSQLt.Private_ResettSQLtTable.ssp.sql +++ /dev/null @@ -1,12 +0,0 @@ -IF OBJECT_ID('tSQLt.Private_ResettSQLtTable') IS NOT NULL DROP PROCEDURE tSQLt.Private_ResettSQLtTable; -GO ----Build+ -GO -CREATE PROCEDURE tSQLt.Private_ResettSQLtTable -@FullTableName NVARCHAR(MAX), -@Action NVARCHAR(MAX) -AS -BEGIN - RETURN; -END; -GO diff --git a/Source/tSQLt.Private_ResettSQLtTables.ssp.sql b/Source/tSQLt.Private_ResettSQLtTables.ssp.sql deleted file mode 100644 index 8f63da72b..000000000 --- a/Source/tSQLt.Private_ResettSQLtTables.ssp.sql +++ /dev/null @@ -1,16 +0,0 @@ -IF OBJECT_ID('tSQLt.Private_ResettSQLtTables') IS NOT NULL DROP PROCEDURE tSQLt.Private_ResettSQLtTables; -GO ----Build+ -GO -CREATE PROCEDURE tSQLt.Private_ResettSQLtTables -AS -BEGIN - DECLARE @cmd NVARCHAR(MAX) = ( - SELECT 'EXEC tSQLt.Private_ResettSQLtTable @FullTableName = '''+X.Name+''', @Action = '''+X.Action+''';' - FROM tSQLt.Private_ResettSQLtTableAction X - WHERE X.Action = 'Restore' - FOR XML PATH(''),TYPE - ).value('.','NVARCHAR(MAX)'); - EXEC(@cmd); -END; -GO \ No newline at end of file diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql index 0aa27ceb6..aa6a94816 100644 --- a/Tests/Private_CleanUpTests.class.sql +++ b/Tests/Private_CleanUpTests.class.sql @@ -14,15 +14,15 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.Private_ResettSQLtTables] +CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.Private_NoTransactionHandleTables] AS BEGIN EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTables'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @ErrorMsg = NULL; - SELECT _id_ INTO #Actual FROM tSQLt.Private_ResettSQLtTables_SpyProcedureLog; + SELECT _id_ INTO #Actual FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected VALUES(1); diff --git a/Tests/Private_ResettSQLtTableActionTests.class.sql b/Tests/Private_ResettSQLtTableActionTests.class.sql index d14713198..8acec169b 100644 --- a/Tests/Private_ResettSQLtTableActionTests.class.sql +++ b/Tests/Private_ResettSQLtTableActionTests.class.sql @@ -1,9 +1,9 @@ -EXEC tSQLt.NewTestClass 'Private_ResettSQLtTableActionTests'; +EXEC tSQLt.NewTestClass 'Private_NoTransactionTableActionTests'; GO -CREATE PROCEDURE Private_ResettSQLtTableActionTests.[test contains all tSQLt tables] +CREATE PROCEDURE Private_NoTransactionTableActionTests.[test contains all tSQLt tables] AS BEGIN - SELECT Name INTO #Actual FROM tSQLt.Private_ResettSQLtTableAction; + SELECT Name INTO #Actual FROM tSQLt.Private_NoTransactionTableAction; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected @@ -13,10 +13,10 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -CREATE PROCEDURE Private_ResettSQLtTableActionTests.[test has the correct actions for all tSQLt tables] +CREATE PROCEDURE Private_NoTransactionTableActionTests.[test has the correct actions for all tSQLt tables] AS BEGIN - SELECT Name, Action INTO #Actual FROM tSQLt.Private_ResettSQLtTableAction; + SELECT Name, Action INTO #Actual FROM tSQLt.Private_NoTransactionTableAction; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected diff --git a/Tests/Private_ResettSQLtTablesTests.class.sql b/Tests/Private_ResettSQLtTablesTests.class.sql index c37051097..070f6e083 100644 --- a/Tests/Private_ResettSQLtTablesTests.class.sql +++ b/Tests/Private_ResettSQLtTablesTests.class.sql @@ -1,29 +1,29 @@ -EXEC tSQLt.NewTestClass 'Private_ResettSQLtTablesTests'; +EXEC tSQLt.NewTestClass 'Private_NoTransactionHandleTablesTests'; GO -CREATE PROCEDURE Private_ResettSQLtTablesTests.[test does not call tSQLt.Private_ResettSQLtTable if tSQLt.Private_ResettSQLtTableAction is empty] +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test does not call tSQLt.Private_NoTransactionHandleTable if tSQLt.Private_NoTransactionTableAction is empty] AS BEGIN --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTable'; - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_ResettSQLtTableAction'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; - EXEC tSQLt.Private_ResettSQLtTables; + EXEC tSQLt.Private_NoTransactionHandleTables; - EXEC tSQLt.AssertEmptyTable @TableName = 'tSQLt.Private_ResettSQLtTable_SpyProcedureLog'; + EXEC tSQLt.AssertEmptyTable @TableName = 'tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog'; END; GO -CREATE PROCEDURE Private_ResettSQLtTablesTests.[test calls tSQLt.Private_ResettSQLtTable if tSQLt.Private_ResettSQLtTableAction contains a table] +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test calls tSQLt.Private_NoTransactionHandleTable if tSQLt.Private_NoTransactionTableAction contains a table] AS BEGIN --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTable'; - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_ResettSQLtTableAction'; - EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl1'',''Restore'');') + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl1'',''Restore'');') - EXEC tSQLt.Private_ResettSQLtTables; + EXEC tSQLt.Private_NoTransactionHandleTables; - SELECT FullTableName INTO #Actual FROM tSQLt.Private_ResettSQLtTable_SpyProcedureLog; + SELECT FullTableName INTO #Actual FROM tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected @@ -32,19 +32,19 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -CREATE PROCEDURE Private_ResettSQLtTablesTests.[test calls tSQLt.Private_ResettSQLtTable for each table in tSQLt.Private_ResettSQLtTableAction] +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test calls tSQLt.Private_NoTransactionHandleTable for each table in tSQLt.Private_NoTransactionTableAction] AS BEGIN --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTable'; - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_ResettSQLtTableAction'; - EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl1'',''Restore'');') - EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl2'',''Restore'');') - EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl3'',''Restore'');') + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl1'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl2'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl3'',''Restore'');') - EXEC tSQLt.Private_ResettSQLtTables; + EXEC tSQLt.Private_NoTransactionHandleTables; - SELECT FullTableName INTO #Actual FROM tSQLt.Private_ResettSQLtTable_SpyProcedureLog; + SELECT FullTableName INTO #Actual FROM tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected @@ -53,20 +53,20 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -CREATE PROCEDURE Private_ResettSQLtTablesTests.[test calls tSQLt.Private_ResettSQLtTable only for 'Restore' tables] +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test calls tSQLt.Private_NoTransactionHandleTable only for 'Restore' tables] AS BEGIN --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_ResettSQLtTable'; - EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_ResettSQLtTableAction'; - EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl1'',''Restore'');') - EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl2'',''Ignore'');') - EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl3'',''Restore'');') - EXEC('INSERT INTO tSQLt.Private_ResettSQLtTableAction VALUES(''tbl4'',''Other'');') + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl1'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl2'',''Ignore'');') + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl3'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl4'',''Other'');') - EXEC tSQLt.Private_ResettSQLtTables @save=0; + EXEC tSQLt.Private_NoTransactionHandleTables @save=0; - SELECT FullTableName INTO #Actual FROM tSQLt.Private_ResettSQLtTable_SpyProcedureLog; + SELECT FullTableName INTO #Actual FROM tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 23b3779eb..ef63b2f22 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -264,17 +264,17 @@ Private_ResetNewTestClassListTests.class.sql - + - Private_ResettSQLtTableActionTests.class.sql + Private_NoTransactionTableActionTests.class.sql - + - Private_ResettSQLtTablesTests.class.sql + Private_NoTransactionHandleTablesTests.class.sql From 1e8eff36a43ec33fae3fe75336319aa44d029b4f Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 15 Nov 2021 14:13:44 -0500 Subject: [PATCH 022/119] Missing Tests.ssmsqlproj. Need to add because we added a new file to the Tests project. --- ...t.Private_NoTransactionHandleTable.ssp.sql | 5 ++- ....Private_NoTransactionHandleTables.ssp.sql | 5 ++- ...te_NoTransactionHandleTableTests.class.sql | 11 +++++ ..._NoTransactionHandleTablesTests.class.sql} | 45 +++++++++---------- ...e_NoTransactionTableActionTests.class.sql} | 0 5 files changed, 39 insertions(+), 27 deletions(-) create mode 100644 Tests/Private_NoTransactionHandleTableTests.class.sql rename Tests/{Private_ResettSQLtTablesTests.class.sql => Private_NoTransactionHandleTablesTests.class.sql} (66%) rename Tests/{Private_ResettSQLtTableActionTests.class.sql => Private_NoTransactionTableActionTests.class.sql} (100%) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 7060a23cd..9693d5879 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -3,10 +3,11 @@ GO ---Build+ GO CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTable +@Action NVARCHAR(MAX), @FullTableName NVARCHAR(MAX), -@Action NVARCHAR(MAX) +@TableAction NVARCHAR(MAX) AS BEGIN - RETURN; + RAISERROR('Invalid Action. @Action parameter must be one of the following: Save, Reset.',16,10); END; GO diff --git a/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql index feda033e1..87a8af3c9 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql @@ -3,12 +3,13 @@ GO ---Build+ GO CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTables + @Action NVARCHAR(MAX) AS BEGIN DECLARE @cmd NVARCHAR(MAX) = ( - SELECT 'EXEC tSQLt.Private_NoTransactionHandleTable @FullTableName = '''+X.Name+''', @Action = '''+X.Action+''';' + SELECT 'EXEC tSQLt.Private_NoTransactionHandleTable @Action = '''+@Action+''', @FullTableName = '''+X.Name+''', @TableAction = '''+X.Action+''';' FROM tSQLt.Private_NoTransactionTableAction X - WHERE X.Action = 'Restore' + WHERE X.Action <> 'Ignore' FOR XML PATH(''),TYPE ).value('.','NVARCHAR(MAX)'); EXEC(@cmd); diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql new file mode 100644 index 000000000..27ba7806a --- /dev/null +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -0,0 +1,11 @@ +EXEC tSQLt.NewTestClass 'Private_NoTransactionHandleTableTests'; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test errors if Action is not an acceptable value] +AS +BEGIN + + EXEC tSQLt.ExpectException @ExpectedMessage = 'Invalid Action. @Action parameter must be one of the following: Save, Reset.', @ExpectedSeverity = 16, @ExpectedState = 10; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Unexpected Action', @FullTableName = '[someschema].[sometable]', @TableAction = 'Restore'; +END; +GO diff --git a/Tests/Private_ResettSQLtTablesTests.class.sql b/Tests/Private_NoTransactionHandleTablesTests.class.sql similarity index 66% rename from Tests/Private_ResettSQLtTablesTests.class.sql rename to Tests/Private_NoTransactionHandleTablesTests.class.sql index 070f6e083..59492ad17 100644 --- a/Tests/Private_ResettSQLtTablesTests.class.sql +++ b/Tests/Private_NoTransactionHandleTablesTests.class.sql @@ -3,11 +3,10 @@ GO CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test does not call tSQLt.Private_NoTransactionHandleTable if tSQLt.Private_NoTransactionTableAction is empty] AS BEGIN - --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; - EXEC tSQLt.Private_NoTransactionHandleTables; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; EXEC tSQLt.AssertEmptyTable @TableName = 'tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog'; @@ -16,12 +15,11 @@ GO CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test calls tSQLt.Private_NoTransactionHandleTable if tSQLt.Private_NoTransactionTableAction contains a table] AS BEGIN - --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl1'',''Restore'');') - EXEC tSQLt.Private_NoTransactionHandleTables; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; SELECT FullTableName INTO #Actual FROM tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog; @@ -35,14 +33,13 @@ GO CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test calls tSQLt.Private_NoTransactionHandleTable for each table in tSQLt.Private_NoTransactionTableAction] AS BEGIN - --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl1'',''Restore'');') EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl2'',''Restore'');') EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl3'',''Restore'');') - EXEC tSQLt.Private_NoTransactionHandleTables; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; SELECT FullTableName INTO #Actual FROM tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog; @@ -53,10 +50,9 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test calls tSQLt.Private_NoTransactionHandleTable only for 'Restore' tables] +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test does not call tSQLt.Private_NoTransactionHandleTable for 'Ignore' tables] AS BEGIN - --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SavetSQLtTable'; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl1'',''Restore'');') @@ -64,31 +60,34 @@ BEGIN EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl3'',''Restore'');') EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl4'',''Other'');') - EXEC tSQLt.Private_NoTransactionHandleTables @save=0; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; SELECT FullTableName INTO #Actual FROM tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('tbl1'),('tbl3'); + VALUES('tbl1'),('tbl3'),('tbl4'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test passes @Action to tSQLt.Private_NoTransactionHandleTable for each table] +AS +BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTable'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction'; + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl1'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl2'',''Restore'');') + EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl3'',''Restore'');') + EXEC tSQLt.Private_NoTransactionHandleTables @Action='A Specific Action'; + SELECT FullTableName, Action INTO #Actual FROM tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES('tbl1', 'A Specific Action'),('tbl2', 'A Specific Action'),('tbl3', 'A Specific Action'); -/*-- -TODO - -- Tables --> SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('tSQLt'); -Action - Table Name -Restore - Private_NewTestClassList -Restore - Run_LastExecution -Restore - Private_Configurations -Ignore - CaptureOutputLog -Ignore - Private_RenamedObjectLog -Ignore - TestResult - ---*/ \ No newline at end of file + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO diff --git a/Tests/Private_ResettSQLtTableActionTests.class.sql b/Tests/Private_NoTransactionTableActionTests.class.sql similarity index 100% rename from Tests/Private_ResettSQLtTableActionTests.class.sql rename to Tests/Private_NoTransactionTableActionTests.class.sql From 02160f6cfd592de371105b91323720a8effc5c6e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 15 Nov 2021 16:09:23 -0500 Subject: [PATCH 023/119] fixing order of tests and making sure that they are all in the Tests.ssmssqlproj file --- Source/tSQLt.Private_CleanUp.ssp.sql | 2 +- Tests/Tests.ssmssqlproj | 30 +++++++++++++++++----------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index bbc1ce95a..10f5aaaf8 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -8,7 +8,7 @@ CREATE PROCEDURE tSQLt.Private_CleanUp AS BEGIN EXEC tSQLt.UndoTestDoubles @Force = 0; - EXEC tSQLt.Private_NoTransactionHandleTables; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; END; GO ---Build- diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index ef63b2f22..6bca1b785 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -234,6 +234,24 @@ Private_MarktSQLtTempObjectTests.class.sql + + + + + Private_NoTransactionHandleTableTests.class.sql + + + + + + Private_NoTransactionTableActionTests.class.sql + + + + + + Private_NoTransactionHandleTablesTests.class.sql + @@ -264,18 +282,6 @@ Private_ResetNewTestClassListTests.class.sql - - - - - Private_NoTransactionTableActionTests.class.sql - - - - - - Private_NoTransactionHandleTablesTests.class.sql - From f3f20248a609fa18eae801b59df959eee9b0b895 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 15 Nov 2021 16:29:40 -0500 Subject: [PATCH 024/119] case sensitivity is a thing. be careful. --- .../Private_RemoveSchemaBoundReferencesTests.class.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {tests => Tests}/Private_RemoveSchemaBoundReferencesTests.class.sql (100%) diff --git a/tests/Private_RemoveSchemaBoundReferencesTests.class.sql b/Tests/Private_RemoveSchemaBoundReferencesTests.class.sql similarity index 100% rename from tests/Private_RemoveSchemaBoundReferencesTests.class.sql rename to Tests/Private_RemoveSchemaBoundReferencesTests.class.sql From 5ea2bbf6f85729304d38f94ac580a6331caac912 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 15 Nov 2021 16:38:33 -0500 Subject: [PATCH 025/119] Starting on requirements for Private_NoTransactionHandleTable. pick up here. --- ...rivate_NoTransactionHandleTableTests.class.sql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 27ba7806a..7be4c02c3 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -9,3 +9,18 @@ BEGIN EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Unexpected Action', @FullTableName = '[someschema].[sometable]', @TableAction = 'Restore'; END; GO + +/*-- +TODO + +- If TableAction is not Restore, throw an error. +- Save +-- TableAction = Restore +--- Saves an exact copy of the table into a tSQLt Temp Object table +--- Saves the name of the temp object table into a #temp table +--- tSQLt Temp Object is marked as IsTempObject = 1 +- Reset +-- TableAction = Restore +--- Restores --> truncates original table and uses tSQLt Temp Object table to insert/restore data + +--*/ \ No newline at end of file From b1030793db6f02e47cd80bd3e4bb7b24788d918e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 15 Nov 2021 22:48:28 -0500 Subject: [PATCH 026/119] More tests for Private_NoTransactionHandleTable. --- ...t.Private_NoTransactionHandleTable.ssp.sql | 12 +++++- ....Private_NoTransactionTableAction.view.sql | 6 +-- ...te_NoTransactionHandleTableTests.class.sql | 43 ++++++++++++++++++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 9693d5879..515f331b9 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -8,6 +8,16 @@ CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTable @TableAction NVARCHAR(MAX) AS BEGIN - RAISERROR('Invalid Action. @Action parameter must be one of the following: Save, Reset.',16,10); + IF (@Action = 'Save') + BEGIN + DECLARE @NewQuotedName NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); + DECLARE @Cmd NVARCHAR(MAX) = 'SELECT * INTO '+@NewQuotedName+' FROM '+@FullTableName+';'; + EXEC (@Cmd); + INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedName); + END; + ELSE + BEGIN + RAISERROR('Invalid Action. @Action parameter must be one of the following: Save, Reset.',16,10); + END; END; GO diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index 6432022b3..7a08f9051 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -6,10 +6,10 @@ CREATE VIEW tSQLt.Private_NoTransactionTableAction AS SELECT * FROM( - VALUES('[tSQLt].[Private_NewTestClassList]','Restore'), - ('[tSQLt].[Run_LastExecution]','Restore'), + VALUES('[tSQLt].[Private_NewTestClassList]','Restore'), -- perhaps Remove is more appropriate + ('[tSQLt].[Run_LastExecution]','Restore'), -- perhaps Remove is more appropriate ('[tSQLt].[Private_Configurations]','Restore'), - ('[tSQLt].[CaptureOutputLog]','Ignore'), + ('[tSQLt].[CaptureOutputLog]','Ignore'), -- technically this should be truncated, but it already is at the beginning of Run. ('[tSQLt].[Private_RenamedObjectLog]','Ignore'), ('[tSQLt].[TestResult]','Ignore') )X(Name, Action); diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 7be4c02c3..b55cddd4b 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -9,15 +9,54 @@ BEGIN EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Unexpected Action', @FullTableName = '[someschema].[sometable]', @TableAction = 'Restore'; END; GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test creates new table and saves its name in #TableBackupLog if Action is Save] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + DECLARE @BackupName NVARCHAR(MAX) = (SELECT BackupName FROM #TableBackupLog WHERE OriginalName = 'Private_NoTransactionHandleTableTests.Table1'); + + EXEC tSQLt.AssertEqualsTableSchema @Expected = 'Private_NoTransactionHandleTableTests.Table1', @Actual = @BackupName; +END; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test calls tSQLt.Private_MarktSQLtTempObject on new object] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_MarktSQLtTempObject'; + TRUNCATE TABLE tSQLt.Private_MarktSQLtTempObject_SpyProcedureLog;--Quirkiness of testing the framework that you use to run the test + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + DECLARE @BackupName NVARCHAR(MAX) = (SELECT BackupName FROM #TableBackupLog WHERE OriginalName = 'Private_NoTransactionHandleTableTests.Table1'); + + SELECT ObjectName, ObjectType, NewNameOfOriginalObject + INTO #Actual + FROM tSQLt.Private_MarktSQLtTempObject_SpyProcedureLog; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES(ISNULL(@BackupName,'Backup table not found.'), N'TABLE', NULL); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO + + /*-- TODO - If TableAction is not Restore, throw an error. +- If FullTableName is not found, throw an error? - Save -- TableAction = Restore ---- Saves an exact copy of the table into a tSQLt Temp Object table ---- Saves the name of the temp object table into a #temp table +--- Saves an exact copy of the table data into a tSQLt Temp Object table --- tSQLt Temp Object is marked as IsTempObject = 1 - Reset -- TableAction = Restore From 5db0c3197276563efee7a3b5efdcaf0242d2e5bb Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 16 Nov 2021 15:12:58 -0500 Subject: [PATCH 027/119] Build is desperately broken. But at least another test is passing. --- Source/Run_Methods.sql | 2 ++ Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 51373d5e3..653788583 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -61,6 +61,8 @@ BEGIN CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); CREATE TABLE #NoTransaction(X INT); + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + IF EXISTS (SELECT 1 FROM sys.extended_properties WHERE name = N'SetFakeViewOnTrigger') BEGIN diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 515f331b9..8974094d8 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -10,10 +10,11 @@ AS BEGIN IF (@Action = 'Save') BEGIN - DECLARE @NewQuotedName NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); - DECLARE @Cmd NVARCHAR(MAX) = 'SELECT * INTO '+@NewQuotedName+' FROM '+@FullTableName+';'; + DECLARE @NewQuotedNameForBackupTable NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); + DECLARE @Cmd NVARCHAR(MAX) = 'SELECT * INTO '+@NewQuotedNameForBackupTable+' FROM '+@FullTableName+';'; EXEC (@Cmd); - INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedName); + INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedNameForBackupTable); + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @NewQuotedNameForBackupTable, @ObjectType = N'TABLE', @NewNameOfOriginalObject = NULL; END; ELSE BEGIN From 7ef8d7ffbdef88a05a060993cb6c52c17a9b349a Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 16 Nov 2021 21:57:57 -0500 Subject: [PATCH 028/119] Wrote 5 more tests for Private_NoTransactionHandleTable. Yay. And the build worked and now it fails. --- ...t.Private_NoTransactionHandleTable.ssp.sql | 31 ++++- ...te_NoTransactionHandleTableTests.class.sql | 121 +++++++++++++++++- 2 files changed, 144 insertions(+), 8 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 8974094d8..37a057979 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -10,15 +10,34 @@ AS BEGIN IF (@Action = 'Save') BEGIN - DECLARE @NewQuotedNameForBackupTable NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); - DECLARE @Cmd NVARCHAR(MAX) = 'SELECT * INTO '+@NewQuotedNameForBackupTable+' FROM '+@FullTableName+';'; - EXEC (@Cmd); - INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedNameForBackupTable); - EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @NewQuotedNameForBackupTable, @ObjectType = N'TABLE', @NewNameOfOriginalObject = NULL; + IF (@TableAction = 'Restore') + BEGIN + DECLARE @NewQuotedNameForBackupTable NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); + DECLARE @Cmd NVARCHAR(MAX) = 'SELECT * INTO '+@NewQuotedNameForBackupTable+' FROM '+@FullTableName+';'; + EXEC (@Cmd); + INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedNameForBackupTable); + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @NewQuotedNameForBackupTable, @ObjectType = N'TABLE', @NewNameOfOriginalObject = NULL; + END; + ELSE IF (@TableAction = 'Remove') + BEGIN + EXEC tSQLt.RemoveObject @ObjectName = @FullTableName; + END; + ELSE IF (@TableAction IN ('Truncate', 'Ignore')) + BEGIN + RETURN; + END; + ELSE + BEGIN + RAISERROR('Invalid @TableAction parameter value. tSQLt is in an unknown state: Stopping execution.',16,10); + END; + END; + ELSE IF (@Action = 'Reset') + BEGIN + PRINT 'Reset!'; END; ELSE BEGIN - RAISERROR('Invalid Action. @Action parameter must be one of the following: Save, Reset.',16,10); + RAISERROR('Invalid @Action parameter value. tSQLt is in an unknown state: Stopping execution.',16,10); END; END; GO diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index b55cddd4b..e869078a2 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -4,7 +4,7 @@ CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test errors if Action is AS BEGIN - EXEC tSQLt.ExpectException @ExpectedMessage = 'Invalid Action. @Action parameter must be one of the following: Save, Reset.', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessage = 'Invalid @Action parameter value. tSQLt is in an unknown state: Stopping execution.', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Unexpected Action', @FullTableName = '[someschema].[sometable]', @TableAction = 'Restore'; END; @@ -46,8 +46,124 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not create a backup table if @Action is Save and the @TableAction is Truncate] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + SELECT object_id, SCHEMA_NAME(schema_id) [schema_name], name INTO #Before FROM sys.tables; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Truncate'; + + SELECT * INTO #Actual + FROM ( + ( + SELECT 'Extra'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + EXCEPT + SELECT 'Extra'[?],* FROM #BEFORE + ) + UNION ALL + ( + SELECT 'Missing'[?],* FROM #BEFORE + EXCEPT + SELECT 'Missing'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + ) + ) X; + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not create a backup table if @Action is Save and the @TableAction is Ignore] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + SELECT object_id, SCHEMA_NAME(schema_id) [schema_name], name INTO #Before FROM sys.tables; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Ignore'; + + SELECT * INTO #Actual + FROM ( + ( + SELECT 'Extra'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + EXCEPT + SELECT 'Extra'[?],* FROM #BEFORE + ) + UNION ALL + ( + SELECT 'Missing'[?],* FROM #BEFORE + EXCEPT + SELECT 'Missing'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + ) + ) X; + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test calls tSQLt.RemoveObject if @Action is Save and @TableAction is Remove] +AS +BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.RemoveObject'; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Some.Table', @TableAction = 'Remove'; + + SELECT ObjectName INTO #Actual FROM tSQLt.RemoveObject_SpyProcedureLog; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES('Some.Table'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not create a backup table if @Action is Save and @TableAction is Remove] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + DECLARE @OriginalObjectId INT = OBJECT_ID('Private_NoTransactionHandleTableTests.Table1'); + + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + SELECT object_id, SCHEMA_NAME(schema_id) [schema_name], name INTO #Before FROM sys.tables WHERE object_id <> @OriginalObjectId; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Remove'; + SELECT * INTO #Actual + FROM ( + ( + SELECT 'Extra'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables WHERE object_id <> @OriginalObjectId + EXCEPT + SELECT 'Extra'[?],* FROM #BEFORE + ) + UNION ALL + ( + SELECT 'Missing'[?],* FROM #BEFORE + EXCEPT + SELECT 'Missing'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables WHERE object_id <> @OriginalObjectId + ) + ) X; + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test errors if @Action is Save and @TableAction is not an acceptable value] +AS +BEGIN + + EXEC tSQLt.ExpectException @ExpectedMessage = 'Invalid @TableAction parameter value. tSQLt is in an unknown state: Stopping execution.', @ExpectedSeverity = 16, @ExpectedState = 10; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[someschema].[sometable]', @TableAction = 'Unacceptable'; +END; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test augments any internal error with ' tSQLt is in an unknown state: Stopping execution.'] +AS +BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.RemoveObject', @CommandToExecute='RAISERROR(''SOME INTERNAL ERROR.'',16,10)'; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'SOME INTERNAL ERROR. tSQLt is in an unknown state: Stopping execution.', @ExpectedSeverity = NULL, @ExpectedState = NULL; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Some.Table', @TableAction = 'Remove'; +END; +GO /*-- TODO @@ -58,8 +174,9 @@ TODO -- TableAction = Restore --- Saves an exact copy of the table data into a tSQLt Temp Object table --- tSQLt Temp Object is marked as IsTempObject = 1 +-- All errors are augmented with "tSQLt is in an unknown state: Stopping execution." - Reset -- TableAction = Restore --- Restores --> truncates original table and uses tSQLt Temp Object table to insert/restore data ---*/ \ No newline at end of file +--*/ From abdd7f03e059c1a82cf24e68e5898e578889df56 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 17 Nov 2021 12:56:19 -0500 Subject: [PATCH 029/119] Finished writing tests for Reset/Restore, but we still need tests for Reset/Error condition, Ignore, Truncate, and Remove. --- ...t.Private_NoTransactionHandleTable.ssp.sql | 73 +++++++---- ...te_NoTransactionHandleTableTests.class.sql | 123 +++++++++++++++--- 2 files changed, 153 insertions(+), 43 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 37a057979..3a2355f40 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -8,36 +8,61 @@ CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTable @TableAction NVARCHAR(MAX) AS BEGIN - IF (@Action = 'Save') - BEGIN - IF (@TableAction = 'Restore') + DECLARE @cmd NVARCHAR(MAX); + BEGIN TRY + IF (OBJECT_ID(@FullTableName) IS NULL) RAISERROR('Table %s does not exist.',16,10,@FullTableName); + + IF (@Action = 'Save') BEGIN - DECLARE @NewQuotedNameForBackupTable NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); - DECLARE @Cmd NVARCHAR(MAX) = 'SELECT * INTO '+@NewQuotedNameForBackupTable+' FROM '+@FullTableName+';'; - EXEC (@Cmd); - INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedNameForBackupTable); - EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @NewQuotedNameForBackupTable, @ObjectType = N'TABLE', @NewNameOfOriginalObject = NULL; + IF (@TableAction = 'Restore') + BEGIN + DECLARE @NewQuotedNameForBackupTable NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); + SET @cmd = 'SELECT * INTO '+@NewQuotedNameForBackupTable+' FROM '+@FullTableName+';'; + EXEC (@Cmd); + INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedNameForBackupTable); + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @NewQuotedNameForBackupTable, @ObjectType = N'TABLE', @NewNameOfOriginalObject = NULL; + END; + ELSE IF (@TableAction = 'Remove') + BEGIN + EXEC tSQLt.RemoveObject @ObjectName = @FullTableName; + END; + ELSE IF (@TableAction IN ('Truncate', 'Ignore')) + BEGIN + RETURN; + END; + ELSE + BEGIN + RAISERROR('Invalid @TableAction parameter value.',16,10); + END; END; - ELSE IF (@TableAction = 'Remove') + ELSE IF (@Action = 'Reset') BEGIN - EXEC tSQLt.RemoveObject @ObjectName = @FullTableName; - END; - ELSE IF (@TableAction IN ('Truncate', 'Ignore')) - BEGIN - RETURN; + IF (@TableAction = 'Restore') + BEGIN + DECLARE @BackupTableName NVARCHAR(MAX) =(SELECT BackupName FROM #TableBackupLog WHERE OriginalName = @FullTableName); + SET @cmd = 'DELETE FROM ' + @FullTableName + ';'; + IF (EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) AND is_identity = 1)) + BEGIN + SET @cmd = @cmd + 'SET IDENTITY_INSERT ' + @FullTableName + ' ON;'; + END; + SET @cmd = @cmd + 'INSERT INTO ' + @FullTableName +'('; + SET @cmd = @cmd + STUFF((SELECT ','+QUOTENAME(name) FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) ORDER BY column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,''); + SET @cmd = @cmd + ') SELECT * FROM ' + @BackupTableName+';'; + EXEC(@cmd); + END END; ELSE BEGIN - RAISERROR('Invalid @TableAction parameter value. tSQLt is in an unknown state: Stopping execution.',16,10); + RAISERROR('Invalid @Action parameter value.',16,10); END; - END; - ELSE IF (@Action = 'Reset') - BEGIN - PRINT 'Reset!'; - END; - ELSE - BEGIN - RAISERROR('Invalid @Action parameter value. tSQLt is in an unknown state: Stopping execution.',16,10); - END; + END TRY + BEGIN CATCH + DECLARE @ErrorLine INT = ERROR_LINE(); + DECLARE @ErrorProcedure NVARCHAR(MAX) = ERROR_PROCEDURE(); + DECLARE @ErrorMessage NVARCHAR(MAX) = ERROR_MESSAGE(); + DECLARE @ErrorSeverity INT = ERROR_SEVERITY(); + DECLARE @ErrorState INT = ERROR_STATE(); + RAISERROR('tSQLt is in an unknown state: Stopping execution. (%s | Procedure: %s | Line: %i)', @ErrorSeverity, @ErrorState, @ErrorMessage, @ErrorProcedure, @ErrorLine); + END CATCH; END; GO diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index e869078a2..53c12e7ae 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -1,14 +1,19 @@ EXEC tSQLt.NewTestClass 'Private_NoTransactionHandleTableTests'; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test errors if Action is not an acceptable value] AS BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT); - EXEC tSQLt.ExpectException @ExpectedMessage = 'Invalid @Action parameter value. tSQLt is in an unknown state: Stopping execution.', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'tSQLt is in an unknown state: Stopping execution. (Invalid @Action parameter value. | Procedure: tSQLt.Private_NoTransactionHandleTable | Line: %)', @ExpectedSeverity = 16, @ExpectedState = 10; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Unexpected Action', @FullTableName = '[someschema].[sometable]', @TableAction = 'Restore'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Unexpected Action', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test creates new table and saves its name in #TableBackupLog if Action is Save] AS BEGIN @@ -23,6 +28,8 @@ BEGIN EXEC tSQLt.AssertEqualsTableSchema @Expected = 'Private_NoTransactionHandleTableTests.Table1', @Actual = @BackupName; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test calls tSQLt.Private_MarktSQLtTempObject on new object] AS BEGIN @@ -46,6 +53,8 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not create a backup table if @Action is Save and the @TableAction is Truncate] AS BEGIN @@ -74,6 +83,8 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not create a backup table if @Action is Save and the @TableAction is Ignore] AS BEGIN @@ -102,21 +113,26 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test calls tSQLt.RemoveObject if @Action is Save and @TableAction is Remove] AS BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.RemoveObject'; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Some.Table', @TableAction = 'Remove'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Remove'; SELECT ObjectName INTO #Actual FROM tSQLt.RemoveObject_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('Some.Table'); + VALUES('Private_NoTransactionHandleTableTests.Table1'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not create a backup table if @Action is Save and @TableAction is Remove] AS BEGIN @@ -146,37 +162,106 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test errors if @Action is Save and @TableAction is not an acceptable value] AS BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.SomeTable(i INT); - EXEC tSQLt.ExpectException @ExpectedMessage = 'Invalid @TableAction parameter value. tSQLt is in an unknown state: Stopping execution.', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'tSQLt is in an unknown state: Stopping execution. (Invalid @TableAction parameter value. | Procedure: tSQLt.Private_NoTransactionHandleTable | Line: %)', @ExpectedSeverity = 16, @ExpectedState = 10; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[someschema].[sometable]', @TableAction = 'Unacceptable'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.SomeTable', @TableAction = 'Unacceptable'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test augments any internal error with ' tSQLt is in an unknown state: Stopping execution.'] AS BEGIN - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.RemoveObject', @CommandToExecute='RAISERROR(''SOME INTERNAL ERROR.'',16,10)'; + CREATE TABLE Private_NoTransactionHandleTableTests.SomeTable(i INT); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.RemoveObject', @CommandToExecute='RAISERROR(''SOME INTERNAL ERROR.'',15,11)'; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'tSQLt is in an unknown state: Stopping execution. (SOME INTERNAL ERROR. | Procedure: tSQLt.RemoveObject | Line: 1)', @ExpectedSeverity = 15, @ExpectedState = 11; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.SomeTable', @TableAction = 'Remove'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test errors if the table does not exist] +AS +BEGIN - EXEC tSQLt.ExpectException @ExpectedMessage = 'SOME INTERNAL ERROR. tSQLt is in an unknown state: Stopping execution.', @ExpectedSeverity = NULL, @ExpectedState = NULL; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Some.Table', @TableAction = 'Remove'; + EXEC tSQLt.ExpectException @ExpectedMessagePattern = '%Nonexistent.Table does not exist%'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'someaction', @FullTableName = 'Nonexistent.Table', @TableAction = 'sometableaction'; END; GO +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test copies data from backup into the emptied original table if @Action is Reset and @TableAction is Restore] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + TRUNCATE TABLE Private_NoTransactionHandleTableTests.Table1; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test deletes original table data before restoring] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + TRUNCATE TABLE Private_NoTransactionHandleTableTests.Table1; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES(4, 'abcdef'),(6, 'dd'),(7, 'khdf'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test can restore table with identity column] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES('a'),('bb'),('cdce'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; /*-- TODO - -- If TableAction is not Restore, throw an error. -- If FullTableName is not found, throw an error? -- Save --- TableAction = Restore ---- Saves an exact copy of the table data into a tSQLt Temp Object table ---- tSQLt Temp Object is marked as IsTempObject = 1 --- All errors are augmented with "tSQLt is in an unknown state: Stopping execution." - Reset --- TableAction = Restore ---- Restores --> truncates original table and uses tSQLt Temp Object table to insert/restore data +-- Error conditions +-- Ignore +-- Truncate +-- Remove --*/ From b3edfff97480ab8dfab688bdcf891b40caf61df0 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 17 Nov 2021 14:56:35 -0500 Subject: [PATCH 030/119] Added more tests! --- ...t.Private_NoTransactionHandleTable.ssp.sql | 6 +++- ...te_NoTransactionHandleTableTests.class.sql | 32 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 3a2355f40..988f45b12 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -49,7 +49,11 @@ BEGIN SET @cmd = @cmd + STUFF((SELECT ','+QUOTENAME(name) FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) ORDER BY column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,''); SET @cmd = @cmd + ') SELECT * FROM ' + @BackupTableName+';'; EXEC(@cmd); - END + END; + ELSE + BEGIN + RAISERROR('Invalid @TableAction parameter value.', 16, 10); + END; END; ELSE BEGIN diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 53c12e7ae..33f39d9cf 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -7,7 +7,7 @@ AS BEGIN CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT); - EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'tSQLt is in an unknown state: Stopping execution. (Invalid @Action parameter value. | Procedure: tSQLt.Private_NoTransactionHandleTable | Line: %)', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessagePattern = '%Invalid @Action parameter value.%', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Unexpected Action', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; END; @@ -169,7 +169,7 @@ AS BEGIN CREATE TABLE Private_NoTransactionHandleTableTests.SomeTable(i INT); - EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'tSQLt is in an unknown state: Stopping execution. (Invalid @TableAction parameter value. | Procedure: tSQLt.Private_NoTransactionHandleTable | Line: %)', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessagePattern = '%Invalid @TableAction parameter value.%', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.SomeTable', @TableAction = 'Unacceptable'; END; @@ -256,10 +256,36 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test Reset @Action with unknown @TableAction causes error] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.SomeTable(i INT); + + EXEC tSQLt.ExpectException @ExpectedMessagePattern = '%Invalid @TableAction parameter value.%', @ExpectedSeverity = 16, @ExpectedState = 10; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.SomeTable', @TableAction = 'Unacceptable'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test Reset @Action with truncate @TableAction deletes all data from the table] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES('a'),('bb'),('cdce'); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Truncate'; + + EXEC tSQLt.AssertEmptyTable @TableName = 'Private_NoTransactionHandleTableTests.Table1'; +END; + /*-- TODO - Reset --- Error conditions -- Ignore -- Truncate -- Remove From d3477e426c74a0b1333719d53c164629796c5227 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 17 Nov 2021 17:48:16 -0500 Subject: [PATCH 031/119] Maybe finished writing tests for Private_NoTransactionHandleTable? Anyway, the build is passing. --- ...t.Private_NoTransactionHandleTable.ssp.sql | 14 ++++++- ...te_NoTransactionHandleTableTests.class.sql | 38 +++++++++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 988f45b12..52f321a95 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -10,8 +10,10 @@ AS BEGIN DECLARE @cmd NVARCHAR(MAX); BEGIN TRY - IF (OBJECT_ID(@FullTableName) IS NULL) RAISERROR('Table %s does not exist.',16,10,@FullTableName); - + IF (OBJECT_ID(@FullTableName) IS NULL AND NOT(@Action='Reset' AND @TableAction='Remove')) + BEGIN + RAISERROR('Table %s does not exist.',16,10,@FullTableName); + END; IF (@Action = 'Save') BEGIN IF (@TableAction = 'Restore') @@ -50,6 +52,14 @@ BEGIN SET @cmd = @cmd + ') SELECT * FROM ' + @BackupTableName+';'; EXEC(@cmd); END; + ELSE IF (@TableAction = 'Truncate') + BEGIN + EXEC('DELETE FROM ' + @FullTableName +';'); + END; + ELSE IF (@TableAction IN ('Ignore','Remove')) + BEGIN + RETURN; + END; ELSE BEGIN RAISERROR('Invalid @TableAction parameter value.', 16, 10); diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 33f39d9cf..80aaba03b 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -282,12 +282,42 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = 'Private_NoTransactionHandleTableTests.Table1'; END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not change the data if @Action is Reset and @TableAction Ignore] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES(4, 'jkdf'),(5, 'adfad'),(6, 'yuio'); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Ignore'; + + SELECT TOP(0) A.* INTO #Expected FROM Private_NoTransactionHandleTableTests.Table1 A RIGHT JOIN Private_NoTransactionHandleTableTests.Table1 X ON 1=0; + INSERT INTO #Expected VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'),(4, 'jkdf'),(5, 'adfad'),(6, 'yuio'); + + EXEC tSQLt.AssertEqualsTable @Expected = '#Expected', @Actual = 'Private_NoTransactionHandleTableTests.Table1'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not error and does not restore if @Action is Reset and @TableAction Remove] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Remove'; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Remove'; + + SELECT * INTO #Actual FROM sys.tables WHERE object_id = OBJECT_ID('Private_NoTransactionHandleTableTests.Table1'); + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO /*-- TODO -- Reset --- Ignore --- Truncate --- Remove --*/ From d46e2ce57db2757451feca6d8bcb9b1916162a6e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 17 Nov 2021 18:29:22 -0500 Subject: [PATCH 032/119] We killed the build good this time. Working on the 'Save' portion of the NoTransaction Annotation. --- Source/Run_Methods.sql | 1 + ....Private_NoTransactionTableAction.view.sql | 6 +- Tests/AnnotationNoTransactionTests.class.sql | 113 ++++++++++++++---- Tests/Private_CleanUpTests.class.sql | 6 + ...te_NoTransactionTableActionTests.class.sql | 6 +- 5 files changed, 103 insertions(+), 29 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 653788583..e87589312 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -106,6 +106,7 @@ BEGIN SET @TransactionStartedFlag = 1; SAVE TRAN @TranName; END; + EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; SET @PreExecTrancount = @@TRANCOUNT; diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index 7a08f9051..8ae86bb00 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -6,10 +6,10 @@ CREATE VIEW tSQLt.Private_NoTransactionTableAction AS SELECT * FROM( - VALUES('[tSQLt].[Private_NewTestClassList]','Restore'), -- perhaps Remove is more appropriate - ('[tSQLt].[Run_LastExecution]','Restore'), -- perhaps Remove is more appropriate + VALUES('[tSQLt].[Private_NewTestClassList]','Remove'), + ('[tSQLt].[Run_LastExecution]','Remove'), ('[tSQLt].[Private_Configurations]','Restore'), - ('[tSQLt].[CaptureOutputLog]','Ignore'), -- technically this should be truncated, but it already is at the beginning of Run. + ('[tSQLt].[CaptureOutputLog]','Truncate'), ('[tSQLt].[Private_RenamedObjectLog]','Ignore'), ('[tSQLt].[TestResult]','Ignore') )X(Name, Action); diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index be4433909..4930be383 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1,5 +1,7 @@ EXEC tSQLt.NewTestClass 'AnnotationNoTransactionTests'; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test runs test without transaction] AS BEGIN @@ -28,6 +30,8 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS IN END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO --[@tSQLt:SkipTest]('TODO: needs other tests first') CREATE PROCEDURE AnnotationNoTransactionTests.[test produces meaningful error when pre and post transactions counts don't match] AS @@ -43,6 +47,8 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BE EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test transaction name is NULL in TestResults table] AS BEGIN @@ -62,6 +68,8 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RE EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test if not NoTransaction TranName is valued in TestResults table] AS BEGIN @@ -75,6 +83,8 @@ BEGIN EXEC tSQLt.AssertLike @ExpectedPattern = 'tSQLtTran%', @Actual = @ActualTranName; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test succeeding test gets correct entry in TestResults table] AS BEGIN @@ -94,6 +104,8 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RE EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test failing test gets correct entry in TestResults table] AS BEGIN @@ -114,6 +126,8 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS EX EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test recoverable erroring test gets correct entry in TestResults table] AS BEGIN @@ -134,6 +148,8 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RA EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO --[@tSQLt:NoTransaction]() --[@tSQLt:SkipTest]('TODO: needs other tests first') CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] @@ -156,6 +172,8 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test calls tSQLt.Private_CleanUp] AS BEGIN @@ -178,6 +196,8 @@ CREATE PROCEDURE MyInnerTests.[test1] AS RETURN; EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and succeeding] AS BEGIN @@ -193,6 +213,8 @@ BEGIN EXEC tSQLt.AssertEmptyTable '#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and failing] AS BEGIN @@ -208,6 +230,8 @@ BEGIN EXEC tSQLt.AssertEmptyTable '#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and erroring] AS BEGIN @@ -223,6 +247,8 @@ BEGIN EXEC tSQLt.AssertEmptyTable '#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test message returned by tSQLt.Private_CleanUp is appended to tSQLt.TestResult.Msg] AS BEGIN @@ -241,6 +267,8 @@ CREATE PROCEDURE MyInnerTests.[test1] AS PRINT 1/0; EXEC tSQLt.AssertLike @ExpectedPattern = '% ', @Actual = @Actual; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test message returned by tSQLt.Private_CleanUp is called before the test result message is printed] AS BEGIN @@ -261,30 +289,69 @@ CREATE PROCEDURE MyInnerTests.[test1] AS RAISERROR('''',16,10); EXEC tSQLt.AssertLike @ExpectedPattern = '% ', @Actual = @Actual; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt tables are backed up before test is executed] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction]() + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + INSERT INTO #Actual SELECT Action FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog; + END; + '); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + SELECT Action INTO #Actual FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog; + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('Save'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + /*-- TODO --- CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) ----- there will be three clean up methods, executed in the following order ----- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter ----- 2. User defined clean up for a test class as specified by [].CleanUp ----- 3. tSQLt.Private_CleanUp ----- test for execution in the correct place in Private_RunTest, after the outer-most test execution try catch ----- Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error ----- If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. ----- appropriate error messages are appended to the test msg ----- tSQLt.Private_CleanUp Tests ------ Tables --> SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('tSQLt'); ------ tSQLt.UndoTestDoubles - --- transaction opened during test --- transaction commited during test --- test skipped? --- inner-transaction-free test errors --- confirm pre and post transaction counts match --- [test produces meaningful error when pre and post transactions counts don't match] --- we still need to save the TranName as something somewhere. --- settings need to be preserved (e.g. SummaryError) --- Ctrl+9 is broken with NoTransaction --- preserve content of all tSQLt.% tables + CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) +- there will be three clean up methods, executed in the following order +- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter +- 2. User defined clean up for a test class as specified by [].CleanUp +- 3. tSQLt.Private_CleanUp +- Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error +- If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. +- appropriate error messages are appended to the test msg + +Transactions +- transaction opened during test +- transaction commited during test +- inner-transaction-free test errors +- confirm pre and post transaction counts match +- [test produces meaningful error when pre and post transactions counts don't match] +- we still need to save the TranName as something somewhere. + +SkipTest Annotation & NoTransaction Annotation +- The test is skipped +- No other objects are dropped or created +- No handler is called +- Transaction something something + +Preserve content of all tSQLt.% tables +- Does not call 'Save' if @NoTransactionFlag=0; +- Does not call 'Save' if @SkipTestFlag = 1 +- Does not call 'Restore' if @NoTransactionFlag=0; +- Does not call 'Restore' if @SkipTestFlag = 1 +- Not a test: Confirm that [tSQLt].[Private_NewTestClassList] and [tSQLt].[Run_LastExecution] are not being used in critical functionality 'inside the reactor'. + +Everything is being called in the right order. +- test for execution in the correct place in Private_RunTest, after the outer-most test execution try catch +- Make sure undotestdoubles and handletables are called in the right order + --*/ \ No newline at end of file diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql index aa6a94816..139c366a0 100644 --- a/Tests/Private_CleanUpTests.class.sql +++ b/Tests/Private_CleanUpTests.class.sql @@ -1,5 +1,7 @@ EXEC tSQLt.NewTestClass 'Private_CleanUpTests'; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.UndoTestDoubles] AS BEGIN @@ -14,6 +16,8 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.Private_NoTransactionHandleTables] AS BEGIN @@ -29,6 +33,8 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO diff --git a/Tests/Private_NoTransactionTableActionTests.class.sql b/Tests/Private_NoTransactionTableActionTests.class.sql index 8acec169b..e26661c62 100644 --- a/Tests/Private_NoTransactionTableActionTests.class.sql +++ b/Tests/Private_NoTransactionTableActionTests.class.sql @@ -20,10 +20,10 @@ BEGIN SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - SELECT '[tSQLt].[Private_NewTestClassList]','Restore' UNION ALL - SELECT '[tSQLt].[Run_LastExecution]', 'Restore' UNION ALL + SELECT '[tSQLt].[Private_NewTestClassList]','Remove' UNION ALL + SELECT '[tSQLt].[Run_LastExecution]', 'Remove' UNION ALL SELECT '[tSQLt].[Private_Configurations]', 'Restore' UNION ALL - SELECT '[tSQLt].[CaptureOutputLog]', 'Ignore' UNION ALL + SELECT '[tSQLt].[CaptureOutputLog]', 'Truncate' UNION ALL SELECT '[tSQLt].[Private_RenamedObjectLog]','Ignore' UNION ALL SELECT '[tSQLt].[TestResult]', 'Ignore'; EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; From 88dc5db3321cf2974e9b352ef65c3088c731ff16 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 17 Nov 2021 22:04:15 -0500 Subject: [PATCH 033/119] Fixed the build, but it is still failing, just not catastrophically. --- Source/Run_Methods.sql | 5 ++- Source/tSQLt.Private_CleanUp.ssp.sql | 2 +- ....Private_NoTransactionTableAction.view.sql | 4 +- Tests/AnnotationNoTransactionTests.class.sql | 41 +++++++++++++++++++ Tests/AssertEqualsTableSchemaTests.class.sql | 2 +- 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index e87589312..3bda60db2 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -106,7 +106,10 @@ BEGIN SET @TransactionStartedFlag = 1; SAVE TRAN @TranName; END; - EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + ELSE + BEGIN + EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + END; SET @PreExecTrancount = @@TRANCOUNT; diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index 10f5aaaf8..02bc3815b 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -7,8 +7,8 @@ CREATE PROCEDURE tSQLt.Private_CleanUp @ErrorMsg NVARCHAR(MAX) OUTPUT AS BEGIN - EXEC tSQLt.UndoTestDoubles @Force = 0; EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; + EXEC tSQLt.UndoTestDoubles @Force = 0; END; GO ---Build- diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index 8ae86bb00..f5d9d2a13 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -6,8 +6,8 @@ CREATE VIEW tSQLt.Private_NoTransactionTableAction AS SELECT * FROM( - VALUES('[tSQLt].[Private_NewTestClassList]','Remove'), - ('[tSQLt].[Run_LastExecution]','Remove'), + VALUES('[tSQLt].[Private_NewTestClassList]','Ignore'), + ('[tSQLt].[Run_LastExecution]','Ignore'), ('[tSQLt].[Private_Configurations]','Restore'), ('[tSQLt].[CaptureOutputLog]','Truncate'), ('[tSQLt].[Private_RenamedObjectLog]','Ignore'), diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 4930be383..008e9e512 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -316,6 +316,47 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[stest using SkipTest and NoTransaction annotation skips the test] +AS +BEGIN + CREATE TABLE #SkippedTestExecutionLog (Id INT); + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:SkipTest]('') + CREATE PROCEDURE MyInnerTests.[skippedTest] + AS + BEGIN + INSERT INTO #SkippedTestExecutionLog VALUES (1); + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[skippedTest]'; + + EXEC tSQLt.AssertEmptyTable @TableName = '#SkippedTestExecutionLog'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call 'Save' if @NoTransactionFlag=0;] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + INSERT INTO #Actual SELECT Action FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog; + END; + '); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + SELECT TOP(0) Action INTO #Actual FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog; + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO /*-- TODO diff --git a/Tests/AssertEqualsTableSchemaTests.class.sql b/Tests/AssertEqualsTableSchemaTests.class.sql index b7c95049b..4215285c3 100644 --- a/Tests/AssertEqualsTableSchemaTests.class.sql +++ b/Tests/AssertEqualsTableSchemaTests.class.sql @@ -288,7 +288,7 @@ BEGIN EXEC tSQLt.AssertEqualsTableSchema @Expected = 'AssertEqualsTableSchemaTests.Tbl1', @Actual = 'AssertEqualsTableSchemaTests.Tbl2'; END; GO ---[@tSQLt:SkipTest]('Not currently supported. See Issue xxxxx') +--[@tSQLt:SkipTest]('Not currently supported. See Issue https://github.com/tSQLt-org/tSQLt/issues/119') CREATE PROCEDURE AssertEqualsTableSchemaTests.[test calls fail if tables are #temporary and their schema does not match] AS BEGIN From 4bd58bc7b0156688accb7b7fe9cbca74ad9d1fd9 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 17 Nov 2021 22:50:50 -0500 Subject: [PATCH 034/119] CURSORs begone. But more tests are needed, and the build is failing but in an ok way. --- Source/Run_Methods.sql | 64 +++++++++++------------------ Tests/Run_Methods_Tests.class.sql | 68 +++++++++++++------------------ 2 files changed, 52 insertions(+), 80 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 3bda60db2..e8c65c817 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -316,26 +316,17 @@ BEGIN DECLARE @SetupProcName NVARCHAR(MAX); EXEC tSQLt.Private_GetSetupProcedureName @TestClassId, @SetupProcName OUTPUT; - DECLARE testCases CURSOR LOCAL FAST_FORWARD - FOR - SELECT tSQLt.Private_GetQuotedFullName(object_id) - FROM sys.procedures - WHERE schema_id = @TestClassId - AND LOWER(name) LIKE 'test%'; - - OPEN testCases; - - FETCH NEXT FROM testCases INTO @TestCaseName; - - WHILE @@FETCH_STATUS = 0 - BEGIN - EXEC tSQLt.Private_RunTest @TestCaseName, @SetupProcName; - - FETCH NEXT FROM testCases INTO @TestCaseName; - END; - - CLOSE testCases; - DEALLOCATE testCases; + DECLARE @cmd NVARCHAR(MAX) = ( + ( + SELECT 'EXEC tSQLt.Private_RunTest '''+REPLACE(tSQLt.Private_GetQuotedFullName(object_id),'''','''''')+''', '+ISNULL(''''+REPLACE(@SetupProcName,'''','''''')+'''','NULL')+';' + FROM sys.procedures + WHERE schema_id = @TestClassId + AND LOWER(name) LIKE 'test%' + ORDER BY NEWID() + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)') + ); + EXEC(@cmd); END; GO @@ -389,34 +380,28 @@ BEGIN DECLARE @TestClassName NVARCHAR(MAX); DECLARE @TestProcName NVARCHAR(MAX); - DECLARE @TestClassCursor CURSOR; - EXEC @GetCursorCallback @TestClassCursor = @TestClassCursor OUT; + CREATE TABLE #TestClassesForRunCursor(Name NVARCHAR(MAX)); + EXEC @GetCursorCallback; ---- - WHILE(1=1) - BEGIN - FETCH NEXT FROM @TestClassCursor INTO @TestClassName; - IF(@@FETCH_STATUS<>0)BREAK; - - EXEC tSQLt.Private_RunTestClass @TestClassName; - - END; - - CLOSE @TestClassCursor; - DEALLOCATE @TestClassCursor; + DECLARE @cmd NVARCHAR(MAX) = ( + ( + SELECT 'EXEC tSQLt.Private_RunTestClass '''+Name+''';' + FROM #TestClassesForRunCursor + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)') + ); + EXEC(@cmd); EXEC tSQLt.Private_OutputTestResults @TestResultFormatter; END; GO CREATE PROCEDURE tSQLt.Private_GetCursorForRunAll - @TestClassCursor CURSOR VARYING OUTPUT AS BEGIN - SET @TestClassCursor = CURSOR LOCAL FAST_FORWARD FOR + INSERT INTO #TestClassesForRunCursor SELECT Name FROM tSQLt.TestClasses; - - OPEN @TestClassCursor; END; GO @@ -429,16 +414,13 @@ END; GO CREATE PROCEDURE tSQLt.Private_GetCursorForRunNew - @TestClassCursor CURSOR VARYING OUTPUT AS BEGIN - SET @TestClassCursor = CURSOR LOCAL FAST_FORWARD FOR + INSERT INTO #TestClassesForRunCursor SELECT TC.Name FROM tSQLt.TestClasses AS TC JOIN tSQLt.Private_NewTestClassList AS PNTCL ON PNTCL.ClassName = TC.Name; - - OPEN @TestClassCursor; END; GO diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 8022d5b06..f0daeebb9 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -1450,29 +1450,14 @@ BEGIN EXEC tSQLt.NewTestClass 'Test Class B'; EXEC tSQLt.NewTestClass 'Test Class C'; - DECLARE @TestClassCursor CURSOR; - EXEC tSQLt.Private_GetCursorForRunNew @TestClassCursor = @TestClassCursor OUT; - - SELECT Class - INTO #Actual - FROM tSQLt.TestResult - WHERE 1=0; - - DECLARE @TestClass NVARCHAR(MAX); - WHILE(1=1) - BEGIN - FETCH NEXT FROM @TestClassCursor INTO @TestClass; - IF(@@FETCH_STATUS<>0)BREAK; - INSERT INTO #Actual VALUES(@TestClass); - END; - CLOSE @TestClassCursor; - DEALLOCATE @TestClassCursor; + CREATE TABLE #TestClassesForRunCursor(Name NVARCHAR(MAX)); + EXEC tSQLt.Private_GetCursorForRunNew; - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + SELECT TOP(0) A.* INTO #Expected FROM #TestClassesForRunCursor A RIGHT JOIN #TestClassesForRunCursor X ON 1=0; INSERT INTO #Expected VALUES('Test Class B'); INSERT INTO #Expected VALUES('Test Class C'); - EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + EXEC tSQLt.AssertEqualsTable '#Expected','#TestClassesForRunCursor'; END; GO @@ -1486,29 +1471,13 @@ BEGIN EXEC tSQLt.NewTestClass 'Test Class C'; EXEC tSQLt.DropClass 'Test Class C'; - DECLARE @TestClassCursor CURSOR; - EXEC tSQLt.Private_GetCursorForRunNew @TestClassCursor = @TestClassCursor OUT; - - SELECT Class - INTO #Actual - FROM tSQLt.TestResult - WHERE 1=0; - - DECLARE @TestClass NVARCHAR(MAX); - WHILE(1=1) - BEGIN - FETCH NEXT FROM @TestClassCursor INTO @TestClass; - IF(@@FETCH_STATUS<>0)BREAK; - INSERT INTO #Actual VALUES(@TestClass); - END; - CLOSE @TestClassCursor; - DEALLOCATE @TestClassCursor; + CREATE TABLE #TestClassesForRunCursor(Name NVARCHAR(MAX)); + EXEC tSQLt.Private_GetCursorForRunNew; - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + SELECT TOP(0) A.* INTO #Expected FROM #TestClassesForRunCursor A RIGHT JOIN #TestClassesForRunCursor X ON 1=0; INSERT INTO #Expected VALUES('Test Class B'); - EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; - + EXEC tSQLt.AssertEqualsTable '#Expected','#TestClassesForRunCursor'; END; GO CREATE PROC Run_Methods_Tests.[test Privat_RunNew calls Private_RunCursor with correct cursor] @@ -2243,3 +2212,24 @@ BEGIN END; END GO +CREATE PROCEDURE Run_Methods_Tests.[test tSQLt.RunAll can handle classes with single quotes] +AS +BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_RunTestClass'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.TestClasses'; + EXEC('INSERT INTO tSQLt.TestClasses VALUES(''a class with a '''' in the middle'',12321);'); + + EXEC tSQLt.RunAll; + + SELECT TestClassName + INTO #Actual + FROM tSQLt.Private_RunTestClass_SpyProcedureLog; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES ('a class with a '' in the middle'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +--ALSO NEEDED: tSQLt.Run and tSQLt.RunTestClass can handle ' in name and the tests get executed!! \ No newline at end of file From 12cacddca7ec218af732109071123e0dd86deebe Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 18 Nov 2021 12:14:30 -0500 Subject: [PATCH 035/119] Wrote some tests to make sure that the public Run methods can handle single quotes in both class and test names. --- Source/Run_Methods.sql | 2 +- Tests/Run_Methods_Tests.class.sql | 113 +++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index e8c65c817..af563a7f9 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -385,7 +385,7 @@ BEGIN ---- DECLARE @cmd NVARCHAR(MAX) = ( ( - SELECT 'EXEC tSQLt.Private_RunTestClass '''+Name+''';' + SELECT 'EXEC tSQLt.Private_RunTestClass '''+REPLACE(Name, '''' ,'''''')+''';' FROM #TestClassesForRunCursor FOR XML PATH(''),TYPE ).value('.','NVARCHAR(MAX)') diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index f0daeebb9..95d240c88 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2232,4 +2232,115 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO ---ALSO NEEDED: tSQLt.Run and tSQLt.RunTestClass can handle ' in name and the tests get executed!! \ No newline at end of file +/* ----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Run_Methods_Tests.[test tSQLt.RunAll executes tests with single quotes in class and name] +AS +BEGIN + CREATE TABLE #Actual (Id INT); + EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); + EXEC (' + --[@'+'tSQLt:NoTransaction]() + CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; + '); + + EXEC tSQLt.FakeTable @TableName = 'tSQLt.TestClasses'; + EXEC('INSERT INTO tSQLt.TestClasses VALUES(''a class with a '''' in the middle'',12321);'); + + EXEC tSQLt.RunAll; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES (1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/* ----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Run_Methods_Tests.[test tSQLt.RunTestClass executes tests with single quotes in class and name] +AS +BEGIN + CREATE TABLE #Actual (Id INT); + EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); + EXEC (' + --[@'+'tSQLt:NoTransaction]() + CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; + '); + + EXEC tSQLt.RunTestClass @TestClassName = '[a class with a '' in the middle]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES (1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/* ----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Run_Methods_Tests.[test tSQLt.Run executes test class with single quotes in name] +AS +BEGIN + CREATE TABLE #Actual (Id INT); + EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); + EXEC (' + --[@'+'tSQLt:NoTransaction]() + CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; + '); + + EXEC tSQLt.Run '[a class with a '' in the middle]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES (1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/* ----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Run_Methods_Tests.[test tSQLt.Run executes test with single quotes in class and test names] +AS +BEGIN + CREATE TABLE #Actual (Id INT); + EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); + EXEC (' + --[@'+'tSQLt:NoTransaction]() + CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; + '); + + EXEC tSQLt.Run '[a class with a '' in the middle].[test with a '' in the middle]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES (1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/* ----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Run_Methods_Tests.[test tSQLt.RunNew executes test class with single quotes in class and test names] +AS +BEGIN + CREATE TABLE #Actual (Id INT); + EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); + EXEC (' + --[@'+'tSQLt:NoTransaction]() + CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; + '); + EXEC tSQLt.FakeTable @TableName = 'tSQLt.TestClasses'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NewTestClassList'; + EXEC('INSERT INTO tSQLt.TestClasses(Name) VALUES(''a class with a '''' in the middle'');'); + EXEC('INSERT INTO tSQLt.Private_NewTestClassList(ClassName) VALUES(''a class with a '''' in the middle'');'); + + EXEC tSQLt.RunNew; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES (1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO From 0c86698a78779ba1679c846680779c33a679e69b Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 18 Nov 2021 18:32:56 -0500 Subject: [PATCH 036/119] checking in despite all the debugging crud. We think we finally understand the issue with the catastrophic build failure: we are first faking an object and then removing it. --- Source/Run_Methods.sql | 19 ++++++++++-- Source/tSQLt.Private_CleanUp.ssp.sql | 14 +++++++++ ....Private_NoTransactionTableAction.view.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 30 +++++++++++++++---- ...te_NoTransactionTableActionTests.class.sql | 1 + Tests/Run_Methods_Tests.class.sql | 9 ++++++ Tests/UndoTestDoublesTests.class.sql | 7 +++++ 7 files changed, 74 insertions(+), 8 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index af563a7f9..ecc2d2b25 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -41,6 +41,9 @@ CREATE PROCEDURE tSQLt.Private_RunTest @SetUp NVARCHAR(MAX) = NULL AS BEGIN + DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; + PRINT '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>TRANCOUNT:'+CAST(@OuterPerimeterTrancount AS NVARCHAR(MAX))+REPLICATE('>?>',10+10*@OuterPerimeterTrancount); + DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; DECLARE @Cmd NVARCHAR(MAX); SET @Cmd = ''; @@ -108,7 +111,16 @@ BEGIN END; ELSE BEGIN - EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + IF(@SkipTestFlag = 0) + BEGIN + SELECT 'RunMethods:PreSave:1',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; + EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + SELECT 'RunMethods:PreSave:2',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; + END; END; SET @PreExecTrancount = @@TRANCOUNT; @@ -263,8 +275,9 @@ BEGIN END; END CATCH; - IF (@NoTransactionFlag = 1) + IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) BEGIN + PRINT '+++++++++++++++++++++++++++++++++++++++++++++++++++++++ RIGHT BEFORE CLEAN UP ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'; DECLARE @CleanUpErrorMsg NVARCHAR(MAX); EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); @@ -304,6 +317,8 @@ BEGIN EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; END; + IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %s <> %s))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); + END; GO diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index 02bc3815b..ff19b8804 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -7,8 +7,22 @@ CREATE PROCEDURE tSQLt.Private_CleanUp @ErrorMsg NVARCHAR(MAX) OUTPUT AS BEGIN + SELECT 'tSQLt.Private_CleanUp:1',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; + + SELECT 'tSQLt.Private_CleanUp:2',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; + EXEC tSQLt.UndoTestDoubles @Force = 0; + + SELECT 'tSQLt.Private_CleanUp:3',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; + END; GO ---Build- diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index f5d9d2a13..178ffa6f9 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -6,7 +6,7 @@ CREATE VIEW tSQLt.Private_NoTransactionTableAction AS SELECT * FROM( - VALUES('[tSQLt].[Private_NewTestClassList]','Ignore'), + VALUES('[tSQLt].[Private_NewTestClassList]','Remove'), ('[tSQLt].[Run_LastExecution]','Ignore'), ('[tSQLt].[Private_Configurations]','Restore'), ('[tSQLt].[CaptureOutputLog]','Truncate'), diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 008e9e512..bc4cffcd0 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -316,7 +316,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[stest using SkipTest and NoTransaction annotation skips the test] +CREATE PROCEDURE AnnotationNoTransactionTests.[test using SkipTest and NoTransaction annotation skips the test] AS BEGIN CREATE TABLE #SkippedTestExecutionLog (Id INT); @@ -357,6 +357,30 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_NoTransactionHandleTables if @NoTransactionFlag=1 and @SkipTestFlag=1] +AS +BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:SkipTest]('''') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + EXEC tSQLt.AssertEmptyTable @TableName = 'tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog'; +END; +GO + /*-- TODO @@ -384,10 +408,6 @@ SkipTest Annotation & NoTransaction Annotation - Transaction something something Preserve content of all tSQLt.% tables -- Does not call 'Save' if @NoTransactionFlag=0; -- Does not call 'Save' if @SkipTestFlag = 1 -- Does not call 'Restore' if @NoTransactionFlag=0; -- Does not call 'Restore' if @SkipTestFlag = 1 - Not a test: Confirm that [tSQLt].[Private_NewTestClassList] and [tSQLt].[Run_LastExecution] are not being used in critical functionality 'inside the reactor'. Everything is being called in the right order. diff --git a/Tests/Private_NoTransactionTableActionTests.class.sql b/Tests/Private_NoTransactionTableActionTests.class.sql index e26661c62..53b67c0b4 100644 --- a/Tests/Private_NoTransactionTableActionTests.class.sql +++ b/Tests/Private_NoTransactionTableActionTests.class.sql @@ -13,6 +13,7 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +--[@tSQLt:SkipTest]('') CREATE PROCEDURE Private_NoTransactionTableActionTests.[test has the correct actions for all tSQLt tables] AS BEGIN diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 95d240c88..b6a7f90ab 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2330,8 +2330,17 @@ BEGIN --[@'+'tSQLt:NoTransaction]() CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; '); + SELECT 1,T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; EXEC tSQLt.FakeTable @TableName = 'tSQLt.TestClasses'; + SELECT 2,T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NewTestClassList'; + SELECT 3,T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; EXEC('INSERT INTO tSQLt.TestClasses(Name) VALUES(''a class with a '''' in the middle'');'); EXEC('INSERT INTO tSQLt.Private_NewTestClassList(ClassName) VALUES(''a class with a '''' in the middle'');'); diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index 6ecd6337c..b9a3a5e05 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -581,3 +581,10 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; END; GO +/*-- + +TODO + +Write a test that ensure that we are restoring objects in the correct order if we first fake them and then remove them. + +--*/ \ No newline at end of file From dabfde494f2057cfc820ed30ef5cb4d103e18a45 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 18 Nov 2021 23:03:34 -0500 Subject: [PATCH 037/119] Build is in need of TLC. But it isn't blowing up anymore, so that's good. More tests needed. --- ....Private_NoTransactionTableAction.view.sql | 2 +- Source/tSQLt.UndoTestDoubles.ssp.sql | 70 +++++++++++------ ...te_NoTransactionTableActionTests.class.sql | 1 - Tests/UndoTestDoublesTests.class.sql | 76 ++++++++++++++++--- 4 files changed, 114 insertions(+), 35 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index 178ffa6f9..f5d9d2a13 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -6,7 +6,7 @@ CREATE VIEW tSQLt.Private_NoTransactionTableAction AS SELECT * FROM( - VALUES('[tSQLt].[Private_NewTestClassList]','Remove'), + VALUES('[tSQLt].[Private_NewTestClassList]','Ignore'), ('[tSQLt].[Run_LastExecution]','Ignore'), ('[tSQLt].[Private_Configurations]','Restore'), ('[tSQLt].[CaptureOutputLog]','Truncate'), diff --git a/Source/tSQLt.UndoTestDoubles.ssp.sql b/Source/tSQLt.UndoTestDoubles.ssp.sql index 050e7c84e..f7aa556f0 100644 --- a/Source/tSQLt.UndoTestDoubles.ssp.sql +++ b/Source/tSQLt.UndoTestDoubles.ssp.sql @@ -41,6 +41,28 @@ BEGIN ) Collisions(List) EXEC(@cmd); + IF(EXISTS( + SELECT ROL.OriginalName, COUNT(1) cnt + FROM tSQLt.Private_RenamedObjectLog ROL + JOIN sys.objects O + ON ROL.ObjectId = O.object_id + LEFT JOIN sys.extended_properties AS EP + ON EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.major_id = O.object_id + AND EP.name = 'tSQLt.IsTempObject' + AND EP.value = 1 + WHERE EP.value IS NULL + GROUP BY ROL.OriginalName + HAVING COUNT(1)>1 + )) + BEGIN + RAISERROR('Catastrophy Averted!',16,10); + END; + + + + + SELECT TOP(0)A.* INTO #RenamedObjects FROM tSQLt.Private_RenamedObjectLog A RIGHT JOIN tSQLt.Private_RenamedObjectLog X ON 1=0; @@ -49,6 +71,31 @@ BEGIN BEGIN TRAN; DELETE FROM tSQLt.Private_RenamedObjectLog OUTPUT Deleted.* INTO #RenamedObjects; + + WITH MarkedTestDoubles AS + ( + SELECT + TempO.Name, + SCHEMA_NAME(TempO.schema_id) SchemaName, + TempO.type ObjectType + FROM sys.objects TempO + JOIN sys.extended_properties AS EP + ON EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.major_id = TempO.object_id + AND EP.name = 'tSQLt.IsTempObject' + AND EP.value = 1 + ) + SELECT @cmd = + ( + SELECT + DC.cmd+';' + FROM MarkedTestDoubles MTD + CROSS APPLY tSQLt.Private_GetDropItemCmd(QUOTENAME(MTD.SchemaName)+'.'+QUOTENAME(MTD.Name),MTD.ObjectType) DC + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)') + RAISERROR('>>>>>>>>>>>>>>>>> CMD 1: %s', 0,1, @cmd) WITH NOWAIT; + EXEC(@cmd); + WITH LL AS ( SELECT @@ -99,30 +146,9 @@ BEGIN ORDER BY L.SortId DESC, L.Id ASC FOR XML PATH(''),TYPE ).value('.','NVARCHAR(MAX)') + RAISERROR('>>>>>>>>>>>>>>>>> CMD 2: %s', 0,1, @cmd) WITH NOWAIT; EXEC(@cmd); - WITH L AS - ( - SELECT - TempO.Name, - SCHEMA_NAME(TempO.schema_id) SchemaName, - TempO.type ObjectType - FROM sys.objects TempO - JOIN sys.extended_properties AS EP - ON EP.class_desc = 'OBJECT_OR_COLUMN' - AND EP.major_id = TempO.object_id - AND EP.name = 'tSQLt.IsTempObject' - AND EP.value = 1 - ) - SELECT @cmd = - ( - SELECT - DC.cmd+';' - FROM L - CROSS APPLY tSQLt.Private_GetDropItemCmd(QUOTENAME(L.SchemaName)+'.'+QUOTENAME(L.Name),L.ObjectType) DC - FOR XML PATH(''),TYPE - ).value('.','NVARCHAR(MAX)') - EXEC(@cmd); COMMIT; END; diff --git a/Tests/Private_NoTransactionTableActionTests.class.sql b/Tests/Private_NoTransactionTableActionTests.class.sql index 53b67c0b4..e26661c62 100644 --- a/Tests/Private_NoTransactionTableActionTests.class.sql +++ b/Tests/Private_NoTransactionTableActionTests.class.sql @@ -13,7 +13,6 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO ---[@tSQLt:SkipTest]('') CREATE PROCEDURE Private_NoTransactionTableActionTests.[test has the correct actions for all tSQLt tables] AS BEGIN diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index b9a3a5e05..792addb3a 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -523,17 +523,18 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; END; GO -CREATE PROCEDURE UndoTestDoublesTests.[test drops a marked object even if it is not conflicting with an original object] +/* --------------------------------------------------------------------------------------------- */ +GO +CREATE PROCEDURE UndoTestDoublesTests.[test non-testdouble object with @IsTempObjects=1 is also dropped] AS BEGIN SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc - INTO #OriginalObjectIds - FROM sys.objects O; + INTO #OriginalObjectIds + FROM sys.objects O; CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); - - EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1', @ObjectType = 'TABLE', @NewNameOfOriginalObject = NULL; - + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1', @ObjectType=N'TABLE', @NewNameOfOriginalObject=NULL; + EXEC tSQLt.UndoTestDoubles; SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc @@ -550,7 +551,9 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; END; GO -CREATE PROCEDURE UndoTestDoublesTests.[test drops multiple marked objects even if they do not conflict with an original object] +/* --------------------------------------------------------------------------------------------- */ +GO +CREATE PROCEDURE UndoTestDoublesTests.[test multiple non-testdouble objects with @IsTempObjects=1 are all dropped] AS BEGIN SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc @@ -581,10 +584,61 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; END; GO -/*-- +/** ------------------------------------------------------------------------------------------- **/ +GO +CREATE PROCEDURE UndoTestDoublesTests.[test recovers table that was first faked and then removed] +AS +BEGIN + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc + INTO #OriginalObjectIds + FROM sys.objects O; -TODO + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.SimpleTable1'; + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; + +SELECT * FROM tSQLt.Private_RenamedObjectLog AS PROL; +SELECT 'UndoTestDoubleTest:1',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' + OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) + WHERE T.schema_id=SCHEMA_ID('UndoTestDoublesTests') ORDER BY T.object_id, E.name; -Write a test that ensure that we are restoring objects in the correct order if we first fake them and then remove them. + EXEC tSQLt.UndoTestDoubles; ---*/ \ No newline at end of file + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc + INTO #RestoredObjectIds + FROM sys.objects O; + + SELECT * INTO #ShouldBeEmpty + FROM + ( + SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E + UNION ALL + SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A + ) T; + EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; +END; +GO +/** ------------------------------------------------------------------------------------------- **/ +GO +CREATE PROCEDURE UndoTestDoublesTests.[test throws useful error if two objects with @IsTempObject<>1 need to be renamed to the same name] +AS +BEGIN + + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'Cannot rename these objects as there are name collisions. Use @Force = 1 to override. ([UndoTestDoublesTests].[SimpleTable1]{tSQLt_TempObject_9287392, tSQLt_TempObject_9283479278})', @ExpectedSeverity = 16, @ExpectedState = 10; + + EXEC tSQLt.UndoTestDoubles; +END; + +/*-----------------------------------------------------------------------------------------------*/ +GO + +/*-- +TODO +test: collision between two renamed @IsTempObject<>1 objects is identified before anthing is dropped or renamed so that we can throw an error. +test: non-testdouble @IsTempObjects=1 are also dropped +--*/ From 11e0e308d612f4bb4787e69102ba2e58411c92dd Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 19 Nov 2021 14:21:54 -0500 Subject: [PATCH 038/119] in progress. --- Source/tSQLt.UndoTestDoubles.ssp.sql | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Source/tSQLt.UndoTestDoubles.ssp.sql b/Source/tSQLt.UndoTestDoubles.ssp.sql index f7aa556f0..884e47c0a 100644 --- a/Source/tSQLt.UndoTestDoubles.ssp.sql +++ b/Source/tSQLt.UndoTestDoubles.ssp.sql @@ -56,6 +56,40 @@ BEGIN HAVING COUNT(1)>1 )) BEGIN + WITH S AS( + SELECT + C.OriginalName, + C.CurrentName, + C.SchemaName + FROM( + SELECT ROL.OriginalName, O.name CurrentName, SCHEMA_NAME(O.schema_id) SchemaName, COUNT(1)OVER(PARTITION BY ROL.OriginalName) Cnt + FROM tSQLt.Private_RenamedObjectLog ROL + JOIN sys.objects O + ON ROL.ObjectId = O.object_id + LEFT JOIN sys.extended_properties AS EP + ON EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.major_id = O.object_id + AND EP.name = 'tSQLt.IsTempObject' + AND EP.value = 1 + WHERE EP.value IS NULL + )C + WHERE C.Cnt>1 + ) + SELECT QUOTENAME(S.SchemaName)+'.'+QUOTENAME(S.OriginalName)+'{'+C.CList+'}' + FROM S + CROSS APPLY ( + SELECT ( + STUFF( + ( + SELECT ','+SC.CurrentName + FROM S AS SC + WHERE SC.OriginalName = S.OriginalName + ORDER BY SC.CurrentName + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'), + 1,1,'') + ) CList + )C RAISERROR('Catastrophy Averted!',16,10); END; From ab6bd987fec568f77c4bd5db83e3d34d5ad01d88 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 19 Nov 2021 17:02:36 -0500 Subject: [PATCH 039/119] made a test work, probably. --- Source/tSQLt.UndoTestDoubles.ssp.sql | 46 ++++++++++++++++++---------- Tests/UndoTestDoublesTests.class.sql | 5 ++- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/Source/tSQLt.UndoTestDoubles.ssp.sql b/Source/tSQLt.UndoTestDoubles.ssp.sql index 884e47c0a..5eab96e65 100644 --- a/Source/tSQLt.UndoTestDoubles.ssp.sql +++ b/Source/tSQLt.UndoTestDoubles.ssp.sql @@ -7,6 +7,8 @@ CREATE PROCEDURE tSQLt.UndoTestDoubles AS BEGIN DECLARE @cmd NVARCHAR(MAX); + DECLARE @ErrorMessageTableList NVARCHAR(MAX); + DECLARE @ErrorMessage NVARCHAR(MAX); IF (@Force = 1) BEGIN @@ -17,6 +19,7 @@ BEGIN SET @cmd = 'RAISERROR(''Cannot drop these objects as they are not marked as temporary. Use @Force = 1 to override. (%s)'',16,10)'; END; + /*-- Two non-temp objects, the first of which should be renamed to the second --*/ SELECT @cmd = REPLACE(@cmd,'%s',Collisions.List) FROM ( @@ -41,6 +44,8 @@ BEGIN ) Collisions(List) EXEC(@cmd); + /*-- Attempting to rename two or more non-temp objects to the same name --*/ + IF(EXISTS( SELECT ROL.OriginalName, COUNT(1) cnt FROM tSQLt.Private_RenamedObjectLog ROL @@ -58,11 +63,12 @@ BEGIN BEGIN WITH S AS( SELECT + C.Id, C.OriginalName, C.CurrentName, C.SchemaName FROM( - SELECT ROL.OriginalName, O.name CurrentName, SCHEMA_NAME(O.schema_id) SchemaName, COUNT(1)OVER(PARTITION BY ROL.OriginalName) Cnt + SELECT ROL.OriginalName, ROL.Id, O.name CurrentName, SCHEMA_NAME(O.schema_id) SchemaName, COUNT(1)OVER(PARTITION BY ROL.OriginalName) Cnt FROM tSQLt.Private_RenamedObjectLog ROL JOIN sys.objects O ON ROL.ObjectId = O.object_id @@ -74,23 +80,29 @@ BEGIN WHERE EP.value IS NULL )C WHERE C.Cnt>1 + ), + ErrorTableLists AS( + SELECT '{'+C.CList+'}-->' + QUOTENAME(SO.SchemaName)+'.'+QUOTENAME(PARSENAME(SO.OriginalName,1)) ErrorTableList + FROM (SELECT DISTINCT SchemaName, OriginalName FROM S) SO + CROSS APPLY ( + SELECT ( + STUFF( + ( + SELECT ', '+QUOTENAME(SC.CurrentName) + FROM S AS SC + WHERE SC.OriginalName = SO.OriginalName + ORDER BY SC.Id + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'), + 1,2,'') + ) CList + )C ) - SELECT QUOTENAME(S.SchemaName)+'.'+QUOTENAME(S.OriginalName)+'{'+C.CList+'}' - FROM S - CROSS APPLY ( - SELECT ( - STUFF( - ( - SELECT ','+SC.CurrentName - FROM S AS SC - WHERE SC.OriginalName = S.OriginalName - ORDER BY SC.CurrentName - FOR XML PATH(''),TYPE - ).value('.','NVARCHAR(MAX)'), - 1,1,'') - ) CList - )C - RAISERROR('Catastrophy Averted!',16,10); + SELECT @ErrorMessageTableList = ETL.ErrorTableList + FROM ErrorTableLists ETL; + + SELECT @ErrorMessage = REPLACE('Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. (%s)','%s',@ErrorMessageTableList); + RAISERROR(@ErrorMessage,16,10); END; diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index 792addb3a..b3c8192ca 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -491,6 +491,8 @@ BEGIN END; GO +/* --------------------------------------------------------------------------------------------- */ +GO CREATE PROCEDURE UndoTestDoublesTests.[test objects that are replaced multiple times by objects not marked as IsTempObject are restored if @Force=1] AS BEGIN @@ -629,7 +631,8 @@ BEGIN CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; - EXEC tSQLt.ExpectException @ExpectedMessage = 'Cannot rename these objects as there are name collisions. Use @Force = 1 to override. ([UndoTestDoublesTests].[SimpleTable1]{tSQLt_TempObject_9287392, tSQLt_TempObject_9283479278})', @ExpectedSeverity = 16, @ExpectedState = 10; + --EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. ({[[]tSQLt_tempobject_%], [[]tSQLt_tempobject_%]}-->[UndoTestDoublesTests].[SimpleTable1])', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. ({[[]tSQLt_tempobject_%], [[]tSQLt_tempobject_%]}-->[[]UndoTestDoublesTests].[[]SimpleTable1])', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.UndoTestDoubles; END; From 9535faebd00980561d914dc517f93a2814f293b0 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 19 Nov 2021 19:02:49 -0500 Subject: [PATCH 040/119] still in progress --- Source/tSQLt.UndoTestDoubles.ssp.sql | 46 ++++++++++++++++++---------- Tests/UndoTestDoublesTests.class.sql | 28 ++++++++++++++++- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/Source/tSQLt.UndoTestDoubles.ssp.sql b/Source/tSQLt.UndoTestDoubles.ssp.sql index 5eab96e65..d63659668 100644 --- a/Source/tSQLt.UndoTestDoubles.ssp.sql +++ b/Source/tSQLt.UndoTestDoubles.ssp.sql @@ -8,19 +8,11 @@ AS BEGIN DECLARE @cmd NVARCHAR(MAX); DECLARE @ErrorMessageTableList NVARCHAR(MAX); - DECLARE @ErrorMessage NVARCHAR(MAX); + DECLARE @ErrorMessage NVARCHAR(MAX) = ''; - IF (@Force = 1) - BEGIN - SET @cmd = 'EXEC tSQLt.Private_Print @Message = ''WARNING: @Force has been set to 1. Dropping the following objects that are not marked as temporary. (%s)'';'; - END; - ELSE - BEGIN - SET @cmd = 'RAISERROR(''Cannot drop these objects as they are not marked as temporary. Use @Force = 1 to override. (%s)'',16,10)'; - END; /*-- Two non-temp objects, the first of which should be renamed to the second --*/ - SELECT @cmd = REPLACE(@cmd,'%s',Collisions.List) + SELECT @ErrorMessage = @ErrorMessage + ISNULL(REPLACE('Attempting to remove object(s) that is/are not marked as temporary. Use @Force = 1 to override. (%s)','%s',Collisions.List),'') FROM ( SELECT @@ -42,7 +34,6 @@ BEGIN ).value('.','NVARCHAR(MAX)'), 1,2,'') ) Collisions(List) - EXEC(@cmd); /*-- Attempting to rename two or more non-temp objects to the same name --*/ @@ -82,7 +73,9 @@ BEGIN WHERE C.Cnt>1 ), ErrorTableLists AS( - SELECT '{'+C.CList+'}-->' + QUOTENAME(SO.SchemaName)+'.'+QUOTENAME(PARSENAME(SO.OriginalName,1)) ErrorTableList + SELECT + '{'+C.CList+'}-->' + QUOTENAME(SO.SchemaName)+'.'+QUOTENAME(PARSENAME(SO.OriginalName,1)) ErrorTableList, + QUOTENAME(SO.SchemaName)+'.'+QUOTENAME(PARSENAME(SO.OriginalName,1)) FullOriginalName FROM (SELECT DISTINCT SchemaName, OriginalName FROM S) SO CROSS APPLY ( SELECT ( @@ -98,13 +91,32 @@ BEGIN ) CList )C ) - SELECT @ErrorMessageTableList = ETL.ErrorTableList - FROM ErrorTableLists ETL; - - SELECT @ErrorMessage = REPLACE('Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. (%s)','%s',@ErrorMessageTableList); + SELECT @ErrorMessageTableList = ( + STUFF( + ( + SELECT '; '+ETL.ErrorTableList + FROM ErrorTableLists ETL + ORDER BY ETL.FullOriginalName + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'), + 1,2,'' + ) + ); + SELECT @ErrorMessage = @ErrorMessage + REPLACE('Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. (%s)','%s',@ErrorMessageTableList); RAISERROR(@ErrorMessage,16,10); END; - + IF(@ErrorMessage <> '') + BEGIN + IF (@Force = 1) + BEGIN + SET @ErrorMessage = 'WARNING: @Force has been set to 1. Overriding the following error(s):'+@ErrorMessage; + EXEC tSQLt.Private_Print @Message = @ErrorMessage; + END; + ELSE + BEGIN + RAISERROR(@ErrorMessage,16,10); + END; + END; diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index b3c8192ca..d23f2ec1f 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -631,12 +631,37 @@ BEGIN CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; - --EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. ({[[]tSQLt_tempobject_%], [[]tSQLt_tempobject_%]}-->[UndoTestDoublesTests].[SimpleTable1])', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. ({[[]tSQLt_tempobject_%], [[]tSQLt_tempobject_%]}-->[[]UndoTestDoublesTests].[[]SimpleTable1])', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.UndoTestDoubles; END; +GO +/** ------------------------------------------------------------------------------------------- **/ +GO +CREATE PROCEDURE UndoTestDoublesTests.[test throws useful error if there are multiple object tuples with @IsTempObject<>1 needing to be renamed to the same name] +AS +BEGIN + + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; + CREATE TABLE UndoTestDoublesTests.SimpleTable2 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable2'; + CREATE TABLE UndoTestDoublesTests.SimpleTable2 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable2'; + CREATE TABLE UndoTestDoublesTests.SimpleTable2 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable2'; + CREATE TABLE UndoTestDoublesTests.SimpleTable3 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable3'; + CREATE TABLE UndoTestDoublesTests.SimpleTable3 (i INT); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable3'; + + EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. ({[[]tSQLt_tempobject_%], [[]tSQLt_tempobject_%]}-->[[]UndoTestDoublesTests].[[]SimpleTable1]; {[[]tSQLt_tempobject_%], [[]tSQLt_tempobject_%], [[]tSQLt_tempobject_%]}-->[[]UndoTestDoublesTests].[[]SimpleTable2]; {[[]tSQLt_tempobject_%], [[]tSQLt_tempobject_%]}-->[[]UndoTestDoublesTests].[[]SimpleTable3])', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.UndoTestDoubles; +END; +GO /*-----------------------------------------------------------------------------------------------*/ GO @@ -645,3 +670,4 @@ TODO test: collision between two renamed @IsTempObject<>1 objects is identified before anthing is dropped or renamed so that we can throw an error. test: non-testdouble @IsTempObjects=1 are also dropped --*/ +--EXEC tSQLt.Run UndoTestDoublesTests \ No newline at end of file From 8d874409a980e25201d0ee31be73f8c041c95807 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 19 Nov 2021 22:52:37 -0500 Subject: [PATCH 041/119] tests were written. no one was hurt. --- Source/tSQLt.UndoTestDoubles.ssp.sql | 34 ++++++++++++++++++---- Tests/UndoTestDoublesTests.class.sql | 43 +++++++++++++++++++++------- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/Source/tSQLt.UndoTestDoubles.ssp.sql b/Source/tSQLt.UndoTestDoubles.ssp.sql index d63659668..6d7eea69f 100644 --- a/Source/tSQLt.UndoTestDoubles.ssp.sql +++ b/Source/tSQLt.UndoTestDoubles.ssp.sql @@ -103,7 +103,6 @@ BEGIN ) ); SELECT @ErrorMessage = @ErrorMessage + REPLACE('Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. (%s)','%s',@ErrorMessageTableList); - RAISERROR(@ErrorMessage,16,10); END; IF(@ErrorMessage <> '') BEGIN @@ -150,10 +149,36 @@ BEGIN FROM MarkedTestDoubles MTD CROSS APPLY tSQLt.Private_GetDropItemCmd(QUOTENAME(MTD.SchemaName)+'.'+QUOTENAME(MTD.Name),MTD.ObjectType) DC FOR XML PATH(''),TYPE - ).value('.','NVARCHAR(MAX)') - RAISERROR('>>>>>>>>>>>>>>>>> CMD 1: %s', 0,1, @cmd) WITH NOWAIT; + ).value('.','NVARCHAR(MAX)'); EXEC(@cmd); + SELECT @cmd = + ( + SELECT + DC.cmd+';' + FROM( + SELECT + * + FROM( + SELECT + ROL.OriginalName, + O.object_id, + O.type ObjectType, + SCHEMA_NAME(O.schema_id) SchemaName, + O.name CurrentName, + ROW_NUMBER()OVER(PARTITION BY ROL.OriginalName ORDER BY ROL.Id) RN + FROM #RenamedObjects AS ROL + JOIN sys.objects O + ON O.object_id = ROL.ObjectId + )ROLI + WHERE ROLI.RN>1 + )Deletables + CROSS APPLY tSQLt.Private_GetDropItemCmd(QUOTENAME(Deletables.SchemaName)+'.'+QUOTENAME(Deletables.CurrentName),Deletables.ObjectType) DC + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'); + EXEC(@cmd); + + WITH LL AS ( SELECT @@ -203,8 +228,7 @@ BEGIN CROSS APPLY tSQLt.Private_GetDropItemCmd(QUOTENAME(L.SchemaName)+'.'+QUOTENAME(L.OriginalName),L.ObjectType) DC ORDER BY L.SortId DESC, L.Id ASC FOR XML PATH(''),TYPE - ).value('.','NVARCHAR(MAX)') - RAISERROR('>>>>>>>>>>>>>>>>> CMD 2: %s', 0,1, @cmd) WITH NOWAIT; + ).value('.','NVARCHAR(MAX)'); EXEC(@cmd); diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index d23f2ec1f..a1c48be07 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -395,7 +395,7 @@ BEGIN EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); - EXEC tSQLt.ExpectException @ExpectedMessage = 'Cannot drop these objects as they are not marked as temporary. Use @Force = 1 to override. ([UndoTestDoublesTests].[SimpleTable1])', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessage = 'Attempting to remove object(s) that is/are not marked as temporary. Use @Force = 1 to override. ([UndoTestDoublesTests].[SimpleTable1])', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.UndoTestDoubles; END; @@ -415,7 +415,7 @@ BEGIN CREATE TABLE UndoTestDoublesTests.SimpleTable2 (i INT); CREATE TABLE UndoTestDoublesTests.SimpleTable3 (i INT); - EXEC tSQLt.ExpectException @ExpectedMessage = 'Cannot drop these objects as they are not marked as temporary. Use @Force = 1 to override. ([UndoTestDoublesTests].[SimpleTable1], [UndoTestDoublesTests].[SimpleTable2], [UndoTestDoublesTests].[SimpleTable3])', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessage = 'Attempting to remove object(s) that is/are not marked as temporary. Use @Force = 1 to override. ([UndoTestDoublesTests].[SimpleTable1], [UndoTestDoublesTests].[SimpleTable2], [UndoTestDoublesTests].[SimpleTable3])', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.UndoTestDoubles; END; @@ -433,7 +433,7 @@ BEGIN @level0type = N'SCHEMA', @level0name = 'UndoTestDoublesTests', @level1type = N'TABLE', @level1name = 'SimpleTable1'; - EXEC tSQLt.ExpectException @ExpectedMessage = 'Cannot drop these objects as they are not marked as temporary. Use @Force = 1 to override. ([UndoTestDoublesTests].[SimpleTable1])', @ExpectedSeverity = 16, @ExpectedState = 10; + EXEC tSQLt.ExpectException @ExpectedMessage = 'Attempting to remove object(s) that is/are not marked as temporary. Use @Force = 1 to override. ([UndoTestDoublesTests].[SimpleTable1])', @ExpectedSeverity = 16, @ExpectedState = 10; EXEC tSQLt.UndoTestDoubles; END; @@ -485,7 +485,7 @@ BEGIN SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('WARNING: @Force has been set to 1. Dropping the following objects that are not marked as temporary. ([UndoTestDoublesTests].[aSimpleTable])'); + VALUES('WARNING: @Force has been set to 1. Overriding the following error(s):Attempting to remove object(s) that is/are not marked as temporary. Use @Force = 1 to override. ([UndoTestDoublesTests].[aSimpleTable])'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; @@ -591,19 +591,15 @@ GO CREATE PROCEDURE UndoTestDoublesTests.[test recovers table that was first faked and then removed] AS BEGIN + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc INTO #OriginalObjectIds FROM sys.objects O; - CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.SimpleTable1'; EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; -SELECT * FROM tSQLt.Private_RenamedObjectLog AS PROL; -SELECT 'UndoTestDoubleTest:1',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('UndoTestDoublesTests') ORDER BY T.object_id, E.name; - EXEC tSQLt.UndoTestDoubles; SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc @@ -664,10 +660,37 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE UndoTestDoublesTests.[test if two objects with @IsTempObject<>1 need to be renamed to the same name and @Force=1 the oldest survives] +AS +BEGIN + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + INSERT INTO UndoTestDoublesTests.SimpleTable1 VALUES (6); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + INSERT INTO UndoTestDoublesTests.SimpleTable1 VALUES (4); + EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1'; + + EXEC tSQLt.UndoTestDoubles @Force=1; + + SELECT i INTO #Actual FROM UndoTestDoublesTests.SimpleTable1; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(6); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/** ------------------------------------------------------------------------------------------- **/ +GO /*-- TODO test: collision between two renamed @IsTempObject<>1 objects is identified before anthing is dropped or renamed so that we can throw an error. test: non-testdouble @IsTempObjects=1 are also dropped + + +test: two objects with same name in separate schema that were removed will both be restoreds + --*/ --EXEC tSQLt.Run UndoTestDoublesTests \ No newline at end of file From 51b47dc632d47b6cb6e63964b504baf1b63b1f1a Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 20 Nov 2021 15:03:57 -0500 Subject: [PATCH 042/119] More tests without tears. Yay, us. --- Source/tSQLt.UndoTestDoubles.ssp.sql | 9 ++-- Tests/UndoTestDoublesTests.class.sql | 81 ++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/Source/tSQLt.UndoTestDoubles.ssp.sql b/Source/tSQLt.UndoTestDoubles.ssp.sql index 6d7eea69f..59a7bb63a 100644 --- a/Source/tSQLt.UndoTestDoubles.ssp.sql +++ b/Source/tSQLt.UndoTestDoubles.ssp.sql @@ -38,7 +38,7 @@ BEGIN /*-- Attempting to rename two or more non-temp objects to the same name --*/ IF(EXISTS( - SELECT ROL.OriginalName, COUNT(1) cnt + SELECT O.schema_id, ROL.OriginalName, COUNT(1) cnt FROM tSQLt.Private_RenamedObjectLog ROL JOIN sys.objects O ON ROL.ObjectId = O.object_id @@ -48,7 +48,7 @@ BEGIN AND EP.name = 'tSQLt.IsTempObject' AND EP.value = 1 WHERE EP.value IS NULL - GROUP BY ROL.OriginalName + GROUP BY O.schema_id, ROL.OriginalName HAVING COUNT(1)>1 )) BEGIN @@ -59,7 +59,7 @@ BEGIN C.CurrentName, C.SchemaName FROM( - SELECT ROL.OriginalName, ROL.Id, O.name CurrentName, SCHEMA_NAME(O.schema_id) SchemaName, COUNT(1)OVER(PARTITION BY ROL.OriginalName) Cnt + SELECT ROL.OriginalName, ROL.Id, O.name CurrentName, SCHEMA_NAME(O.schema_id) SchemaName, COUNT(1)OVER(PARTITION BY O.schema_id, ROL.OriginalName) Cnt FROM tSQLt.Private_RenamedObjectLog ROL JOIN sys.objects O ON ROL.ObjectId = O.object_id @@ -84,6 +84,7 @@ BEGIN SELECT ', '+QUOTENAME(SC.CurrentName) FROM S AS SC WHERE SC.OriginalName = SO.OriginalName + AND SC.SchemaName = SO.SchemaName ORDER BY SC.Id FOR XML PATH(''),TYPE ).value('.','NVARCHAR(MAX)'), @@ -166,7 +167,7 @@ BEGIN O.type ObjectType, SCHEMA_NAME(O.schema_id) SchemaName, O.name CurrentName, - ROW_NUMBER()OVER(PARTITION BY ROL.OriginalName ORDER BY ROL.Id) RN + ROW_NUMBER()OVER(PARTITION BY O.schema_id, ROL.OriginalName ORDER BY ROL.Id) RN FROM #RenamedObjects AS ROL JOIN sys.objects O ON O.object_id = ROL.ObjectId diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index a1c48be07..6e00bb01d 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -684,13 +684,84 @@ END; GO /** ------------------------------------------------------------------------------------------- **/ GO -/*-- -TODO -test: collision between two renamed @IsTempObject<>1 objects is identified before anthing is dropped or renamed so that we can throw an error. -test: non-testdouble @IsTempObjects=1 are also dropped +CREATE PROCEDURE UndoTestDoublesTests.[test two objects with @IsTempObject<>1 and the same name but in different schemas are restored] +AS +BEGIN + EXEC('CREATE SCHEMA RandomSchema1;'); + CREATE TABLE RandomSchema1.SimpleTable1 (i INT); + INSERT INTO RandomSchema1.SimpleTable1 VALUES (4); + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema1.SimpleTable1'; + + EXEC('CREATE SCHEMA RandomSchema2;'); + CREATE TABLE RandomSchema2.SimpleTable1 (i INT); + INSERT INTO RandomSchema2.SimpleTable1 VALUES (6); + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema2.SimpleTable1'; + + EXEC tSQLt.UndoTestDoubles; + + SELECT * INTO #Actual + FROM( + SELECT 'RandomSchema1' [schema_name], i FROM RandomSchema1.SimpleTable1 + UNION + SELECT 'RandomSchema2' [schema_name], i FROM RandomSchema2.SimpleTable1 + ) A + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('RandomSchema1', 4),('RandomSchema2',6); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/** ------------------------------------------------------------------------------------------- **/ +GO +CREATE PROCEDURE UndoTestDoublesTests.[test throws useful error if there are multiple object tuples in separate schemata but all with the same name] +AS +BEGIN + EXEC('CREATE SCHEMA RandomSchema1;'); + EXEC('CREATE SCHEMA RandomSchema2;'); -test: two objects with same name in separate schema that were removed will both be restoreds + DECLARE @CurrentTableObjectId INT + CREATE TABLE RandomSchema1.SimpleTable1 (i INT); + SET @CurrentTableObjectId = OBJECT_ID('RandomSchema1.SimpleTable1'); + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema1.SimpleTable1'; + DECLARE @Name1A NVARCHAR(MAX) = OBJECT_NAME(@CurrentTableObjectId); + + CREATE TABLE RandomSchema1.SimpleTable1 (i INT); + SET @CurrentTableObjectId = OBJECT_ID('RandomSchema1.SimpleTable1'); + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema1.SimpleTable1'; + DECLARE @Name1B NVARCHAR(MAX) = OBJECT_NAME(@CurrentTableObjectId); + + CREATE TABLE RandomSchema2.SimpleTable1 (i INT); + SET @CurrentTableObjectId = OBJECT_ID('RandomSchema2.SimpleTable1'); + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema2.SimpleTable1'; + DECLARE @Name2A NVARCHAR(MAX) = OBJECT_NAME(@CurrentTableObjectId); + + CREATE TABLE RandomSchema2.SimpleTable1 (i INT); + SET @CurrentTableObjectId = OBJECT_ID('RandomSchema2.SimpleTable1'); + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema2.SimpleTable1'; + DECLARE @Name2B NVARCHAR(MAX) = OBJECT_NAME(@CurrentTableObjectId); + + DECLARE @ExpectedMessage NVARCHAR(MAX) = 'Attempting to rename two or more objects to the same name. Use @Force = 1 to override, only first object of each rename survives. ({['+@Name1A+'], ['+@Name1B+']}-->[RandomSchema1].[SimpleTable1]; {['+@Name2A+'], ['+@Name2B+']}-->[RandomSchema2].[SimpleTable1])' + EXEC tSQLt.ExpectException @ExpectedMessage = @ExpectedMessage, @ExpectedSeverity = 16, @ExpectedState = 10; + + EXEC tSQLt.UndoTestDoubles; +END; +GO +/** ------------------------------------------------------------------------------------------- **/ +GO +CREATE PROCEDURE UndoTestDoublesTests.[test does this missing schema link cause other issues?] +AS +BEGIN + EXEC tSQLt.Fail 'It might. You better check!'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +/*-- +TODO --*/ --EXEC tSQLt.Run UndoTestDoublesTests \ No newline at end of file From 77eb8ac86d0498a7bb1a7864885064281eff51e8 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 20 Nov 2021 23:22:15 -0500 Subject: [PATCH 043/119] Removed debugging statements; restored correct actions for Private_NewTestClassList and Run_LastExecution; Updated SpyProcedure to move objects which collide with _SpyProcedureLog tables; Wrote more tests. --- Source/Run_Methods.sql | 8 --- Source/tSQLt.Private_CleanUp.ssp.sql | 11 ---- ....Private_NoTransactionTableAction.view.sql | 4 +- Source/tSQLt.SpyProcedure.ssp.sql | 7 +- Tests/SpyProcedureTests.class.sql | 20 +++++- Tests/UndoTestDoublesTests.class.sql | 66 ++++++++++++++++++- 6 files changed, 91 insertions(+), 25 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index ecc2d2b25..64ee3219b 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -42,7 +42,6 @@ CREATE PROCEDURE tSQLt.Private_RunTest AS BEGIN DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; - PRINT '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>TRANCOUNT:'+CAST(@OuterPerimeterTrancount AS NVARCHAR(MAX))+REPLICATE('>?>',10+10*@OuterPerimeterTrancount); DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; @@ -113,13 +112,7 @@ BEGIN BEGIN IF(@SkipTestFlag = 0) BEGIN - SELECT 'RunMethods:PreSave:1',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; - SELECT 'RunMethods:PreSave:2',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; END; END; @@ -277,7 +270,6 @@ BEGIN IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) BEGIN - PRINT '+++++++++++++++++++++++++++++++++++++++++++++++++++++++ RIGHT BEFORE CLEAN UP ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'; DECLARE @CleanUpErrorMsg NVARCHAR(MAX); EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index ff19b8804..8b6070065 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -7,22 +7,11 @@ CREATE PROCEDURE tSQLt.Private_CleanUp @ErrorMsg NVARCHAR(MAX) OUTPUT AS BEGIN - SELECT 'tSQLt.Private_CleanUp:1',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; - SELECT 'tSQLt.Private_CleanUp:2',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; - EXEC tSQLt.UndoTestDoubles @Force = 0; - SELECT 'tSQLt.Private_CleanUp:3',T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; - END; GO ---Build- diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index f5d9d2a13..8ae86bb00 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -6,8 +6,8 @@ CREATE VIEW tSQLt.Private_NoTransactionTableAction AS SELECT * FROM( - VALUES('[tSQLt].[Private_NewTestClassList]','Ignore'), - ('[tSQLt].[Run_LastExecution]','Ignore'), + VALUES('[tSQLt].[Private_NewTestClassList]','Remove'), + ('[tSQLt].[Run_LastExecution]','Remove'), ('[tSQLt].[Private_Configurations]','Restore'), ('[tSQLt].[CaptureOutputLog]','Truncate'), ('[tSQLt].[Private_RenamedObjectLog]','Ignore'), diff --git a/Source/tSQLt.SpyProcedure.ssp.sql b/Source/tSQLt.SpyProcedure.ssp.sql index 4f14acd35..dc680f02e 100644 --- a/Source/tSQLt.SpyProcedure.ssp.sql +++ b/Source/tSQLt.SpyProcedure.ssp.sql @@ -27,8 +27,13 @@ BEGIN DECLARE @NewNameOfOriginalObject NVARCHAR(MAX); - EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ProcedureObjectId, @NewName = @NewNameOfOriginalObject OUTPUT; + DECLARE @LogTableObjectId INT = OBJECT_ID(@LogTableName); + IF(@LogTableObjectId IS NOT NULL) + BEGIN + EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ObjectId = @LogTableObjectId; + END; + EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ProcedureObjectId, @NewName = @NewNameOfOriginalObject OUTPUT; EXEC(@CreateLogTableStatement); EXEC(@CreateProcedureStatement); diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index 74279d1ed..ca42d72cc 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -723,7 +723,8 @@ BEGIN EXEC tSQLt.SpyProcedure @ProcedureName = 'dbo.InnerProcedure'; - EXEC dbo.InnerProcedure @expectedCommand = 'Select 1 [Int]', @actualCommand = 'Select ''c'' [char]'; + DECLARE @ProcName NVARCHAR(MAX) = 'dbo.InnerProcedure'; + EXEC @ProcName @expectedCommand = 'Select 1 [Int]', @actualCommand = 'Select ''c'' [char]'; SELECT expectedCommand, actualCommand INTO #Actual FROM dbo.InnerProcedure_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; @@ -776,6 +777,8 @@ BEGIN END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROC SpyProcedureTests.[test new SpyProcedureLog table is marked as tSQLt.IsTempObject] AS BEGIN @@ -797,5 +800,20 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test can handle existing SpyProcedureLog table] +AS +BEGIN + CREATE TABLE SpyProcedureTests.TempProcedure1_SpyProcedureLog ([Please don't do this] INT, [but just in case, we can handle it] NVARCHAR(MAX)); + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 AS RETURN;'); + + EXEC tSQLt.ExpectNoException; + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index 6e00bb01d..fff4c39d7 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -751,10 +751,72 @@ END; GO /** ------------------------------------------------------------------------------------------- **/ GO -CREATE PROCEDURE UndoTestDoublesTests.[test does this missing schema link cause other issues?] +CREATE PROCEDURE UndoTestDoublesTests.[test can handle the kitchen sink] AS BEGIN - EXEC tSQLt.Fail 'It might. You better check!'; + EXEC('CREATE SCHEMA RandomSchema1;'); + EXEC('CREATE SCHEMA RandomSchema2;'); + CREATE TABLE RandomSchema1.SimpleTable1 (i INT); + CREATE TABLE RandomSchema1.SimpleTable2 (i INT CONSTRAINT [s1t2pk] PRIMARY KEY); + CREATE TABLE RandomSchema1.SimpleTable3 (i INT); + + CREATE TABLE RandomSchema2.SimpleTable1 (i INT); + CREATE TABLE RandomSchema2.SimpleTable2 (i INT); + CREATE TABLE RandomSchema2.SimpleTable3 (i INT); + + EXEC('CREATE PROCEDURE RandomSchema1.Proc1 AS RETURN;') + EXEC('CREATE PROCEDURE RandomSchema1.Proc2 AS RETURN;') + EXEC('CREATE PROCEDURE RandomSchema1.Proc3 AS RETURN;') + + + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc + INTO #OriginalObjectIds + FROM sys.objects O; + + EXEC tSQLt.FakeTable @TableName = 'RandomSchema1.SimpleTable1'; + CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT); + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1', @ObjectType = 'TABLE', @NewNameOfOriginalObject = NULL; + EXEC tSQLt.SpyProcedure @ProcedureName = 'RandomSchema1.Proc1'; + EXEC tSQLt.FakeTable @TableName = 'RandomSchema1.SimpleTable1'; + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema1.SimpleTable3'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'RandomSchema1.Proc1'; + EXEC tSQLt.FakeTable @TableName = 'RandomSchema1.SimpleTable2'; + CREATE TABLE UndoTestDoublesTests.SimpleTable2 (i INT); + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = 'UndoTestDoublesTests.SimpleTable2', @ObjectType = 'TABLE', @NewNameOfOriginalObject = NULL; + + EXEC tSQLt.FakeTable @TableName = 'RandomSchema1.SimpleTable1'; + EXEC tSQLt.ApplyConstraint @TableName = 'RandomSchema1.SimpleTable2', @ConstraintName = 's1t2pk'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'RandomSchema1.Proc1'; + EXEC tSQLt.FakeTable @TableName = 'RandomSchema2.SimpleTable3'; + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema1.Proc3'; + EXEC tSQLt.FakeTable @TableName = 'RandomSchema1.SimpleTable2'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'RandomSchema1.Proc1'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'RandomSchema1.Proc2'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'RandomSchema1.Proc1'; + EXEC tSQLt.FakeTable @TableName = 'RandomSchema2.SimpleTable1'; + EXEC tSQLt.FakeTable @TableName = 'RandomSchema2.SimpleTable3'; + CREATE TABLE UndoTestDoublesTests.SimpleTable3 (i INT); + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = 'UndoTestDoublesTests.SimpleTable3', @ObjectType = 'TABLE', @NewNameOfOriginalObject = NULL; + + EXEC tSQLt.ApplyConstraint @TableName = 'RandomSchema1.SimpleTable2', @ConstraintName = 's1t2pk'; + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema1.SimpleTable2'; + EXEC tSQLt.RemoveObject @ObjectName = 'RandomSchema1.SimpleTable1'; + + EXEC tSQLt.UndoTestDoubles; + + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc + INTO #RestoredObjectIds + FROM sys.objects O; + + SELECT * INTO #ShouldBeEmpty + FROM + ( + SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E + UNION ALL + SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A + ) T; + EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; + END; GO /*-----------------------------------------------------------------------------------------------*/ From 85ff3c983055437c32840068862e7438ad28e6c3 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 21 Nov 2021 19:37:45 -0500 Subject: [PATCH 044/119] AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] is causing catastrophic failures. You should start there. --- Tests/AnnotationNoTransactionTests.class.sql | 59 ++++++++++---------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index bc4cffcd0..b109f5890 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -150,30 +150,6 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:NoTransaction]() ---[@tSQLt:SkipTest]('TODO: needs other tests first') -CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] -AS -BEGIN - EXEC tSQLt.NewTestClass 'MyInnerTests' - EXEC(' ---[@'+'tSQLt:NoTransaction]() -CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT CAST(''Some obscure string'' AS INT); - '); - - EXEC tSQLt.SetSummaryError 0; - EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; - SELECT Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; - - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - - INSERT INTO #Expected - VALUES('Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); - EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO CREATE PROCEDURE AnnotationNoTransactionTests.[test calls tSQLt.Private_CleanUp] AS BEGIN @@ -380,6 +356,34 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = 'tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:NoTransaction]() +CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] +AS +BEGIN + EXEC tSQLt.DropClass 'MyInnerTests'; + EXEC ('CREATE SCHEMA MyInnerTests --AUTHORIZATION [tSQLt.TestClass];'); + EXEC(' +--[@'+'tSQLt:NoTransaction]() +CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT CAST(''Some obscure string'' AS INT); + '); + + EXEC tSQLt.SetSummaryError 0; + RAISERROR('1', 0, 1) WITH NOWAIT; + EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; + RAISERROR('2', 0, 1) WITH NOWAIT; + SELECT Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + VALUES('Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO @@ -396,7 +400,7 @@ GO Transactions - transaction opened during test - transaction commited during test -- inner-transaction-free test errors +- inner-transaction-free test errors with uncommittable transaction - confirm pre and post transaction counts match - [test produces meaningful error when pre and post transactions counts don't match] - we still need to save the TranName as something somewhere. @@ -405,10 +409,7 @@ SkipTest Annotation & NoTransaction Annotation - The test is skipped - No other objects are dropped or created - No handler is called -- Transaction something something - -Preserve content of all tSQLt.% tables -- Not a test: Confirm that [tSQLt].[Private_NewTestClassList] and [tSQLt].[Run_LastExecution] are not being used in critical functionality 'inside the reactor'. +- Transaction something something <-- this! Everything is being called in the right order. - test for execution in the correct place in Private_RunTest, after the outer-most test execution try catch From 3a2a59882931d49ac99f4b5e0db72b591186b331 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 22 Nov 2021 11:50:39 -0500 Subject: [PATCH 045/119] We have a failing test and a broken build. Yay! --- Source/Run_Methods.sql | 2 +- Source/tSQLt.class.sql | 25 ++++++++---------- Tests/AnnotationNoTransactionTests.class.sql | 13 ++++++++-- ...e_NoTransactionHandleTablesTests.class.sql | 26 +++++++++++++++++++ Tests/Run_Methods_Tests.class.sql | 11 ++------ 5 files changed, 51 insertions(+), 26 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 64ee3219b..317cd9aa3 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -350,7 +350,7 @@ SET NOCOUNT ON; DECLARE @IsSchema BIT; DECLARE @SetUp NVARCHAR(MAX);SET @SetUp = NULL; - SELECT @TestName = tSQLt.Private_GetLastTestNameIfNotProvided(@TestName); + SELECT @TestName = TestName FROM tSQLt.Private_GetLastTestNameIfNotProvided(@TestName); EXEC tSQLt.Private_SaveTestNameForSession @TestName; SELECT @TestClassId = schemaId, diff --git a/Source/tSQLt.class.sql b/Source/tSQLt.class.sql index da00c4035..8ae118d90 100644 --- a/Source/tSQLt.class.sql +++ b/Source/tSQLt.class.sql @@ -121,22 +121,19 @@ GO ---------------------------------------------------------------------- CREATE FUNCTION tSQLt.Private_GetLastTestNameIfNotProvided(@TestName NVARCHAR(MAX)) -RETURNS NVARCHAR(MAX) +RETURNS TABLE AS -BEGIN - IF(LTRIM(ISNULL(@TestName,'')) = '') - BEGIN - SELECT @TestName = TestName - FROM tSQLt.Run_LastExecution le - JOIN sys.dm_exec_sessions es - ON le.SessionId = es.session_id - AND le.LoginTime = es.login_time - WHERE es.session_id = @@SPID; - END - - RETURN @TestName; -END +RETURN + SELECT CASE WHEN (LTRIM(ISNULL(@TestName,'')) = '') THEN LE.TestName ELSE @TestName END TestName + FROM tSQLt.Run_LastExecution LE + RIGHT JOIN sys.dm_exec_sessions ES + ON LE.SessionId = ES.session_id + AND LE.LoginTime = ES.login_time + WHERE ES.session_id = @@SPID; GO +/*-- +IF(LTRIM(ISNULL(@TestName,'')) = '') +--*/ CREATE PROCEDURE tSQLt.Private_SaveTestNameForSession @TestName NVARCHAR(MAX) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index b109f5890..dabe134de 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -358,10 +358,19 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE FUNCTION AnnotationNoTransactionTests.PassThrough(@TestName NVARCHAR(MAX)) +RETURNS TABLE +AS +RETURN + SELECT @TestName TestName +GO --[@tSQLt:NoTransaction]() +--[@tSQLt:SkipTest]('') CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] AS BEGIN + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetLastTestNameIfNotProvided', @FakeFunctionName = 'AnnotationNoTransactionTests.PassThrough'; /* --<-- Prevent tSQLt-internal turmoil */ + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SaveTestNameForSession';/* --<-- Prevent tSQLt-internal turmoil */ EXEC tSQLt.DropClass 'MyInnerTests'; EXEC ('CREATE SCHEMA MyInnerTests --AUTHORIZATION [tSQLt.TestClass];'); EXEC(' @@ -373,12 +382,12 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT RAISERROR('1', 0, 1) WITH NOWAIT; EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; RAISERROR('2', 0, 1) WITH NOWAIT; - SELECT Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; + SELECT Name, Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); + VALUES('MyInnerTests.[test should cause unrecoverable error]', 'Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO diff --git a/Tests/Private_NoTransactionHandleTablesTests.class.sql b/Tests/Private_NoTransactionHandleTablesTests.class.sql index 59492ad17..00049f3cc 100644 --- a/Tests/Private_NoTransactionHandleTablesTests.class.sql +++ b/Tests/Private_NoTransactionHandleTablesTests.class.sql @@ -91,3 +91,29 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test is rerunnable] +AS +BEGIN + + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc + INTO #OriginalObjectIds + FROM sys.objects O; + + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Save'; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Restore'; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Restore'; + + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc + INTO #RestoredObjectIds + FROM sys.objects O; + + SELECT * INTO #ShouldBeEmpty + FROM + ( + SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E + UNION ALL + SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A + ) T; + EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; +END; +GO diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index b6a7f90ab..17e1f908f 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2330,17 +2330,10 @@ BEGIN --[@'+'tSQLt:NoTransaction]() CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; '); - SELECT 1,T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; EXEC tSQLt.FakeTable @TableName = 'tSQLt.TestClasses'; - SELECT 2,T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NewTestClassList'; - SELECT 3,T.object_id,T.name,X.*, E.* FROM sys.tables T LEFT JOIN sys.extended_properties AS E ON T.object_id = E.major_id AND E.class_desc='OBJECT_OR_COLUMN' - OUTER APPLY (SELECT(SELECT QUOTENAME(name)+' ' FROM sys.columns C WHERE T.object_id = C.object_id ORDER BY C.column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'))X(cols) - WHERE T.schema_id=SCHEMA_ID('tSQLt') ORDER BY T.object_id, E.name; + EXEC('INSERT INTO tSQLt.TestClasses(Name) VALUES(''a class with a '''' in the middle'');'); EXEC('INSERT INTO tSQLt.Private_NewTestClassList(ClassName) VALUES(''a class with a '''' in the middle'');'); From d4dd134ddb7eb6470d67b351f89d72353d942a1d Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 22 Nov 2021 16:08:41 -0500 Subject: [PATCH 046/119] Private_NoTransactionHandleTable is now rerunnable. And we've agreed on new terminology for renaming objects to hide them --> 'Hide'. --- ...t.Private_NoTransactionHandleTable.ssp.sql | 28 +- ....Private_NoTransactionTableAction.view.sql | 4 +- ...te_NoTransactionHandleTableTests.class.sql | 282 +++++++++++++++++- ...e_NoTransactionHandleTablesTests.class.sql | 7 +- ...te_NoTransactionTableActionTests.class.sql | 4 +- 5 files changed, 301 insertions(+), 24 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 52f321a95..37b2ec678 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -10,7 +10,7 @@ AS BEGIN DECLARE @cmd NVARCHAR(MAX); BEGIN TRY - IF (OBJECT_ID(@FullTableName) IS NULL AND NOT(@Action='Reset' AND @TableAction='Remove')) + IF (OBJECT_ID(@FullTableName) IS NULL AND @TableAction <> 'Hide') BEGIN RAISERROR('Table %s does not exist.',16,10,@FullTableName); END; @@ -18,15 +18,25 @@ BEGIN BEGIN IF (@TableAction = 'Restore') BEGIN - DECLARE @NewQuotedNameForBackupTable NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); - SET @cmd = 'SELECT * INTO '+@NewQuotedNameForBackupTable+' FROM '+@FullTableName+';'; - EXEC (@Cmd); - INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedNameForBackupTable); - EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @NewQuotedNameForBackupTable, @ObjectType = N'TABLE', @NewNameOfOriginalObject = NULL; + IF(NOT EXISTS(SELECT 1 FROM #TableBackupLog TBL WHERE TBL.OriginalName = @FullTableName)) + BEGIN + DECLARE @NewQuotedNameForBackupTable NVARCHAR(MAX) = '[tSQLt].'+QUOTENAME(tSQLt.Private::CreateUniqueObjectName()); + SET @cmd = 'SELECT * INTO '+@NewQuotedNameForBackupTable+' FROM '+@FullTableName+';'; + EXEC (@Cmd); + INSERT INTO #TableBackupLog (OriginalName, BackupName) VALUES (@FullTableName, @NewQuotedNameForBackupTable); + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @NewQuotedNameForBackupTable, @ObjectType = N'TABLE', @NewNameOfOriginalObject = NULL; + END; END; - ELSE IF (@TableAction = 'Remove') + ELSE IF (@TableAction = 'Hide') BEGIN - EXEC tSQLt.RemoveObject @ObjectName = @FullTableName; + IF (NOT EXISTS (SELECT 1 FROM tSQLt.Private_RenamedObjectLog ROL WHERE QUOTENAME(OBJECT_SCHEMA_NAME(ROL.ObjectId))+'.'+OriginalName = @FullTableName)) + BEGIN + IF(OBJECT_ID(@FullTableName) IS NULL) + BEGIN + RAISERROR('Table %s does not exist.',16,10,@FullTableName); + END; + EXEC tSQLt.RemoveObject @ObjectName = @FullTableName; + END; END; ELSE IF (@TableAction IN ('Truncate', 'Ignore')) BEGIN @@ -56,7 +66,7 @@ BEGIN BEGIN EXEC('DELETE FROM ' + @FullTableName +';'); END; - ELSE IF (@TableAction IN ('Ignore','Remove')) + ELSE IF (@TableAction IN ('Ignore','Hide')) BEGIN RETURN; END; diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index 8ae86bb00..7f5a3d82b 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -6,8 +6,8 @@ CREATE VIEW tSQLt.Private_NoTransactionTableAction AS SELECT * FROM( - VALUES('[tSQLt].[Private_NewTestClassList]','Remove'), - ('[tSQLt].[Run_LastExecution]','Remove'), + VALUES('[tSQLt].[Private_NewTestClassList]','Hide'), + ('[tSQLt].[Run_LastExecution]','Hide'), ('[tSQLt].[Private_Configurations]','Restore'), ('[tSQLt].[CaptureOutputLog]','Truncate'), ('[tSQLt].[Private_RenamedObjectLog]','Ignore'), diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 80aaba03b..6a63d6376 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -115,13 +115,13 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test calls tSQLt.RemoveObject if @Action is Save and @TableAction is Remove] +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test calls tSQLt.RemoveObject if @Action is Save and @TableAction is Hide] AS BEGIN CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.RemoveObject'; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Remove'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Hide'; SELECT ObjectName INTO #Actual FROM tSQLt.RemoveObject_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; @@ -133,7 +133,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not create a backup table if @Action is Save and @TableAction is Remove] +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not create a backup table if @Action is Save and @TableAction is Hide] AS BEGIN CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); @@ -142,7 +142,7 @@ BEGIN CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); SELECT object_id, SCHEMA_NAME(schema_id) [schema_name], name INTO #Before FROM sys.tables WHERE object_id <> @OriginalObjectId; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Remove'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Hide'; SELECT * INTO #Actual FROM ( @@ -183,7 +183,7 @@ BEGIN EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.RemoveObject', @CommandToExecute='RAISERROR(''SOME INTERNAL ERROR.'',15,11)'; EXEC tSQLt.ExpectException @ExpectedMessage = 'tSQLt is in an unknown state: Stopping execution. (SOME INTERNAL ERROR. | Procedure: tSQLt.RemoveObject | Line: 1)', @ExpectedSeverity = 15, @ExpectedState = 11; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.SomeTable', @TableAction = 'Remove'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.SomeTable', @TableAction = 'Hide'; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -304,20 +304,286 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not error and does not restore if @Action is Reset and @TableAction Remove] +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test does not error and does not restore if @Action is Reset and @TableAction Hide] AS BEGIN CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Remove'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Hide'; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Remove'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Hide'; SELECT * INTO #Actual FROM sys.tables WHERE object_id = OBJECT_ID('Private_NoTransactionHandleTableTests.Table1'); EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Restore, @Action Save, Save: the second Save does nothing] +AS +BEGIN + /* No additional temp objects are created, and any data in the backup table is unchanged (even if deleted for example) */ + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + SELECT object_id, SCHEMA_NAME(schema_id) [schema_name], name INTO #Before FROM sys.tables; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + SELECT * INTO #Actual + FROM ( + ( + SELECT 'Extra'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + EXCEPT + SELECT 'Extra'[?],* FROM #Before + ) + UNION ALL + ( + SELECT 'Missing'[?],* FROM #Before + EXCEPT + SELECT 'Missing'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + ) + ) X; + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Restore, @Action Save, Save: the second Save does nothing to the existing backup table] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + DECLARE @OriginalBackupTableName NVARCHAR(MAX) = (SELECT BackupName FROM #TableBackupLog WHERE OriginalName = '[Private_NoTransactionHandleTableTests].[Table1]'); + EXEC('DELETE FROM '+@OriginalBackupTableName+';'); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + + + EXEC tSQLt.AssertEmptyTable @TableName = @OriginalBackupTableName; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[Eclipse #TableBackupLog and execute Save & Reset] +AS +BEGIN + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; +END; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Restore, @Action Save, Save(Eclipsed), Reset(Eclipsed), Reset: gets back to point A] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + /*Point A*/ + SELECT * INTO #Expected FROM Private_NoTransactionHandleTableTests.Table1; + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC Private_NoTransactionHandleTableTests.[Eclipse #TableBackupLog and execute Save & Reset]; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + + SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Restore, @Action Save, Save, Reset, Reset: gets back to point A] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + /*Point A*/ + SELECT * INTO #Expected FROM Private_NoTransactionHandleTableTests.Table1; + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + + SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[Eclipse #TableBackupLog and execute Save] +AS +BEGIN + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; +END; +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Restore, @Action Save, Save(Eclipsed), Reset: gets back to point A] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + /*Point A*/ + SELECT * INTO #Expected FROM Private_NoTransactionHandleTableTests.Table1; + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC Private_NoTransactionHandleTableTests.[Eclipse #TableBackupLog and execute Save]; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + + SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Restore, @Action Save, Reset, Reset: gets back to point A] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + /*Point A*/ + SELECT * INTO #Expected FROM Private_NoTransactionHandleTableTests.Table1; + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + + SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Hide, @Action Save, and table has be previously hidden, do nothing] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + + EXEC tSQLt.RemoveObject @ObjectName = '[Private_NoTransactionHandleTableTests].[Table1]'; /* Hide here */ + SELECT object_id, SCHEMA_NAME(schema_id) [schema_name], name INTO #Before FROM sys.tables; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Hide'; + + SELECT * INTO #Actual + FROM ( + ( + SELECT 'Extra'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + EXCEPT + SELECT 'Extra'[?],* FROM #Before + ) + UNION ALL + ( + SELECT 'Missing'[?],* FROM #Before + EXCEPT + SELECT 'Missing'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + ) + ) X; + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Truncate, @Action Save, Save, Reset, Save, Reset, Reset: table is empty] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Truncate'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (4,'e'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Truncate'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (5,'d'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Truncate'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (6,'f'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Truncate'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (7,'t'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Truncate'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (8,'g'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Truncate'; + + EXEC tSQLt.AssertEmptyTable @TableName = 'Private_NoTransactionHandleTableTests.Table1'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Ignore, @Action Save, Save, Reset, Reset: table is unaltered] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Ignore'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (4,'e'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Ignore'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (6,'f'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Ignore'; + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (7,'t'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Ignore'; + + SELECT TOP(0) A.* INTO #Expected FROM Private_NoTransactionHandleTableTests.Table1 A RIGHT JOIN Private_NoTransactionHandleTableTests.Table1 X ON 1=0; + + INSERT INTO #Expected VALUES (2,'c'), (3,'a'), (4,'e'), (6,'f'), (7,'t'); + + EXEC tSQLt.AssertEqualsTable @Expected ='#Expected', @Actual = 'Private_NoTransactionHandleTableTests.Table1'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + + + +--tSQLt.Run 'Private_NoTransactionHandleTableTests'.[test if @TableAction is Restore, @Action Save, Save: the second Save does nothing]' /*-- TODO +I can rerun them and nothing "bad" happens. But what is "bad"? + +Some scenarios to consider +1: Save, Reset +2: Save, Save, Reset +3: Save, Reset, Reset +4: Save, Save, Reset, Reset + +@TableAction = Restore +5: ?*test* Save (Restore), Save (Restore) --> The second save does nothing, because it checks the #TableBackupLog. +6: Save (Restore), Save (Restore) Eclipsed #TableBackupLog --> The second save takes a new backup because it cannot see #TableBackupLog. +7: ?*test* Save (Restore), Save (Restore) Eclipsed #TableBackupLog, Reset (Restore) Eclipsed #TableBackupLog, Reset (Restore) --> Should be equivalent to scenario 1. +8: ?*test* Save (Restore), Save (Restore), Reset (Restore), Reset (Restore) --> Should be equivalent to scenario 1. +9: ?*test* Save (Restore), Save (Restore) Eclipsed #TableBackupLog, Reset (Restore) --> Should be equivalent to scenario 1. +17: ?*test* Save (Restore), Reset (Restore), Reset (Restore) --> Should be equivalent to scenario 1. + +@TableAction = Hide +10: ?*test* Save (Hide), Save (Hide) --> We can't hide something we can't see. Check to see if the object is already hidden, if so do nothing. If not, throw an error. +11: Save (Hide), Save (Hide) Eclipsed #TableBackupLog --> Same as scenario 10. +12: Save (Hide), Save (Hide) Eclipsed #TableBackupLog, Reset (Hide) Eclipsed #TableBackupLog, Reset (Hide) --> Should be equivalent to Scenario 1. +13: Save (Hide), Save (Hide), Reset (Hide), Reset (Hide) --> Should be equivalent to Scenario 1. +14: Save (Hide), Save (Hide) Eclipsed #TableBackupLog, Reset (Hide) --> Should be equivalent to Scenario 1. +18: Save (Hide), Reset (Hide), Reset (Hide) --> Should be equivalent to scenario 1. + +@TableAction = Truncate +15: ?*test* Save (Truncate), Save (Truncate), Reset (Truncate), Save (Truncate), Reset (Truncate), Reset (Truncate) --> Should be idempotent. Any table with TableAction=Truncate should be empty after any number of save, reset actions. + +@TableAction = Ignore +16: ?*test* Save (Ignore), Save (Ignore), Reset (Ignore), Reset (Ignore) --> No Op. ExpectNoException. + + + + + +test that repeated calls to @Action=Save for any of the @TableActions does nothing that can't be "Reset" +test that repeated calls to @Action=Reset for any of the @TableActions does nothing. + +Possible @Action: Save, Reset +Possible @TableAction: Hide, Restore, Truncate, Ignore + --*/ diff --git a/Tests/Private_NoTransactionHandleTablesTests.class.sql b/Tests/Private_NoTransactionHandleTablesTests.class.sql index 00049f3cc..4f4de0fbb 100644 --- a/Tests/Private_NoTransactionHandleTablesTests.class.sql +++ b/Tests/Private_NoTransactionHandleTablesTests.class.sql @@ -91,7 +91,7 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test is rerunnable] +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test is rerunnable (though it still needs some help from UndoTestDoubles)] AS BEGIN @@ -100,8 +100,9 @@ BEGIN FROM sys.objects O; EXEC tSQLt.Private_NoTransactionHandleTables @Action='Save'; - EXEC tSQLt.Private_NoTransactionHandleTables @Action='Restore'; - EXEC tSQLt.Private_NoTransactionHandleTables @Action='Restore'; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; + EXEC tSQLt.UndoTestDoubles; SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc INTO #RestoredObjectIds diff --git a/Tests/Private_NoTransactionTableActionTests.class.sql b/Tests/Private_NoTransactionTableActionTests.class.sql index e26661c62..2634c80cc 100644 --- a/Tests/Private_NoTransactionTableActionTests.class.sql +++ b/Tests/Private_NoTransactionTableActionTests.class.sql @@ -20,8 +20,8 @@ BEGIN SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - SELECT '[tSQLt].[Private_NewTestClassList]','Remove' UNION ALL - SELECT '[tSQLt].[Run_LastExecution]', 'Remove' UNION ALL + SELECT '[tSQLt].[Private_NewTestClassList]','Hide' UNION ALL + SELECT '[tSQLt].[Run_LastExecution]', 'Hide' UNION ALL SELECT '[tSQLt].[Private_Configurations]', 'Restore' UNION ALL SELECT '[tSQLt].[CaptureOutputLog]', 'Truncate' UNION ALL SELECT '[tSQLt].[Private_RenamedObjectLog]','Ignore' UNION ALL From 9c17f86268a7d422e6bd64d9330ed123c46ebd03 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 22 Nov 2021 16:16:25 -0500 Subject: [PATCH 047/119] Build is broken. Writing more tests. --- Tests/AnnotationNoTransactionTests.class.sql | 3 +- ...e_NoTransactionHandleTablesTests.class.sql | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index dabe134de..7f9e6c3dc 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -365,7 +365,6 @@ RETURN SELECT @TestName TestName GO --[@tSQLt:NoTransaction]() ---[@tSQLt:SkipTest]('') CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] AS BEGIN @@ -387,7 +386,7 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('MyInnerTests.[test should cause unrecoverable error]', 'Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); + VALUES('[MyInnerTests].[test should cause unrecoverable error]', 'Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO diff --git a/Tests/Private_NoTransactionHandleTablesTests.class.sql b/Tests/Private_NoTransactionHandleTablesTests.class.sql index 4f4de0fbb..977e8b348 100644 --- a/Tests/Private_NoTransactionHandleTablesTests.class.sql +++ b/Tests/Private_NoTransactionHandleTablesTests.class.sql @@ -118,3 +118,35 @@ BEGIN EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test combination of Private_NoTransactionHandleTables and UndoTestDoubles is rerunnable] +AS +BEGIN + + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc + INTO #OriginalObjectIds + FROM sys.objects O; + + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Save'; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; + EXEC tSQLt.UndoTestDoubles; + EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; + EXEC tSQLt.UndoTestDoubles; + + SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc + INTO #RestoredObjectIds + FROM sys.objects O; + + SELECT * INTO #ShouldBeEmpty + FROM + ( + SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E + UNION ALL + SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A + ) T; + EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO From b94e92c19cde9b1fb85a65db62a1e5b5447c10a1 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 22 Nov 2021 18:32:48 -0500 Subject: [PATCH 048/119] More tests, but no solution yet. --- ...t.Private_NoTransactionHandleTable.ssp.sql | 26 ++++++++------ Tests/AnnotationNoTransactionTests.class.sql | 14 ++++++-- ...te_NoTransactionHandleTableTests.class.sql | 34 ++++++++++++------- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 37b2ec678..848cca789 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -51,16 +51,22 @@ BEGIN BEGIN IF (@TableAction = 'Restore') BEGIN - DECLARE @BackupTableName NVARCHAR(MAX) =(SELECT BackupName FROM #TableBackupLog WHERE OriginalName = @FullTableName); - SET @cmd = 'DELETE FROM ' + @FullTableName + ';'; - IF (EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) AND is_identity = 1)) - BEGIN - SET @cmd = @cmd + 'SET IDENTITY_INSERT ' + @FullTableName + ' ON;'; - END; - SET @cmd = @cmd + 'INSERT INTO ' + @FullTableName +'('; - SET @cmd = @cmd + STUFF((SELECT ','+QUOTENAME(name) FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) ORDER BY column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,''); - SET @cmd = @cmd + ') SELECT * FROM ' + @BackupTableName+';'; - EXEC(@cmd); + BEGIN TRAN; + DECLARE @BackupTableName TABLE(TableName NVARCHAR(MAX)); + DELETE FROM #TableBackupLog OUTPUT DELETED.BackupName INTO @BackupTableName WHERE OriginalName = @FullTableName; + IF(EXISTS(SELECT 1 FROM @BackupTableName AS BTN)) + BEGIN + SET @cmd = 'DELETE FROM ' + @FullTableName + ';'; + IF (EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) AND is_identity = 1)) + BEGIN + SET @cmd = @cmd + 'SET IDENTITY_INSERT ' + @FullTableName + ' ON;'; + END; + SET @cmd = @cmd + 'INSERT INTO ' + @FullTableName +'('; + SET @cmd = @cmd + STUFF((SELECT ','+QUOTENAME(name) FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) ORDER BY column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,''); + SET @cmd = @cmd + ') SELECT * FROM ' + (SELECT TableName FROM @BackupTableName)+';'; + EXEC(@cmd); + END; + COMMIT; END; ELSE IF (@TableAction = 'Truncate') BEGIN diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 7f9e6c3dc..e117cbbeb 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -365,6 +365,7 @@ RETURN SELECT @TestName TestName GO --[@tSQLt:NoTransaction]() +---[@tSQLt:SkipTest]('') CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] AS BEGIN @@ -378,9 +379,16 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT '); EXEC tSQLt.SetSummaryError 0; - RAISERROR('1', 0, 1) WITH NOWAIT; - EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; - RAISERROR('2', 0, 1) WITH NOWAIT; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; + DELETE FROM tSQLt.Private_RenamedObjectLog; + BEGIN TRY + EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; + END TRY + BEGIN CATCH + /*-- more work todo --*/ + END CATCH; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; + SELECT Name, Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 6a63d6376..d334f48d8 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -462,6 +462,25 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Restore, @Action Save, Reset: removes entry from #TableBackupLog] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + INSERT INTO #TableBackupLog VALUES ('SomeUnrelatedTable','tSQLt_SomeUnrelatedTable_Temp'); + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Restore'; + + SELECT TOP(0) A.* INTO #Expected FROM #TableBackupLog A RIGHT JOIN #TableBackupLog X ON 1=0; + + INSERT INTO #Expected VALUES ('SomeUnrelatedTable','tSQLt_SomeUnrelatedTable_Temp'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#TableBackupLog'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Hide, @Action Save, and table has be previously hidden, do nothing] AS BEGIN @@ -546,7 +565,8 @@ GO /*-- TODO -I can rerun them and nothing "bad" happens. But what is "bad"? +I can rerun them and nothing "bad" happens. But what is "bad"? +What about when I intersperse calls to Save/Reset with UndoTestDoubles? Some scenarios to consider 1: Save, Reset @@ -574,16 +594,6 @@ Some scenarios to consider 15: ?*test* Save (Truncate), Save (Truncate), Reset (Truncate), Save (Truncate), Reset (Truncate), Reset (Truncate) --> Should be idempotent. Any table with TableAction=Truncate should be empty after any number of save, reset actions. @TableAction = Ignore -16: ?*test* Save (Ignore), Save (Ignore), Reset (Ignore), Reset (Ignore) --> No Op. ExpectNoException. - - - - - -test that repeated calls to @Action=Save for any of the @TableActions does nothing that can't be "Reset" -test that repeated calls to @Action=Reset for any of the @TableActions does nothing. - -Possible @Action: Save, Reset -Possible @TableAction: Hide, Restore, Truncate, Ignore +16: ?*test* Save (Ignore), Save (Ignore), Reset (Ignore), Reset (Ignore) --> No Op. ExpectNoException.s --*/ From 0fb93d19fa0f06d3bfe134c66cf369aa775e8a77 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 22 Nov 2021 23:34:42 -0500 Subject: [PATCH 049/119] Nothing works and everything is terrible. We can't find Private_NewTestClassList but why on earth are we looking for it? --- Tests/AnnotationNoTransactionTests.class.sql | 61 +++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index e117cbbeb..5ffdb6bd1 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -358,6 +358,60 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[Redact IsTestObject status on all objects] +AS +BEGIN + DECLARE @cmd NVARCHAR(MAX); + WITH MarkedTestDoubles AS + ( + SELECT + TempO.Name, + SCHEMA_NAME(TempO.schema_id) SchemaName, + TempO.type ObjectType + FROM sys.tables TempO + JOIN sys.extended_properties AS EP + ON EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.major_id = TempO.object_id + AND EP.name = 'tSQLt.IsTempObject' + AND EP.value = 1 + ) + SELECT @cmd = + ( + SELECT + 'EXEC sp_updateextendedproperty ''tSQLt.IsTempObject'',-1342,''SCHEMA'', '''+MTD.SchemaName+''', ''TABLE'', '''+MTD.Name+''';' + FROM MarkedTestDoubles MTD + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'); + EXEC(@cmd); +END; +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[Restore IsTestObject status on all objects] +AS +BEGIN + DECLARE @cmd NVARCHAR(MAX); + WITH MarkedTestDoubles AS + ( + SELECT + TempO.Name, + SCHEMA_NAME(TempO.schema_id) SchemaName, + TempO.type ObjectType + FROM sys.tables TempO + JOIN sys.extended_properties AS EP + ON EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.major_id = TempO.object_id + AND EP.name = 'tSQLt.IsTempObject' + AND EP.value = -1342 + ) + SELECT @cmd = + ( + SELECT + 'EXEC sp_updateextendedproperty ''tSQLt.IsTempObject'',1,''SCHEMA'', '''+MTD.SchemaName+''', ''TABLE'', '''+MTD.Name+''';' + FROM MarkedTestDoubles MTD + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)'); + EXEC(@cmd); +END; +GO CREATE FUNCTION AnnotationNoTransactionTests.PassThrough(@TestName NVARCHAR(MAX)) RETURNS TABLE AS @@ -365,7 +419,7 @@ RETURN SELECT @TestName TestName GO --[@tSQLt:NoTransaction]() ----[@tSQLt:SkipTest]('') +--[@tSQLt:SkipTest]('') CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] AS BEGIN @@ -380,13 +434,18 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; + + DELETE FROM tSQLt.Private_RenamedObjectLog; + + EXEC AnnotationNoTransactionTests.[Redact IsTestObject status on all objects]; BEGIN TRY EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; END TRY BEGIN CATCH /*-- more work todo --*/ END CATCH; + EXEC AnnotationNoTransactionTests.[Restore IsTestObject status on all objects]; EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; SELECT Name, Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; From 48419756e6112d93642cab2693a1b0449aabf609 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 23 Nov 2021 11:51:09 -0500 Subject: [PATCH 050/119] Debugging stuff. --- Experiments/SM query.sql | 61 +++++++++++++++++++ Source/Run_Methods.sql | 10 +++ ...t.Private_NoTransactionHandleTable.ssp.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 29 ++++----- 4 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 Experiments/SM query.sql diff --git a/Experiments/SM query.sql b/Experiments/SM query.sql new file mode 100644 index 000000000..9c4f8e183 --- /dev/null +++ b/Experiments/SM query.sql @@ -0,0 +1,61 @@ +SELECT + R.RowNumber, + TE.name, + TSV.subclass_name, + R.ObjectName, + R.LineNumber, + R.TransactionID, + R.XactSequence, + ISNULL(REPLICATE(' ',(R.NestLevel-1)),'')+CAST(R.TextData AS NVARCHAR(MAX)) AS TextData, + R.Error, + R.Severity, + R.NestLevel, + R.EventClass, + R.EventSubClass, + R.ApplicationName, + R.ClientProcessID, + R.DatabaseID, + R.DatabaseName, + R.EventSequence, + R.GroupID, + R.HostName, + R.IsSystem, + R.LoginName, + R.LoginSid, + R.NTDomainName, + R.NTUserName, + R.RequestID, + R.SPID, + R.ServerName, + R.SessionLoginName, + R.StartTime, + R.Success, + R.GUID, + R.BinaryData, + R.Duration, + R.EndTime, + R.IntegerData, + R.CPU, + R.IntegerData2, + R.Offset, + R.Reads, + R.RowCounts, + R.Writes, + R.State, + R.ObjectID, + R.ObjectType, + R.SourceDatabaseID, + R.IndexID, + R.Type, + R.Mode, + R.OwnerID, + R.ObjectID2, + R.BigintData1 + FROM dbo.run1 R + JOIN sys.trace_events AS TE + ON R.EventClass = TE.trace_event_id + LEFT JOIN sys.trace_subclass_values AS TSV + ON TSV.trace_event_id = TE.trace_event_id + AND R.EventSubClass = TSV.subclass_value + WHERE R.ObjectName NOT IN ('Private_Print','sp_rename', 'sp_validname','GetTestResultFormatter') + ORDER BY R.EventSequence \ No newline at end of file diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 317cd9aa3..baaa322d9 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -123,6 +123,9 @@ BEGIN BEGIN TRY IF(@SkipTestFlag = 0) BEGIN +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +DECLARE @TempMsg127 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(127) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg);RAISERROR(@TempMsg127, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- IF (@SetUp IS NOT NULL) EXEC @SetUp; EXEC (@Cmd); @@ -245,6 +248,9 @@ BEGIN SET @Msg = ERROR_MESSAGE(); END CATCH +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +DECLARE @TempMsg249 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(249) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg);RAISERROR(@TempMsg249, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran @@ -268,6 +274,10 @@ BEGIN END; END CATCH; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +DECLARE @TempMsg270 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(270) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg);RAISERROR(@TempMsg270, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- + IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) BEGIN DECLARE @CleanUpErrorMsg NVARCHAR(MAX); diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 848cca789..66836faf8 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -92,7 +92,7 @@ BEGIN DECLARE @ErrorMessage NVARCHAR(MAX) = ERROR_MESSAGE(); DECLARE @ErrorSeverity INT = ERROR_SEVERITY(); DECLARE @ErrorState INT = ERROR_STATE(); - RAISERROR('tSQLt is in an unknown state: Stopping execution. (%s | Procedure: %s | Line: %i)', @ErrorSeverity, @ErrorState, @ErrorMessage, @ErrorProcedure, @ErrorLine); + RAISERROR('tSQLt is in an unknown state: Stopping execution. (%s | Procedure: %s | Line: %i | tSQLt.Private_NoTransactionHandleTable)', @ErrorSeverity, @ErrorState, @ErrorMessage, @ErrorProcedure, @ErrorLine); END CATCH; END; GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 5ffdb6bd1..c6b096022 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -418,13 +418,14 @@ AS RETURN SELECT @TestName TestName GO ---[@tSQLt:NoTransaction]() ---[@tSQLt:SkipTest]('') +---[@tSQLt:NoTransaction]() +---[@tSQLt:SkipTest]('') +/* This test must be NoTransaction because */ CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] AS BEGIN - EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetLastTestNameIfNotProvided', @FakeFunctionName = 'AnnotationNoTransactionTests.PassThrough'; /* --<-- Prevent tSQLt-internal turmoil */ - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SaveTestNameForSession';/* --<-- Prevent tSQLt-internal turmoil */ + --EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetLastTestNameIfNotProvided', @FakeFunctionName = 'AnnotationNoTransactionTests.PassThrough'; /* --<-- Prevent tSQLt-internal turmoil */ + --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SaveTestNameForSession';/* --<-- Prevent tSQLt-internal turmoil */ EXEC tSQLt.DropClass 'MyInnerTests'; EXEC ('CREATE SCHEMA MyInnerTests --AUTHORIZATION [tSQLt.TestClass];'); EXEC(' @@ -433,20 +434,20 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT '); EXEC tSQLt.SetSummaryError 0; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; + --EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; - DELETE FROM tSQLt.Private_RenamedObjectLog; + --DELETE FROM tSQLt.Private_RenamedObjectLog; - EXEC AnnotationNoTransactionTests.[Redact IsTestObject status on all objects]; - BEGIN TRY + --EXEC AnnotationNoTransactionTests.[Redact IsTestObject status on all objects]; + --BEGIN TRY EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; - END TRY - BEGIN CATCH - /*-- more work todo --*/ - END CATCH; - EXEC AnnotationNoTransactionTests.[Restore IsTestObject status on all objects]; - EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; + --END TRY + --BEGIN CATCH + -- /*-- more work todo --*/ + --END CATCH; + --EXEC AnnotationNoTransactionTests.[Restore IsTestObject status on all objects]; + --EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; SELECT Name, Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; From 3b7c3c278377c148cda8acdf457d37c1f43fb343 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 23 Nov 2021 16:19:08 -0500 Subject: [PATCH 051/119] A whole lot of debugging statements. They need to be removed at some point. --- Source/Run_Methods.sql | 22 ++++++++++++++++--- ...t.Private_NoTransactionHandleTable.ssp.sql | 8 +++++++ Tests/AnnotationNoTransactionTests.class.sql | 8 ++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index baaa322d9..4b231b269 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -124,7 +124,7 @@ BEGIN IF(@SkipTestFlag = 0) BEGIN --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg127 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(127) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg);RAISERROR(@TempMsg127, 0,1) WITH NOWAIT; +DECLARE @TempMsg127 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(127) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg127, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- IF (@SetUp IS NOT NULL) EXEC @SetUp; EXEC (@Cmd); @@ -249,7 +249,7 @@ DECLARE @TempMsg127 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(127) - END CATCH --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg249 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(249) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg);RAISERROR(@TempMsg249, 0,1) WITH NOWAIT; +DECLARE @TempMsg249 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(249) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg249, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match @@ -275,7 +275,7 @@ DECLARE @TempMsg249 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(249) - END CATCH; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg270 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(270) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg);RAISERROR(@TempMsg270, 0,1) WITH NOWAIT; +DECLARE @TempMsg277 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(277) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg277, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) @@ -285,6 +285,12 @@ DECLARE @TempMsg270 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(270) - SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); END; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +DECLARE @TempMsg289 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(289) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg289, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- + + + If(@Result NOT IN ('Success','Skipped')) BEGIN SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; @@ -308,6 +314,12 @@ DECLARE @TempMsg270 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(270) - 'Error', 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; END; + +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +DECLARE @TempMsg316 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(316) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg316, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- + + IF(@TransactionStartedFlag = 1) BEGIN COMMIT; @@ -319,6 +331,10 @@ DECLARE @TempMsg270 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(270) - EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; END; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +DECLARE @TempMsg333 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(333) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg333, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- + IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %s <> %s))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); END; diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 66836faf8..6f95b7032 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -8,6 +8,10 @@ CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTable @TableAction NVARCHAR(MAX) AS BEGIN +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +DECLARE @TempMsg12 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(12) - @Action = %s, @FullTableName = %s, @TableAction = %s, XACT_STATE = %i, SummaryError = %i', @Action, @FullTableName, @TableAction, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg12, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- + DECLARE @cmd NVARCHAR(MAX); BEGIN TRY IF (OBJECT_ID(@FullTableName) IS NULL AND @TableAction <> 'Hide') @@ -54,6 +58,10 @@ BEGIN BEGIN TRAN; DECLARE @BackupTableName TABLE(TableName NVARCHAR(MAX)); DELETE FROM #TableBackupLog OUTPUT DELETED.BackupName INTO @BackupTableName WHERE OriginalName = @FullTableName; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +DECLARE @TempMsg58 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(58) - @BackupTableName = %s, @FullTableName = %s, XACT_STATE = %i, SummaryError = %i',(SELECT TableName FROM @BackupTableName), @FullTableName, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg58, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- + IF(EXISTS(SELECT 1 FROM @BackupTableName AS BTN)) BEGIN SET @cmd = 'DELETE FROM ' + @FullTableName + ';'; diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index c6b096022..8ba3f8c3d 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -421,18 +421,20 @@ GO ---[@tSQLt:NoTransaction]() ---[@tSQLt:SkipTest]('') /* This test must be NoTransaction because */ -CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] +CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] AS BEGIN --EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetLastTestNameIfNotProvided', @FakeFunctionName = 'AnnotationNoTransactionTests.PassThrough'; /* --<-- Prevent tSQLt-internal turmoil */ --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SaveTestNameForSession';/* --<-- Prevent tSQLt-internal turmoil */ - EXEC tSQLt.DropClass 'MyInnerTests'; - EXEC ('CREATE SCHEMA MyInnerTests --AUTHORIZATION [tSQLt.TestClass];'); + --EXEC tSQLt.DropClass 'MyInnerTests'; +-- EXEC ('CREATE SCHEMA MyInnerTests --AUTHORIZATION [tSQLt.TestClass];'); + EXEC ('CREATE SCHEMA MyInnerTests;'); EXEC(' --[@'+'tSQLt:NoTransaction]() CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT CAST(''Some obscure string'' AS INT); '); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; EXEC tSQLt.SetSummaryError 0; --EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; From 8a0288cadca0fb5f4e6798a8aef4c4d4321a70ff Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 23 Nov 2021 23:36:34 -0500 Subject: [PATCH 052/119] The build is toast. The server is probably also broken. But we learned things. --- Source/Run_Methods.sql | 14 +++++++------- ...t.Private_NoTransactionHandleTable.ssp.sql | 6 +++--- Tests/AnnotationNoTransactionTests.class.sql | 12 ++++++------ ...te_NoTransactionHandleTableTests.class.sql | 19 +++++++++++++++++++ 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 4b231b269..1234aad06 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -124,7 +124,7 @@ BEGIN IF(@SkipTestFlag = 0) BEGIN --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg127 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(127) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg127, 0,1) WITH NOWAIT; +--DECLARE @TempMsg127 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(127) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg127, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- IF (@SetUp IS NOT NULL) EXEC @SetUp; EXEC (@Cmd); @@ -249,7 +249,7 @@ DECLARE @TempMsg127 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(127) - END CATCH --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg249 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(249) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg249, 0,1) WITH NOWAIT; +--DECLARE @TempMsg249 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(249) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg249, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match @@ -275,22 +275,22 @@ DECLARE @TempMsg249 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(249) - END CATCH; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg277 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(277) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg277, 0,1) WITH NOWAIT; +--DECLARE @TempMsg277 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(277) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg277, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) BEGIN + DECLARE @CleanUpErrorMsg NVARCHAR(MAX); EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); END; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg289 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(289) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg289, 0,1) WITH NOWAIT; +--DECLARE @TempMsg289 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(289) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg289, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- - If(@Result NOT IN ('Success','Skipped')) BEGIN SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; @@ -316,7 +316,7 @@ DECLARE @TempMsg289 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(289) - END; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg316 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(316) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg316, 0,1) WITH NOWAIT; +--DECLARE @TempMsg316 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(316) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg316, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- @@ -332,7 +332,7 @@ DECLARE @TempMsg316 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(316) - END; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg333 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(333) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg333, 0,1) WITH NOWAIT; +--DECLARE @TempMsg333 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(333) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg333, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %s <> %s))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 6f95b7032..7ad909617 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -9,7 +9,7 @@ CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTable AS BEGIN --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg12 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(12) - @Action = %s, @FullTableName = %s, @TableAction = %s, XACT_STATE = %i, SummaryError = %i', @Action, @FullTableName, @TableAction, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg12, 0,1) WITH NOWAIT; +--DECLARE @TempMsg12 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(12) - @Action = %s, @FullTableName = %s, @TableAction = %s, XACT_STATE = %i, SummaryError = %i', @Action, @FullTableName, @TableAction, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg12, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- DECLARE @cmd NVARCHAR(MAX); @@ -59,7 +59,7 @@ DECLARE @TempMsg12 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(12) - @Action = %s DECLARE @BackupTableName TABLE(TableName NVARCHAR(MAX)); DELETE FROM #TableBackupLog OUTPUT DELETED.BackupName INTO @BackupTableName WHERE OriginalName = @FullTableName; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- -DECLARE @TempMsg58 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(58) - @BackupTableName = %s, @FullTableName = %s, XACT_STATE = %i, SummaryError = %i',(SELECT TableName FROM @BackupTableName), @FullTableName, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg58, 0,1) WITH NOWAIT; +--DECLARE @TempMsg58 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(58) - @BackupTableName = %s, @FullTableName = %s, XACT_STATE = %i, SummaryError = %i',(SELECT TableName FROM @BackupTableName), @FullTableName, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg58, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- IF(EXISTS(SELECT 1 FROM @BackupTableName AS BTN)) @@ -100,7 +100,7 @@ DECLARE @TempMsg58 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(58) - @BackupTable DECLARE @ErrorMessage NVARCHAR(MAX) = ERROR_MESSAGE(); DECLARE @ErrorSeverity INT = ERROR_SEVERITY(); DECLARE @ErrorState INT = ERROR_STATE(); - RAISERROR('tSQLt is in an unknown state: Stopping execution. (%s | Procedure: %s | Line: %i | tSQLt.Private_NoTransactionHandleTable)', @ErrorSeverity, @ErrorState, @ErrorMessage, @ErrorProcedure, @ErrorLine); + RAISERROR('tSQLt is in an unknown state: Stopping execution. (%s | Procedure: %s | Line: %i)', @ErrorSeverity, @ErrorState, @ErrorMessage, @ErrorProcedure, @ErrorLine); END CATCH; END; GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 8ba3f8c3d..7323aac22 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -418,15 +418,15 @@ AS RETURN SELECT @TestName TestName GO ----[@tSQLt:NoTransaction]() ----[@tSQLt:SkipTest]('') +--[@tSQLt:NoTransaction]() +--[@tSQLt:SkipTest]('') /* This test must be NoTransaction because */ CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] AS BEGIN - --EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetLastTestNameIfNotProvided', @FakeFunctionName = 'AnnotationNoTransactionTests.PassThrough'; /* --<-- Prevent tSQLt-internal turmoil */ - --EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SaveTestNameForSession';/* --<-- Prevent tSQLt-internal turmoil */ - --EXEC tSQLt.DropClass 'MyInnerTests'; + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetLastTestNameIfNotProvided', @FakeFunctionName = 'AnnotationNoTransactionTests.PassThrough'; /* --<-- Prevent tSQLt-internal turmoil */ + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SaveTestNameForSession';/* --<-- Prevent tSQLt-internal turmoil */ + EXEC tSQLt.DropClass 'MyInnerTests'; -- EXEC ('CREATE SCHEMA MyInnerTests --AUTHORIZATION [tSQLt.TestClass];'); EXEC ('CREATE SCHEMA MyInnerTests;'); EXEC(' @@ -434,7 +434,7 @@ BEGIN CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT CAST(''Some obscure string'' AS INT); '); - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'IF(@FullTestName <> ''[MyInnerTests].[test should cause unrecoverable error]'')BEGIN EXEC tSQLt.Private_NoTransactionHandleTables @Action=''Reset'';EXEC tSQLt.UndoTestDoubles @Force = 0;END;'; EXEC tSQLt.SetSummaryError 0; --EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index d334f48d8..4c7fdb694 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -259,6 +259,25 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test can restore table with computed column] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, compcol AS UPPER(col1), col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES(1,'a'),(2,'bb'),(3,'cdce'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; + + SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test Reset @Action with unknown @TableAction causes error] AS BEGIN From a2da8ca500750f861825a2c2973fad99d2c69367 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 23 Nov 2021 23:39:18 -0500 Subject: [PATCH 053/119] Very important query for debugging also needs to be saved. --- Experiments/SM query.sql | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Experiments/SM query.sql b/Experiments/SM query.sql index 9c4f8e183..13d2094ca 100644 --- a/Experiments/SM query.sql +++ b/Experiments/SM query.sql @@ -1,5 +1,5 @@ SELECT - R.RowNumber, + R.EventSequence, TE.name, TSV.subclass_name, R.ObjectName, @@ -10,13 +10,13 @@ SELECT R.Error, R.Severity, R.NestLevel, + R.RowNumber, R.EventClass, R.EventSubClass, R.ApplicationName, R.ClientProcessID, R.DatabaseID, R.DatabaseName, - R.EventSequence, R.GroupID, R.HostName, R.IsSystem, @@ -51,11 +51,11 @@ SELECT R.OwnerID, R.ObjectID2, R.BigintData1 - FROM dbo.run1 R - JOIN sys.trace_events AS TE + FROM dbo.run4 R + LEFT JOIN sys.trace_events AS TE ON R.EventClass = TE.trace_event_id LEFT JOIN sys.trace_subclass_values AS TSV ON TSV.trace_event_id = TE.trace_event_id AND R.EventSubClass = TSV.subclass_value - WHERE R.ObjectName NOT IN ('Private_Print','sp_rename', 'sp_validname','GetTestResultFormatter') + WHERE ISNULL(R.ObjectName,'???') NOT IN ('Private_Print','sp_rename', 'sp_validname','GetTestResultFormatter','sp_addextendedproperty','sp_updateextendedproperty') ORDER BY R.EventSequence \ No newline at end of file From 3552020c05b79f3f437d267b926cd4f07f0fe3f0 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 24 Nov 2021 15:55:21 -0500 Subject: [PATCH 054/119] Fixed the RAISERROR for mismatched transaction counts; fixed the Private_NoTransactionHandleTable for computed columns aka compound columns --- Source/Run_Methods.sql | 2 +- Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql | 6 ++++-- Tests/Private_NoTransactionHandleTableTests.class.sql | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 1234aad06..fd27fb4ae 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -335,7 +335,7 @@ BEGIN --DECLARE @TempMsg333 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(333) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg333, 0,1) WITH NOWAIT; --XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- - IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %s <> %s))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); + IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); END; GO diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 7ad909617..5fd5ad057 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -70,8 +70,10 @@ BEGIN SET @cmd = @cmd + 'SET IDENTITY_INSERT ' + @FullTableName + ' ON;'; END; SET @cmd = @cmd + 'INSERT INTO ' + @FullTableName +'('; - SET @cmd = @cmd + STUFF((SELECT ','+QUOTENAME(name) FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) ORDER BY column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,''); - SET @cmd = @cmd + ') SELECT * FROM ' + (SELECT TableName FROM @BackupTableName)+';'; + DECLARE @ColumnList NVARCHAR(MAX) = STUFF((SELECT ','+QUOTENAME(name) FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) AND is_computed = 0 ORDER BY column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,''); + SET @cmd = @cmd + @ColumnList; + SET @cmd = @cmd + ') SELECT ' + @ColumnList + ' FROM ' + (SELECT TableName FROM @BackupTableName)+';'; + PRINT @cmd; EXEC(@cmd); END; COMMIT; diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 4c7fdb694..1625d95b0 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -262,13 +262,13 @@ GO CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test can restore table with computed column] AS BEGIN - CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, compcol AS UPPER(col1), col1 NVARCHAR(MAX)); - INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES(1,'a'),(2,'bb'),(3,'cdce'); + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, computedcolumn AS UPPER(col1), col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES(1,'a'),(2,'bb'),(3,'cdce'); EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Restore'; - - SELECT * INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; + + SELECT Id, col1 INTO #Actual FROM Private_NoTransactionHandleTableTests.Table1; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected VALUES(1, 'a'),(2, 'bb'),(3, 'cdce'); From 0c1fc8397f5682cb8e04922facf18639da0b8674 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 24 Nov 2021 17:13:34 -0500 Subject: [PATCH 055/119] Removed debugging statements; started working on code and tests required for passing a user defined CleanUp function as part of the NoAnnotation --- Source/Run_Methods.sql | 24 ------- Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql | 2 +- ...t.Private_NoTransactionHandleTable.ssp.sql | 13 ++-- ....Private_NoTransactionTableAction.view.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 62 +++++++++++++------ 5 files changed, 49 insertions(+), 54 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index fd27fb4ae..9ac9c5a9c 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -123,9 +123,6 @@ BEGIN BEGIN TRY IF(@SkipTestFlag = 0) BEGIN ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- ---DECLARE @TempMsg127 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(127) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg127, 0,1) WITH NOWAIT; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- IF (@SetUp IS NOT NULL) EXEC @SetUp; EXEC (@Cmd); @@ -248,9 +245,6 @@ BEGIN SET @Msg = ERROR_MESSAGE(); END CATCH ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- ---DECLARE @TempMsg249 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(249) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg249, 0,1) WITH NOWAIT; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran @@ -274,10 +268,6 @@ BEGIN END; END CATCH; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- ---DECLARE @TempMsg277 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(277) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg277, 0,1) WITH NOWAIT; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- - IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) BEGIN @@ -286,11 +276,6 @@ BEGIN SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); END; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- ---DECLARE @TempMsg289 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(289) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg289, 0,1) WITH NOWAIT; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- - - If(@Result NOT IN ('Success','Skipped')) BEGIN SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; @@ -315,11 +300,6 @@ BEGIN 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; END; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- ---DECLARE @TempMsg316 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(316) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg316, 0,1) WITH NOWAIT; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- - - IF(@TransactionStartedFlag = 1) BEGIN COMMIT; @@ -331,10 +311,6 @@ BEGIN EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; END; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- ---DECLARE @TempMsg333 NVARCHAR(MAX) = FORMATMESSAGE('tSQLt.Private_RunTest(333) - @TestName = %s, @NoTransactionFlag = %i, @SkipTestFlag = %i, @TransactionStartedFlag = %i, @Msg = %s, XACT_STATE = %i, SummaryError = %i',@TestName, CAST(@NoTransactionFlag AS INT), CAST(@SkipTestFlag AS INT), CAST(@TransactionStartedFlag AS INT), @Msg, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg333, 0,1) WITH NOWAIT; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- - IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); END; diff --git a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql index e6b6c10a0..670c36ad7 100644 --- a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql +++ b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql @@ -2,7 +2,7 @@ IF OBJECT_ID('tSQLt.[@tSQLt:NoTransaction]') IS NOT NULL DROP FUNCTION tSQLt.[@t GO ---Build+ GO -CREATE FUNCTION tSQLt.[@tSQLt:NoTransaction]() +CREATE FUNCTION tSQLt.[@tSQLt:NoTransaction](@CleanUpProcedureName NVARCHAR(MAX) = NULL) RETURNS TABLE AS RETURN diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index 5fd5ad057..c4b4270cf 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -8,10 +8,6 @@ CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTable @TableAction NVARCHAR(MAX) AS BEGIN ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- ---DECLARE @TempMsg12 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(12) - @Action = %s, @FullTableName = %s, @TableAction = %s, XACT_STATE = %i, SummaryError = %i', @Action, @FullTableName, @TableAction, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg12, 0,1) WITH NOWAIT; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- - DECLARE @cmd NVARCHAR(MAX); BEGIN TRY IF (OBJECT_ID(@FullTableName) IS NULL AND @TableAction <> 'Hide') @@ -58,10 +54,6 @@ BEGIN BEGIN TRAN; DECLARE @BackupTableName TABLE(TableName NVARCHAR(MAX)); DELETE FROM #TableBackupLog OUTPUT DELETED.BackupName INTO @BackupTableName WHERE OriginalName = @FullTableName; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- ---DECLARE @TempMsg58 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(58) - @BackupTableName = %s, @FullTableName = %s, XACT_STATE = %i, SummaryError = %i',(SELECT TableName FROM @BackupTableName), @FullTableName, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg58, 0,1) WITH NOWAIT; ---XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- - IF(EXISTS(SELECT 1 FROM @BackupTableName AS BTN)) BEGIN SET @cmd = 'DELETE FROM ' + @FullTableName + ';'; @@ -73,7 +65,6 @@ BEGIN DECLARE @ColumnList NVARCHAR(MAX) = STUFF((SELECT ','+QUOTENAME(name) FROM sys.columns WHERE object_id = OBJECT_ID(@FullTableName) AND is_computed = 0 ORDER BY column_id FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,''); SET @cmd = @cmd + @ColumnList; SET @cmd = @cmd + ') SELECT ' + @ColumnList + ' FROM ' + (SELECT TableName FROM @BackupTableName)+';'; - PRINT @cmd; EXEC(@cmd); END; COMMIT; @@ -106,3 +97,7 @@ BEGIN END CATCH; END; GO +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- +--DECLARE @TempMsg58 NVARCHAR(MAX) = FORMATMESSAGE('HandleTable(58) - @BackupTableName = %s, @FullTableName = %s, XACT_STATE = %i, SummaryError = %i',(SELECT TableName FROM @BackupTableName), @FullTableName, XACT_STATE(), CAST((SELECT PGC.Value FROM tSQLt.Private_GetConfiguration('SummaryError') AS PGC) AS INT));RAISERROR(@TempMsg58, 0,1) WITH NOWAIT; +--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX-- + diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index 7f5a3d82b..d22d1a517 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -11,6 +11,6 @@ SELECT * ('[tSQLt].[Private_Configurations]','Restore'), ('[tSQLt].[CaptureOutputLog]','Truncate'), ('[tSQLt].[Private_RenamedObjectLog]','Ignore'), - ('[tSQLt].[TestResult]','Ignore') + ('[tSQLt].[TestResult]','Restore') )X(Name, Action); GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 7323aac22..f084ad691 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -419,37 +419,22 @@ RETURN SELECT @TestName TestName GO --[@tSQLt:NoTransaction]() ---[@tSQLt:SkipTest]('') -/* This test must be NoTransaction because */ +/* This test must be NoTransaction because the inner test will invalidate any open transaction causing chaos and turmoil in the reactor. */ CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] AS BEGIN EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetLastTestNameIfNotProvided', @FakeFunctionName = 'AnnotationNoTransactionTests.PassThrough'; /* --<-- Prevent tSQLt-internal turmoil */ EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SaveTestNameForSession';/* --<-- Prevent tSQLt-internal turmoil */ - EXEC tSQLt.DropClass 'MyInnerTests'; --- EXEC ('CREATE SCHEMA MyInnerTests --AUTHORIZATION [tSQLt.TestClass];'); - EXEC ('CREATE SCHEMA MyInnerTests;'); + EXEC ('CREATE SCHEMA MyInnerTests AUTHORIZATION [tSQLt.TestClass];'); EXEC(' --[@'+'tSQLt:NoTransaction]() -CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS SELECT CAST(''Some obscure string'' AS INT); +CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS PRINT CAST(''Some obscure string'' AS INT); '); EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'IF(@FullTestName <> ''[MyInnerTests].[test should cause unrecoverable error]'')BEGIN EXEC tSQLt.Private_NoTransactionHandleTables @Action=''Reset'';EXEC tSQLt.UndoTestDoubles @Force = 0;END;'; EXEC tSQLt.SetSummaryError 0; - --EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; - - --DELETE FROM tSQLt.Private_RenamedObjectLog; - - --EXEC AnnotationNoTransactionTests.[Redact IsTestObject status on all objects]; - --BEGIN TRY - EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]'; - --END TRY - --BEGIN CATCH - -- /*-- more work todo --*/ - --END CATCH; - --EXEC AnnotationNoTransactionTests.[Restore IsTestObject status on all objects]; - --EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName='tSQLt.Private_RenamedObjectLog', @TableAction = 'Restore'; + EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; SELECT Name, Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; @@ -462,6 +447,39 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test calls user supplied clean up procedure after test completes] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp1] + AS + BEGIN + INSERT INTO #Actual VALUES (''UserCleanUp1''); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp1]'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + INSERT INTO #Actual VALUES (''test1''); + END; + '); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(1, 'test1'), (2, 'UserCleanUp1'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO @@ -469,12 +487,18 @@ GO CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) - there will be three clean up methods, executed in the following order - 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter +- 1.a [].[.CleanUp] +- 1.b --[@tSQLt:NoTransaction]('[].[]') +--[@tSQLt:NoTransaction](DEFAULT) - 2. User defined clean up for a test class as specified by [].CleanUp - 3. tSQLt.Private_CleanUp - Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error - If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. - appropriate error messages are appended to the test msg +- tSQLt.SpyProcedure needs a "call original" option +- test that the three cleanups are running in the correct order. might need ^^ to wrok. (duplicate of line 455) + Transactions - transaction opened during test - transaction commited during test From 5f4ae44ce2365dd6d57bbf6e912f5d2505394cbc Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 24 Nov 2021 19:46:47 -0500 Subject: [PATCH 056/119] {"Build": "Pass"} --- Tests/AnnotationNoTransactionTests.class.sql | 30 ++++++++++--------- ...te_NoTransactionTableActionTests.class.sql | 2 +- Tests/Run_Methods_Tests.class.sql | 10 +++---- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index f084ad691..6c558f612 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -7,7 +7,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS INSERT INTO #TranCount VALUES(''I'',@@TRANCOUNT);; '); @@ -38,7 +38,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN; '); @@ -54,7 +54,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RETURN; '); @@ -90,7 +90,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RETURN; '); @@ -111,7 +111,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS EXEC tSQLt.Fail ''Some Obscure Reason''; '); @@ -133,7 +133,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RAISERROR (''Some Obscure Recoverable Error'', 16, 10); '); @@ -155,7 +155,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test1] AS RETURN; '); EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; @@ -230,7 +230,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test1] AS PRINT 1/0; '); @@ -250,7 +250,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test1] AS RAISERROR('''',16,10); '); @@ -272,7 +272,7 @@ AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' - --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test1] AS BEGIN @@ -298,7 +298,7 @@ BEGIN CREATE TABLE #SkippedTestExecutionLog (Id INT); EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' - --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:NoTransaction](DEFAULT) --[@'+'tSQLt:SkipTest]('') CREATE PROCEDURE MyInnerTests.[skippedTest] AS @@ -342,7 +342,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' - --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:NoTransaction](DEFAULT) --[@'+'tSQLt:SkipTest]('''') CREATE PROCEDURE MyInnerTests.[test1] AS @@ -418,7 +418,8 @@ AS RETURN SELECT @TestName TestName GO ---[@tSQLt:NoTransaction]() +--[@tSQLt:NoTransaction](DEFAULT) +--[@tSQLt:SkipTest]('') /* This test must be NoTransaction because the inner test will invalidate any open transaction causing chaos and turmoil in the reactor. */ CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] AS @@ -427,7 +428,7 @@ BEGIN EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_SaveTestNameForSession';/* --<-- Prevent tSQLt-internal turmoil */ EXEC ('CREATE SCHEMA MyInnerTests AUTHORIZATION [tSQLt.TestClass];'); EXEC(' ---[@'+'tSQLt:NoTransaction]() +--[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS PRINT CAST(''Some obscure string'' AS INT); '); @@ -447,6 +448,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +--[@tSQLt:SkipTest]('') CREATE PROCEDURE AnnotationNoTransactionTests.[test calls user supplied clean up procedure after test completes] AS BEGIN diff --git a/Tests/Private_NoTransactionTableActionTests.class.sql b/Tests/Private_NoTransactionTableActionTests.class.sql index 2634c80cc..76a7aa2f8 100644 --- a/Tests/Private_NoTransactionTableActionTests.class.sql +++ b/Tests/Private_NoTransactionTableActionTests.class.sql @@ -25,7 +25,7 @@ BEGIN SELECT '[tSQLt].[Private_Configurations]', 'Restore' UNION ALL SELECT '[tSQLt].[CaptureOutputLog]', 'Truncate' UNION ALL SELECT '[tSQLt].[Private_RenamedObjectLog]','Ignore' UNION ALL - SELECT '[tSQLt].[TestResult]', 'Ignore'; + SELECT '[tSQLt].[TestResult]', 'Restore'; EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO \ No newline at end of file diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 17e1f908f..4cf2310b2 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2240,7 +2240,7 @@ BEGIN CREATE TABLE #Actual (Id INT); EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); EXEC (' - --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; '); @@ -2264,7 +2264,7 @@ BEGIN CREATE TABLE #Actual (Id INT); EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); EXEC (' - --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; '); @@ -2285,7 +2285,7 @@ BEGIN CREATE TABLE #Actual (Id INT); EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); EXEC (' - --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; '); @@ -2306,7 +2306,7 @@ BEGIN CREATE TABLE #Actual (Id INT); EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); EXEC (' - --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; '); @@ -2327,7 +2327,7 @@ BEGIN CREATE TABLE #Actual (Id INT); EXEC ('CREATE SCHEMA [a class with a '' in the middle];'); EXEC (' - --[@'+'tSQLt:NoTransaction]() + --[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE [a class with a '' in the middle].[test with a '' in the middle] AS BEGIN INSERT INTO #Actual VALUES (1); END; '); EXEC tSQLt.FakeTable @TableName = 'tSQLt.TestClasses'; From a3eca1b2c6b9bb37b4db730164902b08a2c9c3da Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:27:43 -0500 Subject: [PATCH 057/119] {"Build":"Fail"} Working on passing in the @NoTransactionTestCleanUpProcedureName! yay. --- Source/Run_Methods.sql | 8 ++- Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql | 5 +- Tests/AnnotationNoTransactionTests.class.sql | 63 +++++++++++++++++++- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 9ac9c5a9c..439d96465 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -62,7 +62,7 @@ BEGIN CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); - CREATE TABLE #NoTransaction(X INT); + CREATE TABLE #NoTransaction(CleanUpProcedureName NVARCHAR(MAX)); CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); @@ -93,6 +93,7 @@ BEGIN SET @Result = 'Success'; DECLARE @SkipTestFlag BIT = 0; DECLARE @NoTransactionFlag BIT = 0; + DECLARE @NoTransactionTestCleanUpProcedureName NVARCHAR(MAX) = NULL; DECLARE @TransactionStartedFlag BIT = 0; BEGIN TRY @@ -270,6 +271,11 @@ BEGIN IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) BEGIN + SET @NoTransactionTestCleanUpProcedureName = ( + SELECT 'EXEC '+ NT.CleanUpProcedureName + FROM #NoTransaction NT + ); + EXEC(@NoTransactionTestCleanUpProcedureName); DECLARE @CleanUpErrorMsg NVARCHAR(MAX); EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; diff --git a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql index 670c36ad7..ac5de4eb6 100644 --- a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql +++ b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql @@ -6,7 +6,10 @@ CREATE FUNCTION tSQLt.[@tSQLt:NoTransaction](@CleanUpProcedureName NVARCHAR(MAX) RETURNS TABLE AS RETURN - SELECT 'INSERT INTO #NoTransaction DEFAULT VALUES;' AS AnnotationCmd; + SELECT + 'IF(OBJECT_ID('+X.QuotedName+') IS NULL) BEGIN RAISERROR(''sss'',16,10); END;'+ + 'INSERT INTO #NoTransaction VALUES('+X.QuotedName+');' AS AnnotationCmd + FROM (VALUES(''''+REPLACE(@CleanUpProcedureName,'''','''''')+''''))X(QuotedName); GO ---Build- GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 6c558f612..75989812e 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -448,7 +448,6 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('') CREATE PROCEDURE AnnotationNoTransactionTests.[test calls user supplied clean up procedure after test completes] AS BEGIN @@ -482,6 +481,62 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test calls user supplied clean up procedure if it has a single quote in its name] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserClean''Up1] + AS + BEGIN + INSERT INTO #Actual VALUES (''UserClean''''Up1''); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserClean''''Up1]'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN + END; + '); + + CREATE TABLE #Actual (col1 NVARCHAR(MAX)); + + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('UserClean''Up1'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test throw appropriate error if specified TestCleanUpProcedure does not exist] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaXction](''[MyInnerTests].[UserCleanUpDoesNotExist]'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN + END; + '); + + EXEC tSQLt.ExpectException @ExpectedMessage = 'Some error goes here.', @ExpectedSeverity = 16, @ExpectedState = 10; + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + --needs to check TestResult instead of tee because it is an inner test. + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO @@ -498,6 +553,12 @@ GO - If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. - appropriate error messages are appended to the test msg +- handle multiple TestCleanUpProcedures +- ? handle TestCleanUpProcedures with ' in name +- error in annotation if specified TestCleanUpProcedure does not exist +- error in annotation if specified TestCleanUpProcedure is not a procedure (any of the 4ish types) + + - tSQLt.SpyProcedure needs a "call original" option - test that the three cleanups are running in the correct order. might need ^^ to wrok. (duplicate of line 455) From 83366c08a5bb86eb6f98a51338a95c7862412502 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 24 Nov 2021 23:14:54 -0500 Subject: [PATCH 058/119] {"Build":"Pass"} Created a new CLR stored procedure in testutil; implemented test clean up stored procedure for #NoTransaction annotation; more tests needed. --- Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql | 7 +- TestUtil/tSQLtTestUtilCLR_CreateItems.sql | 3 + Tests/AnnotationNoTransactionTests.class.sql | 82 ++++++++++++++++--- .../tSQLtTestUtilCLR/ClrStoredProcedures.cs | 15 ++++ .../tSQLtTestUtilCLR/tSQLtTestUtilCLR.csproj | 1 + 5 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 tSQLtCLR/tSQLtTestUtilCLR/ClrStoredProcedures.cs diff --git a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql index ac5de4eb6..60bb2c6af 100644 --- a/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql +++ b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql @@ -7,8 +7,11 @@ RETURNS TABLE AS RETURN SELECT - 'IF(OBJECT_ID('+X.QuotedName+') IS NULL) BEGIN RAISERROR(''sss'',16,10); END;'+ - 'INSERT INTO #NoTransaction VALUES('+X.QuotedName+');' AS AnnotationCmd + CASE + WHEN (X.QuotedName IS NULL) + THEN 'INSERT INTO #NoTransaction VALUES(NULL);' + ELSE 'IF(NOT EXISTS (SELECT 1 FROM sys.procedures WHERE object_id = OBJECT_ID('+X.QuotedName+'))) BEGIN RAISERROR(''Test CleanUp Procedure %s does not exist or is not a procedure.'',16,10,'+X.QuotedName+'); END;INSERT INTO #NoTransaction VALUES('+X.QuotedName+');' + END AS AnnotationCmd FROM (VALUES(''''+REPLACE(@CleanUpProcedureName,'''','''''')+''''))X(QuotedName); GO ---Build- diff --git a/TestUtil/tSQLtTestUtilCLR_CreateItems.sql b/TestUtil/tSQLtTestUtilCLR_CreateItems.sql index 300b8dcea..143ec61ef 100644 --- a/TestUtil/tSQLtTestUtilCLR_CreateItems.sql +++ b/TestUtil/tSQLtTestUtilCLR_CreateItems.sql @@ -24,3 +24,6 @@ GO CREATE FUNCTION tSQLt_testutil.AnEmptyClrTvf(@p1 NVARCHAR(MAX), @p2 NVARCHAR(MAX))RETURNS TABLE(id INT, val NVARCHAR(MAX)) AS EXTERNAL NAME tSQLtTestUtilCLR.[tSQLtTestUtilCLR.ClrFunctions].AnEmptyClrTvf; GO +CREATE PROCEDURE tSQLt_testutil.AClrSsp + AS EXTERNAL NAME tSQLtTestUtilCLR.[tSQLtTestUtilCLR.ClrStoredProcedures].AClrSsp; +GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 75989812e..de2faa20c 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -418,8 +418,13 @@ AS RETURN SELECT @TestName TestName GO ---[@tSQLt:NoTransaction](DEFAULT) ---[@tSQLt:SkipTest]('') +CREATE PROCEDURE AnnotationNoTransactionTests.[CLEANUP: test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] +AS +BEGIN + EXEC tSQLt.DropClass MyInnerTests; +END; +GO +--[@tSQLt:NoTransaction]('AnnotationNoTransactionTests.[CLEANUP: test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table]') /* This test must be NoTransaction because the inner test will invalidate any open transaction causing chaos and turmoil in the reactor. */ CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] AS @@ -514,12 +519,12 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test throw appropriate error if specified TestCleanUpProcedure does not exist] +CREATE PROCEDURE AnnotationNoTransactionTests.[test throws appropriate error if specified TestCleanUpProcedure does not exist] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' - --[@'+'tSQLt:NoTransaXction](''[MyInnerTests].[UserCleanUpDoesNotExist]'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUpDoesNotExist]'') CREATE PROCEDURE MyInnerTests.[test1] AS BEGIN @@ -527,36 +532,89 @@ BEGIN END; '); - EXEC tSQLt.ExpectException @ExpectedMessage = 'Some error goes here.', @ExpectedSeverity = 16, @ExpectedState = 10; + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; - EXEC tSQLt.Run 'MyInnerTests.[test1]'; + SELECT TR.Name, + TR.Result, + TR.Msg + INTO #Actual + FROM tSQLt.TestResult AS TR + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES ( + '[MyInnerTests].[test1]', + 'Error', + 'There is a problem with this annotation: [@tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUpDoesNotExist]'') +Original Error: {16,10;(null)} Test CleanUp Procedure [MyInnerTests].[UserCleanUpDoesNotExist] does not exist or is not a procedure.') + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test throws appropriate error if specified TestCleanUpProcedure is not a procedure] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE VIEW [MyInnerTests].[NotAProcedure] AS SELECT 1 X;'); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[NotAProcedure]'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN + END; + '); - --needs to check TestResult instead of tee because it is an inner test. + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + DECLARE @Actual NVARCHAR(MAX) = (SELECT Msg FROM tSQLt.TestResult AS TR); + EXEC tSQLt.AssertLike @ExpectedPattern = '%Test CleanUp Procedure [[]MyInnerTests].[[]NotAProcedure] does not exist or is not a procedure.', @Actual = @Actual; END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test does not throw error if specified TestCleanUpProcedure is a CLR stored procedure] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction](''tSQLt_testutil.AClrSsp'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN + END; + '); + EXEC tSQLt.SetSummaryError @SummaryError = 1; + EXEC tSQLt.ExpectNoException; + + EXEC tSQLt.Run 'MyInnerTests.[test1]'--, @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) - there will be three clean up methods, executed in the following order -- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter -- 1.a [].[.CleanUp] -- 1.b --[@tSQLt:NoTransaction]('[].[]') +- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter "--[@tSQLt:NoTransaction]('[].[]')" --[@tSQLt:NoTransaction](DEFAULT) - 2. User defined clean up for a test class as specified by [].CleanUp - 3. tSQLt.Private_CleanUp - Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error - If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. - appropriate error messages are appended to the test msg +- If a test errors (even catastrophically), all indicated CleanUp procedures run. - handle multiple TestCleanUpProcedures - ? handle TestCleanUpProcedures with ' in name -- error in annotation if specified TestCleanUpProcedure does not exist -- error in annotation if specified TestCleanUpProcedure is not a procedure (any of the 4ish types) +- ? error in annotation if specified TestCleanUpProcedure does not exist +- ? error in annotation if specified TestCleanUpProcedure is not a procedure (any of the 4ish types) - tSQLt.SpyProcedure needs a "call original" option diff --git a/tSQLtCLR/tSQLtTestUtilCLR/ClrStoredProcedures.cs b/tSQLtCLR/tSQLtTestUtilCLR/ClrStoredProcedures.cs new file mode 100644 index 000000000..cf36112dc --- /dev/null +++ b/tSQLtCLR/tSQLtTestUtilCLR/ClrStoredProcedures.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace tSQLtTestUtilCLR +{ + public class ClrStoredProcedures + { + public static void AClrSsp() + { + return; + } + + } +} diff --git a/tSQLtCLR/tSQLtTestUtilCLR/tSQLtTestUtilCLR.csproj b/tSQLtCLR/tSQLtTestUtilCLR/tSQLtTestUtilCLR.csproj index 100270f54..977061726 100644 --- a/tSQLtCLR/tSQLtTestUtilCLR/tSQLtTestUtilCLR.csproj +++ b/tSQLtCLR/tSQLtTestUtilCLR/tSQLtTestUtilCLR.csproj @@ -55,6 +55,7 @@ + From ae923d71db1a3e0277897094b41b71abd56f88a1 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 25 Nov 2021 22:44:09 -0500 Subject: [PATCH 059/119] {"Build":"Pass"} Successfully working on the test clean up and schema clean up stored procedures. --- Source/Run_Methods.sql | 44 ++++-- Tests/AnnotationNoTransactionTests.class.sql | 150 ++++++++++++++++++- 2 files changed, 174 insertions(+), 20 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 439d96465..9c8b138b4 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -1,4 +1,4 @@ -IF OBJECT_ID('tSQLt.Private_GetSetupProcedureName') IS NOT NULL DROP PROCEDURE tSQLt.Private_GetSetupProcedureName; +IF OBJECT_ID('tSQLt.Private_GetClassHelperProcedureName') IS NOT NULL DROP PROCEDURE tSQLt.Private_GetClassHelperProcedureName; IF OBJECT_ID('tSQLt.Private_RunTest') IS NOT NULL DROP PROCEDURE tSQLt.Private_RunTest; IF OBJECT_ID('tSQLt.Private_RunTestClass') IS NOT NULL DROP PROCEDURE tSQLt.Private_RunTestClass; IF OBJECT_ID('tSQLt.Private_Run') IS NOT NULL DROP PROCEDURE tSQLt.Private_Run; @@ -24,21 +24,27 @@ IF OBJECT_ID('tSQLt.Private_PrepareTestResultForOutput') IS NOT NULL DROP FUNCTI GO ---Build+ -CREATE PROCEDURE tSQLt.Private_GetSetupProcedureName +CREATE PROCEDURE tSQLt.Private_GetClassHelperProcedureName @TestClassId INT = NULL, - @SetupProcName NVARCHAR(MAX) OUTPUT + @SetupProcName NVARCHAR(MAX) OUTPUT, + @CleanUpProcName NVARCHAR(MAX) OUTPUT AS BEGIN SELECT @SetupProcName = tSQLt.Private_GetQuotedFullName(object_id) FROM sys.procedures WHERE schema_id = @TestClassId AND LOWER(name) = 'setup'; + SELECT @CleanUpProcName = tSQLt.Private_GetQuotedFullName(object_id) + FROM sys.procedures + WHERE schema_id = @TestClassId + AND LOWER(name) = 'cleanup'; END; GO CREATE PROCEDURE tSQLt.Private_RunTest @TestName NVARCHAR(MAX), - @SetUp NVARCHAR(MAX) = NULL + @SetUp NVARCHAR(MAX) = NULL, + @CleanUp NVARCHAR(MAX) = NULL AS BEGIN DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; @@ -62,7 +68,7 @@ BEGIN CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); - CREATE TABLE #NoTransaction(CleanUpProcedureName NVARCHAR(MAX)); + CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); @@ -124,7 +130,10 @@ BEGIN BEGIN TRY IF(@SkipTestFlag = 0) BEGIN - IF (@SetUp IS NOT NULL) EXEC @SetUp; + IF (@SetUp IS NOT NULL) + BEGIN + EXEC @SetUp; + END; EXEC (@Cmd); --TODO:NoTran @@ -272,11 +281,20 @@ BEGIN IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) BEGIN SET @NoTransactionTestCleanUpProcedureName = ( - SELECT 'EXEC '+ NT.CleanUpProcedureName - FROM #NoTransaction NT + ( + SELECT 'EXEC '+ NT.CleanUpProcedureName +';' + FROM #NoTransaction NT + ORDER BY OrderId + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)') ); EXEC(@NoTransactionTestCleanUpProcedureName); + IF(@CleanUp IS NOT NULL) + BEGIN + EXEC @CleanUp; + END; + DECLARE @CleanUpErrorMsg NVARCHAR(MAX); EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); @@ -329,11 +347,12 @@ BEGIN DECLARE @TestCaseName NVARCHAR(MAX); DECLARE @TestClassId INT; SET @TestClassId = tSQLt.Private_GetSchemaId(@TestClassName); DECLARE @SetupProcName NVARCHAR(MAX); - EXEC tSQLt.Private_GetSetupProcedureName @TestClassId, @SetupProcName OUTPUT; + DECLARE @CleanUpProcName NVARCHAR(MAX); + EXEC tSQLt.Private_GetClassHelperProcedureName @TestClassId, @SetupProcName OUT, @CleanUpProcName OUT; DECLARE @cmd NVARCHAR(MAX) = ( ( - SELECT 'EXEC tSQLt.Private_RunTest '''+REPLACE(tSQLt.Private_GetQuotedFullName(object_id),'''','''''')+''', '+ISNULL(''''+REPLACE(@SetupProcName,'''','''''')+'''','NULL')+';' + SELECT 'EXEC tSQLt.Private_RunTest '''+REPLACE(tSQLt.Private_GetQuotedFullName(object_id),'''','''''')+''', '+ISNULL(''''+REPLACE(@SetupProcName,'''','''''')+'''','NULL')+', '+ISNULL(''''+REPLACE(@CleanUpProcName,'''','''''')+'''','NULL')+';' FROM sys.procedures WHERE schema_id = @TestClassId AND LOWER(name) LIKE 'test%' @@ -376,9 +395,10 @@ SET NOCOUNT ON; IF @IsTestCase = 1 BEGIN DECLARE @SetupProcName NVARCHAR(MAX); - EXEC tSQLt.Private_GetSetupProcedureName @TestClassId, @SetupProcName OUTPUT; + DECLARE @CleanUpProcName NVARCHAR(MAX); + EXEC tSQLt.Private_GetClassHelperProcedureName @TestClassId, @SetupProcName OUT, @CleanUpProcName OUT; - EXEC tSQLt.Private_RunTest @FullName, @SetupProcName; + EXEC tSQLt.Private_RunTest @FullName, @SetupProcName, @CleanUpProcName; END; EXEC tSQLt.Private_OutputTestResults @TestResultFormatter; diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index de2faa20c..892f1c49f 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -597,28 +597,160 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test executes multiple TestCleanUpProcedure in the order they are specified] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp7] AS BEGIN INSERT INTO #Actual VALUES (''CleanUp7''); END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp3] AS BEGIN INSERT INTO #Actual VALUES (''CleanUp3''); END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp9] AS BEGIN INSERT INTO #Actual VALUES (''CleanUp9''); END;'); + EXEC(' + --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUp7'') + --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUp3'') + --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUp9'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + INSERT INTO #Actual VALUES (''test1''); + END; + '); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(1, 'test1'), (2, 'CleanUp7'),(3, 'CleanUp3'), (4, 'CleanUp9'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test executes schema CleanUp after test CleanUp] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE [MyInnerTests].[TestCleanUp] AS BEGIN INSERT INTO #Actual VALUES (''TestCleanUp''); END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp] AS BEGIN INSERT INTO #Actual VALUES (''(Schema)CleanUp''); END;'); + EXEC(' + --[@'+'tSQLt:NoTransaction](''MyInnerTests.TestCleanUp'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + INSERT INTO #Actual VALUES (''test1''); + END; + '); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(1, 'test1'), (2, 'TestCleanUp'),(3, '(Schema)CleanUp'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test executes schema CleanUp only if it is a stored procedure] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE VIEW [MyInnerTests].[CleanUp] AS SELECT 1 X;'); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.ExpectNoException; + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test executes schema CleanUp if schema name contains single quote] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInner''Tests' + EXEC('CREATE PROCEDURE [MyInner''Tests].[CleanUp] AS BEGIN INSERT INTO #Actual VALUES (''[MyInner''''Tests].[CleanUp]''); END;'); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInner''Tests].[test1] + AS + BEGIN + RETURN; + END; + '); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + + EXEC tSQLt.Run '[MyInner''Tests].[test1]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(1, '[MyInner''Tests].[CleanUp]'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test executes schema CleanUp even if name is differently cased] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE [MyInnerTests].[clEaNuP] AS BEGIN INSERT INTO #Actual VALUES (''[MyInnerTests].[clEaNuP]''); END;'); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + + EXEC tSQLt.Run '[MyInnerTests].[test1]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(1, '[MyInnerTests].[clEaNuP]'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) - there will be three clean up methods, executed in the following order -- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter "--[@tSQLt:NoTransaction]('[].[]')" ---[@tSQLt:NoTransaction](DEFAULT) +- X 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter +-- X "--[@tSQLt:NoTransaction]('[].[]')" +-- X --[@tSQLt:NoTransaction](DEFAULT) - 2. User defined clean up for a test class as specified by [].CleanUp +-- X ' in schema name +-- X different case for cLEANuP - 3. tSQLt.Private_CleanUp - Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error - If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. - appropriate error messages are appended to the test msg - If a test errors (even catastrophically), all indicated CleanUp procedures run. -- handle multiple TestCleanUpProcedures -- ? handle TestCleanUpProcedures with ' in name -- ? error in annotation if specified TestCleanUpProcedure does not exist -- ? error in annotation if specified TestCleanUpProcedure is not a procedure (any of the 4ish types) +- X handle multiple TestCleanUpProcedures +- X handle TestCleanUpProcedures with ' in name +- X error in annotation if specified TestCleanUpProcedure does not exist +- X error in annotation if specified TestCleanUpProcedure is not a procedure (any of the 4ish types) + -- tSQLt.SpyProcedure needs a "call original" option -- test that the three cleanups are running in the correct order. might need ^^ to wrok. (duplicate of line 455) Transactions - transaction opened during test @@ -638,5 +770,7 @@ Everything is being called in the right order. - test for execution in the correct place in Private_RunTest, after the outer-most test execution try catch - Make sure undotestdoubles and handletables are called in the right order +- tSQLt.SpyProcedure needs a "call original" option +- What happens when we have multiple annotations for other non-NoTransaction annotations? Did we test this??? --*/ \ No newline at end of file From f9d9192725d35232a0a60d7c3c02e1c071c62f86 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 25 Nov 2021 22:56:31 -0500 Subject: [PATCH 060/119] Wrote a failing test --- Tests/AnnotationNoTransactionTests.class.sql | 32 ++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 892f1c49f..404cfc6b9 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -727,6 +727,38 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test error in test CleanUp procedure causes test result to be Error] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp1] + AS + BEGIN + RAISERROR(''This is an error ;)'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp1]'') + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT TR.Name, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('[MyInnerTests].[test1]','Error'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO From 400c515f87b538f8c73c20c96189fe66159d0c5d Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 26 Nov 2021 11:06:27 -0500 Subject: [PATCH 061/119] Wrote more tests. --- Source/Run_Methods.sql | 10 ++- Tests/AnnotationNoTransactionTests.class.sql | 71 ++++++++++++++++++-- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 9c8b138b4..946e97606 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -273,7 +273,7 @@ BEGIN OR @PostExecTrancount <> 0 ) BEGIN - SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + COALESCE(ERROR_MESSAGE(), '') + '{' + COALESCE(ERROR_PROCEDURE(), '') + ',' + COALESCE(CAST(ERROR_LINE() AS NVARCHAR), '') + '})'; + SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + COALESCE(ERROR_MESSAGE(), '') + '{' + COALESCE(ERROR_PROCEDURE(), '') + ',' + COALESCE(CAST(ERROR_LINE() AS NVARCHAR(MAX)), '') + '})'; SET @Result = 'Error'; END; END CATCH; @@ -292,7 +292,13 @@ BEGIN IF(@CleanUp IS NOT NULL) BEGIN - EXEC @CleanUp; + BEGIN TRY + EXEC @CleanUp; + END TRY + BEGIN CATCH + SET @Result = 'Error'; + SET @Msg = 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ERROR_PROCEDURE() + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; + END CATCH; END; DECLARE @CleanUpErrorMsg NVARCHAR(MAX); diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 404cfc6b9..4ac38e2b7 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -727,19 +727,19 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test error in test CleanUp procedure causes test result to be Error] +CREATE PROCEDURE AnnotationNoTransactionTests.[test error in schema CleanUp procedure causes test result to be Error] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' - CREATE PROCEDURE [MyInnerTests].[UserCleanUp1] + CREATE PROCEDURE [MyInnerTests].[CleanUp] AS BEGIN RAISERROR(''This is an error ;)'',16,10); END; '); EXEC(' - --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp1]'') + --[@'+'tSQLt:NoTransaction](DEFAULT) CREATE PROCEDURE [MyInnerTests].[test1] AS BEGIN @@ -759,6 +759,65 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test writes an appropriate message to the tSQLt.TestResult table if schema CleanUp errors] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[CleanUp] + AS + BEGIN + RAISERROR(''This is an error ;)'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); + + EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is an error ;) | Procedure: [MyInnerTests].[CleanUp] | Line: 4 | Severity, State: 16, 10)', @Actual = @FriendlyMsg; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test writes an appropriate message to the tSQLt.TestResult table if schema CleanUp has a different error] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyOtherInnerTests' + EXEC(' + CREATE PROCEDURE [MyOtherInnerTests].[CleanUp] + AS + BEGIN + /*wasting lines...*/ + RAISERROR(''This is another error ;)'',15,12); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyOtherInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.Run 'MyOtherInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); + + EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is another error ;) | Procedure: MyOtherInnerTests.CleanUp | Line: 6 | Severity, State: 15, 12)', @Actual = @FriendlyMsg; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO @@ -767,9 +826,12 @@ GO - X 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter -- X "--[@tSQLt:NoTransaction]('[].[]')" -- X --[@tSQLt:NoTransaction](DEFAULT) + - 2. User defined clean up for a test class as specified by [].CleanUp -- X ' in schema name -- X different case for cLEANuP +-- If the ERROR_PROCEDURE is somehow returning null, we still get the rest of the error message + - 3. tSQLt.Private_CleanUp - Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error - If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. @@ -782,8 +844,6 @@ GO - X error in annotation if specified TestCleanUpProcedure is not a procedure (any of the 4ish types) - - Transactions - transaction opened during test - transaction commited during test @@ -804,5 +864,6 @@ Everything is being called in the right order. - tSQLt.SpyProcedure needs a "call original" option - What happens when we have multiple annotations for other non-NoTransaction annotations? Did we test this??? +- Simulate Clippy if someone tries to use AssertEquals instead of AssertEqualsString --*/ \ No newline at end of file From 8e36e46b22e0bdebf8fd76f02d7bd67dc6446337 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 26 Nov 2021 15:18:16 -0500 Subject: [PATCH 062/119] {"Build":"Pass"} More tests. --- Source/Run_Methods.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 66 +++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 946e97606..8516217d7 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -297,7 +297,7 @@ BEGIN END TRY BEGIN CATCH SET @Result = 'Error'; - SET @Msg = 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ERROR_PROCEDURE() + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; + SET @Msg = (CASE WHEN @Msg <> '' THEN @Msg + ' || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; END CATCH; END; diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 4ac38e2b7..98f8885ce 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -783,7 +783,7 @@ BEGIN DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); - EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is an error ;) | Procedure: [MyInnerTests].[CleanUp] | Line: 4 | Severity, State: 16, 10)', @Actual = @FriendlyMsg; + EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is an error ;) | Procedure: MyInnerTests.CleanUp | Line: 5 | Severity, State: 16, 10)', @Actual = @FriendlyMsg; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -818,6 +818,66 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test writes an appropriate message to the tSQLt.TestResult if the ERROR_PROCEDURE for the schema CleanUp error is null] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyOtherInnerTests' + EXEC(' + CREATE PROCEDURE [MyOtherInnerTests].[CleanUp] + AS + BEGIN + /*wasting lines...*/ + EXEC(''RAISERROR(''''This is another error ;)'''',15,12)''); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyOtherInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.Run 'MyOtherInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); + + EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is another error ;) | Procedure: | Line: 1 | Severity, State: 15, 12)', @Actual = @FriendlyMsg; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test appends message to any test error if schema CleanUp errors] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[CleanUp] + AS + BEGIN + RAISERROR(''This is a CleanUp error ;)'',15,12); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RAISERROR(''This is a Test error ;)'',16,10); + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); + + EXEC tSQLt.AssertLike @ExpectedPattern = '%This is a Test error ;)% || %This is a CleanUp error ;)%', @Actual = @FriendlyMsg; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + /*-- TODO @@ -830,13 +890,14 @@ GO - 2. User defined clean up for a test class as specified by [].CleanUp -- X ' in schema name -- X different case for cLEANuP --- If the ERROR_PROCEDURE is somehow returning null, we still get the rest of the error message +-- X If the ERROR_PROCEDURE is somehow returning null, we still get the rest of the error message - 3. tSQLt.Private_CleanUp - Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error - If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. - appropriate error messages are appended to the test msg - If a test errors (even catastrophically), all indicated CleanUp procedures run. +- Unify Error Message Generation Across all code - X handle multiple TestCleanUpProcedures - X handle TestCleanUpProcedures with ' in name @@ -865,5 +926,6 @@ Everything is being called in the right order. - tSQLt.SpyProcedure needs a "call original" option - What happens when we have multiple annotations for other non-NoTransaction annotations? Did we test this??? - Simulate Clippy if someone tries to use AssertEquals instead of AssertEqualsString +- add 100x'=' + test status (if not PASS) followed by empty line after test-end message (if verbose) --*/ \ No newline at end of file From 06323ad877007bb8624df5ed996411c294190fe5 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 28 Nov 2021 22:38:07 -0500 Subject: [PATCH 063/119] Cleaned up some test names; wrote more tests; made one pass (but not the build). --- Source/Run_Methods.sql | 11 +- Tests/AnnotationNoTransactionTests.class.sql | 285 +++++++++++++++++-- Tests/_ExploratoryTests.class.sql | 10 + 3 files changed, 278 insertions(+), 28 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 8516217d7..91adf0e43 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -288,7 +288,16 @@ BEGIN FOR XML PATH(''),TYPE ).value('.','NVARCHAR(MAX)') ); - EXEC(@NoTransactionTestCleanUpProcedureName); + IF(@NoTransactionTestCleanUpProcedureName IS NOT NULL) + BEGIN + BEGIN TRY + EXEC(@NoTransactionTestCleanUpProcedureName); + END TRY + BEGIN CATCH + SET @Result = 'Error'; + SET @Msg = (CASE WHEN @Msg <> '' THEN @Msg + ' || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; + END CATCH; + END; IF(@CleanUp IS NOT NULL) BEGIN diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 98f8885ce..8caf489b4 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -150,7 +150,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test calls tSQLt.Private_CleanUp] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is executed] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -174,7 +174,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and succeeding] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if not annotated and succeeding] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -191,7 +191,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and failing] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if not annotated and failing] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -208,7 +208,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test does not call tSQLt.Private_CleanUp if not annotated and erroring] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if not annotated and erroring] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -225,7 +225,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test message returned by tSQLt.Private_CleanUp is appended to tSQLt.TestResult.Msg] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp error message is appended to tSQLt.TestResult.Msg] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -245,7 +245,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test message returned by tSQLt.Private_CleanUp is called before the test result message is printed] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp error message is called before the test result message is printed] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -453,7 +453,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test calls user supplied clean up procedure after test completes] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed after test completes] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -486,7 +486,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test calls user supplied clean up procedure if it has a single quote in its name] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed even if it has a single quote in its name] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -519,7 +519,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test throws appropriate error if specified TestCleanUpProcedure does not exist] +CREATE PROCEDURE AnnotationNoTransactionTests.[test annotation throws appropriate error if specified Test-CleanUp does not exist] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -554,7 +554,8 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test throws appropriate error if specified TestCleanUpProcedure is not a procedure] +CREATE PROCEDURE AnnotationNoTransactionTests.[test annotation throws appropriate error if specified Test-CleanUp is not a procedure] + AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -576,7 +577,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test does not throw error if specified TestCleanUpProcedure is a CLR stored procedure] +CREATE PROCEDURE AnnotationNoTransactionTests.[test annotation does not throw error if specified Test-CleanUp is a CLR stored procedure] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -597,7 +598,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test executes multiple TestCleanUpProcedure in the order they are specified] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt executes multiple Test-CleanUp in the order they are specified] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -627,7 +628,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test executes schema CleanUp after test CleanUp] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed after Test-CleanUp] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -654,7 +655,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test executes schema CleanUp only if it is a stored procedure] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed only if it is a stored procedure] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -675,7 +676,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test executes schema CleanUp if schema name contains single quote] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed if schema name contains single quote] AS BEGIN EXEC tSQLt.NewTestClass 'MyInner''Tests' @@ -701,7 +702,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test executes schema CleanUp even if name is differently cased] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed even if name is differently cased] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -727,7 +728,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test error in schema CleanUp procedure causes test result to be Error] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes test result to be Error] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -759,7 +760,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test writes an appropriate message to the tSQLt.TestResult table if schema CleanUp errors] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes an appropriate message to be written to the tSQLt.TestResult table] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -788,7 +789,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test writes an appropriate message to the tSQLt.TestResult table if schema CleanUp has a different error] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes an appropriate message to be written to the tSQLt.TestResult if there is a different error] AS BEGIN EXEC tSQLt.NewTestClass 'MyOtherInnerTests' @@ -818,7 +819,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test writes an appropriate message to the tSQLt.TestResult if the ERROR_PROCEDURE for the schema CleanUp error is null] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes an appropriate message to be written to tSQLt.TestResult even if ERROR_PROCEDURE is null] AS BEGIN EXEC tSQLt.NewTestClass 'MyOtherInnerTests' @@ -848,7 +849,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test appends message to any test error if schema CleanUp errors] +CREATE PROCEDURE AnnotationNoTransactionTests.[test appends message to any test error if Schema-CleanUp errors] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -877,17 +878,247 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed even if the test errors] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp1] + AS + BEGIN + INSERT INTO #Actual VALUES (''UserCleanUp1''); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp1]'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + INSERT INTO #Actual VALUES (''test1''); + END; + '); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + + + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(1, 'test1'), (2, 'UserCleanUp1'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes failing test to be set to Error] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[CleanUp] + AS + BEGIN + RAISERROR(''This is an error ;)'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + EXEC tSQLt.Fail; + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT TR.Name, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('[MyInnerTests].[test1]','Error'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes passing test to be set to Error] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[CleanUp] + AS + BEGIN + RAISERROR(''This is an error ;)'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT TR.Name, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('[MyInnerTests].[test1]','Error'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error and test error still results in Error] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[CleanUp] + AS + BEGIN + RAISERROR(''This is an error ;)'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RAISERROR(''Some random error!'', 16, 10); + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT TR.Name, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('[MyInnerTests].[test1]','Error'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test appends message to any test error if Test-CleanUp errors] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[TestCleanUp] + AS + BEGIN + RAISERROR(''This is a CleanUp error ;)'',15,12); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[TestCleanUp]'') + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RAISERROR(''This is a Test error ;)'',16,10); + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); + + EXEC tSQLt.AssertLike @ExpectedPattern = '%This is a Test error ;)% || %This is a CleanUp error ;)%', @Actual = @FriendlyMsg; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed if schema name contains single quote] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed only if it is a stored procedure] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes failing test to be set to Error] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes passing test to be set to Error] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error and test error still results in Error] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes an appropriate message to be written to tSQLt.TestResult even if ERROR_PROCEDURE is null] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes an appropriate message to be written to the tSQLt.TestResult table] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes an appropriate message to be written to the tSQLt.TestResult table if there is a different error] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) - there will be three clean up methods, executed in the following order -- X 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter +- X 1. (Test-CleanUp) User defined clean up for an individual test as specified in the NoTransaction annotation parameter -- X "--[@tSQLt:NoTransaction]('[].[]')" -- X --[@tSQLt:NoTransaction](DEFAULT) +-- -- 2. User defined clean up for a test class as specified by [].CleanUp +- 2. (Schema.CleanUp) User defined clean up for a test class as specified by [].CleanUp -- X ' in schema name -- X different case for cLEANuP -- X If the ERROR_PROCEDURE is somehow returning null, we still get the rest of the error message @@ -899,10 +1130,10 @@ GO - If a test errors (even catastrophically), all indicated CleanUp procedures run. - Unify Error Message Generation Across all code -- X handle multiple TestCleanUpProcedures -- X handle TestCleanUpProcedures with ' in name -- X error in annotation if specified TestCleanUpProcedure does not exist -- X error in annotation if specified TestCleanUpProcedure is not a procedure (any of the 4ish types) +- X handle multiple Test-CleanUps +- X handle Test-CleanUps with ' in name +- X error in annotation if specified Test-CleanUp does not exist +- X error in annotation if specified Test-CleanUp is not a procedure (any of the 4ish types) Transactions diff --git a/Tests/_ExploratoryTests.class.sql b/Tests/_ExploratoryTests.class.sql index 81c58ac48..b44debb0b 100644 --- a/Tests/_ExploratoryTests.class.sql +++ b/Tests/_ExploratoryTests.class.sql @@ -68,3 +68,13 @@ BEGIN END; GO +CREATE PROCEDURE [_ExploratoryTests].[test FOR XML returns NULL for empty result set] +AS +BEGIN + SELECT ((SELECT 1 WHERE 1 = 0 FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)')) [FOR XML FROM EMPTY] INTO #Actual; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES(NULL); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO From 8d0622e1082dc61cf4017358c417e9ea51275fcb Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 28 Nov 2021 22:49:51 -0500 Subject: [PATCH 064/119] "now we have a nicely failing test" "nicely". --- Tests/AnnotationNoTransactionTests.class.sql | 61 ++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 8caf489b4..b9f7b2367 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1037,6 +1037,67 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed if previous Test-CleanUp errors] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp1] + AS + BEGIN + INSERT INTO #Actual VALUES (''UserCleanUp1''); + END; + '); + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp2] + AS + BEGIN + INSERT INTO #Actual VALUES (''UserCleanUp2''); + RAISERROR(''some error in UserCleanUp2'',16,10); + END; + '); + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp3] + AS + BEGIN + INSERT INTO #Actual VALUES (''UserCleanUp3''); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp1]'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp2]'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp3]'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + INSERT INTO #Actual VALUES (''test1''); + END; + '); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), col1 NVARCHAR(MAX)); + + + EXEC tSQLt.Run 'MyInnerTests.[test1]', 'tSQLt.NullTestResultFormatter'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(1, 'test1'), (2, 'UserCleanUp1'), (3, 'UserCleanUp2'), (4, 'UserCleanUp3'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp appends all individual error messages] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed if schema name contains single quote] AS BEGIN From ccf405aff9cad9fd5d0ac355153df844183689cb Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 29 Nov 2021 14:16:37 -0500 Subject: [PATCH 065/119] Refactored error handling for Test and Schema CleanUp procedures; fixed a test; unskipped another test. --- Source/BuildOrder.txt | 1 + Source/Run_Methods.sql | 18 +++------------ ...Lt.Private_CleanUpProcedureHandler.ssp.sql | 18 +++++++++++++++ Tests/AnnotationNoTransactionTests.class.sql | 22 +++++-------------- 4 files changed, 28 insertions(+), 31 deletions(-) create mode 100644 Source/tSQLt.Private_CleanUpProcedureHandler.ssp.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 0e6070e25..6b2da330f 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -55,6 +55,7 @@ tSQLt.Private_HostPlatform.svw.sql tSQLt.Private_NoTransactionTableAction.view.sql tSQLt.Private_NoTransactionHandleTable.ssp.sql tSQLt.Private_NoTransactionHandleTables.ssp.sql +tSQLt.Private_CleanUpProcedureHandler.ssp.sql tSQLt.Private_CleanUp.ssp.sql Run_Methods.sql tSQLt.Private_SysTypes.svw.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 91adf0e43..373a6bc07 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -282,7 +282,7 @@ BEGIN BEGIN SET @NoTransactionTestCleanUpProcedureName = ( ( - SELECT 'EXEC '+ NT.CleanUpProcedureName +';' + SELECT 'EXEC tSQLt.Private_CleanUpProcedureHandler '''+ REPLACE(NT.CleanUpProcedureName,'''','''''') +''', @Result OUT, @Msg OUT;' FROM #NoTransaction NT ORDER BY OrderId FOR XML PATH(''),TYPE @@ -290,24 +290,12 @@ BEGIN ); IF(@NoTransactionTestCleanUpProcedureName IS NOT NULL) BEGIN - BEGIN TRY - EXEC(@NoTransactionTestCleanUpProcedureName); - END TRY - BEGIN CATCH - SET @Result = 'Error'; - SET @Msg = (CASE WHEN @Msg <> '' THEN @Msg + ' || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; - END CATCH; + EXEC sys.sp_executesql @NoTransactionTestCleanUpProcedureName, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; END; IF(@CleanUp IS NOT NULL) BEGIN - BEGIN TRY - EXEC @CleanUp; - END TRY - BEGIN CATCH - SET @Result = 'Error'; - SET @Msg = (CASE WHEN @Msg <> '' THEN @Msg + ' || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; - END CATCH; + EXEC tSQLt.Private_CleanUpProcedureHandler @CleanUp, @Result OUT, @Msg OUT; END; DECLARE @CleanUpErrorMsg NVARCHAR(MAX); diff --git a/Source/tSQLt.Private_CleanUpProcedureHandler.ssp.sql b/Source/tSQLt.Private_CleanUpProcedureHandler.ssp.sql new file mode 100644 index 000000000..1056993c7 --- /dev/null +++ b/Source/tSQLt.Private_CleanUpProcedureHandler.ssp.sql @@ -0,0 +1,18 @@ +IF OBJECT_ID('tSQLt.Private_CleanUpProcedureHandler') IS NOT NULL DROP PROCEDURE tSQLt.Private_CleanUpProcedureHandler; +GO +---Build+ +CREATE PROCEDURE tSQLt.Private_CleanUpProcedureHandler + @CleanUpProcedureName NVARCHAR(MAX), + @TestResult NVARCHAR(MAX) OUTPUT, + @TestMsg NVARCHAR(MAX) OUTPUT +AS +BEGIN + BEGIN TRY + EXEC @CleanUpProcedureName; + END TRY + BEGIN CATCH + SET @TestResult = 'Error'; + SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; + END CATCH; +END; +GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index b9f7b2367..36ad95309 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -486,20 +486,20 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed even if it has a single quote in its name] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed even if it has a single quote in its name and/or its schema name] AS BEGIN - EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC tSQLt.NewTestClass 'MyInner''Tests' EXEC(' - CREATE PROCEDURE [MyInnerTests].[UserClean''Up1] + CREATE PROCEDURE [MyInner''Tests].[UserClean''Up1] AS BEGIN INSERT INTO #Actual VALUES (''UserClean''''Up1''); END; '); EXEC(' - --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserClean''''Up1]'') - CREATE PROCEDURE MyInnerTests.[test1] + --[@'+'tSQLt:NoTransaction](''[MyInner''''Tests].[UserClean''''Up1]'') + CREATE PROCEDURE [MyInner''Tests].[test''1] AS BEGIN RETURN @@ -509,7 +509,7 @@ BEGIN CREATE TABLE #Actual (col1 NVARCHAR(MAX)); - EXEC tSQLt.Run 'MyInnerTests.[test1]'; + EXEC tSQLt.Run '[MyInner''Tests].[test''1]'; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; @@ -1088,7 +1088,6 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp appends all individual error messages] AS BEGIN @@ -1098,15 +1097,6 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO --[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed if schema name contains single quote] -AS -BEGIN - EXEC tSQLt.Fail 'TODO'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO ---[@tSQLt:SkipTest]('TODO') CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed only if it is a stored procedure] AS BEGIN From 050f20c63dacfdae817cf1332d2d3089e00c3cfa Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 29 Nov 2021 16:46:14 -0500 Subject: [PATCH 066/119] Even more tests! Yay! --- Tests/AnnotationNoTransactionTests.class.sql | 240 +++++++++++++++---- 1 file changed, 198 insertions(+), 42 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 36ad95309..39d144a02 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -32,23 +32,6 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO: needs other tests first') -CREATE PROCEDURE AnnotationNoTransactionTests.[test produces meaningful error when pre and post transactions counts don't match] -AS -BEGIN - EXEC tSQLt.NewTestClass 'MyInnerTests' - EXEC(' ---[@'+'tSQLt:NoTransaction](DEFAULT) -CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN; - '); - - --EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; - - EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO CREATE PROCEDURE AnnotationNoTransactionTests.[test transaction name is NULL in TestResults table] AS BEGIN @@ -174,7 +157,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if not annotated and succeeding] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if test is not annotated and passing] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -191,7 +174,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if not annotated and failing] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if test is not annotated and failing] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -208,7 +191,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if not annotated and erroring] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if test is not annotated and erroring] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -245,7 +228,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp error message is called before the test result message is printed] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is called before the test result message is printed] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -1091,70 +1074,243 @@ GO CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp appends all individual error messages] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[CleanUp] + AS + BEGIN + RAISERROR(''some error in Schema-CleanUp'',16,10); + END; + '); + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp1] + AS + BEGIN + RAISERROR(''some error in UserCleanUp1'',16,10); + END; + '); + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp2] + AS + BEGIN + RAISERROR(''some error in UserCleanUp2'',16,10); + END; + '); + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp3] + AS + BEGIN + RAISERROR(''some error in UserCleanUp3'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp1]'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp2]'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp3]'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + EXEC tSQLt.Fail ''MyInnerTests.test1 has failed.''; + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', 'tSQLt.NullTestResultFormatter'; + DECLARE @Actual NVARCHAR(MAX) = (SELECT Msg FROM tSQLt.TestResult); + + EXEC tSQLt.AssertLike @ExpectedPattern = '%MyInnerTests.test1%UserCleanUp1%UserCleanUp2%UserCleanUp3%Schema-CleanUp%', @Actual = @Actual; + END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed only if it is a stored procedure] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes failing test to be set to Error] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[TestCleanUp1] + AS + BEGIN + RAISERROR(''This is an error ;)'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[TestCleanUp1]'') + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + EXEC tSQLt.Fail; + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT TR.Name, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('[MyInnerTests].[test1]','Error'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes failing test to be set to Error] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes passing test to be set to Error] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[TestCleanUp1] + AS + BEGIN + RAISERROR(''This is an error ;)'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[TestCleanUp1]'') + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT TR.Name, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('[MyInnerTests].[test1]','Error'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes passing test to be set to Error] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error and test error still results in Error] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[TestCleanUp1] + AS + BEGIN + RAISERROR(''This is an error ;)'',16,10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[TestCleanUp1]'') + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RAISERROR(''test error'',16,10); + END; + '); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT TR.Name, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES('[MyInnerTests].[test1]','Error'); + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error and test error still results in Error] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes an appropriate message to be written to tSQLt.TestResult even if ERROR_PROCEDURE is null] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.NewTestClass 'MyOtherInnerTests' + EXEC(' + CREATE PROCEDURE [MyOtherInnerTests].[TestCleanUp1] + AS + BEGIN + /*wasting lines...*/ + EXEC(''RAISERROR(''''This is another error ;)'''',15,12)''); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyOtherInnerTests].[TestCleanUp1]'') + CREATE PROCEDURE [MyOtherInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.Run 'MyOtherInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); + + EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is another error ;) | Procedure: | Line: 1 | Severity, State: 15, 12)', @Actual = @FriendlyMsg; END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes an appropriate message to be written to tSQLt.TestResult even if ERROR_PROCEDURE is null] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp error stops execution of all subsequent tests] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction](DEFAULT) +CREATE PROCEDURE MyInnerTests.[test1] AS INSERT INTO #Actual DEFAULT VALUES; + '); + EXEC(' +--[@'+'tSQLt:NoTransaction](DEFAULT) +CREATE PROCEDURE MyInnerTests.[test2] AS INSERT INTO #Actual DEFAULT VALUES; + '); + CREATE TABLE #Actual(Id CHAR(1) DEFAULT '*'); + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'RAISERROR(''Error during Private_CleanUp'',16,10);'; + BEGIN TRY + EXEC tSQLt.Run 'MyInnerTests'; + END TRY + BEGIN CATCH + /*Not interested in the specific error here.*/ + END CATCH; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES('*'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes an appropriate message to be written to the tSQLt.TestResult table] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp error message gets written to tSQLt.TestResult before tSQLt stops] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.Fail 'TODO -- This test might exist already. Search for >>SpyProcedure @ProcedureName = ''tSQLt.Private_CleanUp'''; END; GO /*-----------------------------------------------------------------------------------------------*/ GO --[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes an appropriate message to be written to the tSQLt.TestResult table if there is a different error] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp error prevents any subsequent tSQLt.Run% calls.] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.Fail 'TODO -- also needs a good error message'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO: needs other tests first') +CREATE PROCEDURE AnnotationNoTransactionTests.[test produces meaningful error when pre and post transactions counts don't match] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction](DEFAULT) +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN; + '); + + --EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; + + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; END; GO /*-----------------------------------------------------------------------------------------------*/ From b65861297ea6d3944ff541056c59db1f494dff2e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 29 Nov 2021 22:57:55 -0500 Subject: [PATCH 067/119] Added @Result OUT parameter to tSQLt.Private_CleanUp; tests were written; lots of things are broken. --- Source/Run_Methods.sql | 2 +- Source/tSQLt.Private_CleanUp.ssp.sql | 3 +- Tests/AnnotationNoTransactionTests.class.sql | 29 ++++++++++++++-- Tests/UndoTestDoublesTests.class.sql | 35 ++++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 373a6bc07..46773f173 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -299,7 +299,7 @@ BEGIN END; DECLARE @CleanUpErrorMsg NVARCHAR(MAX); - EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT; + EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT, @Result = @Result OUT; SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); END; diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index 8b6070065..c5c7a4ce5 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -4,7 +4,8 @@ GO GO CREATE PROCEDURE tSQLt.Private_CleanUp @FullTestName NVARCHAR(MAX), - @ErrorMsg NVARCHAR(MAX) OUTPUT + @ErrorMsg NVARCHAR(MAX) OUTPUT, + @Result NVARCHAR(MAX) OUTPUT AS BEGIN diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 39d144a02..8262ba289 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1281,10 +1281,35 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp error message gets written to tSQLt.TestResult before tSQLt stops] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp @Result OUTPUT gets written to tSQLt.TestResult before tSQLt stops] AS BEGIN - EXEC tSQLt.Fail 'TODO -- This test might exist already. Search for >>SpyProcedure @ProcedureName = ''tSQLt.Private_CleanUp'''; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction](DEFAULT) +CREATE PROCEDURE MyInnerTests.[test1] AS RETURN; + '); + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'SET @Result = ''V1234'';'; + + EXEC tSQLt.Run 'MyInnerTests'; + + SELECT Result INTO #Actual FROM tSQLt.TestResult + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES('V1234'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Any cleanup that potentially alters the test result adds the previous result to the error message] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; END; GO /*-----------------------------------------------------------------------------------------------*/ diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index fff4c39d7..ea57ff9f7 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -821,6 +821,41 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE UndoTestDoublesTests.[test UndoTestDoubles error is appended to message] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE UndoTestDoublesTests.[test UndoTestDoubles error causes Result to be set to Error] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE UndoTestDoublesTests.[test HandleTables error is appended to message] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE UndoTestDoublesTests.[test HandleTables error causes Result to be set to FATAL] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO /*-- TODO From 41c14fdc16a82eb3f31f6e813cbcbe31b4045c0b Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 30 Nov 2021 11:44:15 -0500 Subject: [PATCH 068/119] Renamed tSQLt.Private_CleanUpProcedureHandler to tSQLt.Private_CleanUpCmdHandler so that we can pass a command, not just the name of a procedure; Updated Private_CleanUp to use the new and improved tSQLt.Private_CleanUpCmdHandler and wrote some of the related tests, more needed; Test name clean up; --- Source/BuildOrder.txt | 2 +- Source/Run_Methods.sql | 6 +- Source/Source.ssmssqlproj | 6 ++ Source/tSQLt.Private_CleanUp.ssp.sql | 14 +++- ...> tSQLt.Private_CleanUpCmdHandler.ssp.sql} | 8 +- Tests/AnnotationNoTransactionTests.class.sql | 4 +- Tests/Private_CleanUpTests.class.sql | 81 +++++++++++++++++-- Tests/UndoTestDoublesTests.class.sql | 35 -------- 8 files changed, 101 insertions(+), 55 deletions(-) rename Source/{tSQLt.Private_CleanUpProcedureHandler.ssp.sql => tSQLt.Private_CleanUpCmdHandler.ssp.sql} (68%) diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 6b2da330f..53a6f1c8a 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -55,7 +55,7 @@ tSQLt.Private_HostPlatform.svw.sql tSQLt.Private_NoTransactionTableAction.view.sql tSQLt.Private_NoTransactionHandleTable.ssp.sql tSQLt.Private_NoTransactionHandleTables.ssp.sql -tSQLt.Private_CleanUpProcedureHandler.ssp.sql +tSQLt.Private_CleanUpCmdHandler.ssp.sql tSQLt.Private_CleanUp.ssp.sql Run_Methods.sql tSQLt.Private_SysTypes.svw.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 46773f173..3d8f41dcd 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -282,7 +282,7 @@ BEGIN BEGIN SET @NoTransactionTestCleanUpProcedureName = ( ( - SELECT 'EXEC tSQLt.Private_CleanUpProcedureHandler '''+ REPLACE(NT.CleanUpProcedureName,'''','''''') +''', @Result OUT, @Msg OUT;' + SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' FROM #NoTransaction NT ORDER BY OrderId FOR XML PATH(''),TYPE @@ -295,11 +295,11 @@ BEGIN IF(@CleanUp IS NOT NULL) BEGIN - EXEC tSQLt.Private_CleanUpProcedureHandler @CleanUp, @Result OUT, @Msg OUT; + EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; END; DECLARE @CleanUpErrorMsg NVARCHAR(MAX); - EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @ErrorMsg = @CleanUpErrorMsg OUT, @Result = @Result OUT; + EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); END; diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 7a65591e8..095757850 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -222,6 +222,12 @@ tSQLt.Private_CleanUp.ssp.sql + + + + + tSQLt.Private_CleanUpCmdHandler.ssp.sql + diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index c5c7a4ce5..8998b3e73 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -4,14 +4,20 @@ GO GO CREATE PROCEDURE tSQLt.Private_CleanUp @FullTestName NVARCHAR(MAX), - @ErrorMsg NVARCHAR(MAX) OUTPUT, - @Result NVARCHAR(MAX) OUTPUT + @Result NVARCHAR(MAX) OUTPUT, + @ErrorMsg NVARCHAR(MAX) OUTPUT AS BEGIN - EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset'; + EXEC tSQLt.Private_CleanUpCmdHandler + @CleanUpCmd = 'EXEC tSQLt.Private_NoTransactionHandleTables @Action=''Reset'';', + @TestResult = NULL, + @TestMsg = NULL; - EXEC tSQLt.UndoTestDoubles @Force = 0; + EXEC tSQLt.Private_CleanUpCmdHandler + @CleanUpCmd = 'EXEC tSQLt.UndoTestDoubles @Force = 0;', + @TestResult = NULL, + @TestMsg = NULL; END; GO diff --git a/Source/tSQLt.Private_CleanUpProcedureHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql similarity index 68% rename from Source/tSQLt.Private_CleanUpProcedureHandler.ssp.sql rename to Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql index 1056993c7..570bab468 100644 --- a/Source/tSQLt.Private_CleanUpProcedureHandler.ssp.sql +++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -1,14 +1,14 @@ -IF OBJECT_ID('tSQLt.Private_CleanUpProcedureHandler') IS NOT NULL DROP PROCEDURE tSQLt.Private_CleanUpProcedureHandler; +IF OBJECT_ID('tSQLt.Private_CleanUpCmdHandler') IS NOT NULL DROP PROCEDURE tSQLt.Private_CleanUpCmdHandler; GO ---Build+ -CREATE PROCEDURE tSQLt.Private_CleanUpProcedureHandler - @CleanUpProcedureName NVARCHAR(MAX), +CREATE PROCEDURE tSQLt.Private_CleanUpCmdHandler + @CleanUpCmd NVARCHAR(MAX), @TestResult NVARCHAR(MAX) OUTPUT, @TestMsg NVARCHAR(MAX) OUTPUT AS BEGIN BEGIN TRY - EXEC @CleanUpProcedureName; + EXEC(@CleanUpCmd); END TRY BEGIN CATCH SET @TestResult = 'Error'; diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 8262ba289..b7e5d20f7 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1281,7 +1281,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp @Result OUTPUT gets written to tSQLt.TestResult before tSQLt stops] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_CleanUp @Result OUTPUT gets written to tSQLt.TestResult before tSQLt stops] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -1315,7 +1315,7 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO --[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp error prevents any subsequent tSQLt.Run% calls.] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_CleanUp error prevents any subsequent tSQLt.Run% calls.] AS BEGIN EXEC tSQLt.Fail 'TODO -- also needs a good error message'; diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql index 139c366a0..38895d7b8 100644 --- a/Tests/Private_CleanUpTests.class.sql +++ b/Tests/Private_CleanUpTests.class.sql @@ -2,12 +2,12 @@ EXEC tSQLt.NewTestClass 'Private_CleanUpTests'; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.UndoTestDoubles] +CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.UndoTestDoubles with @Force=0] AS BEGIN EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; - EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @ErrorMsg = NULL; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = NULL, @ErrorMsg = NULL; SELECT _id_, Force INTO #Actual FROM tSQLt.UndoTestDoubles_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; @@ -18,23 +18,92 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.Private_NoTransactionHandleTables] +CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.Private_NoTransactionHandleTables with @Action='Reset'] AS BEGIN EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; - EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @ErrorMsg = NULL; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = NULL, @ErrorMsg = NULL; - SELECT _id_ INTO #Actual FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog; + SELECT _id_, Action INTO #Actual FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - INSERT INTO #Expected VALUES(1); + INSERT INTO #Expected VALUES(1, 'Reset'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE Private_CleanUpTests.[test calls tSQLt.Private_CleanUpCmdHandler for only UndoTestDoubles and HandleTables in the correct order] +AS +BEGIN + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUpCmdHandler'; + + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = NULL, @ErrorMsg = NULL; + + SELECT _id_, CleanUpCmd INTO #Actual FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog; + SELECT TOP(0) A._id_, A.CleanUpCmd AS [%CleanUpCmd] INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'%tSQLt.Private_NoTransactionHandleTables%'),(2,'%tSQLt.UndoTestDoubles%'); + + SELECT * INTO #Compare + FROM( + SELECT '>' _R_,* FROM #Actual AS A WHERE NOT EXISTS(SELECT 1 FROM #Expected E WHERE A._id_ = E._id_ AND A.CleanUpCmd LIKE E.[%CleanUpCmd]) + UNION ALL + SELECT '<' _R_,* FROM #Expected AS E WHERE NOT EXISTS(SELECT 1 FROM #Actual A WHERE A._id_ = E._id_ AND A.CleanUpCmd LIKE E.[%CleanUpCmd]) + )X + EXEC tSQLt.AssertEmptyTable @TableName = '#Compare'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_CleanUpTests.[test UndoTestDoubles error message is appended to @ErrorMsg] +AS +BEGIN + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles', @CommandToExecute = 'RAISERROR(''some cleanup error'',16, 10)'; + + DECLARE @ErrorMsg NVARCHAR(MAX) = 'previous error'; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = NULL, @ErrorMsg = @ErrorMsg; + + EXEC tSQLt.AssertLike @ExpectedPattern = 'previous error%some cleanup error%', @Actual = @ErrorMsg; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE Private_CleanUpTests.[test UndoTestDoubles error causes @Result to be set to Error] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE Private_CleanUpTests.[test HandleTables error is appended to @ErrorMsg] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE Private_CleanUpTests.[test HandleTables error causes @Result to be set to FATAL] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + /*-- TODO diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index ea57ff9f7..fff4c39d7 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -821,41 +821,6 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE UndoTestDoublesTests.[test UndoTestDoubles error is appended to message] -AS -BEGIN - EXEC tSQLt.Fail 'TODO'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE UndoTestDoublesTests.[test UndoTestDoubles error causes Result to be set to Error] -AS -BEGIN - EXEC tSQLt.Fail 'TODO'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE UndoTestDoublesTests.[test HandleTables error is appended to message] -AS -BEGIN - EXEC tSQLt.Fail 'TODO'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE UndoTestDoublesTests.[test HandleTables error causes Result to be set to FATAL] -AS -BEGIN - EXEC tSQLt.Fail 'TODO'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO /*-- TODO From 3eecbeb58e82d8ae40939cd8340f899e3ed4eb04 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 30 Nov 2021 15:56:27 -0500 Subject: [PATCH 069/119] More tests. --- Source/tSQLt.Private_CleanUp.ssp.sql | 6 ++--- Tests/Private_CleanUpTests.class.sql | 34 +++++++++++++++++++++------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index 8998b3e73..222d9546f 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -12,12 +12,12 @@ BEGIN EXEC tSQLt.Private_CleanUpCmdHandler @CleanUpCmd = 'EXEC tSQLt.Private_NoTransactionHandleTables @Action=''Reset'';', @TestResult = NULL, - @TestMsg = NULL; + @TestMsg = @ErrorMsg OUT; EXEC tSQLt.Private_CleanUpCmdHandler @CleanUpCmd = 'EXEC tSQLt.UndoTestDoubles @Force = 0;', - @TestResult = NULL, - @TestMsg = NULL; + @TestResult = @Result OUT, + @TestMsg = @ErrorMsg OUT; END; GO diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql index 38895d7b8..b84614474 100644 --- a/Tests/Private_CleanUpTests.class.sql +++ b/Tests/Private_CleanUpTests.class.sql @@ -68,37 +68,55 @@ BEGIN EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles', @CommandToExecute = 'RAISERROR(''some cleanup error'',16, 10)'; DECLARE @ErrorMsg NVARCHAR(MAX) = 'previous error'; - EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = NULL, @ErrorMsg = @ErrorMsg; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = NULL, @ErrorMsg = @ErrorMsg OUT; EXEC tSQLt.AssertLike @ExpectedPattern = 'previous error%some cleanup error%', @Actual = @ErrorMsg; - END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') CREATE PROCEDURE Private_CleanUpTests.[test UndoTestDoubles error causes @Result to be set to Error] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles', @CommandToExecute = 'RAISERROR(''some cleanup error'',16, 10)'; + + DECLARE @Result NVARCHAR(MAX) = 'NOT ERROR'; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = @Result OUT, @ErrorMsg = NULL; + + EXEC tSQLt.AssertEqualsString @Expected = 'Error', @Actual = @Result; END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') CREATE PROCEDURE Private_CleanUpTests.[test HandleTables error is appended to @ErrorMsg] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'RAISERROR(''some cleanup error'',16, 10)'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + + DECLARE @ErrorMsg NVARCHAR(MAX) = 'previous error'; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = NULL, @ErrorMsg = @ErrorMsg OUT; + + EXEC tSQLt.AssertLike @ExpectedPattern = 'previous error%some cleanup error%', @Actual = @ErrorMsg; + END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') CREATE PROCEDURE Private_CleanUpTests.[test HandleTables error causes @Result to be set to FATAL] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'RAISERROR(''some cleanup error'',16, 10)'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + + DECLARE @Result NVARCHAR(MAX) = 'NOT ERROR'; + EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = @Result OUT, @ErrorMsg = NULL; + + EXEC tSQLt.AssertEqualsString @Expected = 'FATAL', @Actual = @Result; END; GO /*-----------------------------------------------------------------------------------------------*/ From 5c1bc10cde5d9546357561d42981e12008ddf54e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 30 Nov 2021 16:39:13 -0500 Subject: [PATCH 070/119] Wrote more tests for Private_CleanUp; new failing tests in AnnotationNoTransactionTests --- Source/tSQLt.Private_CleanUp.ssp.sql | 5 +++-- Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql | 5 +++-- Tests/AnnotationNoTransactionTests.class.sql | 3 +-- Tests/Private_CleanUpTests.class.sql | 18 ------------------ 4 files changed, 7 insertions(+), 24 deletions(-) diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index 222d9546f..f6fbd7da9 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -11,8 +11,9 @@ BEGIN EXEC tSQLt.Private_CleanUpCmdHandler @CleanUpCmd = 'EXEC tSQLt.Private_NoTransactionHandleTables @Action=''Reset'';', - @TestResult = NULL, - @TestMsg = @ErrorMsg OUT; + @TestResult = @Result OUT, + @TestMsg = @ErrorMsg OUT, + @ResultInCaseOfError = 'FATAL'; EXEC tSQLt.Private_CleanUpCmdHandler @CleanUpCmd = 'EXEC tSQLt.UndoTestDoubles @Force = 0;', diff --git a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql index 570bab468..9e8e69ec6 100644 --- a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql +++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -4,14 +4,15 @@ GO CREATE PROCEDURE tSQLt.Private_CleanUpCmdHandler @CleanUpCmd NVARCHAR(MAX), @TestResult NVARCHAR(MAX) OUTPUT, - @TestMsg NVARCHAR(MAX) OUTPUT + @TestMsg NVARCHAR(MAX) OUTPUT, + @ResultInCaseOfError NVARCHAR(MAX) = 'Error' AS BEGIN BEGIN TRY EXEC(@CleanUpCmd); END TRY BEGIN CATCH - SET @TestResult = 'Error'; + SET @TestResult = @ResultInCaseOfError; SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; END CATCH; END; diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index b7e5d20f7..6748fbe22 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1305,8 +1305,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Any cleanup that potentially alters the test result adds the previous result to the error message] +CREATE PROCEDURE AnnotationNoTransactionTests.[test any cleanup that potentially alters the test result adds the previous result to the error message] AS BEGIN EXEC tSQLt.Fail 'TODO'; diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql index b84614474..5bd5d2118 100644 --- a/Tests/Private_CleanUpTests.class.sql +++ b/Tests/Private_CleanUpTests.class.sql @@ -121,21 +121,3 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO - - - -/*-- TODO - --- CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) ----- there will be three clean up methods, executed in the following order ----- 1. User defined clean up for an individual test as specified in the NoTransaction annotation parameter ----- 2. User defined clean up for a test class as specified by [].CleanUp ----- 3. tSQLt.Private_CleanUp ----- Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error ----- If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. ----- appropriate error messages are appended to the test msg ----- tSQLt.Private_CleanUp Tests ------ Tables --> SELECT * FROM sys.tables WHERE schema_id = SCHEMA_ID('tSQLt'); ------ tSQLt.UndoTestDoubles - ---*/ \ No newline at end of file From b95fe2b2e6a9dc368a1c91a7ffe3ce895cc6c1f1 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 30 Nov 2021 23:38:21 -0500 Subject: [PATCH 071/119] Created two more tests, one of which is failing on purpose; We need to figure out how to make it fail. --- Tests/AnnotationNoTransactionTests.class.sql | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 6748fbe22..bb2e0f734 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1305,6 +1305,70 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed through tSQLt.Private_CleanUpCmdHandler only] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[CleanUp] + AS + BEGIN + RETURN; + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUpCmdHandler'; + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT _id_, CleanUpCmd INTO #Actual FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog + WHERE(NOT EXISTS(SELECT 1 FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog WHERE CleanUpCmd LIKE '%MyInnerTests%CleanUp%')); + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual', @Message = 'Expected a call for MyInnerTests.Cleanup1'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is not executed outside tSQLt.Private_CleanUpCmdHandler] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[CleanUp] + AS + BEGIN + RETURN; + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.SpyProcedure @ProcedureName = '[MyInnerTests].[CleanUp]'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUpCmdHandler'; + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + + EXEC tSQLt.AssertEmptyTable @TableName = '[MyInnerTests].[CleanUp_SpyProcedureLog]' + EXEC tSQLt.Fail 'prove that this can fail!' +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test any cleanup that potentially alters the test result adds the previous result to the error message] AS BEGIN From 6833fb65e64c9ea8c0320aee0f21851d4ec3b31b Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:54:43 -0500 Subject: [PATCH 072/119] Wrote more tests for common opportunities for ambiguity. --- Source/Run_Methods.sql | 1 + Tests/AnnotationNoTransactionTests.class.sql | 74 ++++++++++++++++---- 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 3d8f41dcd..9d9a56505 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -284,6 +284,7 @@ BEGIN ( SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' FROM #NoTransaction NT + WHERE NT.CleanUpProcedureName <> ISNULL(@CleanUp,'') ORDER BY OrderId FOR XML PATH(''),TYPE ).value('.','NVARCHAR(MAX)') diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index bb2e0f734..2a1194c6e 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1305,19 +1305,43 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed through tSQLt.Private_CleanUpCmdHandler only] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed through tSQLt.Private_CleanUpCmdHandler] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp] AS BEGIN RETURN; END;'); EXEC(' - CREATE PROCEDURE [MyInnerTests].[CleanUp] + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] AS BEGIN RETURN; END; '); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUpCmdHandler'; + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT _id_, CleanUpCmd INTO #Actual FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog + WHERE(NOT EXISTS(SELECT 1 FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog WHERE CleanUpCmd LIKE '%MyInnerTests%CleanUp%')); + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual', @Message = 'Expected a call for MyInnerTests.Cleanup1'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed through tSQLt.Private_CleanUpCmdHandler] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE [MyInnerTests].[Test-CleanUp1] AS BEGIN RETURN; END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[Test-CleanUp2] AS BEGIN RETURN; END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[Test-CleanUp3] AS BEGIN RETURN; END;'); EXEC(' - --[@'+'tSQLt:NoTransaction](DEFAULT) + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp1]'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp2]'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp3]'') CREATE PROCEDURE [MyInnerTests].[test1] AS BEGIN @@ -1330,26 +1354,50 @@ BEGIN EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; SELECT _id_, CleanUpCmd INTO #Actual FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog - WHERE(NOT EXISTS(SELECT 1 FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog WHERE CleanUpCmd LIKE '%MyInnerTests%CleanUp%')); + WHERE(NOT EXISTS(SELECT 1 FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog WHERE CleanUpCmd LIKE '%MyInnerTests%Test-CleanUp1%')) + OR(NOT EXISTS(SELECT 1 FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog WHERE CleanUpCmd LIKE '%MyInnerTests%Test-CleanUp2%')) + OR(NOT EXISTS(SELECT 1 FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog WHERE CleanUpCmd LIKE '%MyInnerTests%Test-CleanUp3%')); - EXEC tSQLt.AssertEmptyTable @TableName = '#Actual', @Message = 'Expected a call for MyInnerTests.Cleanup1'; + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual', @Message = 'Expected a call for MyInnerTests.Test-Cleanup(s)'; END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is not executed outside tSQLt.Private_CleanUpCmdHandler] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed only once even if it is also specified as test-cleanup] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp] AS BEGIN INSERT INTO #Actual DEFAULT VALUES; END;'); EXEC(' - CREATE PROCEDURE [MyInnerTests].[CleanUp] + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[CleanUp]'') + CREATE PROCEDURE [MyInnerTests].[test1] AS BEGIN RETURN; END; '); + + CREATE TABLE #Actual(WasCalled BIT DEFAULT 1); + + EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES(1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed only once even if it is specified as test-cleanup with different quoting] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp] AS BEGIN INSERT INTO #Actual DEFAULT VALUES; END;'); EXEC(' - --[@'+'tSQLt:NoTransaction](DEFAULT) + --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUp'') CREATE PROCEDURE [MyInnerTests].[test1] AS BEGIN @@ -1357,14 +1405,16 @@ BEGIN END; '); - EXEC tSQLt.SpyProcedure @ProcedureName = '[MyInnerTests].[CleanUp]'; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUpCmdHandler'; + CREATE TABLE #Actual(WasCalled BIT DEFAULT 1); EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES(1); - EXEC tSQLt.AssertEmptyTable @TableName = '[MyInnerTests].[CleanUp_SpyProcedureLog]' - EXEC tSQLt.Fail 'prove that this can fail!' + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + END; GO /*-----------------------------------------------------------------------------------------------*/ From 81319a7d9b4f27c9853cf5c186dfc637e46ae5d3 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 1 Dec 2021 23:21:09 -0500 Subject: [PATCH 073/119] Deleted three lines! Wrote some tests; some are passing. --- Source/Run_Methods.sql | 3 - .../tSQLt.Private_CleanUpCmdHandler.ssp.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 58 ++++++++++++++----- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 9d9a56505..ff30fb817 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -136,8 +136,6 @@ BEGIN END; EXEC (@Cmd); - --TODO:NoTran - ----EXEC @CleanUp --Probably further down, called ".CleanUp" IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) BEGIN SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; @@ -284,7 +282,6 @@ BEGIN ( SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' FROM #NoTransaction NT - WHERE NT.CleanUpProcedureName <> ISNULL(@CleanUp,'') ORDER BY OrderId FOR XML PATH(''),TYPE ).value('.','NVARCHAR(MAX)') diff --git a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql index 9e8e69ec6..21efaf952 100644 --- a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql +++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -12,8 +12,8 @@ BEGIN EXEC(@CleanUpCmd); END TRY BEGIN CATCH + SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ @TestResult + '] || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; SET @TestResult = @ResultInCaseOfError; - SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; END CATCH; END; GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 2a1194c6e..1ee523988 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1363,13 +1363,17 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed only once even if it is also specified as test-cleanup] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed in the order specified and again at the end] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' - EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp] AS BEGIN INSERT INTO #Actual DEFAULT VALUES; END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUpA] AS BEGIN INSERT INTO #Actual VALUES(OBJECT_NAME(@@PROCID)); END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUpB] AS BEGIN INSERT INTO #Actual VALUES(OBJECT_NAME(@@PROCID)); END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp] AS BEGIN INSERT INTO #Actual VALUES(OBJECT_NAME(@@PROCID)); END;'); EXEC(' - --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[CleanUp]'') + --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUpA'') + --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUp'') + --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUpB'') CREATE PROCEDURE [MyInnerTests].[test1] AS BEGIN @@ -1377,13 +1381,13 @@ BEGIN END; '); - CREATE TABLE #Actual(WasCalled BIT DEFAULT 1); + CREATE TABLE #Actual(OrderNumber INT IDENTITY(1,1), CleanUpName NVARCHAR(MAX) ); EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES(1); + VALUES(1,'CleanUpA'),(2,'CleanUp'),(3,'CleanUpB'),(4,'CleanUp'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; @@ -1391,13 +1395,15 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp is executed only once even if it is specified as test-cleanup with different quoting] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed multiple times if it is specified multiple times] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' - EXEC('CREATE PROCEDURE [MyInnerTests].[CleanUp] AS BEGIN INSERT INTO #Actual DEFAULT VALUES; END;'); + EXEC('CREATE PROCEDURE [MyInnerTests].[Test-CleanUp1] AS BEGIN INSERT INTO #Actual DEFAULT VALUES; END;'); EXEC(' - --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUp'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp1]'') + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp1]'') + --[@'+'tSQLt:NoTransaction](''MyInnerTests.[Test-CleanUp1]'') CREATE PROCEDURE [MyInnerTests].[test1] AS BEGIN @@ -1411,7 +1417,7 @@ BEGIN SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES(1); + VALUES(1),(1),(1); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; @@ -1419,19 +1425,43 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test any cleanup that potentially alters the test result adds the previous result to the error message] +CREATE PROCEDURE AnnotationNoTransactionTests.[test any CleanUp (Test, Schema, or Private) that alters the test result adds the previous result to the error message] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + DECLARE @TestMessage NVARCHAR(MAX) = 'BeforeMessage'; + + EXEC tSQLt.Private_CleanUpCmdHandler + @CleanUpCmd='RAISERROR(''NewMessage'',16,10)', + @TestMsg = @TestMessage OUT, + @TestResult = 'BeforeResult' , + @ResultInCaseOfError = 'NewResult'; + + EXEC tSQLt.AssertLike @ExpectedPattern = 'BeforeMessage [[]Result: BeforeResult] || %NewMessage%', @Actual = @TestMessage; + END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_CleanUp error prevents any subsequent tSQLt.Run% calls.] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_CleanUp error prevents subsequent tSQLt.Run% calls] AS BEGIN - EXEC tSQLt.Fail 'TODO -- also needs a good error message'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'IF(@Action = ''Reset'')RAISERROR(''Some Fatal Error'',16,10);'; + + EXEC tSQLt.Run 'MyInnerTests', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'asdasdasdas'; + EXEC tSQLt.Run 'MyInnerTests', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + END; GO /*-----------------------------------------------------------------------------------------------*/ From 816a71849b4b354b80f8469bb62820ecc003604c Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 2 Dec 2021 15:15:01 -0500 Subject: [PATCH 074/119] "A good addition" which is going to take the rest of the day, instead of "ten minutes". Tests are required. What happened to TDD??? --- Source/Run_Methods.sql | 16 +++++++++++++++ .../tSQLt.Private_CleanUpCmdHandler.ssp.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 20 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index ff30fb817..159a32310 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -120,6 +120,7 @@ BEGIN IF(@SkipTestFlag = 0) BEGIN EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; END; END; @@ -299,6 +300,21 @@ BEGIN DECLARE @CleanUpErrorMsg NVARCHAR(MAX); EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); + + SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; + SELECT * INTO #ObjectDiscrepancies + FROM( + (SELECT 'Deleted' [Status], B.* FROM #BeforeExecutionObjectSnapshot AS B EXCEPT SELECT 'Deleted' [Status],* FROM #AfterExecutionObjectSnapshot AS A) + UNION ALL + (SELECT 'Added' [Status], A.* FROM #AfterExecutionObjectSnapshot AS A EXCEPT SELECT 'Added' [Status], * FROM #BeforeExecutionObjectSnapshot AS B) + )D; + EXEC tSQLt.Private_CleanUpCmdHandler ' + IF(EXISTS(SELECT 1 FROM #ObjectDiscrepancies)) + BEGIN + DECLARE @TableToText NVARCHAR(MAX); + EXEC tSQLt.TableToText @TableName = ''#ObjectDiscrepancies'' ,@txt = @TableToText OUTPUT; + RAISERROR(''After the test executed, there were unexpected or missing objects in the database: %s'',16,10,@TableToText); + END;', @Result OUT, @Msg OUT; END; If(@Result NOT IN ('Success','Skipped')) diff --git a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql index 21efaf952..4261102b7 100644 --- a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql +++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -12,7 +12,7 @@ BEGIN EXEC(@CleanUpCmd); END TRY BEGIN CATCH - SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ @TestResult + '] || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; + SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ ISNULL(@TestResult,'') + '] || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; SET @TestResult = @ResultInCaseOfError; END CATCH; END; diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 1ee523988..3ffcc551d 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -405,8 +405,11 @@ CREATE PROCEDURE AnnotationNoTransactionTests.[CLEANUP: test an unrecoverable er AS BEGIN EXEC tSQLt.DropClass MyInnerTests; + --EXEC tSQLt.UndoTestDoubles; + --ROLLBACK END; GO +---[@tSQLt:SkipTest]('') --[@tSQLt:NoTransaction]('AnnotationNoTransactionTests.[CLEANUP: test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table]') /* This test must be NoTransaction because the inner test will invalidate any open transaction causing chaos and turmoil in the reactor. */ CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] @@ -1438,6 +1441,23 @@ BEGIN EXEC tSQLt.AssertLike @ExpectedPattern = 'BeforeMessage [[]Result: BeforeResult] || %NewMessage%', @Actual = @TestMessage; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test any CleanUp adds the previous result to the error message even if the previous result is NULL] +AS +BEGIN + DECLARE @TestMessage NVARCHAR(MAX) = 'BeforeMessage'; + + EXEC tSQLt.Private_CleanUpCmdHandler + @CleanUpCmd='RAISERROR(''NewMessage'',16,10)', + @TestMsg = @TestMessage OUT, + @TestResult = NULL , + @ResultInCaseOfError = 'NewResult'; + + EXEC tSQLt.AssertLike @ExpectedPattern = 'BeforeMessage [[]Result: ] || %NewMessage%', @Actual = @TestMessage; + END; GO /*-----------------------------------------------------------------------------------------------*/ From feda942188cfbd2ac67c8d27c6a3b4b69a98b8fd Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 2 Dec 2021 22:42:42 -0500 Subject: [PATCH 075/119] Fixed a couple of AnnotationNoTransactionTests tests; Moved the Result table of AssertEqualsTable from dbo to tSQLt schema; Checked to make sure that a transaction-free test does not leave detritus behind after execution, and if it does, we error the test. --- Source/BuildOrder.txt | 1 + Source/Run_Methods.sql | 22 ++---- Source/Source.ssmssqlproj | 12 +++ Source/tSQLt.AssertEqualsTable.ssp.sql | 12 +-- .../tSQLt.Private_AssertNoSideEffects.ssp.sql | 27 +++++++ ..._CreateResultTableForCompareTables.ssp.sql | 1 + Source/tSQLt.Private_Lock.tbl.sql | 7 ++ Tests/AnnotationNoTransactionTests.class.sql | 5 ++ Tests/AssertEqualsTableTests.class.sql | 73 ++++++++++++++++++- 9 files changed, 139 insertions(+), 21 deletions(-) create mode 100644 Source/tSQLt.Private_AssertNoSideEffects.ssp.sql create mode 100644 Source/tSQLt.Private_Lock.tbl.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 53a6f1c8a..e2c1e89d0 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -57,6 +57,7 @@ tSQLt.Private_NoTransactionHandleTable.ssp.sql tSQLt.Private_NoTransactionHandleTables.ssp.sql tSQLt.Private_CleanUpCmdHandler.ssp.sql tSQLt.Private_CleanUp.ssp.sql +tSQLt.Private_AssertNoSideEffects.ssp.sql Run_Methods.sql tSQLt.Private_SysTypes.svw.sql tSQLt.Private_GetFullTypeName.sfn.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 159a32310..2b4189d5a 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -119,8 +119,8 @@ BEGIN BEGIN IF(@SkipTestFlag = 0) BEGIN - EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; + EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; END; END; @@ -302,20 +302,12 @@ BEGIN SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; - SELECT * INTO #ObjectDiscrepancies - FROM( - (SELECT 'Deleted' [Status], B.* FROM #BeforeExecutionObjectSnapshot AS B EXCEPT SELECT 'Deleted' [Status],* FROM #AfterExecutionObjectSnapshot AS A) - UNION ALL - (SELECT 'Added' [Status], A.* FROM #AfterExecutionObjectSnapshot AS A EXCEPT SELECT 'Added' [Status], * FROM #BeforeExecutionObjectSnapshot AS B) - )D; - EXEC tSQLt.Private_CleanUpCmdHandler ' - IF(EXISTS(SELECT 1 FROM #ObjectDiscrepancies)) - BEGIN - DECLARE @TableToText NVARCHAR(MAX); - EXEC tSQLt.TableToText @TableName = ''#ObjectDiscrepancies'' ,@txt = @TableToText OUTPUT; - RAISERROR(''After the test executed, there were unexpected or missing objects in the database: %s'',16,10,@TableToText); - END;', @Result OUT, @Msg OUT; - END; + EXEC tSQLt.Private_AssertNoSideEffects + @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', + @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', + @TestResult = @Result OUT, + @TestMsg = @Msg OUT + END; If(@Result NOT IN ('Success','Skipped')) BEGIN diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 095757850..d7f452695 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -210,6 +210,12 @@ tSQLt.PrepareServer.ssp.sql + + + + + tSQLt.Private_AssertNoSideEffects.ssp.sql + @@ -402,6 +408,12 @@ tSQLt.Private_ListTestAnnotations.sfn.sql + + + + + tSQLt.Private_Lock.tbl.sql + diff --git a/Source/tSQLt.AssertEqualsTable.ssp.sql b/Source/tSQLt.AssertEqualsTable.ssp.sql index 555f05222..64439fd10 100644 --- a/Source/tSQLt.AssertEqualsTable.ssp.sql +++ b/Source/tSQLt.AssertEqualsTable.ssp.sql @@ -13,6 +13,7 @@ BEGIN EXEC tSQLt.AssertObjectExists @Actual; DECLARE @ResultTable NVARCHAR(MAX); + DECLARE @ResultTableWithSchema NVARCHAR(MAX); DECLARE @ResultColumn NVARCHAR(MAX); DECLARE @ColumnList NVARCHAR(MAX); DECLARE @UnequalRowsExist INT; @@ -20,27 +21,28 @@ BEGIN SELECT @ResultTable = tSQLt.Private::CreateUniqueObjectName(); SELECT @ResultColumn = 'RC_' + @ResultTable; + SELECT @ResultTableWithSchema = 'tSQLt.' + @ResultTable; EXEC tSQLt.Private_CreateResultTableForCompareTables - @ResultTable = @ResultTable, + @ResultTable = @ResultTableWithSchema, @ResultColumn = @ResultColumn, @BaseTable = @Expected; - SELECT @ColumnList = tSQLt.Private_GetCommaSeparatedColumnList(@ResultTable, @ResultColumn); + SELECT @ColumnList = tSQLt.Private_GetCommaSeparatedColumnList(@ResultTableWithSchema, @ResultColumn); - EXEC tSQLt.Private_ValidateThatAllDataTypesInTableAreSupported @ResultTable, @ColumnList; + EXEC tSQLt.Private_ValidateThatAllDataTypesInTableAreSupported @ResultTableWithSchema, @ColumnList; EXEC @UnequalRowsExist = tSQLt.Private_CompareTables @Expected = @Expected, @Actual = @Actual, - @ResultTable = @ResultTable, + @ResultTable = @ResultTableWithSchema, @ColumnList = @ColumnList, @MatchIndicatorColumnName = @ResultColumn; SET @CombinedMessage = ISNULL(@Message + CHAR(13) + CHAR(10),'') + @FailMsg; EXEC tSQLt.Private_CompareTablesFailIfUnequalRowsExists @UnequalRowsExist = @UnequalRowsExist, - @ResultTable = @ResultTable, + @ResultTable = @ResultTableWithSchema, @ResultColumn = @ResultColumn, @ColumnList = @ColumnList, @FailMsg = @CombinedMessage; diff --git a/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql b/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql new file mode 100644 index 000000000..62f18cb11 --- /dev/null +++ b/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql @@ -0,0 +1,27 @@ +IF OBJECT_ID('tSQLt.Private_AssertNoSideEffects') IS NOT NULL DROP PROCEDURE tSQLt.Private_AssertNoSideEffects; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_AssertNoSideEffects + @BeforeExecutionObjectSnapshotTableName NVARCHAR(MAX), + @AfterExecutionObjectSnapshotTableName NVARCHAR(MAX), + @TestResult NVARCHAR(MAX) OUTPUT, + @TestMsg NVARCHAR(MAX) OUTPUT +AS +BEGIN + DECLARE @cmd NVARCHAR(MAX) = ' + SELECT * INTO #ObjectDiscrepancies + FROM( + (SELECT ''Deleted'' [Status], B.* FROM '+@BeforeExecutionObjectSnapshotTableName+' AS B EXCEPT SELECT ''Deleted'' [Status],* FROM '+@AfterExecutionObjectSnapshotTableName+' AS A) + UNION ALL + (SELECT ''Added'' [Status], A.* FROM '+@AfterExecutionObjectSnapshotTableName+' AS A EXCEPT SELECT ''Added'' [Status], * FROM '+@BeforeExecutionObjectSnapshotTableName+' AS B) + )D; + IF(EXISTS(SELECT 1 FROM #ObjectDiscrepancies)) + BEGIN + DECLARE @TableToText NVARCHAR(MAX); + EXEC tSQLt.TableToText @TableName = ''#ObjectDiscrepancies'' ,@txt = @TableToText OUTPUT; + RAISERROR(''After the test executed, there were unexpected or missing objects in the database: %s'',16,10,@TableToText); + END;'; + EXEC tSQLt.Private_CleanUpCmdHandler @cmd, @TestResult OUT, @TestMsg OUT; +END; +GO diff --git a/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql b/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql index f57d3ce18..5f629bab2 100644 --- a/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql +++ b/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql @@ -13,6 +13,7 @@ BEGIN SELECT TOP(0) ''>'' AS ' + @ResultColumn + ', Expected.* INTO ' + @ResultTable + ' FROM ' + @BaseTable + ' AS Expected RIGHT JOIN ' + @BaseTable + ' AS X ON 1=0; ' EXEC(@Cmd); + EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @ResultTable, @ObjectType = N'TABLE'; END GO ---Build- diff --git a/Source/tSQLt.Private_Lock.tbl.sql b/Source/tSQLt.Private_Lock.tbl.sql new file mode 100644 index 000000000..855337854 --- /dev/null +++ b/Source/tSQLt.Private_Lock.tbl.sql @@ -0,0 +1,7 @@ +IF OBJECT_ID('tSQLt.Private_Lock') IS NOT NULL DROP TABLE tSQLt.Private_Lock; +GO +--DECLARE @Lock INT = (SELECT COUNT(1) FROM tSQLt.Lock AS L WITH(TABLOCKX)); +---Build+ +GO +CREATE TABLE tSQLt.Private_Lock(X BIT CONSTRAINT [PK:tSQLt.Private_Lock] PRIMARY KEY CONSTRAINT [CHK:tSQLt.Private_Lock(X)] CHECK(X=13)); +GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 3ffcc551d..ba7b76e19 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -219,6 +219,7 @@ CREATE PROCEDURE MyInnerTests.[test1] AS PRINT 1/0; EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'SET @ErrorMsg = '''';'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; EXEC tSQLt.Run 'MyInnerTests.[test1]'; DECLARE @Actual NVARCHAR(MAX) = (SELECT Msg FROM tSQLt.TestResult); @@ -240,6 +241,7 @@ CREATE PROCEDURE MyInnerTests.[test1] AS RAISERROR('''',16,10); EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'SET @ErrorMsg = '''';'; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_Print'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; EXEC tSQLt.Run 'MyInnerTests.[test1]'; @@ -263,6 +265,7 @@ BEGIN END; '); EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; SELECT Action INTO #Actual FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog; EXEC tSQLt.Run 'MyInnerTests.[test1]'; @@ -1268,6 +1271,7 @@ CREATE PROCEDURE MyInnerTests.[test2] AS INSERT INTO #Actual DEFAULT VALUES; EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'RAISERROR(''Error during Private_CleanUp'',16,10);'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; BEGIN TRY EXEC tSQLt.Run 'MyInnerTests'; END TRY @@ -1295,6 +1299,7 @@ CREATE PROCEDURE MyInnerTests.[test1] AS RETURN; EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'SET @Result = ''V1234'';'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; EXEC tSQLt.Run 'MyInnerTests'; diff --git a/Tests/AssertEqualsTableTests.class.sql b/Tests/AssertEqualsTableTests.class.sql index 6b30c988f..08fb5b046 100644 --- a/Tests/AssertEqualsTableTests.class.sql +++ b/Tests/AssertEqualsTableTests.class.sql @@ -783,4 +783,75 @@ BEGIN EXEC AssertEqualsTableTests.[Assert that AssertEqualsTable can NOT handle a datatype] 'GEOMETRY', 'geometry::STPointFromText(''POINT (10 10)'', 0),geometry::STPointFromText(''POINT (11 11)'', 0),geometry::STPointFromText(''POINT (12 12)'', 0)'; EXEC AssertEqualsTableTests.[Assert that AssertEqualsTable can NOT handle a datatype] 'GEOGRAPHY', 'geography::STGeomFromText(''LINESTRING(-10.10 10.10, -50.10 50.10)'', 4326),geography::STGeomFromText(''LINESTRING(-11.11 11.11, -50.11 50.11)'', 4326),geography::STGeomFromText(''LINESTRING(-12.12 12.12, -50.12 50.12)'', 4326)'; END; -GO \ No newline at end of file +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC AssertEqualsTableTests.[test RC table is marked as tSQLt.IsTempObject] +AS +BEGIN + CREATE TABLE #Table1 (id INT); + CREATE TABLE #Table2 (id INT); + + SELECT name, + object_id, + schema_id + INTO #TableListBefore + FROM sys.tables + WHERE name LIKE 'tSQLt[_]tempobject[_]%'; + + EXEC tSQLt.AssertEqualsTable '#Table1','#Table2'; + + SELECT QUOTENAME(SCHEMA_NAME(NewTable.schema_id))+'.'+QUOTENAME(NewTable.name) TableName, EP.value AS [tSQLt.IsTempObject] + INTO #Actual + FROM( + SELECT name, object_id, schema_id FROM sys.tables WHERE name LIKE 'tSQLt[_]tempobject[_]%' + EXCEPT + SELECT name, object_id, schema_id FROM #TableListBefore AS TLB + ) NewTable + LEFT JOIN sys.extended_properties EP + ON EP.class_desc = 'OBJECT_OR_COLUMN' + AND EP.name = 'tSQLt.IsTempObject' + AND NewTable.object_id = EP.major_id + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected SELECT TableName, 1 FROM #Actual; + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC AssertEqualsTableTests.[test RC table is created in the tSQLt schema] +AS +BEGIN + CREATE TABLE #Table1 (id INT); + CREATE TABLE #Table2 (id INT); + + SELECT name, + object_id, + schema_id + INTO #TableListBefore + FROM sys.tables + WHERE name LIKE 'tSQLt[_]tempobject[_]%'; + + EXEC tSQLt.AssertEqualsTable '#Table1','#Table2'; + + SELECT SCHEMA_NAME(NewTable.schema_id) SchemaName, NewTable.name TableName + INTO #Actual + FROM( + SELECT name, object_id, schema_id FROM sys.tables WHERE name LIKE 'tSQLt[_]tempobject[_]%' + EXCEPT + SELECT name, object_id, schema_id FROM #TableListBefore AS TLB + ) NewTable + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected SELECT 'tSQLt', TableName FROM #Actual; + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + From b087b4e41f81d20350cd2d6b9e8dbe89fded8381 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 2 Dec 2021 23:24:04 -0500 Subject: [PATCH 076/119] Updating to SpyProcedure to allow for calling the original procedure with @CallOriginal=1; wrote tests related to that, more needed. --- ...enerateCreateProcedureSpyStatement.ssp.sql | 15 ++++-- Source/tSQLt.SpyProcedure.ssp.sql | 22 +++++---- Tests/SpyProcedureTests.class.sql | 46 +++++++++++++++++++ 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index b0b17d4bc..a13d9f746 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -6,11 +6,13 @@ CREATE PROCEDURE tSQLt.Private_GenerateCreateProcedureSpyStatement @OriginalProcedureName NVARCHAR(MAX), @LogTableName NVARCHAR(MAX), @CommandToExecute NVARCHAR(MAX), + @CallOriginal BIT, @CreateProcedureStatement NVARCHAR(MAX) OUTPUT, @CreateLogTableStatement NVARCHAR(MAX) OUTPUT AS BEGIN - DECLARE @ProcParmList NVARCHAR(MAX), + DECLARE @ProcParmListForInsert NVARCHAR(MAX), + @ProcParmListForCall NVARCHAR(MAX), @TableColList NVARCHAR(MAX), @ProcParmTypeList NVARCHAR(MAX), @TableColTypeList NVARCHAR(MAX); @@ -24,7 +26,7 @@ BEGIN @IsTableType BIT; SELECT @Separator = '', @ProcParmTypeListSeparator = '', - @ProcParmList = '', @TableColList = '', @ProcParmTypeList = '', @TableColTypeList = ''; + @ProcParmListForInsert = '', @TableColList = '', @ProcParmTypeList = '', @TableColTypeList = ''; DECLARE Parameters CURSOR FOR SELECT p.name, t.TypeName, p.is_output, p.is_cursor_ref, t.IsTableType @@ -39,11 +41,12 @@ BEGIN BEGIN IF @IsCursorRef = 0 BEGIN - SELECT @ProcParmList = @ProcParmList + @Separator + + SELECT @ProcParmListForInsert = @ProcParmListForInsert + @Separator + CASE WHEN @IsTableType = 1 THEN '(SELECT * FROM '+@ParamName+' FOR XML PATH(''row''),TYPE,ROOT('''+STUFF(@ParamName,1,1,'')+'''))' ELSE @ParamName END, +-- @ProcParmListForCall = @ProcParmListForCall + @Separator + @ParmList, @TableColList = @TableColList + @Separator + '[' + STUFF(@ParamName,1,1,'') + ']', @ProcParmTypeList = @ProcParmTypeList + @ProcParmTypeListSeparator + @ParamName + ' ' + @TypeName + CASE WHEN @IsTableType = 1 THEN ' READONLY' ELSE ' = NULL ' END+ @@ -78,7 +81,7 @@ BEGIN DECLARE @InsertStmt NVARCHAR(MAX); SELECT @InsertStmt = 'INSERT INTO ' + @LogTableName + CASE WHEN @TableColList = '' THEN ' DEFAULT VALUES' - ELSE ' (' + @TableColList + ') SELECT ' + @ProcParmList + ELSE ' (' + @TableColList + ') SELECT ' + @ProcParmListForInsert END + ';'; SELECT @CreateLogTableStatement = 'CREATE TABLE ' + @LogTableName + ' (_id_ int IDENTITY(1,1) PRIMARY KEY CLUSTERED ' + @TableColTypeList + ');'; @@ -87,6 +90,10 @@ BEGIN 'CREATE PROCEDURE ' + @OriginalProcedureName + ' ' + @ProcParmTypeList + ' AS BEGIN ' + ISNULL(@InsertStmt,'') + + CASE WHEN @CallOriginal = 1 + THEN 'EXEC '+OBJECT_SCHEMA_NAME(@ProcedureObjectId)+'.'+OBJECT_NAME(@ProcedureObjectId)+';' + ELSE '' + END + ISNULL(@CommandToExecute + ';', '') + ' RETURN;' + ' END;'; diff --git a/Source/tSQLt.SpyProcedure.ssp.sql b/Source/tSQLt.SpyProcedure.ssp.sql index dc680f02e..eb81b287f 100644 --- a/Source/tSQLt.SpyProcedure.ssp.sql +++ b/Source/tSQLt.SpyProcedure.ssp.sql @@ -3,7 +3,8 @@ GO ---Build+ CREATE PROCEDURE tSQLt.SpyProcedure @ProcedureName NVARCHAR(MAX), - @CommandToExecute NVARCHAR(MAX) = NULL + @CommandToExecute NVARCHAR(MAX) = NULL, + @CallOriginal BIT = 0 AS BEGIN DECLARE @ProcedureObjectId INT; @@ -17,14 +18,6 @@ BEGIN DECLARE @CreateProcedureStatement NVARCHAR(MAX); DECLARE @CreateLogTableStatement NVARCHAR(MAX); - EXEC tSQLt.Private_GenerateCreateProcedureSpyStatement - @ProcedureObjectId = @ProcedureObjectId, - @OriginalProcedureName = @ProcedureName, - @LogTableName = @LogTableName, - @CommandToExecute = @CommandToExecute, - @CreateProcedureStatement = @CreateProcedureStatement OUT, - @CreateLogTableStatement = @CreateLogTableStatement OUT; - DECLARE @NewNameOfOriginalObject NVARCHAR(MAX); @@ -34,6 +27,17 @@ BEGIN EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ObjectId = @LogTableObjectId; END; EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ProcedureObjectId, @NewName = @NewNameOfOriginalObject OUTPUT; + + EXEC tSQLt.Private_GenerateCreateProcedureSpyStatement + @ProcedureObjectId = @ProcedureObjectId, + @OriginalProcedureName = @ProcedureName, + @LogTableName = @LogTableName, + @CommandToExecute = @CommandToExecute, + @CallOriginal = @CallOriginal, + @CreateProcedureStatement = @CreateProcedureStatement OUT, + @CreateLogTableStatement = @CreateLogTableStatement OUT; + + EXEC(@CreateLogTableStatement); EXEC(@CreateProcedureStatement); diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index ca42d72cc..56396b3bf 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -685,6 +685,7 @@ BEGIN @OriginalProcedureName = 'dbo.SpiedInnerProcedure', /*using different name to simulate renaming*/ @LogTableName = NULL, @CommandToExecute = NULL, + @CallOriginal = 0, @CreateProcedureStatement = @CreateProcedureStatement OUT, @CreateLogTableStatement = @CreateLogTableStatement OUT; @@ -707,6 +708,7 @@ BEGIN @OriginalProcedureName = 'dbo.SpiedInnerProcedure', /*using different name to simulate renaming*/ @LogTableName = NULL, @CommandToExecute = NULL, + @CallOriginal = 0, @CreateProcedureStatement = @CreateProcedureStatement OUT, @CreateLogTableStatement = @CreateLogTableStatement OUT; @@ -815,5 +817,49 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROC SpyProcedureTests.[test calls the original procedure if @CallOriginal = 1] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 AS BEGIN INSERT INTO #Actual VALUES (''SpyProcedureTests.TempProcedure1 called''); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX)); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1; + + EXEC('SpyProcedureTests.TempProcedure1'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'SpyProcedureTests.TempProcedure1 called'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test calls original procedure with parameters if @CallOriginal = 1] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 @AnInt INT, @AString NVARCHAR(MAX) AS BEGIN INSERT INTO #Actual VALUES (''SpyProcedureTests.TempProcedure1(''+CAST(@AnInt AS NVARCHAR(MAX))+'',''+@AString+'') called''); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX)); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1; + + EXEC('EXEC SpyProcedureTests.TempProcedure1 42,''XYZ'';'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'SpyProcedureTests.TempProcedure1(42,XYZ) called'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +/* Tests for consideration +- different parameter types including table valued parameters and cursors(?) +- @CommandToExecute is executed before the original procedure when @CallOriginal=1 +- CLR data type parameters? +- does not call original if @CallOriginal <> 1 (NULL or 0) and also if not specified +*/ From 20ae4dc30950ce7cc5a48d4795d26a2977f94f69 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 2 Dec 2021 23:34:20 -0500 Subject: [PATCH 077/119] Facade is important too. Don't forget it. --- Facade/Facade.CreateAllObjects.ssp.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/Facade/Facade.CreateAllObjects.ssp.sql b/Facade/Facade.CreateAllObjects.ssp.sql index 6d0e462c6..955d4c3a7 100644 --- a/Facade/Facade.CreateAllObjects.ssp.sql +++ b/Facade/Facade.CreateAllObjects.ssp.sql @@ -45,6 +45,7 @@ BEGIN @CreateProcedureStatement = @CreateProcedureStatement OUT, @LogTableName = NULL, @CommandToExecute = NULL, + @CallOriginal = 0, @CreateLogTableStatement = NULL; EXEC Facade.CreateSchemaIfNotExists @FacadeDbName = @FacadeDbName, @SchemaName = @SchemaName; From 9df66d9c9e9f337dd0ca3973f7fc104329c2007a Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 3 Dec 2021 15:10:41 -0500 Subject: [PATCH 078/119] cursors are hard, but we are writing tests and learning more. --- ...enerateCreateProcedureSpyStatement.ssp.sql | 33 ++++----- Tests/SpyProcedureTests.class.sql | 72 +++++++++++++++++++ 2 files changed, 87 insertions(+), 18 deletions(-) diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index a13d9f746..928bdc565 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -11,23 +11,20 @@ CREATE PROCEDURE tSQLt.Private_GenerateCreateProcedureSpyStatement @CreateLogTableStatement NVARCHAR(MAX) OUTPUT AS BEGIN - DECLARE @ProcParmListForInsert NVARCHAR(MAX), - @ProcParmListForCall NVARCHAR(MAX), - @TableColList NVARCHAR(MAX), - @ProcParmTypeList NVARCHAR(MAX), - @TableColTypeList NVARCHAR(MAX); + DECLARE @ProcParmListForInsert NVARCHAR(MAX) = ''; + DECLARE @ProcParmListForCall NVARCHAR(MAX) = ''; + DECLARE @TableColList NVARCHAR(MAX) = ''; + DECLARE @ProcParmTypeList NVARCHAR(MAX) = ''; + DECLARE @TableColTypeList NVARCHAR(MAX) = ''; + + DECLARE @Separator CHAR(1) = ''; + DECLARE @ProcParmTypeListSeparator CHAR(1) = ''; + DECLARE @ParamName sysname; + DECLARE @TypeName sysname; + DECLARE @IsOutput BIT; + DECLARE @IsCursorRef BIT; + DECLARE @IsTableType BIT; - DECLARE @Separator CHAR(1), - @ProcParmTypeListSeparator CHAR(1), - @ParamName sysname, - @TypeName sysname, - @IsOutput BIT, - @IsCursorRef BIT, - @IsTableType BIT; - - SELECT @Separator = '', @ProcParmTypeListSeparator = '', - @ProcParmListForInsert = '', @TableColList = '', @ProcParmTypeList = '', @TableColTypeList = ''; - DECLARE Parameters CURSOR FOR SELECT p.name, t.TypeName, p.is_output, p.is_cursor_ref, t.IsTableType FROM sys.parameters p @@ -46,7 +43,7 @@ BEGIN THEN '(SELECT * FROM '+@ParamName+' FOR XML PATH(''row''),TYPE,ROOT('''+STUFF(@ParamName,1,1,'')+'''))' ELSE @ParamName END, --- @ProcParmListForCall = @ProcParmListForCall + @Separator + @ParmList, + @ProcParmListForCall = @ProcParmListForCall + @Separator + @ParamName + CASE WHEN @IsOutput = 1 THEN ' OUT' ELSE '' END, @TableColList = @TableColList + @Separator + '[' + STUFF(@ParamName,1,1,'') + ']', @ProcParmTypeList = @ProcParmTypeList + @ProcParmTypeListSeparator + @ParamName + ' ' + @TypeName + CASE WHEN @IsTableType = 1 THEN ' READONLY' ELSE ' = NULL ' END+ @@ -91,7 +88,7 @@ BEGIN ' AS BEGIN ' + ISNULL(@InsertStmt,'') + CASE WHEN @CallOriginal = 1 - THEN 'EXEC '+OBJECT_SCHEMA_NAME(@ProcedureObjectId)+'.'+OBJECT_NAME(@ProcedureObjectId)+';' + THEN 'EXEC '+OBJECT_SCHEMA_NAME(@ProcedureObjectId)+'.'+OBJECT_NAME(@ProcedureObjectId)+' ' + @ProcParmListForCall + ';' ELSE '' END + ISNULL(@CommandToExecute + ';', '') + diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index 56396b3bf..13e57ae19 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -855,6 +855,78 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROC SpyProcedureTests.[test calls original procedure with OUTPUT parameters if @CallOriginal = 1] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 @AnInt INT OUTPUT, @AString NVARCHAR(MAX) OUTPUT AS BEGIN SELECT @AnInt = 8383, @AString = ''424242''; END;'); + + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1; + + DECLARE @InputOutputInt INT = -17; + DECLARE @InputOutputString NVARCHAR(MAX) = '007'; + DECLARE @ProcedureNameVariableSoWeDoNotGetAWarning NVARCHAR(MAX) = 'SpyProcedureTests.TempProcedure1'; + EXEC @ProcedureNameVariableSoWeDoNotGetAWarning @InputOutputInt OUT, @InputOutputString OUT; + + SELECT @InputOutputInt AS AnInt, @InputOutputString AS AString INTO #Actual; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(8383, '424242'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test calls original procedure with cursor parameters if @CallOriginal = 1] +AS +BEGIN + EXEC(' + CREATE PROCEDURE SpyProcedureTests.TempProcedure1 @Cursor1 CURSOR VARYING OUTPUT, @NotACursor INT, @Cursor2 CURSOR VARYING OUTPUT + AS + BEGIN + DECLARE @AnotherInt INT; + OPEN @Cursor2; + FETCH NEXT FROM @Cursor2 INTO @AnotherInt; + CLOSE @Cursor2; + DEALLOCATE @Cursor2; + SET @Cursor1 = CURSOR FOR SELECT @NotACursor AA, @AnotherInt BB; + END;' + ); + + +-- EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1; + + DECLARE @InputOnlyInt INT = 17; + DECLARE @Cursor1 CURSOR; + DECLARE @Cursor2 CURSOR; SET @Cursor2 = CURSOR FOR SELECT 42; + DECLARE @ProcedureNameVariableSoWeDoNotGetAWarning NVARCHAR(MAX) = 'SpyProcedureTests.TempProcedure1'; + + EXEC @ProcedureNameVariableSoWeDoNotGetAWarning @Cursor1 OUT, @InputOnlyInt, @Cursor2; + + + DECLARE @OutputInt1 INT; + DECLARE @OutputInt2 INT; + + EXEC sp_executesql N' + OPEN @Cursor1; + FETCH NEXT FROM @Cursor1 INTO @OutputInt1, @OutputInt2; + CLOSE Cursor1; + DEALLOCATE @Cursor1;', + N'@Cursor1 CURSOR VARYING OUTPUT, @OutputInt1 INT OUTPUT, @OutputInt2 OUTPUT', + @Cursor1,@OutputInt1,@OutputInt2; + + SELECT @OutputInt1 AS OutputInt1, @OutputInt1 AS OutputInt2 INTO #Actual; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(17, 42); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +EXEC tSQLt.Run 'SpyProcedureTests.[test calls original procedure with cursor parameters if @CallOriginal = 1]' /* Tests for consideration - different parameter types including table valued parameters and cursors(?) From 7884c98331077640fd209604588d7f3c032f1958 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 3 Dec 2021 18:17:37 -0500 Subject: [PATCH 079/119] We *still* don't understand cursors, but we do understand enough to make the test work, but not pass (yet). Names are hard. --- Tests/SpyProcedureTests.class.sql | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index 13e57ae19..3e8b2bcda 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -890,33 +890,28 @@ BEGIN FETCH NEXT FROM @Cursor2 INTO @AnotherInt; CLOSE @Cursor2; DEALLOCATE @Cursor2; - SET @Cursor1 = CURSOR FOR SELECT @NotACursor AA, @AnotherInt BB; + SET @Cursor1 = CURSOR FOR SELECT @NotACursor, @AnotherInt; + OPEN @Cursor1; END;' ); - --- EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1; + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1; DECLARE @InputOnlyInt INT = 17; - DECLARE @Cursor1 CURSOR; - DECLARE @Cursor2 CURSOR; SET @Cursor2 = CURSOR FOR SELECT 42; + DECLARE @Cursor11 CURSOR; + DECLARE @Cursor22 CURSOR; SET @Cursor22 = CURSOR FOR SELECT 42; DECLARE @ProcedureNameVariableSoWeDoNotGetAWarning NVARCHAR(MAX) = 'SpyProcedureTests.TempProcedure1'; - EXEC @ProcedureNameVariableSoWeDoNotGetAWarning @Cursor1 OUT, @InputOnlyInt, @Cursor2; - + EXEC @ProcedureNameVariableSoWeDoNotGetAWarning @Cursor11 OUTPUT, @InputOnlyInt, @Cursor22; DECLARE @OutputInt1 INT; DECLARE @OutputInt2 INT; - EXEC sp_executesql N' - OPEN @Cursor1; - FETCH NEXT FROM @Cursor1 INTO @OutputInt1, @OutputInt2; - CLOSE Cursor1; - DEALLOCATE @Cursor1;', - N'@Cursor1 CURSOR VARYING OUTPUT, @OutputInt1 INT OUTPUT, @OutputInt2 OUTPUT', - @Cursor1,@OutputInt1,@OutputInt2; + FETCH NEXT FROM @Cursor11 INTO @OutputInt1, @OutputInt2; + CLOSE @Cursor11; + DEALLOCATE @Cursor11; - SELECT @OutputInt1 AS OutputInt1, @OutputInt1 AS OutputInt2 INTO #Actual; + SELECT @OutputInt1 AS OutputInt1, @OutputInt2 AS OutputInt2 INTO #Actual; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected VALUES(17, 42); @@ -926,7 +921,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -EXEC tSQLt.Run 'SpyProcedureTests.[test calls original procedure with cursor parameters if @CallOriginal = 1]' +--EXEC tSQLt.Run 'SpyProcedureTests.[test calls original procedure with cursor parameters if @CallOriginal = 1]' /* Tests for consideration - different parameter types including table valued parameters and cursors(?) From 0f366be9fe0f413e4c004be85380a2fceeb259db Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 3 Dec 2021 22:54:27 -0500 Subject: [PATCH 080/119] Tests are great, but cursors are terrible and broken. --- ...enerateCreateProcedureSpyStatement.ssp.sql | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index 928bdc565..3aa3cd27f 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -17,8 +17,8 @@ BEGIN DECLARE @ProcParmTypeList NVARCHAR(MAX) = ''; DECLARE @TableColTypeList NVARCHAR(MAX) = ''; - DECLARE @Separator CHAR(1) = ''; - DECLARE @ProcParmTypeListSeparator CHAR(1) = ''; + DECLARE @SeparatorWithoutCursor CHAR(1) = ''; + DECLARE @SeparatorWithCursor CHAR(1) = ''; DECLARE @ParamName sysname; DECLARE @TypeName sysname; DECLARE @IsOutput BIT; @@ -38,14 +38,13 @@ BEGIN BEGIN IF @IsCursorRef = 0 BEGIN - SELECT @ProcParmListForInsert = @ProcParmListForInsert + @Separator + + SELECT @ProcParmListForInsert = @ProcParmListForInsert + @SeparatorWithoutCursor + CASE WHEN @IsTableType = 1 THEN '(SELECT * FROM '+@ParamName+' FOR XML PATH(''row''),TYPE,ROOT('''+STUFF(@ParamName,1,1,'')+'''))' ELSE @ParamName END, - @ProcParmListForCall = @ProcParmListForCall + @Separator + @ParamName + CASE WHEN @IsOutput = 1 THEN ' OUT' ELSE '' END, - @TableColList = @TableColList + @Separator + '[' + STUFF(@ParamName,1,1,'') + ']', - @ProcParmTypeList = @ProcParmTypeList + @ProcParmTypeListSeparator + @ParamName + ' ' + @TypeName + + @TableColList = @TableColList + @SeparatorWithoutCursor + '[' + STUFF(@ParamName,1,1,'') + ']', + @ProcParmTypeList = @ProcParmTypeList + @SeparatorWithCursor + @ParamName + ' ' + @TypeName + CASE WHEN @IsTableType = 1 THEN ' READONLY' ELSE ' = NULL ' END+ CASE WHEN @IsOutput = 1 THEN ' OUT' ELSE '' END, @TableColTypeList = @TableColTypeList + ',[' + STUFF(@ParamName,1,1,'') + '] ' + @@ -60,15 +59,29 @@ BEGIN ELSE @TypeName END + ' NULL'; - SELECT @Separator = ','; - SELECT @ProcParmTypeListSeparator = ','; + SELECT @SeparatorWithoutCursor = ','; END ELSE BEGIN - SELECT @ProcParmTypeList = @ProcParmTypeListSeparator + @ParamName + ' CURSOR VARYING OUTPUT'; - SELECT @ProcParmTypeListSeparator = ','; + SELECT @ProcParmTypeList = @ProcParmTypeList + @SeparatorWithCursor + @ParamName + ' CURSOR VARYING OUTPUT'; END; - + SELECT + @ProcParmListForCall = @ProcParmListForCall + @SeparatorWithCursor + @ParamName + + CASE + WHEN @IsOutput = 1 + THEN CASE + WHEN @IsCursorRef = 1 + THEN CASE + WHEN(EXISTS(SELECT 1 FROM sys.dm_exec_cursors(@@SPID) WHERE name = @ParamName)) + THEN '' + ELSE ' OUT' + END + ELSE ' OUT' + END + ELSE '' + END; + SELECT @SeparatorWithCursor = ','; + FETCH NEXT FROM Parameters INTO @ParamName, @TypeName, @IsOutput, @IsCursorRef, @IsTableType; END; @@ -88,12 +101,13 @@ BEGIN ' AS BEGIN ' + ISNULL(@InsertStmt,'') + CASE WHEN @CallOriginal = 1 - THEN 'EXEC '+OBJECT_SCHEMA_NAME(@ProcedureObjectId)+'.'+OBJECT_NAME(@ProcedureObjectId)+' ' + @ProcParmListForCall + ';' + THEN 'DECLARE @'+OBJECT_NAME(@ProcedureObjectId)+' NVARCHAR(MAX) = ''EXEC '+OBJECT_SCHEMA_NAME(@ProcedureObjectId)+'.'+OBJECT_NAME(@ProcedureObjectId)+' ' + @ProcParmListForCall + ';'';EXEC(@'+OBJECT_NAME(@ProcedureObjectId)+');' ELSE '' END + ISNULL(@CommandToExecute + ';', '') + ' RETURN;' + ' END;'; + RAISERROR(@CreateProcedureStatement, 0, 1) WITH NOWAIT; RETURN; END; From 987547ce0f17d48767bc3239a673e7b2ee23c153 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 4 Dec 2021 12:09:24 -0500 Subject: [PATCH 081/119] Oh cursors. But we are going to set up some exploratory tests so that we a) understand them better and b) document that understanding. --- Tests/_ExploratoryTests.class.sql | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/Tests/_ExploratoryTests.class.sql b/Tests/_ExploratoryTests.class.sql index b44debb0b..7557c9ea6 100644 --- a/Tests/_ExploratoryTests.class.sql +++ b/Tests/_ExploratoryTests.class.sql @@ -68,6 +68,8 @@ BEGIN END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE [_ExploratoryTests].[test FOR XML returns NULL for empty result set] AS BEGIN @@ -78,3 +80,70 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR_STATUS indicates wheter a cursor variable is set (and other things)] +AS +BEGIN + CREATE TABLE #Actual (Situation NVARCHAR(MAX), [Cursor Status] INT, [Fetch Status] INT); + EXEC('INSERT INTO #Actual SELECT ''Variable not defined'', CURSOR_STATUS(''variable'',''@ACursor''), @@FETCH_STATUS;'); + DECLARE @ACursor CURSOR; + INSERT INTO #Actual SELECT 'Variable defined', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + SET @ACursor = CURSOR FOR SELECT 1; + INSERT INTO #Actual SELECT 'Variable set', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + OPEN @ACursor; + INSERT INTO #Actual SELECT 'Cursor opened', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + DECLARE @IgnoreThis INT; + FETCH NEXT FROM @ACursor INTO @IgnoreThis; + INSERT INTO #Actual SELECT 'Cursor after fetch', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + FETCH NEXT FROM @ACursor INTO @IgnoreThis; + INSERT INTO #Actual SELECT 'Cursor after final fetch', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + CLOSE @ACursor; + INSERT INTO #Actual SELECT 'Cursor closed', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + DEALLOCATE @ACursor; + INSERT INTO #Actual SELECT 'Cursor deallocated', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES + ('Variable not defined',-3,-1), + ('Variable defined',-2,-1), + ('Variable set',-1,-1), + ('Cursor opened',1,-1), + ('Cursor after fetch',1,0), + ('Cursor after final fetch',1,-1), + ('Cursor closed',-1,-1), + ('Cursor deallocated',-2,-1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR OUTPUT parameter requires @Cursor to be opened inside the proc to be visible after call] +AS +BEGIN +EXEC tSQLt.Fail 'multibe test'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE [_ExploratoryTests].[test a @Cursor variable with the CURSOR SET (-1) cannot be passed to a proc with OUTPUT specified in call] +AS +BEGIN +EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR_STATUS indicates whether a cursor variable is set (and other things)] +AS +BEGIN +EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +--EXEC tSQLt.Run [_ExploratoryTests] \ No newline at end of file From 36bbe328785315f400aec3c20f15ee837e19588e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 4 Dec 2021 23:10:42 -0500 Subject: [PATCH 082/119] Learned more about cursors, especially global ones. A lot of tests were written to document our understanding; We made a decision (yet to be documented in tests) to allow only cursor parameters without OUTPUT when using @CallOriginal. --- ...enerateCreateProcedureSpyStatement.ssp.sql | 14 +- Tests/SpyProcedureTests.class.sql | 7 +- Tests/_ExploratoryTests.class.sql | 238 ++++++++++++++++-- 3 files changed, 228 insertions(+), 31 deletions(-) diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index 3aa3cd27f..c6b46799e 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -68,16 +68,8 @@ BEGIN SELECT @ProcParmListForCall = @ProcParmListForCall + @SeparatorWithCursor + @ParamName + CASE - WHEN @IsOutput = 1 - THEN CASE - WHEN @IsCursorRef = 1 - THEN CASE - WHEN(EXISTS(SELECT 1 FROM sys.dm_exec_cursors(@@SPID) WHERE name = @ParamName)) - THEN '' - ELSE ' OUT' - END - ELSE ' OUT' - END + WHEN @IsOutput = 1 AND @IsCursorRef <> 1 + THEN ' OUT' ELSE '' END; SELECT @SeparatorWithCursor = ','; @@ -101,7 +93,7 @@ BEGIN ' AS BEGIN ' + ISNULL(@InsertStmt,'') + CASE WHEN @CallOriginal = 1 - THEN 'DECLARE @'+OBJECT_NAME(@ProcedureObjectId)+' NVARCHAR(MAX) = ''EXEC '+OBJECT_SCHEMA_NAME(@ProcedureObjectId)+'.'+OBJECT_NAME(@ProcedureObjectId)+' ' + @ProcParmListForCall + ';'';EXEC(@'+OBJECT_NAME(@ProcedureObjectId)+');' + THEN 'EXEC '+OBJECT_SCHEMA_NAME(@ProcedureObjectId)+'.'+OBJECT_NAME(@ProcedureObjectId)+' ' + @ProcParmListForCall + ';' ELSE '' END + ISNULL(@CommandToExecute + ';', '') + diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index 3e8b2bcda..4b66dd20e 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -881,6 +881,8 @@ GO CREATE PROC SpyProcedureTests.[test calls original procedure with cursor parameters if @CallOriginal = 1] AS BEGIN + EXEC tSQLt.Fail 'This test needs rewriting, as we do not allow OUTPUT on the call to original anymore'; + EXEC(' CREATE PROCEDURE SpyProcedureTests.TempProcedure1 @Cursor1 CURSOR VARYING OUTPUT, @NotACursor INT, @Cursor2 CURSOR VARYING OUTPUT AS @@ -925,8 +927,11 @@ GO /* Tests for consideration - different parameter types including table valued parameters and cursors(?) +- procedure name or schema requires quoting - @CommandToExecute is executed before the original procedure when @CallOriginal=1 - CLR data type parameters? - does not call original if @CallOriginal <> 1 (NULL or 0) and also if not specified - +- provide full name of original procedure in @SpyProcedureOriginalObjectName variable +--- quoting required, contains ' +- */ diff --git a/Tests/_ExploratoryTests.class.sql b/Tests/_ExploratoryTests.class.sql index 7557c9ea6..aaea08e0d 100644 --- a/Tests/_ExploratoryTests.class.sql +++ b/Tests/_ExploratoryTests.class.sql @@ -82,38 +82,38 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE [_ExploratoryTests].[test CURSOR_STATUS indicates wheter a cursor variable is set (and other things)] +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR_STATUS indicates whether a cursor variable is set (and other things)] AS BEGIN CREATE TABLE #Actual (Situation NVARCHAR(MAX), [Cursor Status] INT, [Fetch Status] INT); - EXEC('INSERT INTO #Actual SELECT ''Variable not defined'', CURSOR_STATUS(''variable'',''@ACursor''), @@FETCH_STATUS;'); + EXEC('INSERT INTO #Actual SELECT ''Variable not defined'', CURSOR_STATUS(''variable'',''@ACursor''),NULL;'); DECLARE @ACursor CURSOR; - INSERT INTO #Actual SELECT 'Variable defined', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + INSERT INTO #Actual SELECT 'Variable defined (DECLARE)', CURSOR_STATUS('variable','@ACursor'), NULL; SET @ACursor = CURSOR FOR SELECT 1; - INSERT INTO #Actual SELECT 'Variable set', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + INSERT INTO #Actual SELECT 'Variable allocated (SET)', CURSOR_STATUS('variable','@ACursor'), NULL; OPEN @ACursor; - INSERT INTO #Actual SELECT 'Cursor opened', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + INSERT INTO #Actual SELECT 'Cursor opened', CURSOR_STATUS('variable','@ACursor'), NULL; DECLARE @IgnoreThis INT; FETCH NEXT FROM @ACursor INTO @IgnoreThis; INSERT INTO #Actual SELECT 'Cursor after fetch', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; FETCH NEXT FROM @ACursor INTO @IgnoreThis; INSERT INTO #Actual SELECT 'Cursor after final fetch', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; CLOSE @ACursor; - INSERT INTO #Actual SELECT 'Cursor closed', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + INSERT INTO #Actual SELECT 'Cursor closed', CURSOR_STATUS('variable','@ACursor'), NULL; DEALLOCATE @ACursor; - INSERT INTO #Actual SELECT 'Cursor deallocated', CURSOR_STATUS('variable','@ACursor'), @@FETCH_STATUS; + INSERT INTO #Actual SELECT 'Cursor deallocated', CURSOR_STATUS('variable','@ACursor'), NULL; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected VALUES - ('Variable not defined',-3,-1), - ('Variable defined',-2,-1), - ('Variable set',-1,-1), - ('Cursor opened',1,-1), + ('Variable not defined',-3,NULL), + ('Variable defined (DECLARE)',-2,NULL), + ('Variable allocated (SET)',-1,NULL), + ('Cursor opened',1,NULL), ('Cursor after fetch',1,0), ('Cursor after final fetch',1,-1), - ('Cursor closed',-1,-1), - ('Cursor deallocated',-2,-1); + ('Cursor closed',-1,NULL), + ('Cursor deallocated',-2,NULL); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; @@ -121,29 +121,229 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE [_ExploratoryTests].[test CURSOR OUTPUT parameter requires @Cursor to be opened inside the proc to be visible after call] +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR will not be passed out through OUTPUT @parameter if it has not been opened (CURSOR_STATUS = -1)] AS BEGIN -EXEC tSQLt.Fail 'multibe test'; + DECLARE @CursorProc NVARCHAR(MAX) = '[_ExploratoryTests].CursorProc'; + EXEC(' + CREATE PROCEDURE '+@CursorProc+' + @CursorParameter CURSOR VARYING OUTPUT + AS + BEGIN + SET @CursorParameter = CURSOR FOR SELECT 42; + END; + '); + + DECLARE @CursorVariable CURSOR; + + EXEC @CursorProc @CursorParameter = @CursorVariable OUTPUT; + + CREATE TABLE #Actual ([Cursor Status] INT); + INSERT INTO #Actual SELECT CURSOR_STATUS('variable','@CursorVariable'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES(-2); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE [_ExploratoryTests].[test a @Cursor variable with the CURSOR SET (-1) cannot be passed to a proc with OUTPUT specified in call] +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR can be passed out through OUTPUT @parameter if it has been opened (CURSOR_STATUS = 1)] AS BEGIN -EXEC tSQLt.Fail 'TODO'; + DECLARE @CursorProc NVARCHAR(MAX) = '[_ExploratoryTests].CursorProc'; + EXEC(' + CREATE PROCEDURE '+@CursorProc+' + @CursorParameter CURSOR VARYING OUTPUT + AS + BEGIN + SET @CursorParameter = CURSOR FOR SELECT 42; + OPEN @CursorParameter; + END; + '); + + DECLARE @IntValue INT = NULL; + DECLARE @CursorVariable CURSOR; + + EXEC @CursorProc @CursorParameter = @CursorVariable OUTPUT; + + FETCH NEXT FROM @CursorVariable INTO @IntValue; + CLOSE @CursorVariable; + DEALLOCATE @CursorVariable; + + EXEC tSQLt.AssertEquals @Expected = 42, @Actual = @IntValue; END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE [_ExploratoryTests].[test CURSOR_STATUS indicates whether a cursor variable is set (and other things)] +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR cannot be passed to a proc with OUTPUT specified in the call if it has been allocated (SET, CURSOR_STATUS = -1)] AS BEGIN -EXEC tSQLt.Fail 'TODO'; + DECLARE @CursorProc NVARCHAR(MAX) = '[_ExploratoryTests].CursorProc'; + EXEC(' + CREATE PROCEDURE '+@CursorProc+' + @CursorParameter CURSOR VARYING OUTPUT + AS + BEGIN + RETURN; + END; + '); + + DECLARE @IntValue INT = NULL; + DECLARE @CursorVariable CURSOR; + SET @CursorVariable = CURSOR FOR SELECT 13; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'The variable ''@CursorVariable'' cannot be used as a parameter because a CURSOR OUTPUT parameter must not have a cursor allocated to it before execution of the procedure.'; + EXEC @CursorProc @CursorParameter = @CursorVariable OUTPUT; + END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR can be passed to a proc without OUTPUT specified in the call even if it has been allocated (SET, CURSOR_STATUS = -1)] +AS +BEGIN + DECLARE @CursorProc NVARCHAR(MAX) = '[_ExploratoryTests].CursorProc'; + EXEC(' + CREATE PROCEDURE '+@CursorProc+' + @CursorParameter CURSOR VARYING OUTPUT + AS + BEGIN + RETURN; + END; + '); + + DECLARE @IntValue INT = NULL; + DECLARE @CursorVariable CURSOR; + SET @CursorVariable = CURSOR FOR SELECT 13; + + EXEC tSQLt.ExpectNoException; + EXEC @CursorProc @CursorParameter = @CursorVariable; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE [_ExploratoryTests].[test CURSOR gets deallocated when its variable goes out of scope?] +AS +BEGIN + SELECT IDENTITY(INT,1,1) AS ID, name, is_open, fetch_status + INTO #Actual FROM (SELECT NULL)ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = '@ACursor'; + + EXEC(' + DECLARE @ACursor CURSOR; + SET @ACursor = CURSOR FOR SELECT 42; + INSERT INTO #Actual SELECT name, is_open, fetch_status + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''@ACursor''; + OPEN @ACursor; + INSERT INTO #Actual SELECT name, is_open, fetch_status + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''@ACursor''; + DECLARE @I INT; FETCH NEXT FROM @ACursor INTO @I; + INSERT INTO #Actual SELECT name, is_open, fetch_status + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''@ACursor''; + '); + + INSERT INTO #Actual SELECT name, is_open, fetch_status + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = '@ACursor'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES + (1,NULL,NULL,NULL), + (2,'@ACursor','false', -9), + (3,'@ACursor','true', -9), + (4,'@ACursor','true', 0), + (5,NULL,NULL,NULL); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE [_ExploratoryTests].[test LOCAL CURSOR (not a variable) gets deallocated when it goes out of scope?] +AS +BEGIN + SELECT IDENTITY(INT,1,1) AS ID, name, is_open, fetch_status, CURSOR_STATUS('local','ACursor') AS [cursor status] + INTO #Actual FROM (SELECT NULL)ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = 'ACursor'; + + EXEC(' + DECLARE ACursor CURSOR LOCAL FOR SELECT 42; + INSERT INTO #Actual SELECT name, is_open, fetch_status, CURSOR_STATUS(''local'',''ACursor'') + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''ACursor''; + OPEN ACursor; + INSERT INTO #Actual SELECT name, is_open, fetch_status, CURSOR_STATUS(''local'',''ACursor'') + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''ACursor''; + DECLARE @I INT; FETCH NEXT FROM ACursor INTO @I; + INSERT INTO #Actual SELECT name, is_open, fetch_status, CURSOR_STATUS(''local'',''ACursor'') + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''ACursor''; + '); + + INSERT INTO #Actual SELECT name, is_open, fetch_status, CURSOR_STATUS('local','ACursor') + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = 'ACursor'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES + (1,NULL,NULL,NULL, -3), + (2,'ACursor','false', -9, -1), + (3,'ACursor','true', -9, 1), + (4,'ACursor','true', 0, 1), + (5,NULL,NULL,NULL, -3); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE [_ExploratoryTests].[test GLOBAL CURSOR (not a variable) gets deallocated when it goes out of scope?] +AS +BEGIN + SELECT IDENTITY(INT,1,1) AS ID, name, is_open, fetch_status, CURSOR_STATUS('global','ACursor') AS [cursor status] + INTO #Actual FROM (SELECT NULL)ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = 'ACursor'; + + EXEC(' + DECLARE ACursor CURSOR /*GLOBAL*/ FOR SELECT 42; + INSERT INTO #Actual SELECT name, is_open, fetch_status, CURSOR_STATUS(''global'',''ACursor'') + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''ACursor''; + OPEN ACursor; + INSERT INTO #Actual SELECT name, is_open, fetch_status, CURSOR_STATUS(''global'',''ACursor'') + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''ACursor''; + DECLARE @I INT; FETCH NEXT FROM ACursor INTO @I; + INSERT INTO #Actual SELECT name, is_open, fetch_status, CURSOR_STATUS(''global'',''ACursor'') + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = ''ACursor''; + '); + + INSERT INTO #Actual SELECT name, is_open, fetch_status, CURSOR_STATUS('global','ACursor') + FROM (SELECT NULL )ID(ID) LEFT JOIN sys.dm_exec_cursors(@@SPID) ON name = 'ACursor'; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + VALUES + (1,NULL,NULL,NULL, -3), + (2,'ACursor','false', -9, -1), + (3,'ACursor','true', -9, 1), + (4,'ACursor','true', 0, 1), + (5,'ACursor','true', 0, 1); + BEGIN TRY CLOSE ACursor; END TRY BEGIN CATCH END CATCH; + BEGIN TRY DEALLOCATE ACursor; END TRY BEGIN CATCH END CATCH; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--CREATE PROCEDURE [_ExploratoryTests].[test TBD] +--AS +--BEGIN +-- EXEC tSQLt.Fail 'TODO'; +--END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO --EXEC tSQLt.Run [_ExploratoryTests] \ No newline at end of file From 4d045b42f86a8e7e719114a6967e070dea7332e0 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 5 Dec 2021 10:38:14 -0500 Subject: [PATCH 083/119] Finished cursor parameters; created a test which always throws a deadlock! So sophisticated, much wow. --- ...enerateCreateProcedureSpyStatement.ssp.sql | 4 +- Tests/SpyProcedureTests.class.sql | 134 +++++++++++++++--- 2 files changed, 116 insertions(+), 22 deletions(-) diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index c6b46799e..ccad88d25 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -93,13 +93,13 @@ BEGIN ' AS BEGIN ' + ISNULL(@InsertStmt,'') + CASE WHEN @CallOriginal = 1 - THEN 'EXEC '+OBJECT_SCHEMA_NAME(@ProcedureObjectId)+'.'+OBJECT_NAME(@ProcedureObjectId)+' ' + @ProcParmListForCall + ';' + THEN 'EXEC '+QUOTENAME(OBJECT_SCHEMA_NAME(@ProcedureObjectId))+'.'+QUOTENAME(OBJECT_NAME(@ProcedureObjectId))+' ' + @ProcParmListForCall + ';' ELSE '' END + ISNULL(@CommandToExecute + ';', '') + ' RETURN;' + ' END;'; - RAISERROR(@CreateProcedureStatement, 0, 1) WITH NOWAIT; + --RAISERROR(@CreateProcedureStatement, 0, 1) WITH NOWAIT; RETURN; END; diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index 4b66dd20e..c8ca2bf60 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -836,6 +836,75 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROC SpyProcedureTests.[test does not call the original procedure if @CallOriginal = 0] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 AS BEGIN INSERT INTO #Actual VALUES (''SpyProcedureTests.TempProcedure1 called''); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX)); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 0; + + EXEC('SpyProcedureTests.TempProcedure1'); + + EXEC tSQLt.AssertEmptyTable '#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test does not call the original procedure if @CallOriginal = NULL] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 AS BEGIN INSERT INTO #Actual VALUES (''SpyProcedureTests.TempProcedure1 called''); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX)); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = NULL; + + EXEC('SpyProcedureTests.TempProcedure1'); + + EXEC tSQLt.AssertEmptyTable '#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test does not call the original procedure if @CallOriginal is not specified] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 AS BEGIN INSERT INTO #Actual VALUES (''SpyProcedureTests.TempProcedure1 called''); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX)); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1'; + + EXEC('SpyProcedureTests.TempProcedure1'); + + EXEC tSQLt.AssertEmptyTable '#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test calls the original procedure if @CallOriginal = 1 even if schema or procedure name require quoting] +AS +BEGIN + EXEC('CREATE SCHEMA [Inner Test Schema];'); + EXEC('CREATE PROCEDURE [Inner Test Schema].[Temp Procedure 1] AS BEGIN INSERT INTO #Actual VALUES (''[Inner Test Schema].[Temp Procedure 1] called''); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX)); + + EXEC tSQLt.SpyProcedure @ProcedureName = '[Inner Test Schema].[Temp Procedure 1]', @CallOriginal = 1; + + EXEC('[Inner Test Schema].[Temp Procedure 1]'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'[Inner Test Schema].[Temp Procedure 1] called'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + CREATE PROC SpyProcedureTests.[test calls original procedure with parameters if @CallOriginal = 1] AS BEGIN @@ -881,42 +950,69 @@ GO CREATE PROC SpyProcedureTests.[test calls original procedure with cursor parameters if @CallOriginal = 1] AS BEGIN - EXEC tSQLt.Fail 'This test needs rewriting, as we do not allow OUTPUT on the call to original anymore'; - EXEC(' CREATE PROCEDURE SpyProcedureTests.TempProcedure1 @Cursor1 CURSOR VARYING OUTPUT, @NotACursor INT, @Cursor2 CURSOR VARYING OUTPUT AS BEGIN - DECLARE @AnotherInt INT; + DECLARE @FirstInt INT; + DECLARE @SecondInt INT; + OPEN @Cursor1; + FETCH NEXT FROM @Cursor1 INTO @FirstInt; + CLOSE @Cursor1; + DEALLOCATE @Cursor1; OPEN @Cursor2; - FETCH NEXT FROM @Cursor2 INTO @AnotherInt; + FETCH NEXT FROM @Cursor2 INTO @SecondInt; CLOSE @Cursor2; - DEALLOCATE @Cursor2; - SET @Cursor1 = CURSOR FOR SELECT @NotACursor, @AnotherInt; - OPEN @Cursor1; + DEALLOCATE @Cursor2; + INSERT INTO #Actual VALUES(@FirstInt, @SecondInt, @NotACursor); END;' ); + CREATE TABLE #Actual (intA INT, intB INT, intC INT); EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1; DECLARE @InputOnlyInt INT = 17; - DECLARE @Cursor11 CURSOR; + DECLARE @Cursor11 CURSOR; SET @Cursor11 = CURSOR FOR SELECT 36; DECLARE @Cursor22 CURSOR; SET @Cursor22 = CURSOR FOR SELECT 42; DECLARE @ProcedureNameVariableSoWeDoNotGetAWarning NVARCHAR(MAX) = 'SpyProcedureTests.TempProcedure1'; - EXEC @ProcedureNameVariableSoWeDoNotGetAWarning @Cursor11 OUTPUT, @InputOnlyInt, @Cursor22; + EXEC @ProcedureNameVariableSoWeDoNotGetAWarning @Cursor11, @InputOnlyInt, @Cursor22; - DECLARE @OutputInt1 INT; - DECLARE @OutputInt2 INT; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(36, 42, 17); - FETCH NEXT FROM @Cursor11 INTO @OutputInt1, @OutputInt2; - CLOSE @Cursor11; - DEALLOCATE @Cursor11; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test calls original procedure with table valued parameters if @CallOriginal = 1] +AS +BEGIN + CREATE TYPE SpyProcedureTests.Type1 AS TABLE(A INT); + EXEC(' + CREATE PROCEDURE SpyProcedureTests.TempProcedure1 @Table1 SpyProcedureTests.Type1 READONLY, @NotATable INT, @Table2 SpyProcedureTests.Type1 READONLY + AS + BEGIN + DECLARE @FirstInt INT = (SELECT A FROM @Table1); + DECLARE @SecondInt INT = (SELECT A FROM @Table2); + INSERT INTO #Actual VALUES(@FirstInt, @SecondInt, @NotATable); + END;' + ); + CREATE TABLE #Actual (intA INT, intB INT, intC INT); - SELECT @OutputInt1 AS OutputInt1, @OutputInt2 AS OutputInt2 INTO #Actual; + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1; + EXEC(' + DECLARE @InputOnlyInt INT = 17; + DECLARE @Table11 AS SpyProcedureTests.Type1; INSERT INTO @Table11 VALUES(37); + DECLARE @Table22 AS SpyProcedureTests.Type1; INSERT INTO @Table22 VALUES(42); + + DECLARE @ProcedureNameVariableSoWeDoNotGetAWarning NVARCHAR(MAX) = ''SpyProcedureTests.TempProcedure1''; + EXEC @ProcedureNameVariableSoWeDoNotGetAWarning @Table11, @InputOnlyInt, @Table22; + '); SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - INSERT INTO #Expected VALUES(17, 42); + INSERT INTO #Expected VALUES(36, 42, 17); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; @@ -926,12 +1022,10 @@ GO --EXEC tSQLt.Run 'SpyProcedureTests.[test calls original procedure with cursor parameters if @CallOriginal = 1]' /* Tests for consideration -- different parameter types including table valued parameters and cursors(?) -- procedure name or schema requires quoting +- different parameter types including table valued parameters - @CommandToExecute is executed before the original procedure when @CallOriginal=1 - CLR data type parameters? -- does not call original if @CallOriginal <> 1 (NULL or 0) and also if not specified - provide full name of original procedure in @SpyProcedureOriginalObjectName variable --- quoting required, contains ' -- +- TODO?: We need to document how cursors are handled and how the user can work around. local, global, and variable. */ From 8bcefee4a153aab32bbb697f9b9ec0993dde5e2e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 5 Dec 2021 14:22:26 -0500 Subject: [PATCH 084/119] Wrote more tests. One is failing. It's suspicious. --- ...enerateCreateProcedureSpyStatement.ssp.sql | 5 +- Tests/SpyProcedureTests.class.sql | 92 +++++++++++++++++-- 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index ccad88d25..8b82c9a87 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -92,14 +92,15 @@ BEGIN 'CREATE PROCEDURE ' + @OriginalProcedureName + ' ' + @ProcParmTypeList + ' AS BEGIN ' + ISNULL(@InsertStmt,'') + + 'DECLARE @SpyProcedureOriginalObjectName NVARCHAR(MAX) = '''+REPLACE(QUOTENAME(OBJECT_SCHEMA_NAME(@ProcedureObjectId))+'.'+QUOTENAME(OBJECT_NAME(@ProcedureObjectId)),'''','''''')+''';'+ + ISNULL(@CommandToExecute + ';', '') + CASE WHEN @CallOriginal = 1 THEN 'EXEC '+QUOTENAME(OBJECT_SCHEMA_NAME(@ProcedureObjectId))+'.'+QUOTENAME(OBJECT_NAME(@ProcedureObjectId))+' ' + @ProcParmListForCall + ';' ELSE '' END + - ISNULL(@CommandToExecute + ';', '') + ' RETURN;' + ' END;'; - --RAISERROR(@CreateProcedureStatement, 0, 1) WITH NOWAIT; + RAISERROR(@CreateProcedureStatement, 0, 1) WITH NOWAIT; RETURN; END; diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index c8ca2bf60..a4322ea19 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -986,10 +986,11 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE TYPE SpyProcedureTests.Type1 AS TABLE(A INT); +GO CREATE PROC SpyProcedureTests.[test calls original procedure with table valued parameters if @CallOriginal = 1] AS BEGIN - CREATE TYPE SpyProcedureTests.Type1 AS TABLE(A INT); EXEC(' CREATE PROCEDURE SpyProcedureTests.TempProcedure1 @Table1 SpyProcedureTests.Type1 READONLY, @NotATable INT, @Table2 SpyProcedureTests.Type1 READONLY AS @@ -1005,7 +1006,7 @@ BEGIN EXEC(' DECLARE @InputOnlyInt INT = 17; - DECLARE @Table11 AS SpyProcedureTests.Type1; INSERT INTO @Table11 VALUES(37); + DECLARE @Table11 AS SpyProcedureTests.Type1; INSERT INTO @Table11 VALUES(36); DECLARE @Table22 AS SpyProcedureTests.Type1; INSERT INTO @Table22 VALUES(42); DECLARE @ProcedureNameVariableSoWeDoNotGetAWarning NVARCHAR(MAX) = ''SpyProcedureTests.TempProcedure1''; @@ -1019,12 +1020,91 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---EXEC tSQLt.Run 'SpyProcedureTests.[test calls original procedure with cursor parameters if @CallOriginal = 1]' +CREATE PROC SpyProcedureTests.[test calls the original procedure after @CommandToExecute if @CallOriginal = 1] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 AS BEGIN INSERT INTO #Actual VALUES (''SpyProcedureTests.TempProcedure1 called''); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX)); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CommandToExecute='INSERT INTO #Actual VALUES (''CommandToExecute called'');', @CallOriginal = 1; + + EXEC('SpyProcedureTests.TempProcedure1'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'CommandToExecute called'),(2,'SpyProcedureTests.TempProcedure1 called'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test @SpyProcedureOriginalObjectName contains original proc name inside spy] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 @I INT = NULL AS BEGIN INSERT INTO #Actual VALUES (''SpyProcedureTests.TempProcedure1 called'', @I); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX), I INT); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CommandToExecute='EXEC @SpyProcedureOriginalObjectName 42;'; + + EXEC('SpyProcedureTests.TempProcedure1'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'SpyProcedureTests.TempProcedure1 called',42); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test @SpyProcedureOriginalObjectName contains original proc name inside spy even if quoting is required] +AS +BEGIN + EXEC('CREATE SCHEMA [Inner Test.Schema 1];'); + EXEC('CREATE PROCEDURE [Inner Test.Schema 1].[Temp Proc.edure 1] @I INT = NULL AS BEGIN INSERT INTO #Actual VALUES (''[Inner Test.Schema 1].[Temp Proc.edure 1] called'', @I); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX), I INT); + + EXEC tSQLt.SpyProcedure @ProcedureName = '[Inner Test.Schema 1].[Temp Proc.edure 1]', @CommandToExecute='EXEC @SpyProcedureOriginalObjectName 42;'; + + EXEC('[Inner Test.Schema 1].[Temp Proc.edure 1]'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'[Inner Test.Schema 1].[Temp Proc.edure 1] called',42); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test @SpyProcedureOriginalObjectName contains original proc name even if there are single quotes involved] +AS +BEGIN + EXEC('CREATE SCHEMA [I''nn''er Test.Schema 1];'); + EXEC('CREATE PROCEDURE [I''nn''er Test.Schema 1].[T''emp Proc.edure 1] @I INT = NULL AS BEGIN RETURN END;');--INSERT INTO #Actual VALUES (''[I''''nn''''er Test.Schema 1].[T''''emp Proc.edure 1] called'', @I); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX), I INT); +RAISERROR('GH',0,1)WITH NOWAIT; + EXEC tSQLt.SpyProcedure @ProcedureName = '[I''nn''er Test.Schema 1].[T''emp Proc.edure 1]', @CommandToExecute='EXEC @SpyProcedureOriginalObjectName 42;'; +RAISERROR('GH2',0,1)WITH NOWAIT; + + EXEC('[I''nn''er Test.Schema 1].[T''emp Proc.edure 1]'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'[I''nn''er Test.Schema 1].[T''emp Proc.edure 1] called',42); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + + + +--EXEC tSQLt.Run 'SpyProcedureTests.[test calls original procedure with table valued parameters if @CallOriginal = 1]' /* Tests for consideration -- different parameter types including table valued parameters -- @CommandToExecute is executed before the original procedure when @CallOriginal=1 -- CLR data type parameters? - provide full name of original procedure in @SpyProcedureOriginalObjectName variable --- quoting required, contains ' - TODO?: We need to document how cursors are handled and how the user can work around. local, global, and variable. From 0f1ec61a5e309583b0ae08ce909ee81adeb3cc9f Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 5 Dec 2021 18:25:07 -0500 Subject: [PATCH 085/119] Refactored tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql to its own file; wrote more tests. --- Source/BuildOrder.txt | 1 + Source/Source.ssmssqlproj | 6 +++ ...enerateCreateProcedureSpyStatement.ssp.sql | 2 +- Source/tSQLt.Private_RenameObject.ssp.sql | 2 +- ...ProcedureCanBeUsedWithSpyProcedure.ssp.sql | 21 ++++++++ Source/tSQLt.SpyProcedure.ssp.sql | 2 - Source/tSQLt.class.sql | 17 ------ ...Private_MarktSQLtTempObjectTests.class.sql | 53 +++++++++++++++++-- Tests/SpyProcedureTests.class.sql | 32 +++++++++-- 9 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 Source/tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index e2c1e89d0..27e07eebb 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -78,6 +78,7 @@ tSQLt.Private_CreateFakeOfTable.ssp.sql tSQLt.FakeTable.ssp.sql tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql tSQLt.Private_CreateProcedureSpy.ssp.sql +tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql tSQLt.SpyProcedure.ssp.sql tSQLt.Private_GetCommaSeparatedColumnList.sfn.sql tSQLt.Private_CreateResultTableForCompareTables.ssp.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index d7f452695..54846dae3 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -576,6 +576,12 @@ tSQLt.Private_ValidateObjectsCompatibleWithFakeFunction.ssp.sql + + + + + tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql + diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index 8b82c9a87..9f75a1e4c 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -100,7 +100,7 @@ BEGIN END + ' RETURN;' + ' END;'; - RAISERROR(@CreateProcedureStatement, 0, 1) WITH NOWAIT; + --RAISERROR(@CreateProcedureStatement, 0, 1) WITH NOWAIT; RETURN; END; diff --git a/Source/tSQLt.Private_RenameObject.ssp.sql b/Source/tSQLt.Private_RenameObject.ssp.sql index e464b7a52..4b27babbf 100644 --- a/Source/tSQLt.Private_RenameObject.ssp.sql +++ b/Source/tSQLt.Private_RenameObject.ssp.sql @@ -9,7 +9,7 @@ AS BEGIN DECLARE @RenameCmd NVARCHAR(MAX); SET @RenameCmd = 'EXEC sp_rename ''' + - @SchemaName + '.' + @ObjectName + ''', ''' + + REPLACE(@SchemaName + '.' + @ObjectName, '''', '''''') + ''', ''' + @NewName + ''',''OBJECT'';'; EXEC tSQLt.SuppressOutput @RenameCmd; diff --git a/Source/tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql b/Source/tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql new file mode 100644 index 000000000..608e394ed --- /dev/null +++ b/Source/tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql @@ -0,0 +1,21 @@ +IF OBJECT_ID('tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure') IS NOT NULL DROP PROCEDURE tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure + @ProcedureName NVARCHAR(MAX) +AS +BEGIN + IF NOT EXISTS(SELECT 1 FROM sys.procedures WHERE object_id = OBJECT_ID(@ProcedureName)) + BEGIN + RAISERROR('Cannot use SpyProcedure on %s because the procedure does not exist', 16, 10, @ProcedureName) WITH NOWAIT; + END; + + IF (1020 < (SELECT COUNT(*) FROM sys.parameters WHERE object_id = OBJECT_ID(@ProcedureName))) + BEGIN + RAISERROR('Cannot use SpyProcedure on procedure %s because it contains more than 1020 parameters', 16, 10, @ProcedureName) WITH NOWAIT; + END; +END; +GO + + diff --git a/Source/tSQLt.SpyProcedure.ssp.sql b/Source/tSQLt.SpyProcedure.ssp.sql index eb81b287f..4a9368688 100644 --- a/Source/tSQLt.SpyProcedure.ssp.sql +++ b/Source/tSQLt.SpyProcedure.ssp.sql @@ -20,7 +20,6 @@ BEGIN DECLARE @NewNameOfOriginalObject NVARCHAR(MAX); - DECLARE @LogTableObjectId INT = OBJECT_ID(@LogTableName); IF(@LogTableObjectId IS NOT NULL) BEGIN @@ -37,7 +36,6 @@ BEGIN @CreateProcedureStatement = @CreateProcedureStatement OUT, @CreateLogTableStatement = @CreateLogTableStatement OUT; - EXEC(@CreateLogTableStatement); EXEC(@CreateProcedureStatement); diff --git a/Source/tSQLt.class.sql b/Source/tSQLt.class.sql index 8ae118d90..cbd5533f3 100644 --- a/Source/tSQLt.class.sql +++ b/Source/tSQLt.class.sql @@ -173,23 +173,6 @@ RETURN WITH A(Cnt, SuccessCnt, SkippedCnt, FailCnt, ErrorCnt) AS ( FROM A; GO -CREATE PROCEDURE tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure - @ProcedureName NVARCHAR(MAX) -AS -BEGIN - IF NOT EXISTS(SELECT 1 FROM sys.procedures WHERE object_id = OBJECT_ID(@ProcedureName)) - BEGIN - RAISERROR('Cannot use SpyProcedure on %s because the procedure does not exist', 16, 10, @ProcedureName) WITH NOWAIT; - END; - - IF (1020 < (SELECT COUNT(*) FROM sys.parameters WHERE object_id = OBJECT_ID(@ProcedureName))) - BEGIN - RAISERROR('Cannot use SpyProcedure on procedure %s because it contains more than 1020 parameters', 16, 10, @ProcedureName) WITH NOWAIT; - END; -END; -GO - - CREATE PROCEDURE tSQLt.AssertEquals @Expected SQL_VARIANT, @Actual SQL_VARIANT, diff --git a/Tests/Private_MarktSQLtTempObjectTests.class.sql b/Tests/Private_MarktSQLtTempObjectTests.class.sql index 83b04387a..097aa710e 100644 --- a/Tests/Private_MarktSQLtTempObjectTests.class.sql +++ b/Tests/Private_MarktSQLtTempObjectTests.class.sql @@ -2,13 +2,14 @@ EXEC tSQLt.NewTestClass 'Private_MarktSQLtTempObjectTests'; GO CREATE PROCEDURE Private_MarktSQLtTempObjectTests.[assert creates two extended properties on object] @ObjectName NVARCHAR(MAX), - @ObjectType NVARCHAR(MAX) + @ObjectType NVARCHAR(MAX), + @NewNameOfOriginalObject NVARCHAR(MAX) = 'ARandomString' AS BEGIN EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @ObjectName, @ObjectType = @ObjectType, - @NewNameOfOriginalObject = 'ARandomString'; + @NewNameOfOriginalObject = @NewNameOfOriginalObject; SELECT name, CAST(value AS NVARCHAR(MAX)) value INTO #Actual @@ -19,8 +20,8 @@ BEGIN SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - INSERT INTO #Expected VALUES('tSQLt.IsTempObject', '1'), - ('tSQLt.Private_TestDouble_OrgObjectName','ARandomString'); + INSERT INTO #Expected VALUES('tSQLt.IsTempObject', '1'); + INSERT INTO #Expected SELECT 'tSQLt.Private_TestDouble_OrgObjectName',@NewNameOfOriginalObject WHERE @NewNameOfOriginalObject IS NOT NULL; EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; @@ -167,4 +168,46 @@ BEGIN EXEC tSQLt.AssertEmptyTable '#Actual'; END; -GO \ No newline at end of file +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_MarktSQLtTempObjectTests.[test can mark an object with a schema and object name which include single quotes] +AS +BEGIN + EXEC('CREATE SCHEMA [Private_Mark''tSQLtTempObjectTests];'); + EXEC('CREATE PROCEDURE [Private_Mark''tSQLtTempObjectTests].[TempProcedure''1] AS RETURN;'); + EXEC Private_MarktSQLtTempObjectTests.[assert creates two extended properties on object] + @ObjectName = '[Private_Mark''tSQLtTempObjectTests].[TempProcedure''1]', + @ObjectType = N'PROCEDURE'; +END +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_MarktSQLtTempObjectTests.[test can mark an object with a schema and object name which includes a single quotes, spaces, and dots] +AS +BEGIN + EXEC('CREATE SCHEMA [P.rivate_Mark''tSQLtTempObj ectTests];'); + EXEC('CREATE PROCEDURE [P.rivate_Mark''tSQLtTempObj ectTests].[Tem.pPr ocedure''1] AS RETURN;'); + EXEC Private_MarktSQLtTempObjectTests.[assert creates two extended properties on object] + @ObjectName = '[P.rivate_Mark''tSQLtTempObj ectTests].[Tem.pPr ocedure''1]', + @ObjectType = N'PROCEDURE'; +END +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_MarktSQLtTempObjectTests.[test can mark a table with a schema and object name which includes a single quotes, spaces, and dots] +AS +BEGIN + EXEC('CREATE SCHEMA [P.rivate_Mark''tSQLtTempObj ectTests];'); + EXEC('CREATE TABLE [P.rivate_Mark''tSQLtTempObj ectTests].[Tem.pPr ocedure''1] (AA INT);'); + EXEC Private_MarktSQLtTempObjectTests.[assert creates two extended properties on object] + @ObjectName = '[P.rivate_Mark''tSQLtTempObj ectTests].[Tem.pPr ocedure''1]', + @ObjectType = N'TABLE', + @NewNameOfOriginalObject = NULL +END +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + + + diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index a4322ea19..d68e44e5b 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -351,6 +351,30 @@ BEGIN END; GO +CREATE PROC SpyProcedureTests.[test SpyProcedure handles procedure and schema names with single quotes] +AS +BEGIN + DECLARE @ErrorRaised INT; SET @ErrorRaised = 0; + + EXEC('CREATE SCHEMA [Tes''tSchema];'); + EXEC('CREATE PROC [Tes''tSchema].[Spye''e Proc] @testParam1 INT AS RETURN 0;'); + + EXEC tSQLt.SpyProcedure '[Tes''tSchema].[Spye''e Proc]'; + + DECLARE @InnerProcedure VARCHAR(MAX) = '[Tes''tSchema].[Spye''e Proc]'; + EXEC @InnerProcedure 27; + + SELECT testParam1 + INTO #Actual + FROM [Tes'tSchema].[Spye'e Proc_SpyProcedureLog]; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(27); + + EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; +END; +GO + CREATE PROC SpyProcedureTests.[test SpyProcedure calls tSQLt.Private_RenameObjectToUniqueName on original proc] AS BEGIN @@ -1082,7 +1106,7 @@ CREATE PROC SpyProcedureTests.[test @SpyProcedureOriginalObjectName contains ori AS BEGIN EXEC('CREATE SCHEMA [I''nn''er Test.Schema 1];'); - EXEC('CREATE PROCEDURE [I''nn''er Test.Schema 1].[T''emp Proc.edure 1] @I INT = NULL AS BEGIN RETURN END;');--INSERT INTO #Actual VALUES (''[I''''nn''''er Test.Schema 1].[T''''emp Proc.edure 1] called'', @I); END;'); + EXEC('CREATE PROCEDURE [I''nn''er Test.Schema 1].[T''emp Proc.edure 1] @I INT = NULL AS BEGIN INSERT INTO #Actual VALUES (''[I''''nn''''er Test.Schema 1].[T''''emp Proc.edure 1] called'', @I); END;'); CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX), I INT); RAISERROR('GH',0,1)WITH NOWAIT; @@ -1100,11 +1124,9 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO - - ---EXEC tSQLt.Run 'SpyProcedureTests.[test calls original procedure with table valued parameters if @CallOriginal = 1]' +--EXEC tSQLt.Run 'SpyProcedureTests.[test SpyProcedure handles procedure and schema names with single quotes]' /* Tests for consideration - +- do we need a test checking that we can run schema.procs with single quotes? - provide full name of original procedure in @SpyProcedureOriginalObjectName variable --- quoting required, contains ' - TODO?: We need to document how cursors are handled and how the user can work around. local, global, and variable. From ae854df982a5eecca8d76b201ee138d43a13721a Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 5 Dec 2021 23:04:39 -0500 Subject: [PATCH 086/119] We fixed things. There were tests. Many pass. Some don't. More tomorrow. (tSQLt.Private_Seize and its cousin tSQLt.Private_Seize_NoTruncate have joined the family.) --- Source/BuildOrder.txt | 1 + Source/Run_Methods.sql | 5 ++ Source/Source.ssmssqlproj | 6 ++ ...enerateCreateProcedureSpyStatement.ssp.sql | 1 + Source/tSQLt.Private_Init.ssp.sql | 2 +- Source/tSQLt.Private_Seize.tbl.sql | 21 +++++++ Tests/AnnotationNoTransactionTests.class.sql | 9 +-- Tests/Private_SeizeTests.class.sql | 59 +++++++++++++++++++ Tests/SpyProcedureTests.class.sql | 43 ++++++++++---- Tests/Tests.ssmssqlproj | 6 ++ 10 files changed, 135 insertions(+), 18 deletions(-) create mode 100644 Source/tSQLt.Private_Seize.tbl.sql create mode 100644 Tests/Private_SeizeTests.class.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 27e07eebb..236062b91 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -50,6 +50,7 @@ tSQLt.Private_SqlVersion.sfn.sql tSQLt.Private_SplitSqlVersion.sfn.sql tSQLt.FriendlySQLServerVersion.sfn.sql tSQLt.Info.sfn.sql +tSQLt.Private_Seize.tbl.sql tSQLt.Private_Init.ssp.sql tSQLt.Private_HostPlatform.svw.sql tSQLt.Private_NoTransactionTableAction.view.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 2b4189d5a..1ec34b2a9 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -344,6 +344,11 @@ BEGIN EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; END; + IF(@Result = 'FATAL') + BEGIN + INSERT INTO tSQLt.Private_Seize VALUES(1); + END; + IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); END; diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 54846dae3..2ba8b2f13 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -522,6 +522,12 @@ tSQLt.Private_ScriptIndex.sfn.sql + + + + + tSQLt.Private_Seize.tbl.sql + diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index 9f75a1e4c..179de0a88 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -94,6 +94,7 @@ BEGIN ISNULL(@InsertStmt,'') + 'DECLARE @SpyProcedureOriginalObjectName NVARCHAR(MAX) = '''+REPLACE(QUOTENAME(OBJECT_SCHEMA_NAME(@ProcedureObjectId))+'.'+QUOTENAME(OBJECT_NAME(@ProcedureObjectId)),'''','''''')+''';'+ ISNULL(@CommandToExecute + ';', '') + + CHAR(13)+CHAR(10)+/*CR,LF*/ CASE WHEN @CallOriginal = 1 THEN 'EXEC '+QUOTENAME(OBJECT_SCHEMA_NAME(@ProcedureObjectId))+'.'+QUOTENAME(OBJECT_NAME(@ProcedureObjectId))+' ' + @ProcParmListForCall + ';' ELSE '' diff --git a/Source/tSQLt.Private_Init.ssp.sql b/Source/tSQLt.Private_Init.ssp.sql index d3bb1ce99..4190cc741 100644 --- a/Source/tSQLt.Private_Init.ssp.sql +++ b/Source/tSQLt.Private_Init.ssp.sql @@ -17,7 +17,7 @@ BEGIN RETURN; END CATCH; SELECT @version_match = CASE WHEN I.SqlVersion = I.InstalledOnSqlVersion THEN 1 ELSE 0 END FROM tSQLt.Info() AS I WHERE @version_match = 1; - IF(@version_match = 0) + IF(@version_match = 0 OR EXISTS(SELECT 1 FROM tSQLt.Private_Seize)) BEGIN RAISERROR('tSQLt is in an invalid state. Please reinstall tSQLt.',16,10); RETURN; diff --git a/Source/tSQLt.Private_Seize.tbl.sql b/Source/tSQLt.Private_Seize.tbl.sql new file mode 100644 index 000000000..a0a0bfc11 --- /dev/null +++ b/Source/tSQLt.Private_Seize.tbl.sql @@ -0,0 +1,21 @@ +IF OBJECT_ID('tSQLt.Private_Seize_NoTruncate') IS NOT NULL DROP TABLE tSQLt.Private_Seize_NoTruncate; +IF OBJECT_ID('tSQLt.Private_Seize') IS NOT NULL DROP TABLE tSQLt.Private_Seize; +GO +---Build+ +GO +CREATE TABLE tSQLt.Private_Seize( + Kaput BIT CONSTRAINT [PK:Private_Seize] PRIMARY KEY CONSTRAINT [CHK:Private_Seize] CHECK(Kaput=1) +); +GO +CREATE TABLE tSQLt.Private_Seize_NoTruncate( + NoTruncate BIT CONSTRAINT [FK:Private_Seize_NoTruncate(NoTruncate)] FOREIGN KEY REFERENCES tSQLt.Private_Seize(Kaput) +); +GO +CREATE TRIGGER tSQLt.Private_Seize_Stop ON tSQLt.Private_Seize INSTEAD OF DELETE,UPDATE +AS +BEGIN + RAISERROR('This is a private table that you should not mess with!',16,10); +END; +GO + + \ No newline at end of file diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index ba7b76e19..f78d008b1 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1467,7 +1467,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_CleanUp error prevents subsequent tSQLt.Run% calls] +CREATE PROCEDURE AnnotationNoTransactionTests.[test FATAL error prevents subsequent tSQLt.Run% calls] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -1479,12 +1479,13 @@ BEGIN RETURN; END; '); - - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'IF(@Action = ''Reset'')RAISERROR(''Some Fatal Error'',16,10);'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'IF(@Action = ''Reset'')BEGIN RAISERROR(''Some Fatal Error'',16,10);END;'; EXEC tSQLt.Run 'MyInnerTests', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; - EXEC tSQLt.ExpectException @ExpectedMessage = 'asdasdasdas'; + EXEC tSQLt.ExpectException @ExpectedMessage = 'tSQLt is in an invalid state. Please reinstall tSQLt.'; EXEC tSQLt.Run 'MyInnerTests', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; END; diff --git a/Tests/Private_SeizeTests.class.sql b/Tests/Private_SeizeTests.class.sql new file mode 100644 index 000000000..32f61d293 --- /dev/null +++ b/Tests/Private_SeizeTests.class.sql @@ -0,0 +1,59 @@ +EXEC tSQLt.NewTestClass 'Private_SeizeTests'; +GO +CREATE PROCEDURE Private_SeizeTests.[test can insert a 1] +AS +BEGIN + INSERT INTO tSQLt.Private_Seize(Kaput) VALUES(1); + + SELECT Kaput INTO #Actual FROM tSQLt.Private_Seize; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES(1); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +CREATE PROCEDURE Private_SeizeTests.[test cannot insert <> 1] +AS +BEGIN + EXEC tSQLt.ExpectException @ExpectedMessagePattern = '%"CHK:Private_Seize"%'; + INSERT INTO tSQLt.Private_Seize(Kaput) VALUES(0); + +END; +GO +CREATE PROCEDURE Private_SeizeTests.[test cannot insert NULL] +AS +BEGIN + EXEC tSQLt.ExpectException @ExpectedMessagePattern = '%column does not allow nulls%'; + INSERT INTO tSQLt.Private_Seize(Kaput) VALUES(NULL); + +END; +GO +CREATE PROCEDURE Private_SeizeTests.[test cannot delete row] +AS +BEGIN + INSERT INTO tSQLt.Private_Seize(Kaput) VALUES(1); + + EXEC tSQLt.ExpectException @ExpectedMessage = 'This is a private table that you should not mess with!', @ExpectedSeverity = 16, @ExpectedState = 10; + DELETE FROM tSQLt.Private_Seize; +END; +GO +CREATE PROCEDURE Private_SeizeTests.[test cannot update row (even to the same value)] +AS +BEGIN + INSERT INTO tSQLt.Private_Seize(Kaput) VALUES(1); + + EXEC tSQLt.ExpectException @ExpectedMessage = 'This is a private table that you should not mess with!', @ExpectedSeverity = 16, @ExpectedState = 10; + UPDATE tSQLt.Private_Seize SET Kaput = 1; +END; +GO +CREATE PROCEDURE Private_SeizeTests.[test cannot TRUNCATE] +AS +BEGIN + INSERT INTO tSQLt.Private_Seize(Kaput) VALUES(1); + + EXEC tSQLt.ExpectException @ExpectedMessagePattern = 'Cannot truncate table _tSQLt.Private_Seize_%'; + TRUNCATE TABLE tSQLt.Private_Seize; +END; +GO diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index d68e44e5b..8d03d4214 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -1102,21 +1102,45 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROC SpyProcedureTests.[test @SpyProcedureOriginalObjectName contains original proc name even if there are single quotes involved] +CREATE PROC SpyProcedureTests.[test @SpyProcedureOriginalObjectName contains original proc name even if it has single quotes, dots, or spaces] AS BEGIN EXEC('CREATE SCHEMA [I''nn''er Test.Schema 1];'); - EXEC('CREATE PROCEDURE [I''nn''er Test.Schema 1].[T''emp Proc.edure 1] @I INT = NULL AS BEGIN INSERT INTO #Actual VALUES (''[I''''nn''''er Test.Schema 1].[T''''emp Proc.edure 1] called'', @I); END;'); + EXEC('CREATE PROCEDURE [I''nn''er Test.Schema 1].[T''emp Proc.edure 1] @I INT = NULL + AS + BEGIN + INSERT INTO #Actual VALUES (''[I''''nn''''er Test.Schema 1].[T''''emp Proc.edure 1] called'', @I); + END;' + ); + + CREATE TABLE #Actual (Msg NVARCHAR(MAX), I INT); - CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX), I INT); -RAISERROR('GH',0,1)WITH NOWAIT; EXEC tSQLt.SpyProcedure @ProcedureName = '[I''nn''er Test.Schema 1].[T''emp Proc.edure 1]', @CommandToExecute='EXEC @SpyProcedureOriginalObjectName 42;'; -RAISERROR('GH2',0,1)WITH NOWAIT; + EXEC('[I''nn''er Test.Schema 1].[T''emp Proc.edure 1]'); SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - INSERT INTO #Expected VALUES(1,'[I''nn''er Test.Schema 1].[T''emp Proc.edure 1] called',42); + INSERT INTO #Expected VALUES('[I''nn''er Test.Schema 1].[T''emp Proc.edure 1] called',42); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROC SpyProcedureTests.[test calls the original procedure even if @CommandToExecute contains inline comment] +AS +BEGIN + EXEC('CREATE PROCEDURE SpyProcedureTests.TempProcedure1 AS BEGIN INSERT INTO #Actual VALUES (''SpyProcedureTests.TempProcedure1 called''); END;'); + + CREATE TABLE #Actual (Id INT IDENTITY (1,1), Msg NVARCHAR(MAX)); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CommandToExecute='DECLARE @II INT = 1;--PRINT @II;', @CallOriginal = 1; + + EXEC('SpyProcedureTests.TempProcedure1'); + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES(1,'SpyProcedureTests.TempProcedure1 called'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; @@ -1124,10 +1148,3 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO ---EXEC tSQLt.Run 'SpyProcedureTests.[test SpyProcedure handles procedure and schema names with single quotes]' -/* Tests for consideration -- do we need a test checking that we can run schema.procs with single quotes? -- provide full name of original procedure in @SpyProcedureOriginalObjectName variable ---- quoting required, contains ' -- TODO?: We need to document how cursors are handled and how the user can work around. local, global, and variable. -*/ diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 6bca1b785..62eca6124 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -288,6 +288,12 @@ Private_ScriptIndexTests.class.sql + + + + + Private_SeizeTests.class.sql + From f40d6439758a7b8cf6aa5139e7b5d4c860f1397e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 6 Dec 2021 23:33:35 -0500 Subject: [PATCH 087/119] Because of tSQLt.Private_Seize, we need to be able to drop constraints before we drop tables in the tSQLt.DropClass sp; Wrote tests, one of them is failing; made notes on what has been done for AnnotationNoTransactionTests. --- Source/tSQLt.DropClass.ssp.sql | 19 +++++++-- Source/tSQLt.Private_GetDropItemCmd.sfn.sql | 12 +++++- Source/tSQLt.Private_Seize.tbl.sql | 6 +-- Tests/AnnotationNoTransactionTests.class.sql | 6 +-- Tests/DropClassTests.class.sql | 41 ++++++++++++++++++++ 5 files changed, 73 insertions(+), 11 deletions(-) diff --git a/Source/tSQLt.DropClass.ssp.sql b/Source/tSQLt.DropClass.ssp.sql index 5c3af0989..70b50fb2d 100644 --- a/Source/tSQLt.DropClass.ssp.sql +++ b/Source/tSQLt.DropClass.ssp.sql @@ -8,13 +8,23 @@ BEGIN /*SnipStart: CreateDropClassStatement.ps1*/ DECLARE @Cmd NVARCHAR(MAX); - WITH ObjectInfo(FullName, ItemType) AS + WITH ConstraintInfo(FullName, ItemType) AS + (/*FOREIGN KEYS need to be dropped before their tables*/ + SELECT + QUOTENAME(SCHEMA_NAME(O.schema_id))+'.'+QUOTENAME(O.name), + O.type + FROM sys.objects AS O + WHERE O.schema_id = SCHEMA_ID(@ClassName) + AND O.type IN ('F') + ), + ObjectInfo(FullName, ItemType) AS ( SELECT QUOTENAME(SCHEMA_NAME(O.schema_id))+'.'+QUOTENAME(O.name), O.type FROM sys.objects AS O WHERE O.schema_id = SCHEMA_ID(@ClassName) + AND O.type NOT IN ('F') ), TypeInfo(FullName, ItemType) AS ( @@ -43,12 +53,15 @@ BEGIN DropStatements(no,FullName,ItemType) AS ( SELECT 10, FullName, ItemType - FROM ObjectInfo + FROM ConstraintInfo UNION ALL SELECT 20, FullName, ItemType - FROM TypeInfo + FROM ObjectInfo UNION ALL SELECT 30, FullName, ItemType + FROM TypeInfo + UNION ALL + SELECT 40, FullName, ItemType FROM XMLSchemaInfo UNION ALL SELECT 10000, FullName, ItemType diff --git a/Source/tSQLt.Private_GetDropItemCmd.sfn.sql b/Source/tSQLt.Private_GetDropItemCmd.sfn.sql index 7148c9929..00fe6787c 100644 --- a/Source/tSQLt.Private_GetDropItemCmd.sfn.sql +++ b/Source/tSQLt.Private_GetDropItemCmd.sfn.sql @@ -14,8 +14,13 @@ AS RETURN /*SnipStart: CreateDropClassStatement.ps1*/ SELECT + CASE @ItemType + WHEN 'F' THEN 'ALTER TABLE '+(SELECT SCHEMA_NAME(schema_id)+'.'+OBJECT_NAME(parent_object_id) FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(@FullName))+' ' + ELSE '' + END+ 'DROP ' + CASE @ItemType + WHEN 'F' THEN 'CONSTRAINT' WHEN 'IF' THEN 'FUNCTION' WHEN 'TF' THEN 'FUNCTION' WHEN 'FN' THEN 'FUNCTION' @@ -30,7 +35,10 @@ SELECT WHEN 'schema' THEN 'SCHEMA' END+ ' ' + - @FullName + + CASE @ItemType + WHEN 'F' THEN OBJECT_NAME(OBJECT_ID(@FullName)) + ELSE @FullName + END+ ';' AS cmd /*SnipEnd: CreateDropClassStatement.ps1*/ GO @@ -40,7 +48,7 @@ Object type: AF = Aggregate function (CLR) - C = CHECK constraint - D = DEFAULT (constraint or stand-alone) -- F = FOREIGN KEY constraint ++ F = FOREIGN KEY constraint + FN = SQL scalar function FS = Assembly (CLR) scalar-function + FT = Assembly (CLR) table-valued function diff --git a/Source/tSQLt.Private_Seize.tbl.sql b/Source/tSQLt.Private_Seize.tbl.sql index a0a0bfc11..0bb8290d6 100644 --- a/Source/tSQLt.Private_Seize.tbl.sql +++ b/Source/tSQLt.Private_Seize.tbl.sql @@ -7,9 +7,9 @@ CREATE TABLE tSQLt.Private_Seize( Kaput BIT CONSTRAINT [PK:Private_Seize] PRIMARY KEY CONSTRAINT [CHK:Private_Seize] CHECK(Kaput=1) ); GO -CREATE TABLE tSQLt.Private_Seize_NoTruncate( - NoTruncate BIT CONSTRAINT [FK:Private_Seize_NoTruncate(NoTruncate)] FOREIGN KEY REFERENCES tSQLt.Private_Seize(Kaput) -); +--CREATE TABLE tSQLt.Private_Seize_NoTruncate( +-- NoTruncate BIT CONSTRAINT [FK:Private_Seize_NoTruncate(NoTruncate)] FOREIGN KEY REFERENCES tSQLt.Private_Seize(Kaput) +--); GO CREATE TRIGGER tSQLt.Private_Seize_Stop ON tSQLt.Private_Seize INSTEAD OF DELETE,UPDATE AS diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index f78d008b1..ce8c1867b 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1525,9 +1525,9 @@ GO -- X If the ERROR_PROCEDURE is somehow returning null, we still get the rest of the error message - 3. tSQLt.Private_CleanUp -- Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error -- If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. -- appropriate error messages are appended to the test msg +- X Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error +- X If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. +- X appropriate error messages are appended to the test msg - If a test errors (even catastrophically), all indicated CleanUp procedures run. - Unify Error Message Generation Across all code diff --git a/Tests/DropClassTests.class.sql b/Tests/DropClassTests.class.sql index a397a6c5e..cea0c4d50 100644 --- a/Tests/DropClassTests.class.sql +++ b/Tests/DropClassTests.class.sql @@ -249,5 +249,46 @@ BEGIN END; GO +CREATE PROC DropClassTests.[test removes tables referencing each other] +AS +BEGIN + + EXEC('CREATE SCHEMA MyTestClass;'); + EXEC('CREATE TABLE MyTestClass.TableA(I INT PRIMARY KEY);'); + EXEC('CREATE TABLE MyTestClass.TableB(I INT PRIMARY KEY REFERENCES MyTestClass.TableA(I));'); + EXEC('ALTER TABLE MyTestClass.TableA ADD FOREIGN KEY (I) REFERENCES MyTestClass.TableB(I);'); + + SELECT * FROM sys.objects WHERE schema_id = SCHEMA_ID('MyTestClass'); + EXEC tSQLt.ExpectNoException; + + EXEC tSQLt.DropClass 'MyTestClass'; + + IF(SCHEMA_ID('MyTestClass') IS NOT NULL) + BEGIN + EXEC tSQLt.Fail 'DropClass did not drop MyTestClass'; + END +END; +GO + +CREATE PROC DropClassTests.[test removes tables referencing each other with names that require quoting ( .')] +AS +BEGIN + + EXEC('CREATE SCHEMA [My.Tes''t Class];'); + EXEC('CREATE TABLE [My.Tes''t Class].[Ta.b''le A](I INT PRIMARY KEY);'); + EXEC('CREATE TABLE [My.Tes''t Class].[Ta.b''le B](I INT PRIMARY KEY CONSTRAINT [FK: Ta.b''le B] REFERENCES [My.Tes''t Class].[Ta.b''le A](I));'); + EXEC('ALTER TABLE [My.Tes''t Class].[Ta.b''le A] ADD CONSTRAINT [FK: Ta.b''le A] FOREIGN KEY (I) REFERENCES [My.Tes''t Class].[Ta.b''le B](I);'); + + SELECT * FROM sys.objects WHERE schema_id = SCHEMA_ID('My.Tes''t Class'); + EXEC tSQLt.ExpectNoException; + + EXEC tSQLt.DropClass 'My.Tes''t Class'; + + IF(SCHEMA_ID('My.Tes''t Class') IS NOT NULL) + BEGIN + EXEC tSQLt.Fail 'DropClass did not drop [My.Tes''t Class]'; + END +END; +GO From 682be4d1fe902452ad1b39740c0adf5a75495e90 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 7 Dec 2021 22:46:29 -0500 Subject: [PATCH 088/119] Used SkipTest instead of a renaming trick to skip a test in AssertResultSetsHaveSameMetaDataTests.class.sql and fixed and re-enabled a test in UninstallTests.class.sql. --- Source/tSQLt.Uninstall.ssp.sql | 2 +- Source/tSQLtCLR_CreateProcs.sql | 10 ++++++++++ Tests/AssertResultSetsHaveSameMetaDataTests.class.sql | 4 ++-- Tests/UninstallTests.class.sql | 10 +++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Source/tSQLt.Uninstall.ssp.sql b/Source/tSQLt.Uninstall.ssp.sql index fa54e7f76..6ca3d5172 100644 --- a/Source/tSQLt.Uninstall.ssp.sql +++ b/Source/tSQLt.Uninstall.ssp.sql @@ -8,7 +8,7 @@ BEGIN EXEC tSQLt.DropClass @ClassName = 'tSQLt'; - DROP ASSEMBLY tSQLtCLR; + IF(EXISTS(SELECT 1 FROM sys.assemblies WHERE name = 'tSQLtCLR'))DROP ASSEMBLY tSQLtCLR; IF USER_ID('tSQLt.TestClass') IS NOT NULL DROP USER [tSQLt.TestClass]; diff --git a/Source/tSQLtCLR_CreateProcs.sql b/Source/tSQLtCLR_CreateProcs.sql index 7c0a181fc..14aed3ff4 100644 --- a/Source/tSQLtCLR_CreateProcs.sql +++ b/Source/tSQLtCLR_CreateProcs.sql @@ -13,6 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. */ +IF OBJECT_ID('tSQLt.ResultSetFilter') IS NOT NULL DROP PROCEDURE tSQLt.ResultSetFilter; +IF OBJECT_ID('tSQLt.AssertResultSetsHaveSameMetaData') IS NOT NULL DROP PROCEDURE tSQLt.AssertResultSetsHaveSameMetaData; +IF OBJECT_ID('tSQLt.NewConnection') IS NOT NULL DROP PROCEDURE tSQLt.NewConnection; +IF OBJECT_ID('tSQLt.CaptureOutput') IS NOT NULL DROP PROCEDURE tSQLt.CaptureOutput; +IF OBJECT_ID('tSQLt.SuppressOutput') IS NOT NULL DROP PROCEDURE tSQLt.SuppressOutput; +IF OBJECT_ID('tSQLt.Private_GetAnnotationList') IS NOT NULL DROP FUNCTION tSQLt.Private_GetAnnotationList; +IF TYPE_ID('tSQLt.[Private]') IS NOT NULL DROP TYPE tSQLt.[Private]; + +GO + GO ---Build+ GO diff --git a/Tests/AssertResultSetsHaveSameMetaDataTests.class.sql b/Tests/AssertResultSetsHaveSameMetaDataTests.class.sql index e0f0177cd..e62472991 100644 --- a/Tests/AssertResultSetsHaveSameMetaDataTests.class.sql +++ b/Tests/AssertResultSetsHaveSameMetaDataTests.class.sql @@ -48,10 +48,10 @@ BEGIN END; GO -CREATE PROC tSQLt_test_AssertResultSetsHaveSameMetaData.[xtest AssertResultSetsHaveSameMetaData fails when one result set has no rows for versions before SQL Server 2012] +--[@tSQLt:SkipTest]('INTENTIONALLY DISABLED UNTIL WE FIGURE OUT WHY IT SOMETIMES FAILS AND SOMETIMES PASSES') +CREATE PROC tSQLt_test_AssertResultSetsHaveSameMetaData.[test AssertResultSetsHaveSameMetaData fails when one result set has no rows for versions before SQL Server 2012] AS BEGIN - -- INTENTIONALLY DISABLED UNTIL WE FIGURE OUT WHY IT SOMETIMES FAILS AND SOMETIMES PASSES IF (SELECT CAST(SUBSTRING(product_version, 1, CHARINDEX('.', product_version) - 1) AS INT) FROM sys.dm_os_loaded_modules WHERE name LIKE '%\sqlservr.exe') >= 11 BEGIN EXEC tSQLt.AssertResultSetsHaveSameMetaData diff --git a/Tests/UninstallTests.class.sql b/Tests/UninstallTests.class.sql index c37adc9ba..7af78b590 100644 --- a/Tests/UninstallTests.class.sql +++ b/Tests/UninstallTests.class.sql @@ -141,7 +141,7 @@ BEGIN @Message = @FailureMessage; END; GO -CREATE PROCEDURE UninstallTests.[-test Uninstall does not fail if the tSQLtCLR assembly is missing] +CREATE PROCEDURE UninstallTests.[test Uninstall does not fail if the tSQLtCLR assembly is missing] AS BEGIN DECLARE @id INT; @@ -150,6 +150,14 @@ BEGIN DECLARE @TranName CHAR(32); EXEC tSQLt.GetNewTranName @TranName OUT; SAVE TRAN @TranName; + DROP PROCEDURE tSQLt.ResultSetFilter; + DROP PROCEDURE tSQLt.AssertResultSetsHaveSameMetaData; + DROP PROCEDURE tSQLt.NewConnection; + DROP PROCEDURE tSQLt.CaptureOutput; + DROP PROCEDURE tSQLt.SuppressOutput; + DROP FUNCTION tSQLt.Private_GetAnnotationList; + DROP TYPE tSQLt.[Private]; + DROP ASSEMBLY tSQLtCLR; BEGIN TRY From ca437edcc1239559ca6be17dcc6f415eb6ec880c Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 7 Dec 2021 23:37:09 -0500 Subject: [PATCH 089/119] Added Private_Seize and Private_Seize_NoTruncate to the view Private_NoTransactionTableActionTests; Modified DropClass and related Private_GetDropItemCmd to handle schema, object, type, and xml schema names with [ .']\ --- Source/tSQLt.DropClass.ssp.sql | 27 +++++------ Source/tSQLt.Private_GetDropItemCmd.sfn.sql | 4 +- ....Private_NoTransactionTableAction.view.sql | 2 + Source/tSQLt.Private_Seize.tbl.sql | 6 +-- Tests/AnnotationNoTransactionTests.class.sql | 1 + Tests/DropClassTests.class.sql | 46 ++++++++++++++++++- ...te_NoTransactionTableActionTests.class.sql | 2 + 7 files changed, 68 insertions(+), 20 deletions(-) diff --git a/Source/tSQLt.DropClass.ssp.sql b/Source/tSQLt.DropClass.ssp.sql index 70b50fb2d..86c8ce058 100644 --- a/Source/tSQLt.DropClass.ssp.sql +++ b/Source/tSQLt.DropClass.ssp.sql @@ -8,13 +8,22 @@ BEGIN /*SnipStart: CreateDropClassStatement.ps1*/ DECLARE @Cmd NVARCHAR(MAX); - WITH ConstraintInfo(FullName, ItemType) AS + WITH SchemaInfo(FullName, ItemType, SchemaId) AS + ( + SELECT + QUOTENAME(S.name), + 'schema', + S.schema_id + FROM sys.schemas AS S + WHERE S.schema_id = ISNULL(SCHEMA_ID(@ClassName), SCHEMA_ID(PARSENAME(@ClassName,1))) + ), + ConstraintInfo(FullName, ItemType) AS (/*FOREIGN KEYS need to be dropped before their tables*/ SELECT QUOTENAME(SCHEMA_NAME(O.schema_id))+'.'+QUOTENAME(O.name), O.type FROM sys.objects AS O - WHERE O.schema_id = SCHEMA_ID(@ClassName) + JOIN SchemaInfo SI ON SI.SchemaId = O.schema_id AND O.type IN ('F') ), ObjectInfo(FullName, ItemType) AS @@ -23,7 +32,7 @@ BEGIN QUOTENAME(SCHEMA_NAME(O.schema_id))+'.'+QUOTENAME(O.name), O.type FROM sys.objects AS O - WHERE O.schema_id = SCHEMA_ID(@ClassName) + JOIN SchemaInfo SI ON SI.SchemaId = O.schema_id AND O.type NOT IN ('F') ), TypeInfo(FullName, ItemType) AS @@ -32,7 +41,7 @@ BEGIN QUOTENAME(SCHEMA_NAME(T.schema_id))+'.'+QUOTENAME(T.name), 'type' FROM sys.types AS T - WHERE T.schema_id = SCHEMA_ID(@ClassName) + JOIN SchemaInfo SI ON SI.SchemaId = T.schema_id ), XMLSchemaInfo(FullName, ItemType) AS ( @@ -40,15 +49,7 @@ BEGIN QUOTENAME(SCHEMA_NAME(XSC.schema_id))+'.'+QUOTENAME(XSC.name), 'xml_schema_collection' FROM sys.xml_schema_collections AS XSC - WHERE XSC.schema_id = SCHEMA_ID(@ClassName) - ), - SchemaInfo(FullName, ItemType) AS - ( - SELECT - QUOTENAME(S.name), - 'schema' - FROM sys.schemas AS S - WHERE S.schema_id = SCHEMA_ID(PARSENAME(@ClassName,1)) + JOIN SchemaInfo SI ON SI.SchemaId = XSC.schema_id ), DropStatements(no,FullName,ItemType) AS ( diff --git a/Source/tSQLt.Private_GetDropItemCmd.sfn.sql b/Source/tSQLt.Private_GetDropItemCmd.sfn.sql index 00fe6787c..bd6d6ef8d 100644 --- a/Source/tSQLt.Private_GetDropItemCmd.sfn.sql +++ b/Source/tSQLt.Private_GetDropItemCmd.sfn.sql @@ -15,7 +15,7 @@ RETURN /*SnipStart: CreateDropClassStatement.ps1*/ SELECT CASE @ItemType - WHEN 'F' THEN 'ALTER TABLE '+(SELECT SCHEMA_NAME(schema_id)+'.'+OBJECT_NAME(parent_object_id) FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(@FullName))+' ' + WHEN 'F' THEN 'ALTER TABLE '+(SELECT QUOTENAME(SCHEMA_NAME(schema_id))+'.'+QUOTENAME(OBJECT_NAME(parent_object_id)) FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(@FullName))+' ' ELSE '' END+ 'DROP ' + @@ -36,7 +36,7 @@ SELECT END+ ' ' + CASE @ItemType - WHEN 'F' THEN OBJECT_NAME(OBJECT_ID(@FullName)) + WHEN 'F' THEN QUOTENAME(OBJECT_NAME(OBJECT_ID(@FullName))) ELSE @FullName END+ ';' AS cmd diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index d22d1a517..096cbb755 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -11,6 +11,8 @@ SELECT * ('[tSQLt].[Private_Configurations]','Restore'), ('[tSQLt].[CaptureOutputLog]','Truncate'), ('[tSQLt].[Private_RenamedObjectLog]','Ignore'), + ('[tSQLt].[Private_Seize]','Ignore'), + ('[tSQLt].[Private_Seize_NoTruncate]','Ignore'), ('[tSQLt].[TestResult]','Restore') )X(Name, Action); GO diff --git a/Source/tSQLt.Private_Seize.tbl.sql b/Source/tSQLt.Private_Seize.tbl.sql index 0bb8290d6..a0a0bfc11 100644 --- a/Source/tSQLt.Private_Seize.tbl.sql +++ b/Source/tSQLt.Private_Seize.tbl.sql @@ -7,9 +7,9 @@ CREATE TABLE tSQLt.Private_Seize( Kaput BIT CONSTRAINT [PK:Private_Seize] PRIMARY KEY CONSTRAINT [CHK:Private_Seize] CHECK(Kaput=1) ); GO ---CREATE TABLE tSQLt.Private_Seize_NoTruncate( --- NoTruncate BIT CONSTRAINT [FK:Private_Seize_NoTruncate(NoTruncate)] FOREIGN KEY REFERENCES tSQLt.Private_Seize(Kaput) ---); +CREATE TABLE tSQLt.Private_Seize_NoTruncate( + NoTruncate BIT CONSTRAINT [FK:Private_Seize_NoTruncate(NoTruncate)] FOREIGN KEY REFERENCES tSQLt.Private_Seize(Kaput) +); GO CREATE TRIGGER tSQLt.Private_Seize_Stop ON tSQLt.Private_Seize INSTEAD OF DELETE,UPDATE AS diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index ce8c1867b..cda095401 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -438,6 +438,7 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS PRINT C INSERT INTO #Expected VALUES('[MyInnerTests].[test should cause unrecoverable error]', 'Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + EXEC tSQLt.Fail 'This test is not done.'; END; GO /*-----------------------------------------------------------------------------------------------*/ diff --git a/Tests/DropClassTests.class.sql b/Tests/DropClassTests.class.sql index cea0c4d50..4d4005976 100644 --- a/Tests/DropClassTests.class.sql +++ b/Tests/DropClassTests.class.sql @@ -258,7 +258,6 @@ BEGIN EXEC('CREATE TABLE MyTestClass.TableB(I INT PRIMARY KEY REFERENCES MyTestClass.TableA(I));'); EXEC('ALTER TABLE MyTestClass.TableA ADD FOREIGN KEY (I) REFERENCES MyTestClass.TableB(I);'); - SELECT * FROM sys.objects WHERE schema_id = SCHEMA_ID('MyTestClass'); EXEC tSQLt.ExpectNoException; EXEC tSQLt.DropClass 'MyTestClass'; @@ -269,6 +268,8 @@ BEGIN END END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROC DropClassTests.[test removes tables referencing each other with names that require quoting ( .')] AS @@ -279,7 +280,6 @@ BEGIN EXEC('CREATE TABLE [My.Tes''t Class].[Ta.b''le B](I INT PRIMARY KEY CONSTRAINT [FK: Ta.b''le B] REFERENCES [My.Tes''t Class].[Ta.b''le A](I));'); EXEC('ALTER TABLE [My.Tes''t Class].[Ta.b''le A] ADD CONSTRAINT [FK: Ta.b''le A] FOREIGN KEY (I) REFERENCES [My.Tes''t Class].[Ta.b''le B](I);'); - SELECT * FROM sys.objects WHERE schema_id = SCHEMA_ID('My.Tes''t Class'); EXEC tSQLt.ExpectNoException; EXEC tSQLt.DropClass 'My.Tes''t Class'; @@ -290,5 +290,47 @@ BEGIN END END; GO +CREATE PROC DropClassTests.[test drop class works if schema name is passed in unquoted] +AS +BEGIN + + EXEC('CREATE SCHEMA [My.Tes''t Class];'); + EXEC('CREATE TYPE [My.Tes''t Class].UDT FROM INT;'); + EXEC('CREATE TABLE [My.Tes''t Class].[Ta.b''le A](I INT PRIMARY KEY, OO [My.Tes''t Class].UDT);'); + EXEC('CREATE TABLE [My.Tes''t Class].[Ta.b''le B](I INT PRIMARY KEY CONSTRAINT [FK: Ta.b''le B] REFERENCES [My.Tes''t Class].[Ta.b''le A](I));'); + EXEC('CREATE XML SCHEMA COLLECTION [My.Tes''t Class].TestXMLSchema + AS'''';'); + + EXEC tSQLt.ExpectNoException; + + EXEC tSQLt.DropClass 'My.Tes''t Class'; + + IF(SCHEMA_ID('My.Tes''t Class') IS NOT NULL) + BEGIN + EXEC tSQLt.Fail 'DropClass did not drop [My.Tes''t Class]'; + END +END; +GO +CREATE PROC DropClassTests.[test drop class works if schema name is passed in quoted] +AS +BEGIN + + EXEC('CREATE SCHEMA [My.Tes''t Class];'); + EXEC('CREATE TYPE [My.Tes''t Class].UDT FROM INT;'); + EXEC('CREATE TABLE [My.Tes''t Class].[Ta.b''le A](I INT PRIMARY KEY, OO [My.Tes''t Class].UDT);'); + EXEC('CREATE TABLE [My.Tes''t Class].[Ta.b''le B](I INT PRIMARY KEY CONSTRAINT [FK: Ta.b''le B] REFERENCES [My.Tes''t Class].[Ta.b''le A](I));'); + EXEC('CREATE XML SCHEMA COLLECTION [My.Tes''t Class].TestXMLSchema + AS'''';'); + + EXEC tSQLt.ExpectNoException; + + EXEC tSQLt.DropClass '[My.Tes''t Class]'; + + IF(SCHEMA_ID('My.Tes''t Class') IS NOT NULL) + BEGIN + EXEC tSQLt.Fail 'DropClass did not drop [My.Tes''t Class]'; + END +END; +GO diff --git a/Tests/Private_NoTransactionTableActionTests.class.sql b/Tests/Private_NoTransactionTableActionTests.class.sql index 76a7aa2f8..35850d49a 100644 --- a/Tests/Private_NoTransactionTableActionTests.class.sql +++ b/Tests/Private_NoTransactionTableActionTests.class.sql @@ -25,6 +25,8 @@ BEGIN SELECT '[tSQLt].[Private_Configurations]', 'Restore' UNION ALL SELECT '[tSQLt].[CaptureOutputLog]', 'Truncate' UNION ALL SELECT '[tSQLt].[Private_RenamedObjectLog]','Ignore' UNION ALL + SELECT '[tSQLt].[Private_Seize]','Ignore' UNION ALL + SELECT '[tSQLt].[Private_Seize_NoTruncate]','Ignore' UNION ALL SELECT '[tSQLt].[TestResult]', 'Restore'; EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; From dc5e1d7412d4a79e9005e3f35f61f673c4c6668a Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 8 Dec 2021 23:08:40 -0500 Subject: [PATCH 090/119] Propped up the complex and terrible test, it seems to be working for now; Cleaned up TODOs; Wrote notes to do some tests for transactions within Run_Methods. --- Tests/AnnotationNoTransactionTests.class.sql | 98 ++++++++------------ Tests/Run_Methods_Tests.class.sql | 33 +++++++ 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index cda095401..f33ec9932 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -407,6 +407,7 @@ GO CREATE PROCEDURE AnnotationNoTransactionTests.[CLEANUP: test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] AS BEGIN + EXEC tSQLt.SetSummaryError 1; EXEC tSQLt.DropClass MyInnerTests; --EXEC tSQLt.UndoTestDoubles; --ROLLBACK @@ -426,7 +427,20 @@ BEGIN CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS PRINT CAST(''Some obscure string'' AS INT); '); - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'IF(@FullTestName <> ''[MyInnerTests].[test should cause unrecoverable error]'')BEGIN EXEC tSQLt.Private_NoTransactionHandleTables @Action=''Reset'';EXEC tSQLt.UndoTestDoubles @Force = 0;END;'; + /*******************************************************************************************************************************/ + /************************* MESSING WITH THIS CODE WILL PUT tSQLt INTO AN INVALID STATE! ****************************************/ + /**/CREATE TABLE #CleanUpProcedures_StopExecutionForInnerTests(I INT); + /**/EXEC tSQLt.SpyProcedure + /**/ @ProcedureName = 'tSQLt.Private_CleanUp', + /**/ @CommandToExecute = 'IF(OBJECT_ID(''tempdb..#CleanUpProcedures_StopExecutionForInnerTests'')IS NOT NULL)BEGIN RETURN;END;', + /**/ @CallOriginal = 1; + /**/EXEC tSQLt.SpyProcedure + /**/ @ProcedureName = 'tSQLt.Private_AssertNoSideEffects', + /**/ @CommandToExecute = 'IF(OBJECT_ID(''tempdb..#CleanUpProcedures_StopExecutionForInnerTests'')IS NOT NULL)BEGIN RETURN;END;', + /**/ @CallOriginal = 1; + /************************* MESSING WITH THIS CODE WILL PUT tSQLt INTO AN INVALID STATE! ****************************************/ + /*******************************************************************************************************************************/ + EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; @@ -438,7 +452,6 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS PRINT C INSERT INTO #Expected VALUES('[MyInnerTests].[test should cause unrecoverable error]', 'Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; - EXEC tSQLt.Fail 'This test is not done.'; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -1493,19 +1506,28 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO: needs other tests first') -CREATE PROCEDURE AnnotationNoTransactionTests.[test produces meaningful error when pre and post transactions counts don't match] +CREATE PROCEDURE AnnotationNoTransactionTests.[test for execution in the correct place in Private_RunTest, after the outer-most test execution try catch] AS BEGIN - EXEC tSQLt.NewTestClass 'MyInnerTests' - EXEC(' ---[@'+'tSQLt:NoTransaction](DEFAULT) -CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN; - '); - - --EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; - - EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test no other objects are dropped or created if SkipTest Annotation & NoTransaction annotations are used] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test no handler is called if SkipTest Annotation & NoTransaction annotations are used] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -1513,52 +1535,12 @@ GO /*-- TODO - CLEANUP: named cleanup x 3 (needs to execute even if there's an error during test execution) -- there will be three clean up methods, executed in the following order -- X 1. (Test-CleanUp) User defined clean up for an individual test as specified in the NoTransaction annotation parameter --- X "--[@tSQLt:NoTransaction]('[].[]')" --- X --[@tSQLt:NoTransaction](DEFAULT) --- - -- 2. (Schema.CleanUp) User defined clean up for a test class as specified by [].CleanUp --- X ' in schema name --- X different case for cLEANuP --- X If the ERROR_PROCEDURE is somehow returning null, we still get the rest of the error message - -- 3. tSQLt.Private_CleanUp -- X Errors thrown in any of the CleanUp methods are captured and causes the test @Result to be set to Error -- X If a previous CleanUp method errors or fails, it does not cause any following CleanUps to be skipped. -- X appropriate error messages are appended to the test msg -- If a test errors (even catastrophically), all indicated CleanUp procedures run. -- Unify Error Message Generation Across all code - -- X handle multiple Test-CleanUps -- X handle Test-CleanUps with ' in name -- X error in annotation if specified Test-CleanUp does not exist -- X error in annotation if specified Test-CleanUp is not a procedure (any of the 4ish types) - - -Transactions -- transaction opened during test -- transaction commited during test -- inner-transaction-free test errors with uncommittable transaction -- confirm pre and post transaction counts match -- [test produces meaningful error when pre and post transactions counts don't match] -- we still need to save the TranName as something somewhere. - -SkipTest Annotation & NoTransaction Annotation -- The test is skipped -- No other objects are dropped or created -- No handler is called -- Transaction something something <-- this! - -Everything is being called in the right order. -- test for execution in the correct place in Private_RunTest, after the outer-most test execution try catch -- Make sure undotestdoubles and handletables are called in the right order - -- tSQLt.SpyProcedure needs a "call original" option +Add to github +- |19|[AnnotationNoTransactionTests].[test Schema-CleanUp error causes an appropr<...>essage to be written to the tSQLt.TestResult if there is a different error]| 94|Success| + This shouldn't happen: ^^^ - What happens when we have multiple annotations for other non-NoTransaction annotations? Did we test this??? -- Simulate Clippy if someone tries to use AssertEquals instead of AssertEqualsString - add 100x'=' + test status (if not PASS) followed by empty line after test-end message (if verbose) + + --*/ \ No newline at end of file diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 4cf2310b2..b7a7fb2e0 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2346,3 +2346,36 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction') +CREATE PROCEDURE AnnotationNoTransactionTests.[test produces meaningful error when pre and post transactions counts don't match] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' +--[@'+'tSQLt:NoTransaction](DEFAULT) +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN; + '); + + EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; + + EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + +/*-- + Transaction Tests + + - NoTransaction, but suddenly has transaction + - with transaction, but creates additional transaction + - transaction, but is committed (FATAL) + - what should we do if the original transaction was rolled back and a new one was created? + - what should we do if the original transaction was committed and a new one was created? + - we still need to save the TranName as something somewhere. + - review existing tests for transactions + +--*/ + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO \ No newline at end of file From 350f3c24baf76df18a7be9573ba1d561709e0cf4 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 11 Dec 2021 07:49:18 -0500 Subject: [PATCH 091/119] Discussion on how to better handle SkipTest Annotation; Force order for TableToText in tSQLt.Private_AssertNoSideEffects; Wrote a couple more tests for AnnotationNoTransactionTests; Run_Methods_Tests --> Something is very broken --- Source/Run_Methods.sql | 8 ++- .../tSQLt.Private_AssertNoSideEffects.ssp.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 62 ++++++++++++++++++- Tests/Run_Methods_Tests.class.sql | 7 ++- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 1ec34b2a9..193efde8b 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -106,7 +106,10 @@ BEGIN EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; SET @SkipTestFlag = CASE WHEN EXISTS(SELECT 1 FROM #SkipTest) THEN 1 ELSE 0 END; SET @NoTransactionFlag = CASE WHEN EXISTS(SELECT 1 FROM #NoTransaction) THEN 1 ELSE 0 END; - + /* Everything below the ----line and above the next one should not be executed if @SkipTestFlag = 1 + Move the annotation processing into its own TRY/CATCH block + Move everything between the lines into a tored procedure */ +------------------------------------------------------------------------------------------------------------------------- IF(@NoTransactionFlag = 0) BEGIN EXEC tSQLt.GetNewTranName @TranName OUT; @@ -307,8 +310,9 @@ BEGIN @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', @TestResult = @Result OUT, @TestMsg = @Msg OUT - END; + END; +---------------------------------------------------------------------------------------------- If(@Result NOT IN ('Success','Skipped')) BEGIN SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; diff --git a/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql b/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql index 62f18cb11..2dec36c6d 100644 --- a/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql +++ b/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql @@ -19,7 +19,7 @@ BEGIN IF(EXISTS(SELECT 1 FROM #ObjectDiscrepancies)) BEGIN DECLARE @TableToText NVARCHAR(MAX); - EXEC tSQLt.TableToText @TableName = ''#ObjectDiscrepancies'' ,@txt = @TableToText OUTPUT; + EXEC tSQLt.TableToText @TableName = ''#ObjectDiscrepancies'' ,@txt = @TableToText OUTPUT, @OrderBy = ''[Status] ASC, SchemaName ASC, ObjectName ASC''; RAISERROR(''After the test executed, there were unexpected or missing objects in the database: %s'',16,10,@TableToText); END;'; EXEC tSQLt.Private_CleanUpCmdHandler @cmd, @TestResult OUT, @TestMsg OUT; diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index f33ec9932..c84cb69c6 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1506,12 +1506,65 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test for execution in the correct place in Private_RunTest, after the outer-most test execution try catch] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Cleanup is executed after the outer-most test execution try catch and before writing to TestResult] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'IF(@Action = ''Save'')RAISERROR(''Error caught in outermost try-catch'', 16, 10);'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + CREATE PROCEDURE [MyInnerTests].[UserCleanUp1] + AS + BEGIN + RAISERROR(''Error in UserCleanUp1'', 16, 10); + END; + '); + EXEC(' + --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUp1]'') + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.SetSummaryError @SummaryError = 0; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + DECLARE @Actual NVARCHAR(MAX) = (SELECT Msg FROM tSQLt.TestResult); + + EXEC tSQLt.AssertLike @ExpectedPattern = '%Error caught in outermost try-catch%Error in UserCleanUp1%', @Actual = @Actual; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_AssertNoSideEffects is executed after all CleanUps and throws an error if they are new, missing, or renamed objects] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC ('CREATE TABLE MyInnerTests.TestTable1 (Id INT);'); + EXEC ('CREATE TABLE MyInnerTests.TestTable2 (Id INT);'); + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN; + END; + '); + + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'DROP TABLE MyInnerTests.TestTable1; CREATE TABLE MyInnerTests.TestTable1 (Id INT); EXEC sys.sp_rename ''MyInnerTests.TestTable2'', ''TestTable3'';'; + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + DECLARE @Actual NVARCHAR(MAX) = (SELECT Msg FROM tSQLt.TestResult); + + EXEC tSQLt.AssertLike @ExpectedPattern = '%Added%TestTable1%Added%TestTable3%Deleted%TestTable1%Deleted%TestTable2%', @Actual = @Actual; + END; GO +GO /*-----------------------------------------------------------------------------------------------*/ GO --[@tSQLt:SkipTest]('TODO') @@ -1535,6 +1588,8 @@ GO /*-- TODO +Mark NoTransaction tests somehow in TestResult + Add to github - |19|[AnnotationNoTransactionTests].[test Schema-CleanUp error causes an appropr<...>essage to be written to the tSQLt.TestResult if there is a different error]| 94|Success| This shouldn't happen: ^^^ @@ -1543,4 +1598,5 @@ Add to github ---*/ \ No newline at end of file +--*/ + diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index b7a7fb2e0..5b657db86 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2247,6 +2247,11 @@ BEGIN EXEC tSQLt.FakeTable @TableName = 'tSQLt.TestClasses'; EXEC('INSERT INTO tSQLt.TestClasses VALUES(''a class with a '''' in the middle'',12321);'); + DECLARE @XXXX NVARCHAR(MAX) = OBJECT_DEFINITION(OBJECT_ID('tSQLt.Private_AssertNoSideEffects')); + RAISERROR('>>>FINDME<<< %s',0,1,@XXXX)WITH NOWAIT; + + EXEC tSQLt.Fail 'TODO: Does this just need a SummaryError = 0, or is the build broken?'; + EXEC tSQLt.RunAll; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; @@ -2349,7 +2354,7 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO --[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction') -CREATE PROCEDURE AnnotationNoTransactionTests.[test produces meaningful error when pre and post transactions counts don't match] +CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' From 459a967c8f5f0290fd6691ccdd46c5b466b11c34 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 11 Dec 2021 12:53:35 -0500 Subject: [PATCH 092/119] Added TWO lines (one line x 2) AND we deleted THREE! Great use of an hour. --- Tests/Run_Methods_Tests.class.sql | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 5b657db86..041b0c8b4 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2247,10 +2247,7 @@ BEGIN EXEC tSQLt.FakeTable @TableName = 'tSQLt.TestClasses'; EXEC('INSERT INTO tSQLt.TestClasses VALUES(''a class with a '''' in the middle'',12321);'); - DECLARE @XXXX NVARCHAR(MAX) = OBJECT_DEFINITION(OBJECT_ID('tSQLt.Private_AssertNoSideEffects')); - RAISERROR('>>>FINDME<<< %s',0,1,@XXXX)WITH NOWAIT; - - EXEC tSQLt.Fail 'TODO: Does this just need a SummaryError = 0, or is the build broken?'; + EXEC tSQLt.SetSummaryError 0; EXEC tSQLt.RunAll; @@ -2342,6 +2339,8 @@ BEGIN EXEC('INSERT INTO tSQLt.TestClasses(Name) VALUES(''a class with a '''' in the middle'');'); EXEC('INSERT INTO tSQLt.Private_NewTestClassList(ClassName) VALUES(''a class with a '''' in the middle'');'); + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.RunNew; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; From c37741d7da21b610ccbd555db2aaace8e6f45dbe Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 11 Dec 2021 18:04:26 -0500 Subject: [PATCH 093/119] Fixed a failing test by adhering to our existing standards of "Private" as the prefix for objects which do not belong in the facade. --- Source/tSQLt.Private_Seize.tbl.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/tSQLt.Private_Seize.tbl.sql b/Source/tSQLt.Private_Seize.tbl.sql index a0a0bfc11..eb17bb3d7 100644 --- a/Source/tSQLt.Private_Seize.tbl.sql +++ b/Source/tSQLt.Private_Seize.tbl.sql @@ -4,11 +4,11 @@ GO ---Build+ GO CREATE TABLE tSQLt.Private_Seize( - Kaput BIT CONSTRAINT [PK:Private_Seize] PRIMARY KEY CONSTRAINT [CHK:Private_Seize] CHECK(Kaput=1) + Kaput BIT CONSTRAINT [Private_Seize:PK] PRIMARY KEY CONSTRAINT [Private_Seize:CHK] CHECK(Kaput=1) ); GO CREATE TABLE tSQLt.Private_Seize_NoTruncate( - NoTruncate BIT CONSTRAINT [FK:Private_Seize_NoTruncate(NoTruncate)] FOREIGN KEY REFERENCES tSQLt.Private_Seize(Kaput) + NoTruncate BIT CONSTRAINT [Private_Seize_NoTruncate(NoTruncate):FK] FOREIGN KEY REFERENCES tSQLt.Private_Seize(Kaput) ); GO CREATE TRIGGER tSQLt.Private_Seize_Stop ON tSQLt.Private_Seize INSTEAD OF DELETE,UPDATE From 5110b0f810be389c294e9ab0163cc68badabfb60 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 12 Dec 2021 06:47:34 -0500 Subject: [PATCH 094/119] Fixed SeizedTest to use the correct constraint name; Change SpyProcedure to allow spying of Private_GenerateCreateProcedureSpyStatement; Updated tSQLt.Private_RenameObjectToUniqueName to allow us to pass in a NewName (to make the change to SpyProcedure possible). --- ...e_GenerateCreateProcedureSpyStatement.ssp.sql | 5 +++-- ...SQLt.Private_RenameObjectToUniqueName.ssp.sql | 2 +- Source/tSQLt.SpyProcedure.ssp.sql | 16 ++++++++-------- Tests/Private_SeizeTests.class.sql | 2 +- Tests/SpyProcedureTests.class.sql | 6 +++--- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql index 179de0a88..6176baf30 100644 --- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql +++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql @@ -4,6 +4,7 @@ GO CREATE PROCEDURE tSQLt.Private_GenerateCreateProcedureSpyStatement @ProcedureObjectId INT, @OriginalProcedureName NVARCHAR(MAX), + @UnquotedNewNameOfProcedure NVARCHAR(MAX) = NULL, @LogTableName NVARCHAR(MAX), @CommandToExecute NVARCHAR(MAX), @CallOriginal BIT, @@ -92,11 +93,11 @@ BEGIN 'CREATE PROCEDURE ' + @OriginalProcedureName + ' ' + @ProcParmTypeList + ' AS BEGIN ' + ISNULL(@InsertStmt,'') + - 'DECLARE @SpyProcedureOriginalObjectName NVARCHAR(MAX) = '''+REPLACE(QUOTENAME(OBJECT_SCHEMA_NAME(@ProcedureObjectId))+'.'+QUOTENAME(OBJECT_NAME(@ProcedureObjectId)),'''','''''')+''';'+ + ISNULL('DECLARE @SpyProcedureOriginalObjectName NVARCHAR(MAX) = '''+REPLACE(QUOTENAME(OBJECT_SCHEMA_NAME(@ProcedureObjectId))+'.'+QUOTENAME(@UnquotedNewNameOfProcedure),'''','''''')+''';','')+ ISNULL(@CommandToExecute + ';', '') + CHAR(13)+CHAR(10)+/*CR,LF*/ CASE WHEN @CallOriginal = 1 - THEN 'EXEC '+QUOTENAME(OBJECT_SCHEMA_NAME(@ProcedureObjectId))+'.'+QUOTENAME(OBJECT_NAME(@ProcedureObjectId))+' ' + @ProcParmListForCall + ';' + THEN 'EXEC @SpyProcedureOriginalObjectName ' + @ProcParmListForCall + ';' ELSE '' END + ' RETURN;' + diff --git a/Source/tSQLt.Private_RenameObjectToUniqueName.ssp.sql b/Source/tSQLt.Private_RenameObjectToUniqueName.ssp.sql index d34ec79fe..e50bf7745 100644 --- a/Source/tSQLt.Private_RenameObjectToUniqueName.ssp.sql +++ b/Source/tSQLt.Private_RenameObjectToUniqueName.ssp.sql @@ -7,7 +7,7 @@ CREATE PROCEDURE tSQLt.Private_RenameObjectToUniqueName @NewName NVARCHAR(MAX) = NULL OUTPUT AS BEGIN - SET @NewName=tSQLt.Private::CreateUniqueObjectName(); + SET @NewName=ISNULL(@NewName, tSQLt.Private::CreateUniqueObjectName()); EXEC tSQLt.Private_MarkObjectBeforeRename @SchemaName, @ObjectName; diff --git a/Source/tSQLt.SpyProcedure.ssp.sql b/Source/tSQLt.SpyProcedure.ssp.sql index 4a9368688..ea05ed842 100644 --- a/Source/tSQLt.SpyProcedure.ssp.sql +++ b/Source/tSQLt.SpyProcedure.ssp.sql @@ -18,26 +18,26 @@ BEGIN DECLARE @CreateProcedureStatement NVARCHAR(MAX); DECLARE @CreateLogTableStatement NVARCHAR(MAX); - DECLARE @NewNameOfOriginalObject NVARCHAR(MAX); - - DECLARE @LogTableObjectId INT = OBJECT_ID(@LogTableName); - IF(@LogTableObjectId IS NOT NULL) - BEGIN - EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ObjectId = @LogTableObjectId; - END; - EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ProcedureObjectId, @NewName = @NewNameOfOriginalObject OUTPUT; + DECLARE @NewNameOfOriginalObject NVARCHAR(MAX) = tSQLt.Private::CreateUniqueObjectName(); EXEC tSQLt.Private_GenerateCreateProcedureSpyStatement @ProcedureObjectId = @ProcedureObjectId, @OriginalProcedureName = @ProcedureName, + @UnquotedNewNameOfProcedure = @NewNameOfOriginalObject, @LogTableName = @LogTableName, @CommandToExecute = @CommandToExecute, @CallOriginal = @CallOriginal, @CreateProcedureStatement = @CreateProcedureStatement OUT, @CreateLogTableStatement = @CreateLogTableStatement OUT; + DECLARE @LogTableObjectId INT = OBJECT_ID(@LogTableName); + IF(@LogTableObjectId IS NOT NULL) + BEGIN + EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ObjectId = @LogTableObjectId; + END; EXEC(@CreateLogTableStatement); + EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ProcedureObjectId, @NewName = @NewNameOfOriginalObject OUTPUT; EXEC(@CreateProcedureStatement); EXEC tSQLt.Private_MarktSQLtTempObject @ProcedureName, N'PROCEDURE', @NewNameOfOriginalObject; diff --git a/Tests/Private_SeizeTests.class.sql b/Tests/Private_SeizeTests.class.sql index 32f61d293..ae0f18982 100644 --- a/Tests/Private_SeizeTests.class.sql +++ b/Tests/Private_SeizeTests.class.sql @@ -17,7 +17,7 @@ GO CREATE PROCEDURE Private_SeizeTests.[test cannot insert <> 1] AS BEGIN - EXEC tSQLt.ExpectException @ExpectedMessagePattern = '%"CHK:Private_Seize"%'; + EXEC tSQLt.ExpectException @ExpectedMessagePattern = '%"Private_Seize:CHK"%'; INSERT INTO tSQLt.Private_Seize(Kaput) VALUES(0); END; diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql index 8d03d4214..d3e138cc5 100644 --- a/Tests/SpyProcedureTests.class.sql +++ b/Tests/SpyProcedureTests.class.sql @@ -706,7 +706,7 @@ BEGIN EXEC tSQLt.Private_GenerateCreateProcedureSpyStatement @ProcedureObjectId = @ProcedureObjectId, - @OriginalProcedureName = 'dbo.SpiedInnerProcedure', /*using different name to simulate renaming*/ + @OriginalProcedureName = 'dbo.OriginalInnerProcedure', @LogTableName = NULL, @CommandToExecute = NULL, @CallOriginal = 0, @@ -720,7 +720,7 @@ GO CREATE PROC SpyProcedureTests.[test Private_CreateProcedureSpy does create spy when @LogTableName is NULL] AS BEGIN - EXEC('CREATE PROC dbo.OriginalInnerProcedure AS RETURN;'); + EXEC('CREATE PROC dbo.OriginalInnerProcedure AS RETURN;'); /* This is the simulated rename of dbo.SpiedInnerProcedure */ DECLARE @ProcedureObjectId INT = OBJECT_ID('dbo.OriginalInnerProcedure'); @@ -729,7 +729,7 @@ BEGIN EXEC tSQLt.Private_GenerateCreateProcedureSpyStatement @ProcedureObjectId = @ProcedureObjectId, - @OriginalProcedureName = 'dbo.SpiedInnerProcedure', /*using different name to simulate renaming*/ + @OriginalProcedureName = 'dbo.SpiedInnerProcedure', @LogTableName = NULL, @CommandToExecute = NULL, @CallOriginal = 0, From 5d19150acb9fa276e3abed37331ff03ca2c6ac6c Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 12 Dec 2021 08:51:27 -0500 Subject: [PATCH 095/119] Refactored Private_RunTest so that we run less code when we are skipping tests. There are failing tests, we'll come back to them. --- Source/Run_Methods.sql | 233 +++++++++++++++++++++++------------------ 1 file changed, 133 insertions(+), 100 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 193efde8b..d486810ce 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -1,5 +1,6 @@ IF OBJECT_ID('tSQLt.Private_GetClassHelperProcedureName') IS NOT NULL DROP PROCEDURE tSQLt.Private_GetClassHelperProcedureName; IF OBJECT_ID('tSQLt.Private_RunTest') IS NOT NULL DROP PROCEDURE tSQLt.Private_RunTest; +IF OBJECT_ID('tSQLt.Private_RunTest_TestExecution') IS NOT NULL DROP PROCEDURE tSQLt.Private_RunTest_TestExecution; IF OBJECT_ID('tSQLt.Private_RunTestClass') IS NOT NULL DROP PROCEDURE tSQLt.Private_RunTestClass; IF OBJECT_ID('tSQLt.Private_Run') IS NOT NULL DROP PROCEDURE tSQLt.Private_Run; IF OBJECT_ID('tSQLt.Private_RunCursor') IS NOT NULL DROP PROCEDURE tSQLt.Private_RunCursor; @@ -41,120 +42,52 @@ BEGIN END; GO -CREATE PROCEDURE tSQLt.Private_RunTest - @TestName NVARCHAR(MAX), - @SetUp NVARCHAR(MAX) = NULL, - @CleanUp NVARCHAR(MAX) = NULL +CREATE PROCEDURE tSQLt.Private_RunTest_TestExecution + @TestName NVARCHAR(MAX), + @SetUp NVARCHAR(MAX), + @CleanUp NVARCHAR(MAX), + @NoTransactionFlag BIT, + @TranName CHAR(32), + @Result NVARCHAR(MAX) OUTPUT, + @Msg NVARCHAR(MAX) OUTPUT, + @TestEndTime DATETIME2 OUTPUT AS BEGIN - DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; - - DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; - DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; - DECLARE @Cmd NVARCHAR(MAX); SET @Cmd = ''; - DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; - DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; - DECLARE @Result NVARCHAR(MAX); - DECLARE @TranName CHAR(32) = NULL; - DECLARE @TestResultId INT; - DECLARE @PreExecTrancount INT = NULL; - DECLARE @TestObjectId INT; - - DECLARE @VerboseMsg NVARCHAR(MAX); - DECLARE @Verbose BIT; - SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); - - TRUNCATE TABLE tSQLt.CaptureOutputLog; - CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); - CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); - CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); - CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); - CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); - - - IF EXISTS (SELECT 1 FROM sys.extended_properties WHERE name = N'SetFakeViewOnTrigger') - BEGIN - RAISERROR('Test system is in an invalid state. SetFakeViewOff must be called if SetFakeViewOn was called. Call SetFakeViewOff after creating all test case procedures.', 16, 10) WITH NOWAIT; - RETURN -1; - END; - - SELECT @Cmd = 'EXEC ' + @TestName; - - SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), - @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), - @TestObjectId = OBJECT_ID(@TestName); - - INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) - SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' - OPTION(MAXDOP 1); - SELECT @TestResultId = SCOPE_IDENTITY(); + DECLARE @TransactionStartedFlag BIT = 0; + DECLARE @PreExecTrancount INT = NULL; + DECLARE @TestExecutionCmd NVARCHAR(MAX) = 'EXEC ' + @TestName; + DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; - IF(@Verbose = 1) - BEGIN - SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; - EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; - END; - - - SET @Result = 'Success'; - DECLARE @SkipTestFlag BIT = 0; - DECLARE @NoTransactionFlag BIT = 0; - DECLARE @NoTransactionTestCleanUpProcedureName NVARCHAR(MAX) = NULL; - DECLARE @TransactionStartedFlag BIT = 0; BEGIN TRY - EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; - SET @SkipTestFlag = CASE WHEN EXISTS(SELECT 1 FROM #SkipTest) THEN 1 ELSE 0 END; - SET @NoTransactionFlag = CASE WHEN EXISTS(SELECT 1 FROM #NoTransaction) THEN 1 ELSE 0 END; - /* Everything below the ----line and above the next one should not be executed if @SkipTestFlag = 1 - Move the annotation processing into its own TRY/CATCH block - Move everything between the lines into a tored procedure */ -------------------------------------------------------------------------------------------------------------------------- IF(@NoTransactionFlag = 0) BEGIN - EXEC tSQLt.GetNewTranName @TranName OUT; - UPDATE tSQLt.TestResult SET TranName = @TranName WHERE Id = @TestResultId; BEGIN TRAN; SET @TransactionStartedFlag = 1; SAVE TRAN @TranName; END; ELSE BEGIN - IF(@SkipTestFlag = 0) - BEGIN - SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; - EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; - END; + SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; + EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; END; SET @PreExecTrancount = @@TRANCOUNT; DECLARE @TmpMsg NVARCHAR(MAX); - DECLARE @TestEndTime DATETIME2; SET @TestEndTime = NULL; + SET @TestEndTime = NULL; BEGIN TRY - IF(@SkipTestFlag = 0) + IF (@SetUp IS NOT NULL) BEGIN - IF (@SetUp IS NOT NULL) - BEGIN - EXEC @SetUp; - END; - EXEC (@Cmd); - - IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) - BEGIN - SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; - EXEC tSQLt.Fail @TmpMsg; - END + EXEC @SetUp; END; - ELSE + EXEC (@TestExecutionCmd); + + IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) BEGIN - SELECT - @Result = 'Skipped', - @Msg = ST.SkipTestMessage - FROM #SkipTest AS ST; - SET @TmpMsg = '-->'+@TestName+' skipped: '+@Msg; - EXEC tSQLt.Private_Print @Message = @TmpMsg; - END; + SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; + EXEC tSQLt.Fail @TmpMsg; + END SET @TestEndTime = SYSDATETIME(); END TRY BEGIN CATCH @@ -280,9 +213,9 @@ BEGIN END; END CATCH; - IF (@NoTransactionFlag = 1 AND @SkipTestFlag = 0) + IF (@NoTransactionFlag = 1) BEGIN - SET @NoTransactionTestCleanUpProcedureName = ( + SET @CleanUpProcedureExecutionCmd = ( ( SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' FROM #NoTransaction NT @@ -290,9 +223,9 @@ BEGIN FOR XML PATH(''),TYPE ).value('.','NVARCHAR(MAX)') ); - IF(@NoTransactionTestCleanUpProcedureName IS NOT NULL) + IF(@CleanUpProcedureExecutionCmd IS NOT NULL) BEGIN - EXEC sys.sp_executesql @NoTransactionTestCleanUpProcedureName, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; + EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; END; IF(@CleanUp IS NOT NULL) @@ -312,6 +245,109 @@ BEGIN @TestMsg = @Msg OUT END; + IF(@TransactionStartedFlag = 1) + BEGIN + COMMIT; + END; +END; +GO + +CREATE PROCEDURE tSQLt.Private_RunTest + @TestName NVARCHAR(MAX), + @SetUp NVARCHAR(MAX) = NULL, + @CleanUp NVARCHAR(MAX) = NULL +AS +BEGIN + DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; + + DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; + DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; + DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; + DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; + DECLARE @Result NVARCHAR(MAX); + DECLARE @TranName CHAR(32) = NULL; + DECLARE @TestResultId INT; + DECLARE @TestObjectId INT; + DECLARE @TestEndTime DATETIME2 = NULL; + + DECLARE @VerboseMsg NVARCHAR(MAX); + DECLARE @Verbose BIT; + SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); + + TRUNCATE TABLE tSQLt.CaptureOutputLog; + CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); + CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); + CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); + CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + + + IF EXISTS (SELECT 1 FROM sys.extended_properties WHERE name = N'SetFakeViewOnTrigger') + BEGIN + RAISERROR('Test system is in an invalid state. SetFakeViewOff must be called if SetFakeViewOn was called. Call SetFakeViewOff after creating all test case procedures.', 16, 10) WITH NOWAIT; + RETURN -1; + END; + + + SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), + @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), + @TestObjectId = OBJECT_ID(@TestName); + + INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) + SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' + OPTION(MAXDOP 1); + SELECT @TestResultId = SCOPE_IDENTITY(); + + IF(@Verbose = 1) + BEGIN + SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; + EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; + END; + + + SET @Result = 'Success'; + DECLARE @SkipTestFlag BIT = 0; + DECLARE @NoTransactionFlag BIT = 0; + + BEGIN TRY + EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; + SET @SkipTestFlag = CASE WHEN EXISTS(SELECT 1 FROM #SkipTest) THEN 1 ELSE 0 END; + SET @NoTransactionFlag = CASE WHEN EXISTS(SELECT 1 FROM #NoTransaction) THEN 1 ELSE 0 END; + + IF(@SkipTestFlag = 0) + BEGIN + IF(@NoTransactionFlag = 0) + BEGIN + EXEC tSQLt.GetNewTranName @TranName OUT; + UPDATE tSQLt.TestResult SET TranName = @TranName WHERE Id = @TestResultId; + END; + EXEC tSQLt.Private_RunTest_TestExecution + @TestName, + @SetUp, + @CleanUp, + @NoTransactionFlag, + @TranName, + @Result OUT, + @Msg OUT, + @TestEndTime OUT + END; + ELSE + BEGIN + DECLARE @TmpMsg NVARCHAR(MAX); + SELECT + @Result = 'Skipped', + @Msg = ST.SkipTestMessage + FROM #SkipTest AS ST; + SET @TmpMsg = '-->'+@TestName+' skipped: '+@Msg; + EXEC tSQLt.Private_Print @Message = @TmpMsg; + SET @TestEndTime = SYSDATETIME(); + END; + END TRY + BEGIN CATCH + SET @Result = 'Error'; + SET @Msg = ISNULL(@Msg,'')+' '+ERROR_MESSAGE(); + --SET @TestEndTime = SYSDATETIME(); + END CATCH; ---------------------------------------------------------------------------------------------- If(@Result NOT IN ('Success','Skipped')) BEGIN @@ -337,10 +373,7 @@ BEGIN 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; END; - IF(@TransactionStartedFlag = 1) - BEGIN - COMMIT; - END; + IF(@Verbose = 1) BEGIN From 6d05e6cd5a60c6fb2461551c1be68e3de0a32dfd Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 12 Dec 2021 12:22:21 -0500 Subject: [PATCH 096/119] Created a function to consistently format error info; Broke the build. --- Source/BuildOrder.txt | 1 + Source/Run_Methods.sql | 7 ++----- Source/Source.ssmssqlproj | 6 ++++++ Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql | 2 +- .../tSQLt.Private_GetFormattedErrorInfo.sfn.sql | 17 +++++++++++++++++ Tests/BootStrapTest.sql | 2 +- 6 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 Source/tSQLt.Private_GetFormattedErrorInfo.sfn.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 236062b91..bdfb43de9 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -41,6 +41,7 @@ tSQLt.Private_RenameObjectToUniqueName.ssp.sql tSQLt.Private_RenameObjectToUniqueNameUsingObjectId.ssp.sql tSQLt.RemoveObject.ssp.sql tSQLt.RemoveObjectIfExists.ssp.sql +tSQLt.Private_GetFormattedErrorInfo.sfn.sql tSQLt.Private_CleanTestResult.ssp.sql tSQLt.Private_ListTestAnnotations.sfn.sql tSQLt.Private_ProcessTestAnnotations.ssp.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index d486810ce..15b6a1e37 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -100,10 +100,7 @@ BEGIN ELSE BEGIN DECLARE @ErrorInfo NVARCHAR(MAX); - SELECT @ErrorInfo = - COALESCE(ERROR_MESSAGE(), '') + - '[' +COALESCE(LTRIM(STR(ERROR_SEVERITY())), '') + ','+COALESCE(LTRIM(STR(ERROR_STATE())), '') + ']' + - '{' + COALESCE(ERROR_PROCEDURE(), '') + ',' + COALESCE(CAST(ERROR_LINE() AS NVARCHAR), '') + '}'; + SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); IF(EXISTS(SELECT 1 FROM #ExpectException)) BEGIN @@ -208,7 +205,7 @@ BEGIN OR @PostExecTrancount <> 0 ) BEGIN - SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + COALESCE(ERROR_MESSAGE(), '') + '{' + COALESCE(ERROR_PROCEDURE(), '') + ',' + COALESCE(CAST(ERROR_LINE() AS NVARCHAR(MAX)), '') + '})'; + SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); SET @Result = 'Error'; END; END CATCH; diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 2ba8b2f13..d79bb01fb 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -348,6 +348,12 @@ tSQLt.Private_GetForeignKeyDefinition.sfn.sql + + + + + tSQLt.Private_GetFormattedErrorInfo.sfn.sql + diff --git a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql index 4261102b7..ad7c12343 100644 --- a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql +++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -12,7 +12,7 @@ BEGIN EXEC(@CleanUpCmd); END TRY BEGIN CATCH - SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ ISNULL(@TestResult,'') + '] || ' ELSE '' END) + 'Error during clean up: (' + ERROR_MESSAGE() + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ' | Line: ' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ' | Severity, State: ' + CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)) + ', ' + CAST(ERROR_STATE() AS NVARCHAR(MAX)) + ')'; + SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ ISNULL(@TestResult,'') + '] || ' ELSE '' END) + 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; SET @TestResult = @ResultInCaseOfError; END CATCH; END; diff --git a/Source/tSQLt.Private_GetFormattedErrorInfo.sfn.sql b/Source/tSQLt.Private_GetFormattedErrorInfo.sfn.sql new file mode 100644 index 000000000..bb38fd081 --- /dev/null +++ b/Source/tSQLt.Private_GetFormattedErrorInfo.sfn.sql @@ -0,0 +1,17 @@ +IF OBJECT_ID('tSQLt.Private_GetFormattedErrorInfo') IS NOT NULL DROP FUNCTION tSQLt.Private_GetFormattedErrorInfo; +GO +---Build+ +CREATE FUNCTION tSQLt.Private_GetFormattedErrorInfo() +RETURNS TABLE +AS +RETURN + SELECT 'Message: ' + ISNULL(ERROR_MESSAGE(),'') + ' | Procedure: ' + ISNULL(ERROR_PROCEDURE(),'') + ISNULL(' (' + CAST(ERROR_LINE() AS NVARCHAR(MAX)) + ')','') + ' | Severity, State: ' + ISNULL(CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)),'') + ', ' + ISNULL(CAST(ERROR_STATE() AS NVARCHAR(MAX)), '') + ' | Number: ' + ISNULL(CAST(ERROR_NUMBER() AS NVARCHAR(MAX)), '') AS FormattedError; +GO +---Build- +GO +BEGIN TRY + SELECT CAST('A' AS INT); +END TRY +BEGIN CATCH + SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); +END CATCH \ No newline at end of file diff --git a/Tests/BootStrapTest.sql b/Tests/BootStrapTest.sql index bb8975070..1f8953ec0 100644 --- a/Tests/BootStrapTest.sql +++ b/Tests/BootStrapTest.sql @@ -49,7 +49,7 @@ BEGIN CATCH END CATCH; BEGIN TRY - IF NOT EXISTS(SELECT 1 FROM tSQLt.TestResult WHERE Msg LIKE 'TestCase was executed! (42)%') + IF NOT EXISTS(SELECT 1 FROM tSQLt.TestResult WHERE Msg LIKE '%TestCase was executed! (42)%') RAISERROR('TestCase was not executed',16,10); PRINT 'Test passed'; From e66f1aa89d03a20b327d6a8f530bc00c654aa2de Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 12 Dec 2021 17:41:41 -0500 Subject: [PATCH 097/119] Fixed tests which were failing because of error format; Added new logic to raise an error if the @Result of a test is Abort; Added more test todos --- Source/Run_Methods.sql | 6 +- Source/tSQLt.Private_CleanUp.ssp.sql | 3 +- Tests/AnnotationNoTransactionTests.class.sql | 63 +++++++++++++++----- Tests/tSQLt_test.class.sql | 3 +- 4 files changed, 57 insertions(+), 18 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 15b6a1e37..8f91c4c08 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -342,7 +342,7 @@ BEGIN END TRY BEGIN CATCH SET @Result = 'Error'; - SET @Msg = ISNULL(@Msg,'')+' '+ERROR_MESSAGE(); + SET @Msg = ISNULL(NULLIF(@Msg,'') + ' ','')+ERROR_MESSAGE(); --SET @TestEndTime = SYSDATETIME(); END CATCH; ---------------------------------------------------------------------------------------------- @@ -382,6 +382,10 @@ BEGIN BEGIN INSERT INTO tSQLt.Private_Seize VALUES(1); END; + IF(@Result = 'Abort') + BEGIN + RAISERROR('Aborting current execution of tSQLt.', 16, 10); + END; IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); diff --git a/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql index f6fbd7da9..c262958d5 100644 --- a/Source/tSQLt.Private_CleanUp.ssp.sql +++ b/Source/tSQLt.Private_CleanUp.ssp.sql @@ -18,7 +18,8 @@ BEGIN EXEC tSQLt.Private_CleanUpCmdHandler @CleanUpCmd = 'EXEC tSQLt.UndoTestDoubles @Force = 0;', @TestResult = @Result OUT, - @TestMsg = @ErrorMsg OUT; + @TestMsg = @ErrorMsg OUT, + @ResultInCaseOfError = 'Abort'; END; GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index c84cb69c6..4ca40bc6c 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -111,9 +111,21 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE FUNCTION AnnotationNoTransactionTests.[Return 42424242 prefix before ERROR_MESSAGE()]() +RETURNS TABLE +AS +RETURN + SELECT '42424242:'+ERROR_MESSAGE() FormattedError; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test recoverable erroring test gets correct entry in TestResults table] AS BEGIN + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetFormattedErrorInfo', @FakeFunctionName = 'AnnotationNoTransactionTests.[Return 42424242 prefix before ERROR_MESSAGE()]'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; + EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:NoTransaction](DEFAULT) @@ -127,7 +139,7 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RA SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; INSERT INTO #Expected - VALUES('Error','Some Obscure Recoverable Error[16,10]{MyInnerTests.test should execute outside of transaction,3}'); + VALUES('Error','42424242:Some Obscure Recoverable Error'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO @@ -416,7 +428,7 @@ GO ---[@tSQLt:SkipTest]('') --[@tSQLt:NoTransaction]('AnnotationNoTransactionTests.[CLEANUP: test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table]') /* This test must be NoTransaction because the inner test will invalidate any open transaction causing chaos and turmoil in the reactor. */ -CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table] +CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] AS BEGIN EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetLastTestNameIfNotProvided', @FakeFunctionName = 'AnnotationNoTransactionTests.PassThrough'; /* --<-- Prevent tSQLt-internal turmoil */ @@ -442,16 +454,11 @@ CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS PRINT C /*******************************************************************************************************************************/ EXEC tSQLt.SetSummaryError 0; - EXEC tSQLt.Run 'MyInnerTests.[test should cause unrecoverable error]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; - SELECT Name, Result, Msg INTO #Actual FROM tSQLt.TestResult AS TR; + DECLARE @Actual NVARCHAR(MAX) = (SELECT ISNULL(Result,'')+'<><><>'+ISNULL(Msg,'') FROM tSQLt.TestResult WHERE Name = '[MyInnerTests].[test should cause unrecoverable error]'); - SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - - INSERT INTO #Expected - VALUES('[MyInnerTests].[test should cause unrecoverable error]', 'Error','Conversion failed when converting the varchar value ''Some obscure string'' to data type int.[16,1]{MyInnerTests.test should cause unrecoverable error,3}'); - EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + EXEC tSQLt.AssertLike @ExpectedPattern = 'Error<><><>%Conversion failed when converting the varchar value ''Some obscure string'' to data type int.%', @Actual = @Actual; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -787,7 +794,7 @@ BEGIN DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); - EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is an error ;) | Procedure: MyInnerTests.CleanUp | Line: 5 | Severity, State: 16, 10)', @Actual = @FriendlyMsg; + EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (Message: This is an error ;) | Procedure: MyInnerTests.CleanUp (5) | Severity, State: 16, 10 | Number: 50000)', @Actual = @FriendlyMsg; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -817,7 +824,7 @@ BEGIN DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); - EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is another error ;) | Procedure: MyOtherInnerTests.CleanUp | Line: 6 | Severity, State: 15, 12)', @Actual = @FriendlyMsg; + EXEC tSQLt.AssertLike @ExpectedPattern = 'Error during clean up: (%This is another error ;)%)', @Actual = @FriendlyMsg; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -847,7 +854,7 @@ BEGIN DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); - EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is another error ;) | Procedure: | Line: 1 | Severity, State: 15, 12)', @Actual = @FriendlyMsg; + EXEC tSQLt.AssertLike @ExpectedPattern = 'Error during clean up: (%This is another error ;)%Procedure: %)', @Actual = @FriendlyMsg; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -1263,8 +1270,7 @@ BEGIN EXEC tSQLt.Run 'MyOtherInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); - - EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (This is another error ;) | Procedure: | Line: 1 | Severity, State: 15, 12)', @Actual = @FriendlyMsg; + EXEC tSQLt.AssertLike @ExpectedPattern = 'Error during clean up: (%This is another error ;)%Procedure: %)', @Actual = @FriendlyMsg; END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -1284,7 +1290,7 @@ CREATE PROCEDURE MyInnerTests.[test2] AS INSERT INTO #Actual DEFAULT VALUES; CREATE TABLE #Actual(Id CHAR(1) DEFAULT '*'); EXEC tSQLt.SetSummaryError 0; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'RAISERROR(''Error during Private_CleanUp'',16,10);'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp', @CommandToExecute = 'SET @Result = ''Abort''; SET @ErrorMsg = ''Error in Private_CleanUp'';'; EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; BEGIN TRY EXEC tSQLt.Run 'MyInnerTests'; @@ -1564,6 +1570,31 @@ BEGIN END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp when @Result = FATAL, it is not overwritten by another Result] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp @Result is not overwritten by an error in Private_AssertNoSideEffects] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:SkipTest]('TODO') +CREATE PROCEDURE AnnotationNoTransactionTests.[test when @Result=Abort appropriate error message is raised] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; GO /*-----------------------------------------------------------------------------------------------*/ GO @@ -1571,6 +1602,8 @@ GO CREATE PROCEDURE AnnotationNoTransactionTests.[test no other objects are dropped or created if SkipTest Annotation & NoTransaction annotations are used] AS BEGIN +/* When we write the function to manage the error messages, that function should have the logic to make sure that @Result can't get from a bad state to a better state + e.g. FATAL --> Abort --> Error --> Failure --> Success */ EXEC tSQLt.Fail 'TODO'; END; GO diff --git a/Tests/tSQLt_test.class.sql b/Tests/tSQLt_test.class.sql index 26acc7c20..4a0aa8d4c 100644 --- a/Tests/tSQLt_test.class.sql +++ b/Tests/tSQLt_test.class.sql @@ -96,6 +96,7 @@ GO CREATE PROC tSQLt_test.test_Run_handles_uncommitable_transaction AS BEGIN +EXEC tSQLt.Fail 'TODO: Rewrite to produce more helpful failure message, split in two tests'; DECLARE @TranName sysname; DECLARE @ProductMajorVersion INT; EXEC @ProductMajorVersion = tSQLt.Private_GetSQLProductMajorVersion; @@ -113,7 +114,7 @@ BEGIN AND Result = 'Error' AND Msg LIKE '%There is already an object named ''t1'' in the database.[[]%]{'+ CASE WHEN @ProductMajorVersion >= 14 THEN 'tSQLt_test.' ELSE '' END+ - 'testUncommitable00A1030051764AE7A946E827159E7063,1}%' + 'testUncommitable00A1030051764AE7A946E827159E7063 (1)%' AND Msg LIKE '%The current transaction cannot be committed and cannot be rolled back to a savepoint.%' ) BEGIN From e3f54e188dda99fd13bdffcfbbbfb519384710f0 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 13 Dec 2021 07:43:27 -0500 Subject: [PATCH 098/119] Fixed a whole bunch of tests related to the error formatting; And now we are just going to change it again. Yay! --- Source/BuildOrder.txt | 1 + Source/Source.ssmssqlproj | 6 ++ .../tSQLt.Private_CleanUpCmdHandler.ssp.sql | 3 + ...QLt.Private_HandleMessageAndResult.sfn.sql | 17 +++++ Tests/AnnotationHostPlatformTests.class.sql | 4 +- .../AnnotationSqlServerVersionTests.class.sql | 16 ++--- Tests/ExpectExceptionTests.class.sql | 2 +- Tests/ExpectNoExceptionTests.class.sql | 31 +++++---- Tests/Private_CleanUpTests.class.sql | 4 +- ...vate_HandleMessageAndResultTests.class.sql | 11 ++++ Tests/Run_Methods_Tests.class.sql | 42 ++++++++----- Tests/Tests.ssmssqlproj | 6 ++ Tests/tSQLt_test.class.sql | 63 ++++++++++++++----- 13 files changed, 147 insertions(+), 59 deletions(-) create mode 100644 Source/tSQLt.Private_HandleMessageAndResult.sfn.sql create mode 100644 Tests/Private_HandleMessageAndResultTests.class.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index bdfb43de9..2d14d5f76 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -42,6 +42,7 @@ tSQLt.Private_RenameObjectToUniqueNameUsingObjectId.ssp.sql tSQLt.RemoveObject.ssp.sql tSQLt.RemoveObjectIfExists.ssp.sql tSQLt.Private_GetFormattedErrorInfo.sfn.sql +tSQLt.Private_HandleMessageAndResult.sfn.sql tSQLt.Private_CleanTestResult.ssp.sql tSQLt.Private_ListTestAnnotations.sfn.sql tSQLt.Private_ProcessTestAnnotations.ssp.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index d79bb01fb..058335ae8 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -378,6 +378,12 @@ tSQLt.Private_GetUniqueConstraintDefinition.sfn.sql + + + + + tSQLt.Private_HandleMessageAndResult.sfn.sql + diff --git a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql index ad7c12343..261377b09 100644 --- a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql +++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -14,6 +14,9 @@ BEGIN BEGIN CATCH SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ ISNULL(@TestResult,'') + '] || ' ELSE '' END) + 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; SET @TestResult = @ResultInCaseOfError; + + --SET @NewMsg = 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; + --SET @TestMsg = Msg, @TestResult = Result FROM tSQLt.Private_HandleMessageAndResult(@TestMsg /*PrevMsg*/, @TestResult /*PrevResult*/, @NewMsg /*NewMsg*/, @ResultInCaseOfError /*NewResult*/); END CATCH; END; GO diff --git a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql new file mode 100644 index 000000000..55babf79a --- /dev/null +++ b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql @@ -0,0 +1,17 @@ +IF OBJECT_ID('tSQLt.Private_HandleMessageAndResult') IS NOT NULL DROP FUNCTION tSQLt.Private_HandleMessageAndResult; +GO +---Build+ +GO +CREATE FUNCTION tSQLt.Private_HandleMessageAndResult ( + @PrevMessage NVARCHAR(MAX), + @PrevResult NVARCHAR(MAX), + @NewMessage NVARCHAR(MAX), + @NewResult NVARCHAR(MAX) +) +RETURNS TABLE +AS +RETURN + SELECT 'NotYetEmpty' Message; +GO +---Build- +GO diff --git a/Tests/AnnotationHostPlatformTests.class.sql b/Tests/AnnotationHostPlatformTests.class.sql index e25029ef1..6a5e4d5bf 100644 --- a/Tests/AnnotationHostPlatformTests.class.sql +++ b/Tests/AnnotationHostPlatformTests.class.sql @@ -6,7 +6,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:RunOnlyOnHostPlatform](''SomePlatform'') -CREATE PROCEDURE MyInnerTests.[test should execute] AS RAISERROR(''test executed'',16,10); +CREATE PROCEDURE MyInnerTests.[test should execute] AS RAISERROR(''MM>M>M'',16,10); '); EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_HostPlatform'; @@ -14,7 +14,7 @@ CREATE PROCEDURE MyInnerTests.[test should execute] AS RAISERROR(''test executed EXEC tSQLt_testutil.AssertTestErrors @TestName = 'MyInnerTests.[test should execute]', - @ExpectedMessage = 'test executed%'; + @ExpectedMessage = '%MM>M>M%'; END; GO CREATE PROCEDURE AnnotationHostPlatformTests.[test doesn't allow test to execute if actual host platform is not equal to the provided one] diff --git a/Tests/AnnotationSqlServerVersionTests.class.sql b/Tests/AnnotationSqlServerVersionTests.class.sql index 7290296c3..6faa12a79 100644 --- a/Tests/AnnotationSqlServerVersionTests.class.sql +++ b/Tests/AnnotationSqlServerVersionTests.class.sql @@ -16,7 +16,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:MinSqlMajorVersion](13) -CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''test executed'',16,10); +CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''<><><> test executed <><><>'',16,10); '); EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_SqlVersion', @FakeFunctionName = 'AnnotationSqlServerVersionTests.[42.17.1986.57]'; @@ -24,7 +24,7 @@ CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''test exec EXEC tSQLt_testutil.AssertTestErrors @TestName = 'MyInnerTests.[test should not execute]', - @ExpectedMessage = 'test executed%'; + @ExpectedMessage = '%<><><> test executed <><><>%'; END; GO CREATE PROCEDURE AnnotationSqlServerVersionTests.[test MinSqlMajorVersion doesn't allow test to execute if actual version is smaller] @@ -65,7 +65,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:MinSqlMajorVersion](42) -CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''test executed'',16,10); +CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''<><><> test executed <><><>'',16,10); '); EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_SqlVersion', @FakeFunctionName = 'AnnotationSqlServerVersionTests.[42.17.1986.57]'; @@ -73,7 +73,7 @@ CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''test exec EXEC tSQLt_testutil.AssertTestErrors @TestName = 'MyInnerTests.[test should not execute]', - @ExpectedMessage = 'test executed%'; + @ExpectedMessage = '%<><><> test executed <><><>%'; END; GO CREATE PROCEDURE AnnotationSqlServerVersionTests.[test MinSqlMajorVersion provides a useful skip reason] @@ -116,7 +116,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:MaxSqlMajorVersion](43) -CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''test executed'',16,10); +CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''<><><> test executed! <><><>'',16,10); '); EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_SqlVersion', @FakeFunctionName = 'AnnotationSqlServerVersionTests.[42.17.1986.57]'; @@ -124,7 +124,7 @@ CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''test exec EXEC tSQLt_testutil.AssertTestErrors @TestName = 'MyInnerTests.[test should not execute]', - @ExpectedMessage = 'test executed%'; + @ExpectedMessage = '%<><><> test executed! <><><>%'; END; GO CREATE PROCEDURE AnnotationSqlServerVersionTests.[test MaxSqlMajorVersion doesn't allow test to execute if actual version is larger] @@ -165,7 +165,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:MaxSqlMajorVersion](42) -CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''test executed'',16,10); +CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''<><><> test executed <><><>'',16,10); '); EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_SqlVersion', @FakeFunctionName = 'AnnotationSqlServerVersionTests.[42.17.1986.57]'; @@ -173,7 +173,7 @@ CREATE PROCEDURE MyInnerTests.[test should not execute] AS RAISERROR(''test exec EXEC tSQLt_testutil.AssertTestErrors @TestName = 'MyInnerTests.[test should not execute]', - @ExpectedMessage = 'test executed%'; + @ExpectedMessage = '%<><><> test executed <><><>%'; END; GO CREATE PROCEDURE AnnotationSqlServerVersionTests.[test MaxSqlMajorVersion provides a useful skip reason] diff --git a/Tests/ExpectExceptionTests.class.sql b/Tests/ExpectExceptionTests.class.sql index 066784fb5..ca07ed2b4 100644 --- a/Tests/ExpectExceptionTests.class.sql +++ b/Tests/ExpectExceptionTests.class.sql @@ -186,7 +186,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyTestClass'; EXEC('CREATE PROC MyTestClass.TestExpectingNoException AS EXEC tSQLt.ExpectException;EXEC tSQLt.ExpectException;'); - EXEC tSQLt_testutil.AssertTestErrors 'MyTestClass.TestExpectingNoException','Each test can only contain one call to tSQLt.ExpectException.%'; + EXEC tSQLt_testutil.AssertTestErrors 'MyTestClass.TestExpectingNoException','%Each test can only contain one call to tSQLt.ExpectException.%'; END; GO CREATE PROCEDURE ExpectExceptionTests.[test expecting error number fails when unexpected error number is used] diff --git a/Tests/ExpectNoExceptionTests.class.sql b/Tests/ExpectNoExceptionTests.class.sql index fa4d77702..6fb2be2bf 100644 --- a/Tests/ExpectNoExceptionTests.class.sql +++ b/Tests/ExpectNoExceptionTests.class.sql @@ -16,22 +16,29 @@ BEGIN EXEC tSQLt_testutil.AssertTestFails 'MyTestClass.TestExpectingNoException'; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE FUNCTION ExpectNoExceptionTests.[Return 42424242 prefix before ERROR_MESSAGE()]() +RETURNS TABLE +AS +RETURN + SELECT '42424242: '+ERROR_MESSAGE() FormattedError; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE ExpectNoExceptionTests.[test tSQLt.ExpectNoException includes error information in fail message ] AS BEGIN + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetFormattedErrorInfo', @FakeFunctionName = 'ExpectNoExceptionTests.[Return 42424242 prefix before ERROR_MESSAGE()]'; - EXEC tSQLt.NewTestClass 'MyTestClass'; - EXEC('CREATE PROC MyTestClass.TestExpectingNoException AS EXEC tSQLt.ExpectNoException;RAISERROR(''test error message'',16,10);'); + EXEC tSQLt.NewTestClass 'MyTestClass'; + EXEC('CREATE PROC MyTestClass.TestExpectingNoException AS EXEC tSQLt.ExpectNoException;RAISERROR(''test error message'',16,10);'); - DECLARE @ExpectedMessage NVARCHAR(MAX); - DECLARE @ProductMajorVersion INT; - EXEC @ProductMajorVersion = tSQLt.Private_GetSQLProductMajorVersion; + DECLARE @ExpectedMessage NVARCHAR(MAX) = + 'Expected no error to be raised. Instead this error was encountered:'+CHAR(13)+CHAR(10)+ + '42424242: test error message'; - SET @ExpectedMessage = 'Expected no error to be raised. Instead this error was encountered:'+CHAR(13)+CHAR(10)+ - 'test error message[[]16,10]{'+ - CASE WHEN @ProductMajorVersion >= 14 THEN 'MyTestClass.' ELSE '' END+ - 'TestExpectingNoException,1}'; - EXEC tSQLt_testutil.AssertTestFails 'MyTestClass.TestExpectingNoException', @ExpectedMessage; + EXEC tSQLt_testutil.AssertTestFails 'MyTestClass.TestExpectingNoException', @ExpectedMessage; END; GO @@ -53,7 +60,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyTestClass'; EXEC('CREATE PROC MyTestClass.TestExpectingNoException AS EXEC tSQLt.ExpectNoException;EXEC tSQLt.ExpectNoException;'); - EXEC tSQLt_testutil.AssertTestErrors 'MyTestClass.TestExpectingNoException','Each test can only contain one call to tSQLt.ExpectNoException.%'; + EXEC tSQLt_testutil.AssertTestErrors 'MyTestClass.TestExpectingNoException','%Each test can only contain one call to tSQLt.ExpectNoException.%'; END; GO CREATE PROCEDURE ExpectNoExceptionTests.[test a ExpectNoException cannot follow an ExpectException] @@ -62,7 +69,7 @@ BEGIN EXEC tSQLt.NewTestClass 'MyTestClass'; EXEC('CREATE PROC MyTestClass.TestExpectingNoException AS EXEC tSQLt.ExpectException;EXEC tSQLt.ExpectNoException;'); - EXEC tSQLt_testutil.AssertTestErrors 'MyTestClass.TestExpectingNoException','tSQLt.ExpectNoException cannot follow tSQLt.ExpectException inside a single test.%'; + EXEC tSQLt_testutil.AssertTestErrors 'MyTestClass.TestExpectingNoException','%tSQLt.ExpectNoException cannot follow tSQLt.ExpectException inside a single test.%'; END; GO diff --git a/Tests/Private_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql index 5bd5d2118..2a07b968e 100644 --- a/Tests/Private_CleanUpTests.class.sql +++ b/Tests/Private_CleanUpTests.class.sql @@ -75,7 +75,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_CleanUpTests.[test UndoTestDoubles error causes @Result to be set to Error] +CREATE PROCEDURE Private_CleanUpTests.[test UndoTestDoubles error causes @Result to be set to Abort] AS BEGIN @@ -85,7 +85,7 @@ BEGIN DECLARE @Result NVARCHAR(MAX) = 'NOT ERROR'; EXEC tSQLt.Private_CleanUp @FullTestName = NULL, @Result = @Result OUT, @ErrorMsg = NULL; - EXEC tSQLt.AssertEqualsString @Expected = 'Error', @Actual = @Result; + EXEC tSQLt.AssertEqualsString @Expected = 'Abort', @Actual = @Result; END; GO /*-----------------------------------------------------------------------------------------------*/ diff --git a/Tests/Private_HandleMessageAndResultTests.class.sql b/Tests/Private_HandleMessageAndResultTests.class.sql new file mode 100644 index 000000000..735985624 --- /dev/null +++ b/Tests/Private_HandleMessageAndResultTests.class.sql @@ -0,0 +1,11 @@ +EXEC tSQLt.NewTestClass 'Private_HandleMessageAndResultTests'; +GO +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns empty Message if all parameters are null] +AS +BEGIN + DECLARE @Message NVARCHAR(MAX); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (NULL, NULL, NULL, NULL)); + + EXEC tSQLt.AssertEqualsString @Expected = '', @Actual = @Message; +END; +GO diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 041b0c8b4..5f2837f8b 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -81,27 +81,35 @@ GO CREATE PROC Run_Methods_Tests.test_Run_handles_test_names_with_spaces AS BEGIN - DECLARE @ProductMajorVersion INT; - EXEC @ProductMajorVersion = tSQLt.Private_GetSQLProductMajorVersion; + DECLARE @ProductMajorVersion INT; + EXEC @ProductMajorVersion = tSQLt.Private_GetSQLProductMajorVersion; - EXEC('CREATE SCHEMA MyTestClass;'); - EXEC('CREATE PROC MyTestClass.[Test Case A] AS RAISERROR(''GotHere'',16,10);'); + EXEC('CREATE SCHEMA MyTestClass;'); + EXEC('CREATE PROC MyTestClass.[Test Case A] AS RAISERROR(''<><><> GotHere <><><>'',16,10);'); - BEGIN TRY - EXEC tSQLt.Run 'MyTestClass.Test Case A'; - END TRY - BEGIN CATCH - --This space left intentionally blank - END CATCH - SELECT Class, TestCase, Msg - INTO Run_Methods_Tests.actual - FROM tSQLt.TestResult; - SELECT TOP(0)* INTO Run_Methods_Tests.expected FROM Run_Methods_Tests.actual; + BEGIN TRY + EXEC tSQLt.Run 'MyTestClass.Test Case A'; + END TRY + BEGIN CATCH + --This space left intentionally blank + END CATCH + SELECT Class, TestCase, Msg + INTO #Actual + FROM tSQLt.TestResult; + + SELECT TOP(0) A.Class,A.TestCase,A.Msg AS [%Msg] INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + SELECT 'MyTestClass' Class, 'Test Case A' TestCase, '%<><><> GotHere <><><>%' Msg - INSERT INTO Run_Methods_Tests.expected - SELECT 'MyTestClass' Class, 'Test Case A' TestCase, 'GotHere[16,10]{'+CASE WHEN @ProductMajorVersion >= 14 THEN 'MyTestClass.' ELSE '' END+'Test Case A,1}' Msg + SELECT * INTO #Compare + FROM( + SELECT '>' _R_,* FROM #Actual AS A WHERE NOT EXISTS(SELECT 1 FROM #Expected E WHERE A.Class = E.Class AND A.TestCase = E.TestCase AND A.Msg LIKE E.[%Msg]) + UNION ALL + SELECT '<' _R_,* FROM #Expected AS E WHERE NOT EXISTS(SELECT 1 FROM #Actual A WHERE A.Class = E.Class AND A.TestCase = E.TestCase AND A.Msg LIKE E.[%Msg]) + )X - EXEC tSQLt.AssertEqualsTable 'Run_Methods_Tests.expected', 'Run_Methods_Tests.actual'; + EXEC tSQLt.AssertEmptyTable '#Compare'; END; GO diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 62eca6124..12e388238 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -216,6 +216,12 @@ Private_GetSQLProductMajorVersionTests.class.sql + + + + + Private_HandleMessageAndResultTests.class.sql + diff --git a/Tests/tSQLt_test.class.sql b/Tests/tSQLt_test.class.sql index 4a0aa8d4c..5107cdb49 100644 --- a/Tests/tSQLt_test.class.sql +++ b/Tests/tSQLt_test.class.sql @@ -96,30 +96,59 @@ GO CREATE PROC tSQLt_test.test_Run_handles_uncommitable_transaction AS BEGIN -EXEC tSQLt.Fail 'TODO: Rewrite to produce more helpful failure message, split in two tests'; + DECLARE @TranName sysname; + + SELECT TOP(1) @TranName = TranName FROM tSQLt.TestResult WHERE Class = 'tSQLt_test' AND TestCase = 'test_Run_handles_uncommitable_transaction' ORDER BY Id DESC; + EXEC ('CREATE PROC tSQLt_test.testUncommitable00A1030051764AE7A946E827159E7063 AS BEGIN CREATE TABLE t1 (i int); CREATE TABLE t1 (i int); END;'); + BEGIN TRY + EXEC tSQLt.Run 'tSQLt_test.testUncommitable00A1030051764AE7A946E827159E7063'; + END TRY + BEGIN CATCH + --Left intentionally empty + END CATCH; + + SELECT Class, TestCase, Result, Msg + INTO #Actual + FROM tSQLt.TestResult + WHERE TestCase = 'testUncommitable00A1030051764AE7A946E827159E7063'; + + SELECT TOP(0) A.Class,A.TestCase,A.Result,A.Msg AS [%Msg] INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected + SELECT 'tSQLt_test' Class, 'testUncommitable00A1030051764AE7A946E827159E7063' TestCase, 'Error' Result, '%There is already an object named ''t1'' in the database.%The current transaction cannot be committed and cannot be rolled back to a savepoint.%' [%Msg]; + + SELECT * INTO #Compare + FROM( + SELECT '>' _R_,* FROM #Actual AS A WHERE NOT EXISTS(SELECT 1 FROM #Expected E WHERE A.Class = E.Class AND A.TestCase = E.TestCase AND A.Result = E.Result AND A.Msg LIKE E.[%Msg]) + UNION ALL + SELECT '<' _R_,* FROM #Expected AS E WHERE NOT EXISTS(SELECT 1 FROM #Actual A WHERE A.Class = E.Class AND A.TestCase = E.TestCase AND A.Result = E.Result AND A.Msg LIKE E.[%Msg]) + )X; + IF(@@ROWCOUNT>0) + BEGIN + INSERT INTO #Compare + SELECT '=' _R_,* FROM (SELECT * FROM #Actual INTERSECT SELECT * FROM #Expected)X; + END; + EXEC tSQLt.AssertEmptyTable '#Compare'; + + DELETE FROM tSQLt.TestResult + WHERE TestCase = 'testUncommitable00A1030051764AE7A946E827159E7063'; + BEGIN TRAN; + SAVE TRAN @TranName; +END; +GO + + +CREATE PROC tSQLt_test.test_Run_ROLLsBACK_uncommitable_transaction +AS +BEGIN DECLARE @TranName sysname; - DECLARE @ProductMajorVersion INT; - EXEC @ProductMajorVersion = tSQLt.Private_GetSQLProductMajorVersion; + SELECT TOP(1) @TranName = TranName FROM tSQLt.TestResult WHERE Class = 'tSQLt_test' AND TestCase = 'test_Run_ROLLsBACK_uncommitable_transaction' ORDER BY Id DESC; - SELECT TOP(1) @TranName = TranName FROM tSQLt.TestResult WHERE Class = 'tSQLt_test' AND TestCase = 'test_Run_handles_uncommitable_transaction' ORDER BY Id DESC; EXEC ('CREATE PROC tSQLt_test.testUncommitable00A1030051764AE7A946E827159E7063 AS BEGIN CREATE TABLE t1 (i int); CREATE TABLE t1 (i int); END;'); BEGIN TRY EXEC tSQLt.Run 'tSQLt_test.testUncommitable00A1030051764AE7A946E827159E7063'; END TRY BEGIN CATCH - --SELECT * FROM tSQLt.TestResult WHERE TestCase = 'testUncommitable00A1030051764AE7A946E827159E7063'; - IF NOT EXISTS(SELECT 1 - FROM tSQLt.TestResult - WHERE TestCase = 'testUncommitable00A1030051764AE7A946E827159E7063' - AND Result = 'Error' - AND Msg LIKE '%There is already an object named ''t1'' in the database.[[]%]{'+ - CASE WHEN @ProductMajorVersion >= 14 THEN 'tSQLt_test.' ELSE '' END+ - 'testUncommitable00A1030051764AE7A946E827159E7063 (1)%' - AND Msg LIKE '%The current transaction cannot be committed and cannot be rolled back to a savepoint.%' - ) - BEGIN - EXEC tSQLt.Fail 'tSQLt.Run ''tSQLt_test.testUncommitable00A1030051764AE7A946E827159E7063'' did not error correctly'; - END; IF(@@TRANCOUNT > 0) BEGIN EXEC tSQLt.Fail 'tSQLt.Run ''tSQLt_test.testUncommitable00A1030051764AE7A946E827159E7063'' did not rollback the transactions'; From 2ef6123980a2fb38aebc5b1620ab720b3128db1a Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 13 Dec 2021 18:02:59 -0500 Subject: [PATCH 099/119] Started writing unit tests for Private_HandleMessageAndResultTests function. At least one is failing. --- ...QLt.Private_HandleMessageAndResult.sfn.sql | 2 +- ...vate_HandleMessageAndResultTests.class.sql | 63 ++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql index 55babf79a..33269d50e 100644 --- a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql +++ b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql @@ -11,7 +11,7 @@ CREATE FUNCTION tSQLt.Private_HandleMessageAndResult ( RETURNS TABLE AS RETURN - SELECT 'NotYetEmpty' Message; + SELECT ' [Result: ] || '+ISNULL(@NewMessage,'') Message; GO ---Build- GO diff --git a/Tests/Private_HandleMessageAndResultTests.class.sql b/Tests/Private_HandleMessageAndResultTests.class.sql index 735985624..82da4f0a8 100644 --- a/Tests/Private_HandleMessageAndResultTests.class.sql +++ b/Tests/Private_HandleMessageAndResultTests.class.sql @@ -1,11 +1,68 @@ EXEC tSQLt.NewTestClass 'Private_HandleMessageAndResultTests'; GO -CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns empty Message if all parameters are null] +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns appropriately formatted Message if the first three parameters are null] +AS +BEGIN + DECLARE @Message NVARCHAR(MAX); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (NULL, NULL, NULL, DEFAULT)); + + EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ] || ', @Actual = @Message; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @NewMessage in appropriately formatted Message if only @NewMessage is valued] +AS +BEGIN + DECLARE @Message NVARCHAR(MAX); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (NULL, NULL, 'a random message', DEFAULT)); + + EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ] || a random message', @Actual = @Message; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @PrevMessage in appropriately formatted Message if only @PrevMessage is valued] AS BEGIN DECLARE @Message NVARCHAR(MAX); - SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (NULL, NULL, NULL, NULL)); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult ('another random message', NULL, NULL, DEFAULT)); - EXEC tSQLt.AssertEqualsString @Expected = '', @Actual = @Message; + EXEC tSQLt.AssertEqualsString @Expected = 'another random message [Result: ] || ', @Actual = @Message; END; GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns empty Message if the first three parameters are empty strings] +--AS +--BEGIN +-- DECLARE @Message NVARCHAR(MAX); +-- SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult ('', '', '', DEFAULT)); + +-- EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ] || ', @Actual = @Message; +--END; +--GO +--/*-----------------------------------------------------------------------------------------------*/ +--GO +--CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns appropriate Message if only @PrevResult is valued] +--AS +--BEGIN +-- DECLARE @Message NVARCHAR(MAX); +-- SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (NULL, 'ResultOne', NULL, DEFAULT)); +-- EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ResultOne] ||', @Actual = @Message; +--END; +--GO +--/*-----------------------------------------------------------------------------------------------*/ +--GO +--CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns empty Message if the first three parameters are white space only strings] +--AS +--BEGIN +-- DECLARE @Message NVARCHAR(MAX); +-- SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (CHAR(9), ' ', ' '+CHAR(9)+' ', DEFAULT)); +-- EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ] || ', @Actual = @Message; +--END; +--GO +--/*-----------------------------------------------------------------------------------------------*/ +--GO + + From e600f88d41e5a49eb31285a6a32b2f81d1615bd7 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 14 Dec 2021 07:44:53 -0500 Subject: [PATCH 100/119] Created tSQLt.Private_Results to help manage how we report out Messages and their related Statuses; Updated both views to enforce specific column definitions; --- Source/BuildOrder.txt | 1 + Source/Source.ssmssqlproj | 12 +- .../tSQLt.Private_CleanUpCmdHandler.ssp.sql | 10 +- ...QLt.Private_HandleMessageAndResult.sfn.sql | 5 +- ....Private_NoTransactionTableAction.view.sql | 2 +- Source/tSQLt.Private_Results.view.sql | 18 +++ ...vate_HandleMessageAndResultTests.class.sql | 149 ++++++++++++++---- Tests/Tests.ssmssqlproj | 6 - 8 files changed, 151 insertions(+), 52 deletions(-) create mode 100644 Source/tSQLt.Private_Results.view.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index 2d14d5f76..f5a7afd2a 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -3,6 +3,7 @@ tSQLt._Header.sql tSQLt.schema.sql tSQLt.TestClass.user.sql tSQLt.Private_GetDropItemCmd.sfn.sql +tSQLt.Private_Results.view.sql tSQLt.DropClass.ssp.sql tSQLt.Uninstall.ssp.sql tSQLt.TestClasses.view.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 058335ae8..cfe84c3e1 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -229,9 +229,6 @@ tSQLt.Private_CleanUp.ssp.sql - - - tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -379,9 +376,6 @@ tSQLt.Private_GetUniqueConstraintDefinition.sfn.sql - - - tSQLt.Private_HandleMessageAndResult.sfn.sql @@ -517,9 +511,6 @@ tSQLt.Private_NoTransactionHandleTable.ssp.sql - - - tSQLt.Private_NoTransactionTableAction.view.sql @@ -528,6 +519,9 @@ tSQLt.Private_NoTransactionHandleTables.ssp.sql + + tSQLt.Private_Results.view.sql + diff --git a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql index 261377b09..0a2fdfce5 100644 --- a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql +++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -12,11 +12,11 @@ BEGIN EXEC(@CleanUpCmd); END TRY BEGIN CATCH - SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ ISNULL(@TestResult,'') + '] || ' ELSE '' END) + 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; - SET @TestResult = @ResultInCaseOfError; - - --SET @NewMsg = 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; - --SET @TestMsg = Msg, @TestResult = Result FROM tSQLt.Private_HandleMessageAndResult(@TestMsg /*PrevMsg*/, @TestResult /*PrevResult*/, @NewMsg /*NewMsg*/, @ResultInCaseOfError /*NewResult*/); + --SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ ISNULL(@TestResult,'') + '] || ' ELSE '' END) + 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; + --SET @TestResult = @ResultInCaseOfError; + + DECLARE @NewMsg NVARCHAR(MAX) = 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; + SELECT @TestMsg = Message, @TestResult = Result FROM tSQLt.Private_HandleMessageAndResult(@TestMsg /*PrevMsg*/, @TestResult /*PrevResult*/, @NewMsg /*NewMsg*/, @ResultInCaseOfError /*NewResult*/); END CATCH; END; GO diff --git a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql index 33269d50e..03fe07cc6 100644 --- a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql +++ b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql @@ -11,7 +11,10 @@ CREATE FUNCTION tSQLt.Private_HandleMessageAndResult ( RETURNS TABLE AS RETURN - SELECT ' [Result: ] || '+ISNULL(@NewMessage,'') Message; + SELECT CASE WHEN @PrevMessage NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@PrevMessage,'') END+' [Result: '+ + CASE WHEN @PrevResult NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@PrevResult,'') END+'] || '+ + CASE WHEN @NewMessage NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@NewMessage,'') END Message, + (SELECT TOP(1) Result FROM tSQLt.Private_Results WHERE Result IN (@PrevResult, @NewResult) ORDER BY Severity DESC) Result; GO ---Build- GO diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql index 096cbb755..3b80a0673 100644 --- a/Source/tSQLt.Private_NoTransactionTableAction.view.sql +++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql @@ -4,7 +4,7 @@ GO GO CREATE VIEW tSQLt.Private_NoTransactionTableAction AS -SELECT * +SELECT CAST(Name AS NVARCHAR(MAX)) Name, CAST(Action AS NVARCHAR(MAX)) Action FROM( VALUES('[tSQLt].[Private_NewTestClassList]','Hide'), ('[tSQLt].[Run_LastExecution]','Hide'), diff --git a/Source/tSQLt.Private_Results.view.sql b/Source/tSQLt.Private_Results.view.sql new file mode 100644 index 000000000..4a628284d --- /dev/null +++ b/Source/tSQLt.Private_Results.view.sql @@ -0,0 +1,18 @@ +IF OBJECT_ID('tSQLt.Private_Results') IS NOT NULL DROP VIEW tSQLt.Private_Results; +GO +---Build+ +GO +CREATE VIEW tSQLt.Private_Results +AS +SELECT CAST(Severity AS INT) Severity,CAST(Result AS NVARCHAR(MAX)) Result + FROM( + VALUES(1, 'Success') + , + (2, 'Skipped'), + (3, 'Failure'), + (4, 'Error'), + (5, 'Abort'), + (6, 'FATAL') + )X(Severity, Result); +GO + diff --git a/Tests/Private_HandleMessageAndResultTests.class.sql b/Tests/Private_HandleMessageAndResultTests.class.sql index 82da4f0a8..4a2efd707 100644 --- a/Tests/Private_HandleMessageAndResultTests.class.sql +++ b/Tests/Private_HandleMessageAndResultTests.class.sql @@ -33,36 +33,125 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns empty Message if the first three parameters are empty strings] ---AS ---BEGIN --- DECLARE @Message NVARCHAR(MAX); --- SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult ('', '', '', DEFAULT)); +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns appropriate Message if only @PrevResult is valued] +AS +BEGIN + DECLARE @Message NVARCHAR(MAX); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (NULL, 'ResultOne', NULL, DEFAULT)); + EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ResultOne] || ', @Actual = @Message; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns in Message for each of the first three parameters if it is an empty string] +AS +BEGIN + DECLARE @Message NVARCHAR(MAX); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult ('', '', '', DEFAULT)); --- EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ] || ', @Actual = @Message; ---END; ---GO ---/*-----------------------------------------------------------------------------------------------*/ ---GO ---CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns appropriate Message if only @PrevResult is valued] ---AS ---BEGIN --- DECLARE @Message NVARCHAR(MAX); --- SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (NULL, 'ResultOne', NULL, DEFAULT)); --- EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ResultOne] ||', @Actual = @Message; ---END; ---GO ---/*-----------------------------------------------------------------------------------------------*/ ---GO ---CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns empty Message if the first three parameters are white space only strings] ---AS ---BEGIN --- DECLARE @Message NVARCHAR(MAX); --- SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (CHAR(9), ' ', ' '+CHAR(9)+' ', DEFAULT)); --- EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ] || ', @Actual = @Message; ---END; ---GO ---/*-----------------------------------------------------------------------------------------------*/ ---GO + EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ] || ', @Actual = @Message; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns empty Message if the first three parameters are white space only strings] +AS +BEGIN + DECLARE @Message NVARCHAR(MAX); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (CHAR(9), CHAR(9)+' '+CHAR(9), ' '+CHAR(9)+' ', DEFAULT)); + EXEC tSQLt.AssertEqualsString @Expected = ' [Result: ] || ', @Actual = @Message; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @NewResult as Result] +AS +BEGIN + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_Results'; + EXEC ('INSERT INTO tSQLt.Private_Results(Result, Severity)VALUES(''SomeResult'',42)'); + + DECLARE @Result NVARCHAR(MAX); + SET @Result = (SELECT Result FROM tSQLt.Private_HandleMessageAndResult (DEFAULT, DEFAULT, DEFAULT, 'SomeResult')); + EXEC tSQLt.AssertEqualsString @Expected = 'SomeResult', @Actual = @Result; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @PrevResult as Result if @NewResult is less severe] +AS +BEGIN + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_Results'; + EXEC ('INSERT INTO tSQLt.Private_Results(Result, Severity)VALUES(''SomeSevereResult'',7),(''SomeLessSevereResult'',3)'); + + DECLARE @Result NVARCHAR(MAX); + SET @Result = (SELECT Result FROM tSQLt.Private_HandleMessageAndResult (DEFAULT, 'SomeSevereResult', DEFAULT, 'SomeLessSevereResult')); + EXEC tSQLt.AssertEqualsString @Expected = 'SomeSevereResult', @Actual = @Result; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @PrevResult as Result if @NewResult is less severe if there are other values in Private_Results] +AS +BEGIN + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_Results'; + EXEC ('INSERT INTO tSQLt.Private_Results(Result, Severity)VALUES(''aaa'',10),(''SomeSevereResult'',7),(''SomeLessSevereResult'',3)'); + + DECLARE @Result NVARCHAR(MAX); + SET @Result = (SELECT Result FROM tSQLt.Private_HandleMessageAndResult (DEFAULT, 'SomeSevereResult', DEFAULT, 'SomeLessSevereResult')); + EXEC tSQLt.AssertEqualsString @Expected = 'SomeSevereResult', @Actual = @Result; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @PrevResult??? if @NewResult is not known] +AS +BEGIN + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_Results'; + EXEC ('INSERT INTO tSQLt.Private_Results(Result, Severity)VALUES(''SomePreviousResult'',3)'); + + DECLARE @Result NVARCHAR(MAX); + SET @Result = (SELECT Result FROM tSQLt.Private_HandleMessageAndResult (DEFAULT, 'SomePreviousResult', DEFAULT, 'SomeUnknownResult')); + EXEC tSQLt.AssertEqualsString @Expected = 'SomePreviousResult', @Actual = @Result; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @NewResult if @PrevResult is not known] +AS +BEGIN + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_Results'; + EXEC ('INSERT INTO tSQLt.Private_Results(Result, Severity)VALUES(''SomeNewResult'',3)'); + + DECLARE @Result NVARCHAR(MAX); + SET @Result = (SELECT Result FROM tSQLt.Private_HandleMessageAndResult (DEFAULT, 'SomeUnknownResult', DEFAULT, 'SomeNewResult')); + EXEC tSQLt.AssertEqualsString @Expected = 'SomeNewResult', @Actual = @Result; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns ??? if @PrevResult is NULL] +AS +BEGIN + EXEC tSQLt.Fail 'TODO: what shoud this do?'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test tSQLt.Private_Results double ledger] +AS +BEGIN + EXEC tSQLt.Fail 'TODO: (Needs to live in its own class)'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + \ No newline at end of file diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 12e388238..01dda88c6 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -19,9 +19,6 @@ AnnotationHostPlatformTests.class.sql - - - AnnotationNoTransactionTests.class.sql @@ -217,9 +214,6 @@ Private_GetSQLProductMajorVersionTests.class.sql - - - Private_HandleMessageAndResultTests.class.sql From 51ac0b12f7972e6efb23f0b8d5dc6460efef5761 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 15 Dec 2021 07:26:16 -0500 Subject: [PATCH 101/119] Test altering in progress. --- Tests/AnnotationNoTransactionTests.class.sql | 31 ++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 4ca40bc6c..57b28ac88 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -770,9 +770,33 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE FUNCTION AnnotationNoTransactionTests.[return 42134213 if correct error]() +RETURNS TABLE +AS +RETURN + SELECT '42134213' FormattedError WHERE ERROR_MESSAGE() = 'This is an error ;)'; +GO +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE FUNCTION AnnotationNoTransactionTests.[return 42424242+@NewMessage, @NewResult]( + @PrevMessage NVARCHAR(MAX), + @PrevResult NVARCHAR(MAX), + @NewMessage NVARCHAR(MAX), + @NewResult NVARCHAR(MAX) +) +RETURNS TABLE +AS +RETURN + SELECT '42424242:'+@NewMessage Message, @NewResult Result +GO +/*-----------------------------------------------------------------------------------------------*/ +GO CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes an appropriate message to be written to the tSQLt.TestResult table] AS BEGIN + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetFormattedErrorInfo', @FakeFunctionName = 'AnnotationNoTransactionTests.[return 42134213 if correct error]'; + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_HandleMessageAndResult', @FakeFunctionName = 'AnnotationNoTransactionTests.[return 42424242+@NewMessage, @NewResult]'; EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' CREATE PROCEDURE [MyInnerTests].[CleanUp] @@ -792,9 +816,12 @@ BEGIN EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; - DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); + SELECT TR.Msg, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES('42424242:42134213','Error'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; - EXEC tSQLt.AssertEqualsString @Expected = 'Error during clean up: (Message: This is an error ;) | Procedure: MyInnerTests.CleanUp (5) | Severity, State: 16, 10 | Number: 50000)', @Actual = @FriendlyMsg; END; GO /*-----------------------------------------------------------------------------------------------*/ From 6b8f9d1d4e55d9e9a3490ee429907d7d4d8becd2 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 15 Dec 2021 18:52:00 -0500 Subject: [PATCH 102/119] Writing more tests and many of them persist in failing. --- Source/Source.ssmssqlproj | 12 ++++ ...QLt.Private_HandleMessageAndResult.sfn.sql | 6 +- Tests/AnnotationNoTransactionTests.class.sql | 63 +---------------- .../Private_CleanUpCmdHandlerTests.class.sql | 10 +++ ...ivate_GetFormattedErrorInfoTests.class.sql | 68 +++++++++++++++++++ ...vate_HandleMessageAndResultTests.class.sql | 11 +++ Tests/Tests.ssmssqlproj | 18 +++++ 7 files changed, 126 insertions(+), 62 deletions(-) create mode 100644 Tests/Private_CleanUpCmdHandlerTests.class.sql create mode 100644 Tests/Private_GetFormattedErrorInfoTests.class.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index cfe84c3e1..02c672b42 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -229,6 +229,9 @@ tSQLt.Private_CleanUp.ssp.sql + + + tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -376,6 +379,9 @@ tSQLt.Private_GetUniqueConstraintDefinition.sfn.sql + + + tSQLt.Private_HandleMessageAndResult.sfn.sql @@ -511,6 +517,9 @@ tSQLt.Private_NoTransactionHandleTable.ssp.sql + + + tSQLt.Private_NoTransactionTableAction.view.sql @@ -520,6 +529,9 @@ tSQLt.Private_NoTransactionHandleTables.ssp.sql + + + tSQLt.Private_Results.view.sql diff --git a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql index 03fe07cc6..300263c8d 100644 --- a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql +++ b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql @@ -11,8 +11,10 @@ CREATE FUNCTION tSQLt.Private_HandleMessageAndResult ( RETURNS TABLE AS RETURN - SELECT CASE WHEN @PrevMessage NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@PrevMessage,'') END+' [Result: '+ - CASE WHEN @PrevResult NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@PrevResult,'') END+'] || '+ + SELECT CASE WHEN @PrevMessage NOT LIKE '%[^ '+CHAR(9)+']%' AND @PrevResult = 'Success' THEN '' + ELSE CASE WHEN @PrevMessage NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@PrevMessage,'') END+' [Result: '+ + CASE WHEN @PrevResult NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@PrevResult,'') END+'] || ' + END+ CASE WHEN @NewMessage NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@NewMessage,'') END Message, (SELECT TOP(1) Result FROM tSQLt.Private_Results WHERE Result IN (@PrevResult, @NewResult) ORDER BY Severity DESC) Result; GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 57b28ac88..6afa49b0a 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -797,6 +797,8 @@ AS BEGIN EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetFormattedErrorInfo', @FakeFunctionName = 'AnnotationNoTransactionTests.[return 42134213 if correct error]'; EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_HandleMessageAndResult', @FakeFunctionName = 'AnnotationNoTransactionTests.[return 42424242+@NewMessage, @NewResult]'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' CREATE PROCEDURE [MyInnerTests].[CleanUp] @@ -818,72 +820,13 @@ BEGIN SELECT TR.Msg, TR.Result INTO #Actual FROM tSQLt.TestResult AS TR; SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; - INSERT INTO #Expected VALUES('42424242:42134213','Error'); + INSERT INTO #Expected VALUES('42424242:Error during clean up: (42134213)','Error'); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO -/*-----------------------------------------------------------------------------------------------*/ -GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes an appropriate message to be written to the tSQLt.TestResult if there is a different error] -AS -BEGIN - EXEC tSQLt.NewTestClass 'MyOtherInnerTests' - EXEC(' - CREATE PROCEDURE [MyOtherInnerTests].[CleanUp] - AS - BEGIN - /*wasting lines...*/ - RAISERROR(''This is another error ;)'',15,12); - END; - '); - EXEC(' - --[@'+'tSQLt:NoTransaction](DEFAULT) - CREATE PROCEDURE [MyOtherInnerTests].[test1] - AS - BEGIN - RETURN; - END; - '); - - EXEC tSQLt.Run 'MyOtherInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; - - DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); - - EXEC tSQLt.AssertLike @ExpectedPattern = 'Error during clean up: (%This is another error ;)%)', @Actual = @FriendlyMsg; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes an appropriate message to be written to tSQLt.TestResult even if ERROR_PROCEDURE is null] -AS -BEGIN - EXEC tSQLt.NewTestClass 'MyOtherInnerTests' - EXEC(' - CREATE PROCEDURE [MyOtherInnerTests].[CleanUp] - AS - BEGIN - /*wasting lines...*/ - EXEC(''RAISERROR(''''This is another error ;)'''',15,12)''); - END; - '); - EXEC(' - --[@'+'tSQLt:NoTransaction](DEFAULT) - CREATE PROCEDURE [MyOtherInnerTests].[test1] - AS - BEGIN - RETURN; - END; - '); - EXEC tSQLt.Run 'MyOtherInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; - - DECLARE @FriendlyMsg NVARCHAR(MAX) = (SELECT TR.Msg FROM tSQLt.TestResult AS TR); - - EXEC tSQLt.AssertLike @ExpectedPattern = 'Error during clean up: (%This is another error ;)%Procedure: %)', @Actual = @FriendlyMsg; -END; -GO /*-----------------------------------------------------------------------------------------------*/ GO CREATE PROCEDURE AnnotationNoTransactionTests.[test appends message to any test error if Schema-CleanUp errors] diff --git a/Tests/Private_CleanUpCmdHandlerTests.class.sql b/Tests/Private_CleanUpCmdHandlerTests.class.sql new file mode 100644 index 000000000..7ab9dfba3 --- /dev/null +++ b/Tests/Private_CleanUpCmdHandlerTests.class.sql @@ -0,0 +1,10 @@ +EXEC tSQLt.NewTestClass 'Private_CleanUpCmdHandlerTests'; +GO +/* What is +*/ +CREATE PROCEDURE Private_CleanUpCmdHandlerTests.[test TODO] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; +END; +GO diff --git a/Tests/Private_GetFormattedErrorInfoTests.class.sql b/Tests/Private_GetFormattedErrorInfoTests.class.sql new file mode 100644 index 000000000..40f8788bd --- /dev/null +++ b/Tests/Private_GetFormattedErrorInfoTests.class.sql @@ -0,0 +1,68 @@ +EXEC tSQLt.NewTestClass 'Private_GetFormattedErrorInfoTests'; +GO +CREATE PROCEDURE Private_GetFormattedErrorInfoTests.[test does not return null] +AS +BEGIN + DECLARE @FormattedError NVARCHAR(MAX) = (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()); + + EXEC tSQLt.AssertEqualsString @Expected = 'Message: | Procedure: | Severity, State: , | Number: ', @Actual = @FormattedError; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_GetFormattedErrorInfoTests.[test returns the ERROR information formatted correctly] +AS +BEGIN + DECLARE @FormattedError NVARCHAR(MAX); + + BEGIN TRY + EXEC ('RAISERROR(''my test message'', 15, 11);'); + END TRY + BEGIN CATCH + SET @FormattedError = (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()); + END CATCH; + + EXEC tSQLt.AssertEqualsString @Expected = 'Message: my test message | Procedure: (1) | Severity, State: 15, 11 | Number: 50000', @Actual = @FormattedError; +END +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_GetFormattedErrorInfoTests.[test returns the correct ERROR number] +AS +BEGIN + DECLARE @FormattedError NVARCHAR(MAX); + + BEGIN TRY + EXEC ('RAISERROR (13042,14,13);'); + END TRY + BEGIN CATCH + SET @FormattedError = (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()); + END CATCH; + + EXEC tSQLt.AssertLike @ExpectedPattern = '%| Number: 13042', @Actual = @FormattedError; +END +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_GetFormattedErrorInfoTests.[test returns the correct ERROR procedure name and line number] +AS +BEGIN + DECLARE @FormattedError NVARCHAR(MAX); + + BEGIN TRY + EXEC ('/*Line 1*/CREATE PROCEDURE #myInnerError + /*Line 2*/AS + /*Line 3*/BEGIN + /*Line 4*/ RAISERROR (13042,14,13); + /*Line 5*/END;'); + EXEC #myInnerError; + END TRY + BEGIN CATCH + SET @FormattedError = (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()); + END CATCH; + + EXEC tSQLt.AssertLike @ExpectedPattern = '%| Procedure: #myInnerError (4) |%', @Actual = @FormattedError; +END +GO +/*-----------------------------------------------------------------------------------------------*/ +GO diff --git a/Tests/Private_HandleMessageAndResultTests.class.sql b/Tests/Private_HandleMessageAndResultTests.class.sql index 4a2efd707..11e3949e0 100644 --- a/Tests/Private_HandleMessageAndResultTests.class.sql +++ b/Tests/Private_HandleMessageAndResultTests.class.sql @@ -136,6 +136,17 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns only the @NewMessage if @PrevMessage is empty and @PrevResult is Success] +AS +BEGIN + DECLARE @Message NVARCHAR(MAX); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult ('', 'Success', 'this is the new message', DEFAULT)); + EXEC tSQLt.AssertEqualsString @Expected = 'this is the new message', @Actual = @Message; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns ??? if @PrevResult is NULL] AS BEGIN diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 01dda88c6..4da27803e 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -19,6 +19,9 @@ AnnotationHostPlatformTests.class.sql + + + AnnotationNoTransactionTests.class.sql @@ -183,6 +186,12 @@ NewTestClassTests.class.sql + + + + + Private_CleanUpCmdHandlerTests.class.sql + @@ -201,6 +210,12 @@ Private_GetAnnotationListTests.class.sql + + + + + Private_GetFormattedErrorInfoTests.class.sql + @@ -214,6 +229,9 @@ Private_GetSQLProductMajorVersionTests.class.sql + + + Private_HandleMessageAndResultTests.class.sql From 52da4c872205cc1ec1c724e5c18d4fd21f63c694 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 16 Dec 2021 07:14:56 -0500 Subject: [PATCH 103/119] Added double ledger test for Private_Results view; Test for Private_CleanUpCmdHandler; Completed(!?) tests for Private_HandleMessageAndResult; More AnnotationNoTransactionTests. --- .../tSQLt.Private_CleanUpCmdHandler.ssp.sql | 3 -- ...QLt.Private_HandleMessageAndResult.sfn.sql | 2 +- Tests/AnnotationNoTransactionTests.class.sql | 24 +++++++++-- .../Private_CleanUpCmdHandlerTests.class.sql | 42 +++++++++++++++++-- ...vate_HandleMessageAndResultTests.class.sql | 18 +++++--- Tests/Private_ResultsTests.class.sql | 19 +++++++++ Tests/Tests.ssmssqlproj | 6 +++ 7 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 Tests/Private_ResultsTests.class.sql diff --git a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql index 0a2fdfce5..e8fdd04c0 100644 --- a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql +++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -12,9 +12,6 @@ BEGIN EXEC(@CleanUpCmd); END TRY BEGIN CATCH - --SET @TestMsg = (CASE WHEN @TestMsg <> '' THEN @TestMsg + ' [Result: '+ ISNULL(@TestResult,'') + '] || ' ELSE '' END) + 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; - --SET @TestResult = @ResultInCaseOfError; - DECLARE @NewMsg NVARCHAR(MAX) = 'Error during clean up: (' + (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()) + ')'; SELECT @TestMsg = Message, @TestResult = Result FROM tSQLt.Private_HandleMessageAndResult(@TestMsg /*PrevMsg*/, @TestResult /*PrevResult*/, @NewMsg /*NewMsg*/, @ResultInCaseOfError /*NewResult*/); END CATCH; diff --git a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql index 300263c8d..2379fa0e5 100644 --- a/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql +++ b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql @@ -11,7 +11,7 @@ CREATE FUNCTION tSQLt.Private_HandleMessageAndResult ( RETURNS TABLE AS RETURN - SELECT CASE WHEN @PrevMessage NOT LIKE '%[^ '+CHAR(9)+']%' AND @PrevResult = 'Success' THEN '' + SELECT CASE WHEN ISNULL(@PrevMessage,'') NOT LIKE '%[^ '+CHAR(9)+']%' AND @PrevResult = 'Success' THEN '' ELSE CASE WHEN @PrevMessage NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@PrevMessage,'') END+' [Result: '+ CASE WHEN @PrevResult NOT LIKE '%[^ '+CHAR(9)+']%' THEN '' ELSE ISNULL(@PrevResult,'') END+'] || ' END+ diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 6afa49b0a..42131b428 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1513,7 +1513,7 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_AssertNoSideEffects is executed after all CleanUps and throws an error if they are new, missing, or renamed objects] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_AssertNoSideEffects is executed after all CleanUps and throws an error if there are new/missing/renamed objects] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' @@ -1545,13 +1545,29 @@ GO CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp when @Result = FATAL, it is not overwritten by another Result] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN; + END; + '); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'IF(@Action=''Reset'')RAISERROR(''AFatalError'',16,10);'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles', @CommandToExecute = 'RAISERROR(''AnAbortError'',16,10);'; + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + DECLARE @Actual NVARCHAR(MAX) = (SELECT Result FROM tSQLt.TestResult); + + EXEC tSQLt.AssertEqualsString @Expected = 'FATAL', @Actual = @Actual; END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp @Result is not overwritten by an error in Private_AssertNoSideEffects] +CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_AssertNoSideEffects is using Private_CleanUpCmdHandler] AS BEGIN EXEC tSQLt.Fail 'TODO'; diff --git a/Tests/Private_CleanUpCmdHandlerTests.class.sql b/Tests/Private_CleanUpCmdHandlerTests.class.sql index 7ab9dfba3..a2caa9613 100644 --- a/Tests/Private_CleanUpCmdHandlerTests.class.sql +++ b/Tests/Private_CleanUpCmdHandlerTests.class.sql @@ -1,10 +1,44 @@ EXEC tSQLt.NewTestClass 'Private_CleanUpCmdHandlerTests'; GO -/* What is -*/ -CREATE PROCEDURE Private_CleanUpCmdHandlerTests.[test TODO] +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE FUNCTION Private_CleanUpCmdHandlerTests.[return 42134213 if correct error]() +RETURNS TABLE +AS +RETURN + SELECT '9999' + ERROR_MESSAGE() FormattedError; +GO +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE FUNCTION Private_CleanUpCmdHandlerTests.[return 42424242+@NewMessage, @NewResult]( + @PrevMessage NVARCHAR(MAX), + @PrevResult NVARCHAR(MAX), + @NewMessage NVARCHAR(MAX), + @NewResult NVARCHAR(MAX) +) +RETURNS TABLE +AS +RETURN + SELECT @PrevResult+':7777:'+@NewMessage Message, @NewResult Result +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_CleanUpCmdHandlerTests.[test is using the two error functions correctly] AS BEGIN - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_GetFormattedErrorInfo', @FakeFunctionName = 'Private_CleanUpCmdHandlerTests.[return 42134213 if correct error]'; + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_HandleMessageAndResult', @FakeFunctionName = 'Private_CleanUpCmdHandlerTests.[return 42424242+@NewMessage, @NewResult]'; + + DECLARE @TestResult NVARCHAR(MAX) = 'PrevResult'; + DECLARE @TestMessage NVARCHAR(MAX) = 'PrevMessage'; + EXEC tSQLt.Private_CleanUpCmdHandler @CleanUpCmd = 'RAISERROR(''ACleanUpError'',16,10);', @TestResult=@TestResult OUT, @TestMsg = @TestMessage OUT, @ResultInCaseOfError = 'NewResult'; + + SELECT @TestMessage Message, @TestResult Result INTO #Actual; + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected VALUES('PrevResult:7777:Error during clean up: (9999ACleanUpError)','NewResult'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO diff --git a/Tests/Private_HandleMessageAndResultTests.class.sql b/Tests/Private_HandleMessageAndResultTests.class.sql index 11e3949e0..ca0bef266 100644 --- a/Tests/Private_HandleMessageAndResultTests.class.sql +++ b/Tests/Private_HandleMessageAndResultTests.class.sql @@ -108,7 +108,7 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @PrevResult??? if @NewResult is not known] +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @PrevResult if @NewResult is not known] AS BEGIN EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_Results'; @@ -147,22 +147,28 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns ??? if @PrevResult is NULL] +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns only the @NewMessage if @PrevMessage is NULL and @PrevResult is Success] AS BEGIN - EXEC tSQLt.Fail 'TODO: what shoud this do?'; + DECLARE @Message NVARCHAR(MAX); + SET @Message = (SELECT Message FROM tSQLt.Private_HandleMessageAndResult (NULL, 'Success', 'this is the new message', DEFAULT)); + EXEC tSQLt.AssertEqualsString @Expected = 'this is the new message', @Actual = @Message; END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_HandleMessageAndResultTests.[test tSQLt.Private_Results double ledger] +CREATE PROCEDURE Private_HandleMessageAndResultTests.[test returns @NewResult if @PrevResult is NULL] AS BEGIN - EXEC tSQLt.Fail 'TODO: (Needs to live in its own class)'; + EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_Results'; + EXEC ('INSERT INTO tSQLt.Private_Results(Result, Severity)VALUES(''SomeNewResult'',3)'); + + DECLARE @Result NVARCHAR(MAX); + SET @Result = (SELECT Result FROM tSQLt.Private_HandleMessageAndResult (DEFAULT, NULL, DEFAULT, 'SomeNewResult')); + EXEC tSQLt.AssertEqualsString @Expected = 'SomeNewResult', @Actual = @Result; END; GO /*-----------------------------------------------------------------------------------------------*/ GO - \ No newline at end of file diff --git a/Tests/Private_ResultsTests.class.sql b/Tests/Private_ResultsTests.class.sql new file mode 100644 index 000000000..26460f1a6 --- /dev/null +++ b/Tests/Private_ResultsTests.class.sql @@ -0,0 +1,19 @@ +EXEC tSQLt.NewTestClass 'Private_ResultsTests'; +GO +CREATE PROCEDURE Private_ResultsTests.[test Contains all Result values (double ledger)] +AS +BEGIN + SELECT Severity, Result INTO #Actual FROM tSQLt.Private_Results; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + INSERT INTO #Expected + SELECT 1,'Success' UNION ALL + SELECT 2,'Skipped' UNION ALL + SELECT 3,'Failure' UNION ALL + SELECT 4,'Error' UNION ALL + SELECT 5,'Abort' UNION ALL + SELECT 6,'FATAL' ; + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index 4da27803e..e60d1673b 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -300,6 +300,12 @@ Private_ResetNewTestClassListTests.class.sql + + + + + Private_ResultsTests.class.sql + From 262af4175a647fa43f3519d37b64adb1076c9ad8 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Thu, 16 Dec 2021 18:58:43 -0500 Subject: [PATCH 104/119] Refactored; Broke @Result=FATAL temporarily; Wrote more tests and then broke some tests. --- Source/BuildOrder.txt | 1 + Source/Run_Methods.sql | 5 +- Source/Source.ssmssqlproj | 18 ++--- .../tSQLt.Private_AssertNoSideEffects.ssp.sql | 16 +--- ...ssertNoSideEffects_GenerateCommand.sfn.sql | 25 ++++++ Tests/AnnotationNoTransactionTests.class.sql | 68 +++++----------- ...Private_AssertNoSideEffectsTests.class.sql | 55 +++++++++++++ Tests/Run_Methods_Tests.class.sql | 81 +++++++++++++++++++ Tests/Tests.ssmssqlproj | 12 ++- 9 files changed, 197 insertions(+), 84 deletions(-) create mode 100644 Source/tSQLt.Private_AssertNoSideEffects_GenerateCommand.sfn.sql create mode 100644 Tests/Private_AssertNoSideEffectsTests.class.sql diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index f5a7afd2a..df1c46aeb 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -61,6 +61,7 @@ tSQLt.Private_NoTransactionHandleTable.ssp.sql tSQLt.Private_NoTransactionHandleTables.ssp.sql tSQLt.Private_CleanUpCmdHandler.ssp.sql tSQLt.Private_CleanUp.ssp.sql +tSQLt.Private_AssertNoSideEffects_GenerateCommand.sfn.sql tSQLt.Private_AssertNoSideEffects.ssp.sql Run_Methods.sql tSQLt.Private_SysTypes.svw.sql diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 8f91c4c08..46cc68bc4 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -380,11 +380,12 @@ BEGIN IF(@Result = 'FATAL') BEGIN - INSERT INTO tSQLt.Private_Seize VALUES(1); + INSERT INTO tSQLt.Private_Seize VALUES(1); + RAISERROR('The last test has invalidated the current installation of tSQLt. Please reinstall tSQLt.',16,10); END; IF(@Result = 'Abort') BEGIN - RAISERROR('Aborting current execution of tSQLt.', 16, 10); + RAISERROR('Aborting the current execution of tSQLt due to a severe error.', 16, 10); END; IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 02c672b42..85f3316e9 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -2,7 +2,8 @@ - + + @@ -19,9 +20,6 @@ ExecutePrepareServer.sql - - - Run_Methods.sql @@ -211,11 +209,11 @@ tSQLt.PrepareServer.ssp.sql - - - tSQLt.Private_AssertNoSideEffects.ssp.sql + + tSQLt.Private_AssertNoSideEffects_GenerateCommand.sfn.sql + @@ -229,9 +227,6 @@ tSQLt.Private_CleanUp.ssp.sql - - - tSQLt.Private_CleanUpCmdHandler.ssp.sql @@ -397,9 +392,6 @@ tSQLt.Private_HostPlatform.svw.sql - - - tSQLt.Private_Init.ssp.sql diff --git a/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql b/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql index 2dec36c6d..8febaac61 100644 --- a/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql +++ b/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql @@ -9,19 +9,7 @@ CREATE PROCEDURE tSQLt.Private_AssertNoSideEffects @TestMsg NVARCHAR(MAX) OUTPUT AS BEGIN - DECLARE @cmd NVARCHAR(MAX) = ' - SELECT * INTO #ObjectDiscrepancies - FROM( - (SELECT ''Deleted'' [Status], B.* FROM '+@BeforeExecutionObjectSnapshotTableName+' AS B EXCEPT SELECT ''Deleted'' [Status],* FROM '+@AfterExecutionObjectSnapshotTableName+' AS A) - UNION ALL - (SELECT ''Added'' [Status], A.* FROM '+@AfterExecutionObjectSnapshotTableName+' AS A EXCEPT SELECT ''Added'' [Status], * FROM '+@BeforeExecutionObjectSnapshotTableName+' AS B) - )D; - IF(EXISTS(SELECT 1 FROM #ObjectDiscrepancies)) - BEGIN - DECLARE @TableToText NVARCHAR(MAX); - EXEC tSQLt.TableToText @TableName = ''#ObjectDiscrepancies'' ,@txt = @TableToText OUTPUT, @OrderBy = ''[Status] ASC, SchemaName ASC, ObjectName ASC''; - RAISERROR(''After the test executed, there were unexpected or missing objects in the database: %s'',16,10,@TableToText); - END;'; - EXEC tSQLt.Private_CleanUpCmdHandler @cmd, @TestResult OUT, @TestMsg OUT; + DECLARE @Command NVARCHAR(MAX) = (SELECT Command FROM tSQLt.Private_AssertNoSideEffects_GenerateCommand(@BeforeExecutionObjectSnapshotTableName, @AfterExecutionObjectSnapshotTableName)); + EXEC tSQLt.Private_CleanUpCmdHandler @CleanUpCmd=@Command, @TestResult=@TestResult OUT, @TestMsg=@TestMsg OUT; END; GO diff --git a/Source/tSQLt.Private_AssertNoSideEffects_GenerateCommand.sfn.sql b/Source/tSQLt.Private_AssertNoSideEffects_GenerateCommand.sfn.sql new file mode 100644 index 000000000..aa179eeb3 --- /dev/null +++ b/Source/tSQLt.Private_AssertNoSideEffects_GenerateCommand.sfn.sql @@ -0,0 +1,25 @@ +IF OBJECT_ID('tSQLt.Private_AssertNoSideEffects_GenerateCommand') IS NOT NULL DROP FUNCTION tSQLt.Private_AssertNoSideEffects_GenerateCommand; +GO +---Build+ +GO +CREATE FUNCTION tSQLt.Private_AssertNoSideEffects_GenerateCommand( + @BeforeExecutionObjectSnapshotTableName NVARCHAR(MAX), + @AfterExecutionObjectSnapshotTableName NVARCHAR(MAX) +) +RETURNS TABLE +AS +RETURN + SELECT ' + SELECT * INTO #ObjectDiscrepancies + FROM( + (SELECT ''Deleted'' [Status], B.* FROM '+@BeforeExecutionObjectSnapshotTableName+' AS B EXCEPT SELECT ''Deleted'' [Status],* FROM '+@AfterExecutionObjectSnapshotTableName+' AS A) + UNION ALL + (SELECT ''Added'' [Status], A.* FROM '+@AfterExecutionObjectSnapshotTableName+' AS A EXCEPT SELECT ''Added'' [Status], * FROM '+@BeforeExecutionObjectSnapshotTableName+' AS B) + )D; + IF(EXISTS(SELECT 1 FROM #ObjectDiscrepancies)) + BEGIN + DECLARE @TableToText NVARCHAR(MAX); + EXEC tSQLt.TableToText @TableName = ''#ObjectDiscrepancies'' ,@txt = @TableToText OUTPUT, @OrderBy = ''[Status] ASC, SchemaName ASC, ObjectName ASC''; + RAISERROR(''After the test executed, there were unexpected or missing objects in the database: %s'',16,10,@TableToText); + END;' Command; +GO diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 42131b428..82a53b145 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1453,31 +1453,6 @@ BEGIN EXEC tSQLt.AssertLike @ExpectedPattern = 'BeforeMessage [[]Result: ] || %NewMessage%', @Actual = @TestMessage; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test FATAL error prevents subsequent tSQLt.Run% calls] -AS -BEGIN - EXEC tSQLt.NewTestClass 'MyInnerTests' - EXEC(' - --[@'+'tSQLt:NoTransaction](DEFAULT) - CREATE PROCEDURE [MyInnerTests].[test1] - AS - BEGIN - RETURN; - END; - '); - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'IF(@Action = ''Reset'')BEGIN RAISERROR(''Some Fatal Error'',16,10);END;'; - - EXEC tSQLt.Run 'MyInnerTests', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; - - EXEC tSQLt.ExpectException @ExpectedMessage = 'tSQLt is in an invalid state. Please reinstall tSQLt.'; - EXEC tSQLt.Run 'MyInnerTests', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; - END; GO /*-----------------------------------------------------------------------------------------------*/ @@ -1567,32 +1542,29 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_AssertNoSideEffects is using Private_CleanUpCmdHandler] +CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_RunTest_TestExecution is not called if SkipTest & NoTransaction] AS BEGIN - EXEC tSQLt.Fail 'TODO'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test when @Result=Abort appropriate error message is raised] -AS -BEGIN - EXEC tSQLt.Fail 'TODO'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test no other objects are dropped or created if SkipTest Annotation & NoTransaction annotations are used] -AS -BEGIN -/* When we write the function to manage the error messages, that function should have the logic to make sure that @Result can't get from a bad state to a better state - e.g. FATAL --> Abort --> Error --> Failure --> Success */ - EXEC tSQLt.Fail 'TODO'; + EXEC tSQLt.Fail 'This test has gone horribly wrong and kills tSQLt.'; + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + --[@'+'tSQLt:SkipTest](DEFAULT) + CREATE PROCEDURE MyInnerTests.[test1] + AS + BEGIN + RETURN; + END; + '); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_RunTest_TestExecution', @CallOriginal=1; + + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + + SELECT * INTO #Actual FROM tSQLt.Private_RunTest_TestExecution_SpyProcedureLog; + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; END; -GO +GO /*-----------------------------------------------------------------------------------------------*/ GO --[@tSQLt:SkipTest]('TODO') diff --git a/Tests/Private_AssertNoSideEffectsTests.class.sql b/Tests/Private_AssertNoSideEffectsTests.class.sql new file mode 100644 index 000000000..549340b07 --- /dev/null +++ b/Tests/Private_AssertNoSideEffectsTests.class.sql @@ -0,0 +1,55 @@ +EXEC tSQLt.NewTestClass 'Private_AssertNoSideEffectsTests'; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE FUNCTION Private_AssertNoSideEffectsTests.[Faked_GenerateCommand]( + @BeforeExecutionObjectSnapshotTableName NVARCHAR(MAX), + @AfterExecutionObjectSnapshotTableName NVARCHAR(MAX) +) +RETURNS TABLE +AS +RETURN + SELECT @BeforeExecutionObjectSnapshotTableName + '<><><>' + @AfterExecutionObjectSnapshotTableName Command; +GO +CREATE PROCEDURE Private_AssertNoSideEffectsTests.[test Private_AssertNoSideEffects is using Private_CleanUpCmdHandler] +AS +BEGIN + EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt.Private_AssertNoSideEffects_GenerateCommand', @FakeFunctionName = 'Private_AssertNoSideEffectsTests.[Faked_GenerateCommand]'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUpCmdHandler'; + + EXEC tSQLt.Private_AssertNoSideEffects @BeforeExecutionObjectSnapshotTableName = 'BeforeTable', @AfterExecutionObjectSnapshotTableName = 'AfterTable', @TestResult = 'Result1', @TestMsg = 'Message1'; + + SELECT CleanUpCmd, TestResult, TestMsg INTO #Actual FROM tSQLt.Private_CleanUpCmdHandler_SpyProcedureLog; + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES ('BeforeTable<><><>AfterTable','Result1','Message1'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_AssertNoSideEffectsTests.[test Private_AssertNoSideEffects is using Private_CleanUpCmdHandler with @TestResult and @TestMsg as OUTPUT parameters] +AS +BEGIN + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUpCmdHandler', @CommandToExecute = 'SET @TestResult = ''Altered Test Result'';SET @TestMsg = ''Altered Test Message'';'; + + DECLARE @TestResult NVARCHAR(MAX) = 'Result1'; + DECLARE @TestMessage NVARCHAR(MAX) = 'Message1'; + + EXEC tSQLt.Private_AssertNoSideEffects @BeforeExecutionObjectSnapshotTableName = 'BeforeTable', @AfterExecutionObjectSnapshotTableName = 'AfterTable', @TestResult = @TestResult OUT, @TestMsg = @TestMessage OUT; + + SELECT @TestResult TestResult, @TestMessage TestMsg INTO #Actual + + SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0; + + INSERT INTO #Expected VALUES ('Altered Test Result','Altered Test Message'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 5f2837f8b..cdf051c44 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2360,6 +2360,87 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO +CREATE PROCEDURE Run_Methods_Tests.[test FATAL error prevents subsequent tSQLt.Run% calls] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'IF(@Action = ''Reset'')BEGIN RAISERROR(''Some Fatal Error'',16,10);END;'; + + BEGIN TRY + EXEC tSQLt.Run 'MyInnerTests', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + END TRY + BEGIN CATCH + /* not interested in this error */ + END CATCH; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'tSQLt is in an invalid state. Please reinstall tSQLt.'; + EXEC tSQLt.Run 'MyInnerTests', @TestResultFormatter = 'tSQLt.NullTestResultFormatter'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Run_Methods_Tests.[test when @Result=FATAL an appropriate error message is raised] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables', @CommandToExecute = 'IF(@Action = ''Reset'')BEGIN RAISERROR(''Some Fatal Error'',16,10);END;'; + + EXEC tSQLt.SetSummaryError @SummaryError = 1; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'The last test has invalidated the current installation of tSQLt. Please reinstall tSQLt.'; + EXEC tSQLt.Run 'MyInnerTests'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Run_Methods_Tests.[test when @Result=Abort an appropriate error message is raised] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC(' + --[@'+'tSQLt:NoTransaction](DEFAULT) + CREATE PROCEDURE [MyInnerTests].[test1] + AS + BEGIN + RETURN; + END; + '); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles', @CommandToExecute = 'RAISERROR(''Some Fatal Error'',16,10);'; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables'; + + EXEC tSQLt.SetSummaryError @SummaryError = 1; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'Aborting the current execution of tSQLt due to a severe error.'; + EXEC tSQLt.Run 'MyInnerTests'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + --[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction') CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match] AS diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj index e60d1673b..66968e619 100644 --- a/Tests/Tests.ssmssqlproj +++ b/Tests/Tests.ssmssqlproj @@ -2,7 +2,8 @@ - + + @@ -19,9 +20,6 @@ AnnotationHostPlatformTests.class.sql - - - AnnotationNoTransactionTests.class.sql @@ -186,6 +184,9 @@ NewTestClassTests.class.sql + + Private_AssertNoSideEffectsTests.class.sql + @@ -361,9 +362,6 @@ ResultSetFilterTests.class.sql - - - Run_Methods_Tests.class.sql From 9ecdaff4c758f688e7ccabdcf6fe41e82ea57061 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 17 Dec 2021 07:15:52 -0500 Subject: [PATCH 105/119] Fixed a test; Bickering over MORE ascii art. --- Source/Run_Methods.sql | 4 ++- Tests/AnnotationNoTransactionTests.class.sql | 36 ++++++++++---------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 46cc68bc4..cc455e253 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -374,8 +374,10 @@ BEGIN IF(@Verbose = 1) BEGIN - SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Finished'; + SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Finished'; EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; + --DECLARE @AsciiArtLine NVARCHAR(MAX) = CASE WHEN @Result<>'Success' THEN REPLICATE(CHAR(168),150)+' '+CHAR(155)+CHAR(155)+' '+@Result + ' ' +CHAR(139)+CHAR(139) ELSE '' END + CHAR(13)+CHAR(10) + CHAR(173); + --EXEC tSQLt.Private_Print @Message = @AsciiArtLine, @Severity = 0; END; IF(@Result = 'FATAL') diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 82a53b145..ea356571f 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1533,7 +1533,12 @@ BEGIN EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles', @CommandToExecute = 'RAISERROR(''AnAbortError'',16,10);'; EXEC tSQLt.SetSummaryError 0; - EXEC tSQLt.Run 'MyInnerTests.[test1]'; + BEGIN TRY + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + END TRY + BEGIN CATCH + /*-- not really interested in this error --*/ + END CATCH; DECLARE @Actual NVARCHAR(MAX) = (SELECT Result FROM tSQLt.TestResult); @@ -1545,7 +1550,7 @@ GO CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_RunTest_TestExecution is not called if SkipTest & NoTransaction] AS BEGIN - EXEC tSQLt.Fail 'This test has gone horribly wrong and kills tSQLt.'; + --EXEC tSQLt.Fail 'This test has gone horribly wrong and kills tSQLt.'; EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:NoTransaction](DEFAULT) @@ -1556,35 +1561,30 @@ BEGIN RETURN; END; '); - EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_RunTest_TestExecution', @CallOriginal=1; - EXEC tSQLt.SetSummaryError 0; - EXEC tSQLt.Run 'MyInnerTests.[test1]'; + DECLARE @Actual TABLE(_id_ INT, TestName NVARCHAR(MAX));/*-- @TableVariables persist regardless of transaction rollbacks --*/ + DECLARE @TranName CHAR(32);EXEC tSQLt.GetNewTranName @TranName=@TranName OUT; + SAVE TRAN @TranName; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_RunTest_TestExecution', @CallOriginal=1; + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.UndoTestDoubles'; /*--<-- only needed if test fails, so we get a clearer fail message --<--*/ - SELECT * INTO #Actual FROM tSQLt.Private_RunTest_TestExecution_SpyProcedureLog; + EXEC tSQLt.SetSummaryError 0; + EXEC tSQLt.Run 'MyInnerTests.[test1]'; + INSERT INTO @Actual SELECT _id_,TestName FROM tSQLt.Private_RunTest_TestExecution_SpyProcedureLog; + ROLLBACK TRAN @TranName; /*-- Rolling back just the last 5 lines (though, not the insert into @Actual). --*/ + + SELECT * INTO #Actual FROM @Actual; EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; END; GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO') -CREATE PROCEDURE AnnotationNoTransactionTests.[test no handler is called if SkipTest Annotation & NoTransaction annotations are used] -AS -BEGIN - EXEC tSQLt.Fail 'TODO'; -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO /*-- TODO Mark NoTransaction tests somehow in TestResult Add to github -- |19|[AnnotationNoTransactionTests].[test Schema-CleanUp error causes an appropr<...>essage to be written to the tSQLt.TestResult if there is a different error]| 94|Success| - This shouldn't happen: ^^^ -- What happens when we have multiple annotations for other non-NoTransaction annotations? Did we test this??? - add 100x'=' + test status (if not PASS) followed by empty line after test-end message (if verbose) From 661fffc535ae7caae4c59a6caf33e459d1a71749 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Fri, 17 Dec 2021 07:41:29 -0500 Subject: [PATCH 106/119] Unskipping test for further review and understanding before release. --- Tests/AnnotationNoTransactionTests.class.sql | 5 ----- Tests/Run_Methods_Tests.class.sql | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index ea356571f..2fa6352a4 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -1584,10 +1584,5 @@ GO Mark NoTransaction tests somehow in TestResult -Add to github -- add 100x'=' + test status (if not PASS) followed by empty line after test-end message (if verbose) - - - --*/ diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index cdf051c44..23bc369e0 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2441,7 +2441,7 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction') +---[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction') CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match] AS BEGIN From 02f0a06d8b63617df88a699ac6a714d4bd923f6f Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 18 Dec 2021 07:56:15 -0500 Subject: [PATCH 107/119] Removed a TODO on the FakeTable ssp --- Source/tSQLt.FakeTable.ssp.sql | 11 ++++++++--- Source/tSQLt.Private_CreateFakeOfTable.ssp.sql | 4 ++-- Source/tSQLt.Private_RenameObject.ssp.sql | 2 +- Tests/FakeTableTests.class.sql | 13 ++++++++++++- Tests/_ExploratoryTests.class.sql | 17 +++++++++++++++++ Tests/tSQLtclr_test.class.sql | 10 ++++++++++ 6 files changed, 50 insertions(+), 7 deletions(-) diff --git a/Source/tSQLt.FakeTable.ssp.sql b/Source/tSQLt.FakeTable.ssp.sql index 9f7e3469b..95790ea13 100644 --- a/Source/tSQLt.FakeTable.ssp.sql +++ b/Source/tSQLt.FakeTable.ssp.sql @@ -14,6 +14,8 @@ BEGIN DECLARE @OrigObjectNewName NVARCHAR(4000); DECLARE @OrigObjectFullName NVARCHAR(MAX) = NULL; DECLARE @TargetObjectFullName NVARCHAR(MAX) = NULL; + DECLARE @OriginalObjectObjectId INT; + DECLARE @TargetObjectObjectId INT; IF(@TableName NOT IN (PARSENAME(@TableName,1),QUOTENAME(PARSENAME(@TableName,1))) AND @SchemaName IS NOT NULL) @@ -31,9 +33,11 @@ BEGIN EXEC tSQLt.Private_RenameObjectToUniqueName @OrigObjectCleanQuotedSchemaName, @OrigObjectCleanQuotedName, @OrigObjectNewName OUTPUT; + SET @OriginalObjectObjectId = OBJECT_ID(@OrigObjectCleanQuotedSchemaName + '.' + QUOTENAME(@OrigObjectNewName)); + SELECT @TargetObjectFullName = S.base_object_name FROM sys.synonyms AS S - WHERE S.object_id = OBJECT_ID(@OrigObjectCleanQuotedSchemaName + '.' + @OrigObjectNewName); + WHERE S.object_id = @OriginalObjectObjectId; IF(@TargetObjectFullName IS NOT NULL) BEGIN @@ -41,13 +45,14 @@ BEGIN BEGIN RAISERROR('Cannot fake synonym %s as it is pointing to %s, which is not a table or view!',16,10,@OrigObjectFullName,@TargetObjectFullName); END; + SET @TargetObjectObjectId = OBJECT_ID(@TargetObjectFullName); END; ELSE BEGIN - SET @TargetObjectFullName = @OrigObjectCleanQuotedSchemaName + '.' + QUOTENAME(@OrigObjectNewName); --TODO:Test for QUOTENAME + SET @TargetObjectObjectId = @OriginalObjectObjectId; END; - EXEC tSQLt.Private_CreateFakeOfTable @OrigObjectCleanQuotedSchemaName, @OrigObjectCleanQuotedName, @TargetObjectFullName, @Identity, @ComputedColumns, @Defaults; + EXEC tSQLt.Private_CreateFakeOfTable @OrigObjectCleanQuotedSchemaName, @OrigObjectCleanQuotedName, @TargetObjectObjectId, @Identity, @ComputedColumns, @Defaults; EXEC tSQLt.Private_MarktSQLtTempObject @OrigObjectFullName, N'TABLE', @OrigObjectNewName; END diff --git a/Source/tSQLt.Private_CreateFakeOfTable.ssp.sql b/Source/tSQLt.Private_CreateFakeOfTable.ssp.sql index 55dbbd504..625d910b8 100644 --- a/Source/tSQLt.Private_CreateFakeOfTable.ssp.sql +++ b/Source/tSQLt.Private_CreateFakeOfTable.ssp.sql @@ -4,7 +4,7 @@ GO CREATE PROCEDURE tSQLt.Private_CreateFakeOfTable @SchemaName NVARCHAR(MAX), @TableName NVARCHAR(MAX), - @OrigTableFullName NVARCHAR(MAX), + @OrigTableObjectId INT, @Identity BIT, @ComputedColumns BIT, @Defaults BIT @@ -12,7 +12,7 @@ AS BEGIN DECLARE @cmd NVARCHAR(MAX) = (SELECT CreateTableStatement - FROM tSQLt.Private_CreateFakeTableStatement(OBJECT_ID(@OrigTableFullName), @SchemaName+'.'+@TableName,@Identity,@ComputedColumns,@Defaults,0)); + FROM tSQLt.Private_CreateFakeTableStatement(@OrigTableObjectId, @SchemaName+'.'+@TableName,@Identity,@ComputedColumns,@Defaults,0)); EXEC (@cmd); END; ---Build- diff --git a/Source/tSQLt.Private_RenameObject.ssp.sql b/Source/tSQLt.Private_RenameObject.ssp.sql index 4b27babbf..7dd62db87 100644 --- a/Source/tSQLt.Private_RenameObject.ssp.sql +++ b/Source/tSQLt.Private_RenameObject.ssp.sql @@ -10,7 +10,7 @@ BEGIN DECLARE @RenameCmd NVARCHAR(MAX); SET @RenameCmd = 'EXEC sp_rename ''' + REPLACE(@SchemaName + '.' + @ObjectName, '''', '''''') + ''', ''' + - @NewName + ''',''OBJECT'';'; + REPLACE(@NewName, '''', '''''') + ''',''OBJECT'';'; EXEC tSQLt.SuppressOutput @RenameCmd; END; diff --git a/Tests/FakeTableTests.class.sql b/Tests/FakeTableTests.class.sql index 2926423bf..49ef0401a 100644 --- a/Tests/FakeTableTests.class.sql +++ b/Tests/FakeTableTests.class.sql @@ -377,7 +377,7 @@ BEGIN END; GO -CREATE PROC FakeTableTests.[test FakeTable works with ugly column and table names] +CREATE PROC FakeTableTests.[test FakeTable works with special characters in column and table names] AS BEGIN IF OBJECT_ID('dbo.[tst!@#$%^&*()_+ 1]') IS NOT NULL DROP TABLE dbo.[tst!@#$%^&*()_+ 1]; @@ -1032,3 +1032,14 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test FakeTable works if new name of original table requires quoting] +AS +BEGIN + CREATE TABLE FakeTableTests.TempTable1(i INT NULL); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_RenameObjectToUniqueName', @CommandToExecute = 'SET @NewName = ''A Name.Needs''''Quoting'';',@CallOriginal = 1; + + EXEC tSQLt.FakeTable @TableName = 'FakeTableTests.TempTable1'; + + EXEC tSQLt.AssertEqualsTableSchema @Expected = 'FakeTableTests.[A Name.Needs''Quoting]', @Actual = 'FakeTableTests.TempTable1'; +END; +GO diff --git a/Tests/_ExploratoryTests.class.sql b/Tests/_ExploratoryTests.class.sql index aaea08e0d..838d24044 100644 --- a/Tests/_ExploratoryTests.class.sql +++ b/Tests/_ExploratoryTests.class.sql @@ -333,6 +333,23 @@ BEGIN BEGIN TRY DEALLOCATE ACursor; END TRY BEGIN CATCH END CATCH; EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE [_ExploratoryTests].[test sp_addextendedproperty can handle odd values] +AS +BEGIN + CREATE TABLE [_ExploratoryTests].ATable(I INT); + + EXEC sys.sp_addextendedproperty + @name = N'ATestProperty', + @value = 'a string.with''special chars', + @level0type = N'SCHEMA', @level0name = '_ExploratoryTests', + @level1type = 'TABLE', @level1name = 'ATable'; + + SELECT * FROM sys.extended_properties AS EP WHERE EP.major_id = OBJECT_ID('[_ExploratoryTests].ATable'); + END; GO /*-----------------------------------------------------------------------------------------------*/ diff --git a/Tests/tSQLtclr_test.class.sql b/Tests/tSQLtclr_test.class.sql index 37fb16300..5a3ce3b9e 100644 --- a/Tests/tSQLtclr_test.class.sql +++ b/Tests/tSQLtclr_test.class.sql @@ -184,4 +184,14 @@ BEGIN END; END; GO + +CREATE PROC tSQLtclr_test.[test name returned by tSQLt.Private::CreateUniqueObjectName() should not require quoting] +AS +BEGIN + DECLARE @NewName NVARCHAR(MAX) = tSQLt.Private::CreateUniqueObjectName(); + + EXEC tSQLt.ExpectNoException; + EXEC('CREATE TABLE tSQLtclr_test.'+@NewName+'(I INT);'); +END; +GO --ROLLBACK From 52261a52edccd5117aeb5cd80e2c051f4796a3fe Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sat, 18 Dec 2021 08:19:44 -0500 Subject: [PATCH 108/119] Clean up TODOs; Organize around what needs to be finished --- Tests/AnnotationNoTransactionTests.class.sql | 9 +-------- Tests/InfoTests.class.sql | 2 -- Tests/Private_NoTransactionHandleTableTests.class.sql | 9 ++++++++- Tests/UndoTestDoublesTests.class.sql | 5 ----- Tests/_ExploratoryTests.class.sql | 2 +- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Tests/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql index 2fa6352a4..798b7748b 100644 --- a/Tests/AnnotationNoTransactionTests.class.sql +++ b/Tests/AnnotationNoTransactionTests.class.sql @@ -425,7 +425,7 @@ BEGIN --ROLLBACK END; GO ----[@tSQLt:SkipTest]('') + --[@tSQLt:NoTransaction]('AnnotationNoTransactionTests.[CLEANUP: test an unrecoverable erroring test gets correct (Success/Failure but not Error) entry in TestResults table]') /* This test must be NoTransaction because the inner test will invalidate any open transaction causing chaos and turmoil in the reactor. */ CREATE PROCEDURE AnnotationNoTransactionTests.[test an unrecoverable erroring test gets correct entry in TestResults table] @@ -1579,10 +1579,3 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO - -/*-- TODO - -Mark NoTransaction tests somehow in TestResult - ---*/ - diff --git a/Tests/InfoTests.class.sql b/Tests/InfoTests.class.sql index 60ea21bb2..e6d6b4821 100644 --- a/Tests/InfoTests.class.sql +++ b/Tests/InfoTests.class.sql @@ -159,5 +159,3 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; ---TODO: --- include minimum supported version, like column with the lowest number that we run CI tests on (hardcoded) \ No newline at end of file diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 1625d95b0..3d5ba0f4f 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -579,7 +579,10 @@ GO GO - +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test TODO] +AS +BEGIN + EXEC tSQLt.Fail 'TODO'; --tSQLt.Run 'Private_NoTransactionHandleTableTests'.[test if @TableAction is Restore, @Action Save, Save: the second Save does nothing]' /*-- TODO @@ -616,3 +619,7 @@ Some scenarios to consider 16: ?*test* Save (Ignore), Save (Ignore), Reset (Ignore), Reset (Ignore) --> No Op. ExpectNoException.s --*/ +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO \ No newline at end of file diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql index fff4c39d7..8040ac3d3 100644 --- a/Tests/UndoTestDoublesTests.class.sql +++ b/Tests/UndoTestDoublesTests.class.sql @@ -822,8 +822,3 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO -/*-- -TODO - ---*/ ---EXEC tSQLt.Run UndoTestDoublesTests \ No newline at end of file diff --git a/Tests/_ExploratoryTests.class.sql b/Tests/_ExploratoryTests.class.sql index 838d24044..633043507 100644 --- a/Tests/_ExploratoryTests.class.sql +++ b/Tests/_ExploratoryTests.class.sql @@ -357,7 +357,7 @@ GO --CREATE PROCEDURE [_ExploratoryTests].[test TBD] --AS --BEGIN --- EXEC tSQLt.Fail 'TODO'; +-- EXEC tSQLt.Fail 'TemplateTest'; --END; GO /*-----------------------------------------------------------------------------------------------*/ From c2e1bc474a20799b273beace8cd41e7d5d75a3dc Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 19 Dec 2021 08:07:33 -0500 Subject: [PATCH 109/119] Trying to debug an issue with transactions when we open a transaction within an inner no-transaction test <-- we have no issues when the inner test is not a no-transaction test and we should include this test in the test class. --- Source/Run_Methods.sql | 8 ++------ Tests/Run_Methods_Tests.class.sql | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index cc455e253..fc2a97683 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -209,7 +209,6 @@ BEGIN SET @Result = 'Error'; END; END CATCH; - IF (@NoTransactionFlag = 1) BEGIN SET @CleanUpProcedureExecutionCmd = ( @@ -241,7 +240,6 @@ BEGIN @TestResult = @Result OUT, @TestMsg = @Msg OUT END; - IF(@TransactionStartedFlag = 1) BEGIN COMMIT; @@ -326,7 +324,8 @@ BEGIN @TranName, @Result OUT, @Msg OUT, - @TestEndTime OUT + @TestEndTime OUT; + END; ELSE BEGIN @@ -351,7 +350,6 @@ BEGIN SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; EXEC tSQLt.Private_Print @Message = @Msg2, @Severity = 0; END; - IF EXISTS(SELECT 1 FROM tSQLt.TestResult WHERE Id = @TestResultId) BEGIN UPDATE tSQLt.TestResult SET @@ -370,8 +368,6 @@ BEGIN 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; END; - - IF(@Verbose = 1) BEGIN SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Finished'; diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 23bc369e0..741decb42 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2448,13 +2448,26 @@ BEGIN EXEC tSQLt.NewTestClass 'MyInnerTests' EXEC(' --[@'+'tSQLt:NoTransaction](DEFAULT) -CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN; +CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN;SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); '); EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; + BEGIN TRY EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + END TRY + BEGIN CATCH + SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); + END CATCH; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +EXEC tSQLt.Run 'Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don''t match]'; +GO +SELECT * FROM tSQLt.TestResult; /*-- Transaction Tests @@ -2464,11 +2477,6 @@ CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BE - what should we do if the original transaction was rolled back and a new one was created? - what should we do if the original transaction was committed and a new one was created? - we still need to save the TranName as something somewhere. - - review existing tests for transactions + - do existing tests already cover some of the scenarios described above? --*/ - -END; -GO -/*-----------------------------------------------------------------------------------------------*/ -GO \ No newline at end of file From 867c8adb9566cda197377be496d48926ac7945b0 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Sun, 19 Dec 2021 17:59:17 -0500 Subject: [PATCH 110/119] Trying to figure out why transaction is in an uncommittable state. --- Experiments/Debugging tSQLt.tdf | Bin 0 -> 2986 bytes ...ry.sql => SM query - read trace table.sql} | 0 Experiments/StartHere 2021-12-20.sql | 353 ++++++++++++++++++ Source/Run_Methods.sql | 10 +- Tests/Run_Methods_Tests.class.sql | 29 +- 5 files changed, 381 insertions(+), 11 deletions(-) create mode 100644 Experiments/Debugging tSQLt.tdf rename Experiments/{SM query.sql => SM query - read trace table.sql} (100%) create mode 100644 Experiments/StartHere 2021-12-20.sql diff --git a/Experiments/Debugging tSQLt.tdf b/Experiments/Debugging tSQLt.tdf new file mode 100644 index 0000000000000000000000000000000000000000..86a69c6de0213fa509522a48909afff92bc7a04b GIT binary patch literal 2986 zcmeHJ+iFum6kVp)HkT%~B(X-Dq%A^wJjPQ~nzY1Nlwuzg6~92LML`g>`0BH7euIdA z;9XGvLH!0_`~f4@wP(&WlVqmUf-hpqNuaa$T5DhDZ2x+aNa8Z?U<3DY4-fckVhdGV z$2DA1zqfeu5c2*1^LH|k#kV0rzRT=y;0Awht6FR!#3B~>)cN=Ce|~hnckpw2ZX82M z;~+9Pgb|D)i4?|=MGhs*VxAePqlHzPRzJ}+gFFw5C}0vtQAUMEbu_T7Ov29&v@DH_ zG&Xo=x`>Z)fDS?~ltq$G(yU5aLt6aG!xj%Gc(B5M&y(mp)#Sg>v(7`Urh=wcP$XnB zR-q$GDWg37wOl)qH!5zq9G_uB&d_28(hQ=S*RO zUHE0VZEn-|ntA<{huKb+UOin6CSh8I07-kaLJw3|LAj@7z3#!fvqegnw%XBDIk@-- z#?4GDOqO(aq1h3drC=i4;Tdzfx~F_~-je{ENg8iTMP#^i8sZ#2_dx8e5fn$dcn>7_W;eqvHjaopbeIj*R!eA_bzDNHlg@xB Hz#5<54Egww literal 0 HcmV?d00001 diff --git a/Experiments/SM query.sql b/Experiments/SM query - read trace table.sql similarity index 100% rename from Experiments/SM query.sql rename to Experiments/SM query - read trace table.sql diff --git a/Experiments/StartHere 2021-12-20.sql b/Experiments/StartHere 2021-12-20.sql new file mode 100644 index 000000000..c0ecdc93b --- /dev/null +++ b/Experiments/StartHere 2021-12-20.sql @@ -0,0 +1,353 @@ + +EXEC tSQLt.NewTestClass 'NotInnerTests'; +GO +CREATE PROCEDURE NotInnerTests.[test brittle transaction test] +AS +BEGIN + BEGIN TRAN; +END; +GO + +--EXEC tSQLt.Private_RunTest @TestName = 'NotInnerTests.[test brittle transaction test]'; + + + DROP TABLE IF EXISTS #TestMessage; + DROP TABLE IF EXISTS #ExpectException; + DROP TABLE IF EXISTS #SkipTest; + DROP TABLE IF EXISTS #NoTransaction; + DROP TABLE IF EXISTS #TableBackupLog; + GO + + DECLARE @TestName NVARCHAR(MAX) = 'NotInnerTests.[test brittle transaction test]'; + DECLARE @SetUp NVARCHAR(MAX) = NULL; + DECLARE @CleanUp NVARCHAR(MAX) = NULL; + + DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; + + DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; + DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; + DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; + DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; + DECLARE @Result NVARCHAR(MAX); + DECLARE @TranName CHAR(32) = NULL; + DECLARE @TestResultId INT; + DECLARE @TestObjectId INT; + DECLARE @TestEndTime DATETIME2 = NULL; + + DECLARE @VerboseMsg NVARCHAR(MAX); + DECLARE @Verbose BIT; + SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); + + TRUNCATE TABLE tSQLt.CaptureOutputLog; + CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); + CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); + CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); + CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); + CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + + + SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), + @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), + @TestObjectId = OBJECT_ID(@TestName); + + INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) + SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' + OPTION(MAXDOP 1); + SELECT @TestResultId = SCOPE_IDENTITY(); + + IF(@Verbose = 1) + BEGIN + SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; + EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; + END; + + + SET @Result = 'Success'; + DECLARE @SkipTestFlag BIT = 0; + DECLARE @NoTransactionFlag BIT = 0; + + --BEGIN TRY + --EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; + --SET @SkipTestFlag = CASE WHEN EXISTS(SELECT 1 FROM #SkipTest) THEN 1 ELSE 0 END; + --SET @NoTransactionFlag = CASE WHEN EXISTS(SELECT 1 FROM #NoTransaction) THEN 1 ELSE 0 END; + + --IF(@SkipTestFlag = 0) + --BEGIN + -- IF(@NoTransactionFlag = 0) + -- BEGIN + -- EXEC tSQLt.GetNewTranName @TranName OUT; + -- UPDATE tSQLt.TestResult SET TranName = @TranName WHERE Id = @TestResultId; + -- END; + --EXEC tSQLt.Private_RunTest_TestExecution + -- @TestName, + -- @SetUp, + -- @CleanUp, + -- @NoTransactionFlag, + -- @TranName, + -- @Result OUT, + -- @Msg OUT, + -- @TestEndTime OUT; +IF (1=1) +BEGIN + DECLARE @TransactionStartedFlag BIT = 0; + DECLARE @PreExecTrancount INT = NULL; + DECLARE @TestExecutionCmd NVARCHAR(MAX) = 'EXEC ' + @TestName; + DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; + + BEGIN TRY + + IF(@NoTransactionFlag = 0) + BEGIN + BEGIN TRAN; + SET @TransactionStartedFlag = 1; + SAVE TRAN @TranName; + END; + ELSE + BEGIN + SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; + EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + END; + + SET @PreExecTrancount = @@TRANCOUNT; + + DECLARE @TmpMsg NVARCHAR(MAX); + SET @TestEndTime = NULL; + BEGIN TRY + IF (@SetUp IS NOT NULL) + BEGIN + EXEC @SetUp; + END; + + BEGIN TRY + SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; + EXEC (@TestExecutionCmd); + END TRY + BEGIN CATCH + SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; + THROW; + END CATCH; + + IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) + BEGIN + SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; + EXEC tSQLt.Fail @TmpMsg; + END + SET @TestEndTime = SYSDATETIME(); + END TRY + BEGIN CATCH + SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); + IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' + BEGIN + SELECT @Msg = Msg FROM #TestMessage; + SET @Result = 'Failure'; + END + ELSE + BEGIN + DECLARE @ErrorInfo NVARCHAR(MAX); + SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); + + IF(EXISTS(SELECT 1 FROM #ExpectException)) + BEGIN + DECLARE @ExpectException INT; + DECLARE @ExpectedMessage NVARCHAR(MAX); + DECLARE @ExpectedMessagePattern NVARCHAR(MAX); + DECLARE @ExpectedSeverity INT; + DECLARE @ExpectedState INT; + DECLARE @ExpectedErrorNumber INT; + DECLARE @FailMessage NVARCHAR(MAX); + SELECT @ExpectException = ExpectException, + @ExpectedMessage = ExpectedMessage, + @ExpectedSeverity = ExpectedSeverity, + @ExpectedState = ExpectedState, + @ExpectedMessagePattern = ExpectedMessagePattern, + @ExpectedErrorNumber = ExpectedErrorNumber, + @FailMessage = FailMessage + FROM #ExpectException; + + IF(@ExpectException = 1) + BEGIN + SET @Result = 'Success'; + SET @TmpMsg = COALESCE(@FailMessage+' ','')+'Exception did not match expectation!'; + IF(ERROR_MESSAGE() <> @ExpectedMessage) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected Message: <'+@ExpectedMessage+'>'+CHAR(13)+CHAR(10)+ + 'Actual Message : <'+ERROR_MESSAGE()+'>'; + SET @Result = 'Failure'; + END + IF(ERROR_MESSAGE() NOT LIKE @ExpectedMessagePattern) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected Message to be like <'+@ExpectedMessagePattern+'>'+CHAR(13)+CHAR(10)+ + 'Actual Message : <'+ERROR_MESSAGE()+'>'; + SET @Result = 'Failure'; + END + IF(ERROR_NUMBER() <> @ExpectedErrorNumber) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected Error Number: '+CAST(@ExpectedErrorNumber AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + 'Actual Error Number : '+CAST(ERROR_NUMBER() AS NVARCHAR(MAX)); + SET @Result = 'Failure'; + END + IF(ERROR_SEVERITY() <> @ExpectedSeverity) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected Severity: '+CAST(@ExpectedSeverity AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + 'Actual Severity : '+CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)); + SET @Result = 'Failure'; + END + IF(ERROR_STATE() <> @ExpectedState) + BEGIN + SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + 'Expected State: '+CAST(@ExpectedState AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + 'Actual State : '+CAST(ERROR_STATE() AS NVARCHAR(MAX)); + SET @Result = 'Failure'; + END + IF(@Result = 'Failure') + BEGIN + SET @Msg = @TmpMsg; + END + END + ELSE + BEGIN + SET @Result = 'Failure'; + SET @Msg = + COALESCE(@FailMessage+' ','')+ + 'Expected no error to be raised. Instead this error was encountered:'+ + CHAR(13)+CHAR(10)+ + @ErrorInfo; + END + END; + ELSE + BEGIN + SET @Result = 'Error'; + SET @Msg = @ErrorInfo; + END; + END; + END CATCH; + END TRY + BEGIN CATCH + SET @Result = 'Error'; + SET @Msg = ERROR_MESSAGE(); + END CATCH + + --TODO:NoTran + ---- Compare @@Trancount, throw up arms if it doesn't match + --TODO:NoTran + BEGIN TRY + IF(@TransactionStartedFlag = 1) + BEGIN + ROLLBACK TRAN @TranName; + END; + END TRY + BEGIN CATCH + DECLARE @PostExecTrancount INT; + SET @PostExecTrancount = @PreExecTrancount - @@TRANCOUNT; + IF (@@TRANCOUNT > 0) ROLLBACK; + BEGIN TRAN; + IF( @Result <> 'Success' + OR @PostExecTrancount <> 0 + ) + BEGIN + SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); + SET @Result = 'Error'; + END; + END CATCH; + IF (@NoTransactionFlag = 1) + BEGIN + SET @CleanUpProcedureExecutionCmd = ( + ( + SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' + FROM #NoTransaction NT + ORDER BY OrderId + FOR XML PATH(''),TYPE + ).value('.','NVARCHAR(MAX)') + ); + IF(@CleanUpProcedureExecutionCmd IS NOT NULL) + BEGIN + EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; + END; + + IF(@CleanUp IS NOT NULL) + BEGIN + EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; + END; + + DECLARE @CleanUpErrorMsg NVARCHAR(MAX); + EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; + SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); + + SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; + EXEC tSQLt.Private_AssertNoSideEffects + @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', + @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', + @TestResult = @Result OUT, + @TestMsg = @Msg OUT + END; + IF(@TransactionStartedFlag = 1) + BEGIN + COMMIT; + END; +END; + + --END; + --ELSE + --BEGIN + -- DECLARE @TmpMsg NVARCHAR(MAX); + -- SELECT + -- @Result = 'Skipped', + -- @Msg = ST.SkipTestMessage + -- FROM #SkipTest AS ST; + -- SET @TmpMsg = '-->'+@TestName+' skipped: '+@Msg; + -- EXEC tSQLt.Private_Print @Message = @TmpMsg; + -- SET @TestEndTime = SYSDATETIME(); + --END; + --END TRY + --BEGIN CATCH + -- SET @Result = 'Error'; + -- SET @Msg = ISNULL(NULLIF(@Msg,'') + ' ','')+ERROR_MESSAGE(); + -- --SET @TestEndTime = SYSDATETIME(); + --END CATCH; +------------------------------------------------------------------------------------------------ +-- If(@Result NOT IN ('Success','Skipped')) +-- BEGIN +-- SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; +-- EXEC tSQLt.Private_Print @Message = @Msg2, @Severity = 0; +-- END; +-- IF EXISTS(SELECT 1 FROM tSQLt.TestResult WHERE Id = @TestResultId) +-- BEGIN +-- UPDATE tSQLt.TestResult SET +-- Result = @Result, +-- Msg = @Msg, +-- TestEndTime = @TestEndTime +-- WHERE Id = @TestResultId; +-- END; +-- ELSE +-- BEGIN +-- INSERT tSQLt.TestResult(Class, TestCase, TranName, Result, Msg) +-- SELECT @TestClassName, +-- @TestProcName, +-- '?', +-- 'Error', +-- 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; +-- END; + +-- IF(@Verbose = 1) +-- BEGIN +-- SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Finished'; +-- EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; +-- --DECLARE @AsciiArtLine NVARCHAR(MAX) = CASE WHEN @Result<>'Success' THEN REPLICATE(CHAR(168),150)+' '+CHAR(155)+CHAR(155)+' '+@Result + ' ' +CHAR(139)+CHAR(139) ELSE '' END + CHAR(13)+CHAR(10) + CHAR(173); +-- --EXEC tSQLt.Private_Print @Message = @AsciiArtLine, @Severity = 0; +-- END; + +-- IF(@Result = 'FATAL') +-- BEGIN +-- INSERT INTO tSQLt.Private_Seize VALUES(1); +-- RAISERROR('The last test has invalidated the current installation of tSQLt. Please reinstall tSQLt.',16,10); +-- END; +-- IF(@Result = 'Abort') +-- BEGIN +-- RAISERROR('Aborting the current execution of tSQLt due to a severe error.', 16, 10); +-- END; + +-- IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index fc2a97683..99ce4ff56 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -81,7 +81,15 @@ BEGIN BEGIN EXEC @SetUp; END; - EXEC (@TestExecutionCmd); + + BEGIN TRY + SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; + EXEC (@TestExecutionCmd); + END TRY + BEGIN CATCH + SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; + THROW; + END CATCH; IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) BEGIN diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 741decb42..a05898d83 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2441,33 +2441,42 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO ----[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction') CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match] AS BEGIN - EXEC tSQLt.NewTestClass 'MyInnerTests' + EXEC tSQLt.NewTestClass 'MyInnerTestsA' + EXEC('CREATE PROCEDURE MyInnerTestsA.[test should execute outside of transaction] AS BEGIN TRAN;'); + + EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; + EXEC tSQLt.Run 'MyInnerTestsA.[test should execute outside of transaction]'; + +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO + +--[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction for NoTransaction tests') +CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match in NoTransaction test] +AS +BEGIN + EXEC tSQLt.NewTestClass 'MyInnerTestsB' EXEC(' --[@'+'tSQLt:NoTransaction](DEFAULT) -CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS BEGIN TRAN;SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); +CREATE PROCEDURE MyInnerTestsB.[test should execute outside of transaction] AS BEGIN TRAN;SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); '); EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; BEGIN TRY - EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]'; + EXEC tSQLt.Run 'MyInnerTestsB.[test should execute outside of transaction]'; END TRY BEGIN CATCH - SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); + SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); END CATCH; - - END; GO /*-----------------------------------------------------------------------------------------------*/ GO -EXEC tSQLt.Run 'Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don''t match]'; -GO -SELECT * FROM tSQLt.TestResult; /*-- Transaction Tests From bd5d4744271a2bf6c425ea40dce94982a4b681f1 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 20 Dec 2021 06:16:46 -0500 Subject: [PATCH 111/119] More experiments --- Experiments/StartHere 2021-12-20 B.sql | 11 + Experiments/StartHere 2021-12-20.sql | 437 +++++++++++++------------ 2 files changed, 230 insertions(+), 218 deletions(-) create mode 100644 Experiments/StartHere 2021-12-20 B.sql diff --git a/Experiments/StartHere 2021-12-20 B.sql b/Experiments/StartHere 2021-12-20 B.sql new file mode 100644 index 000000000..2e3252985 --- /dev/null +++ b/Experiments/StartHere 2021-12-20 B.sql @@ -0,0 +1,11 @@ + +BEGIN TRAN; +SAVE TRAN TranName; + +BEGIN TRY +SELECT XACT_STATE(),@@TRANCOUNT; +EXEC ('BEGIN TRAN'); +END TRY +BEGIN CATCH +SELECT XACT_STATE(),@@TRANCOUNT; +END CATCH; diff --git a/Experiments/StartHere 2021-12-20.sql b/Experiments/StartHere 2021-12-20.sql index c0ecdc93b..319b13bd9 100644 --- a/Experiments/StartHere 2021-12-20.sql +++ b/Experiments/StartHere 2021-12-20.sql @@ -11,60 +11,60 @@ GO --EXEC tSQLt.Private_RunTest @TestName = 'NotInnerTests.[test brittle transaction test]'; - DROP TABLE IF EXISTS #TestMessage; - DROP TABLE IF EXISTS #ExpectException; - DROP TABLE IF EXISTS #SkipTest; - DROP TABLE IF EXISTS #NoTransaction; - DROP TABLE IF EXISTS #TableBackupLog; - GO + --DROP TABLE IF EXISTS #TestMessage; + --DROP TABLE IF EXISTS #ExpectException; + --DROP TABLE IF EXISTS #SkipTest; + --DROP TABLE IF EXISTS #NoTransaction; + --DROP TABLE IF EXISTS #TableBackupLog; + --GO DECLARE @TestName NVARCHAR(MAX) = 'NotInnerTests.[test brittle transaction test]'; - DECLARE @SetUp NVARCHAR(MAX) = NULL; - DECLARE @CleanUp NVARCHAR(MAX) = NULL; + --DECLARE @SetUp NVARCHAR(MAX) = NULL; + --DECLARE @CleanUp NVARCHAR(MAX) = NULL; - DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; + --DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; - DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; - DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; - DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; - DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; - DECLARE @Result NVARCHAR(MAX); - DECLARE @TranName CHAR(32) = NULL; - DECLARE @TestResultId INT; - DECLARE @TestObjectId INT; - DECLARE @TestEndTime DATETIME2 = NULL; + --DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; + --DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; + --DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; + --DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; + --DECLARE @Result NVARCHAR(MAX); + DECLARE @TranName CHAR(32) = 'sadfhksajdf'; + --DECLARE @TestResultId INT; + --DECLARE @TestObjectId INT; + --DECLARE @TestEndTime DATETIME2 = NULL; - DECLARE @VerboseMsg NVARCHAR(MAX); - DECLARE @Verbose BIT; - SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); + --DECLARE @VerboseMsg NVARCHAR(MAX); + --DECLARE @Verbose BIT; + --SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); - TRUNCATE TABLE tSQLt.CaptureOutputLog; - CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); - CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); - CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); - CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); - CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); + --TRUNCATE TABLE tSQLt.CaptureOutputLog; + --CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); + --CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); + --CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); + --CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); + --CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); - SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), - @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), - @TestObjectId = OBJECT_ID(@TestName); + --SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), + -- @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), + -- @TestObjectId = OBJECT_ID(@TestName); - INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) - SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' - OPTION(MAXDOP 1); - SELECT @TestResultId = SCOPE_IDENTITY(); + --INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) + -- SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' + -- OPTION(MAXDOP 1); + --SELECT @TestResultId = SCOPE_IDENTITY(); - IF(@Verbose = 1) - BEGIN - SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; - EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; - END; + --IF(@Verbose = 1) + --BEGIN + -- SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; + -- EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; + --END; - SET @Result = 'Success'; - DECLARE @SkipTestFlag BIT = 0; - DECLARE @NoTransactionFlag BIT = 0; + --SET @Result = 'Success'; + --DECLARE @SkipTestFlag BIT = 0; + --DECLARE @NoTransactionFlag BIT = 0; --BEGIN TRY --EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; @@ -87,36 +87,36 @@ GO -- @Result OUT, -- @Msg OUT, -- @TestEndTime OUT; -IF (1=1) -BEGIN - DECLARE @TransactionStartedFlag BIT = 0; - DECLARE @PreExecTrancount INT = NULL; +--IF (1=1) +--BEGIN + --DECLARE @TransactionStartedFlag BIT = 0; + --DECLARE @PreExecTrancount INT = NULL; DECLARE @TestExecutionCmd NVARCHAR(MAX) = 'EXEC ' + @TestName; - DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; + --DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; - BEGIN TRY + --BEGIN TRY - IF(@NoTransactionFlag = 0) - BEGIN + --IF(@NoTransactionFlag = 0) + --BEGIN BEGIN TRAN; - SET @TransactionStartedFlag = 1; + --SET @TransactionStartedFlag = 1; SAVE TRAN @TranName; - END; - ELSE - BEGIN - SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; - EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; - END; + --END; + --ELSE + --BEGIN + -- SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; + -- EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; + --END; - SET @PreExecTrancount = @@TRANCOUNT; + --SET @PreExecTrancount = @@TRANCOUNT; - DECLARE @TmpMsg NVARCHAR(MAX); - SET @TestEndTime = NULL; - BEGIN TRY - IF (@SetUp IS NOT NULL) - BEGIN - EXEC @SetUp; - END; + --DECLARE @TmpMsg NVARCHAR(MAX); + --SET @TestEndTime = NULL; + --BEGIN TRY + --IF (@SetUp IS NOT NULL) + --BEGIN + -- EXEC @SetUp; + --END; BEGIN TRY SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; @@ -127,168 +127,169 @@ BEGIN THROW; END CATCH; - IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) - BEGIN - SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; - EXEC tSQLt.Fail @TmpMsg; - END - SET @TestEndTime = SYSDATETIME(); - END TRY - BEGIN CATCH - SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); - IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' - BEGIN - SELECT @Msg = Msg FROM #TestMessage; - SET @Result = 'Failure'; - END - ELSE - BEGIN - DECLARE @ErrorInfo NVARCHAR(MAX); - SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); + --IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) + --BEGIN + -- SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; + -- EXEC tSQLt.Fail @TmpMsg; + --END + --SET @TestEndTime = SYSDATETIME(); + --END TRY + --BEGIN CATCH + --SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); + --IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' + --BEGIN + -- SELECT @Msg = Msg FROM #TestMessage; + -- SET @Result = 'Failure'; + --END + --ELSE + --BEGIN + --DECLARE @ErrorInfo NVARCHAR(MAX); + --SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); - IF(EXISTS(SELECT 1 FROM #ExpectException)) - BEGIN - DECLARE @ExpectException INT; - DECLARE @ExpectedMessage NVARCHAR(MAX); - DECLARE @ExpectedMessagePattern NVARCHAR(MAX); - DECLARE @ExpectedSeverity INT; - DECLARE @ExpectedState INT; - DECLARE @ExpectedErrorNumber INT; - DECLARE @FailMessage NVARCHAR(MAX); - SELECT @ExpectException = ExpectException, - @ExpectedMessage = ExpectedMessage, - @ExpectedSeverity = ExpectedSeverity, - @ExpectedState = ExpectedState, - @ExpectedMessagePattern = ExpectedMessagePattern, - @ExpectedErrorNumber = ExpectedErrorNumber, - @FailMessage = FailMessage - FROM #ExpectException; + --IF(EXISTS(SELECT 1 FROM #ExpectException)) + --BEGIN + -- DECLARE @ExpectException INT; + -- DECLARE @ExpectedMessage NVARCHAR(MAX); + -- DECLARE @ExpectedMessagePattern NVARCHAR(MAX); + -- DECLARE @ExpectedSeverity INT; + -- DECLARE @ExpectedState INT; + -- DECLARE @ExpectedErrorNumber INT; + -- DECLARE @FailMessage NVARCHAR(MAX); + -- SELECT @ExpectException = ExpectException, + -- @ExpectedMessage = ExpectedMessage, + -- @ExpectedSeverity = ExpectedSeverity, + -- @ExpectedState = ExpectedState, + -- @ExpectedMessagePattern = ExpectedMessagePattern, + -- @ExpectedErrorNumber = ExpectedErrorNumber, + -- @FailMessage = FailMessage + -- FROM #ExpectException; - IF(@ExpectException = 1) - BEGIN - SET @Result = 'Success'; - SET @TmpMsg = COALESCE(@FailMessage+' ','')+'Exception did not match expectation!'; - IF(ERROR_MESSAGE() <> @ExpectedMessage) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected Message: <'+@ExpectedMessage+'>'+CHAR(13)+CHAR(10)+ - 'Actual Message : <'+ERROR_MESSAGE()+'>'; - SET @Result = 'Failure'; - END - IF(ERROR_MESSAGE() NOT LIKE @ExpectedMessagePattern) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected Message to be like <'+@ExpectedMessagePattern+'>'+CHAR(13)+CHAR(10)+ - 'Actual Message : <'+ERROR_MESSAGE()+'>'; - SET @Result = 'Failure'; - END - IF(ERROR_NUMBER() <> @ExpectedErrorNumber) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected Error Number: '+CAST(@ExpectedErrorNumber AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - 'Actual Error Number : '+CAST(ERROR_NUMBER() AS NVARCHAR(MAX)); - SET @Result = 'Failure'; - END - IF(ERROR_SEVERITY() <> @ExpectedSeverity) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected Severity: '+CAST(@ExpectedSeverity AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - 'Actual Severity : '+CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)); - SET @Result = 'Failure'; - END - IF(ERROR_STATE() <> @ExpectedState) - BEGIN - SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - 'Expected State: '+CAST(@ExpectedState AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - 'Actual State : '+CAST(ERROR_STATE() AS NVARCHAR(MAX)); - SET @Result = 'Failure'; - END - IF(@Result = 'Failure') - BEGIN - SET @Msg = @TmpMsg; - END - END - ELSE - BEGIN - SET @Result = 'Failure'; - SET @Msg = - COALESCE(@FailMessage+' ','')+ - 'Expected no error to be raised. Instead this error was encountered:'+ - CHAR(13)+CHAR(10)+ - @ErrorInfo; - END - END; - ELSE - BEGIN - SET @Result = 'Error'; - SET @Msg = @ErrorInfo; - END; - END; - END CATCH; - END TRY - BEGIN CATCH - SET @Result = 'Error'; - SET @Msg = ERROR_MESSAGE(); - END CATCH + -- IF(@ExpectException = 1) + -- BEGIN + -- SET @Result = 'Success'; + -- SET @TmpMsg = COALESCE(@FailMessage+' ','')+'Exception did not match expectation!'; + -- IF(ERROR_MESSAGE() <> @ExpectedMessage) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected Message: <'+@ExpectedMessage+'>'+CHAR(13)+CHAR(10)+ + -- 'Actual Message : <'+ERROR_MESSAGE()+'>'; + -- SET @Result = 'Failure'; + -- END + -- IF(ERROR_MESSAGE() NOT LIKE @ExpectedMessagePattern) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected Message to be like <'+@ExpectedMessagePattern+'>'+CHAR(13)+CHAR(10)+ + -- 'Actual Message : <'+ERROR_MESSAGE()+'>'; + -- SET @Result = 'Failure'; + -- END + -- IF(ERROR_NUMBER() <> @ExpectedErrorNumber) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected Error Number: '+CAST(@ExpectedErrorNumber AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + -- 'Actual Error Number : '+CAST(ERROR_NUMBER() AS NVARCHAR(MAX)); + -- SET @Result = 'Failure'; + -- END + -- IF(ERROR_SEVERITY() <> @ExpectedSeverity) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected Severity: '+CAST(@ExpectedSeverity AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + -- 'Actual Severity : '+CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)); + -- SET @Result = 'Failure'; + -- END + -- IF(ERROR_STATE() <> @ExpectedState) + -- BEGIN + -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ + -- 'Expected State: '+CAST(@ExpectedState AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ + -- 'Actual State : '+CAST(ERROR_STATE() AS NVARCHAR(MAX)); + -- SET @Result = 'Failure'; + -- END + -- IF(@Result = 'Failure') + -- BEGIN + -- SET @Msg = @TmpMsg; + -- END + -- END + -- ELSE + -- BEGIN + -- SET @Result = 'Failure'; + -- SET @Msg = + -- COALESCE(@FailMessage+' ','')+ + -- 'Expected no error to be raised. Instead this error was encountered:'+ + -- CHAR(13)+CHAR(10)+ + -- @ErrorInfo; + -- END + --PRINT ''; + --END; + --ELSE + --BEGIN + --SET @Result = 'Error'; + --SET @Msg = @ErrorInfo; + --END; + --END; + --END CATCH; + --END TRY + --BEGIN CATCH + -- --SET @Result = 'Error'; + -- --SET @Msg = ERROR_MESSAGE(); + --END CATCH --TODO:NoTran ---- Compare @@Trancount, throw up arms if it doesn't match --TODO:NoTran - BEGIN TRY - IF(@TransactionStartedFlag = 1) - BEGIN - ROLLBACK TRAN @TranName; - END; - END TRY - BEGIN CATCH - DECLARE @PostExecTrancount INT; - SET @PostExecTrancount = @PreExecTrancount - @@TRANCOUNT; - IF (@@TRANCOUNT > 0) ROLLBACK; - BEGIN TRAN; - IF( @Result <> 'Success' - OR @PostExecTrancount <> 0 - ) - BEGIN - SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); - SET @Result = 'Error'; - END; - END CATCH; - IF (@NoTransactionFlag = 1) - BEGIN - SET @CleanUpProcedureExecutionCmd = ( - ( - SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' - FROM #NoTransaction NT - ORDER BY OrderId - FOR XML PATH(''),TYPE - ).value('.','NVARCHAR(MAX)') - ); - IF(@CleanUpProcedureExecutionCmd IS NOT NULL) - BEGIN - EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; - END; + --BEGIN TRY + -- IF(@TransactionStartedFlag = 1) + -- BEGIN + -- ROLLBACK TRAN @TranName; + -- END; + --END TRY + --BEGIN CATCH + -- DECLARE @PostExecTrancount INT; + -- SET @PostExecTrancount = @PreExecTrancount - @@TRANCOUNT; + -- IF (@@TRANCOUNT > 0) ROLLBACK; + -- BEGIN TRAN; + -- IF( @Result <> 'Success' + -- OR @PostExecTrancount <> 0 + -- ) + -- BEGIN + -- SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); + -- SET @Result = 'Error'; + -- END; + --END CATCH; + --IF (@NoTransactionFlag = 1) + --BEGIN + -- SET @CleanUpProcedureExecutionCmd = ( + -- ( + -- SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' + -- FROM #NoTransaction NT + -- ORDER BY OrderId + -- FOR XML PATH(''),TYPE + -- ).value('.','NVARCHAR(MAX)') + -- ); + -- IF(@CleanUpProcedureExecutionCmd IS NOT NULL) + -- BEGIN + -- EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; + -- END; - IF(@CleanUp IS NOT NULL) - BEGIN - EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; - END; + -- IF(@CleanUp IS NOT NULL) + -- BEGIN + -- EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; + -- END; - DECLARE @CleanUpErrorMsg NVARCHAR(MAX); - EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; - SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); + -- DECLARE @CleanUpErrorMsg NVARCHAR(MAX); + -- EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; + -- SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); - SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; - EXEC tSQLt.Private_AssertNoSideEffects - @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', - @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', - @TestResult = @Result OUT, - @TestMsg = @Msg OUT - END; - IF(@TransactionStartedFlag = 1) - BEGIN - COMMIT; - END; -END; + -- SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; + -- EXEC tSQLt.Private_AssertNoSideEffects + -- @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', + -- @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', + -- @TestResult = @Result OUT, + -- @TestMsg = @Msg OUT + --END; + --IF(@TransactionStartedFlag = 1) + --BEGIN + -- COMMIT; + --END; +--END; --END; --ELSE From 7a4f0d669cead6a1fc19fd242df18f3e4dc8a1bc Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 20 Dec 2021 06:39:29 -0500 Subject: [PATCH 112/119] We may have found a defect in mssql 2017 related to starting a transaction (BEGIN TRAN) only within a try-catch block invalidating the transaction. --- Experiments/Experiments.ssmssqlproj | 6 + ...fect Try-Catch Invalidates Transaction.sql | 20 + Experiments/StartHere 2021-12-20 B.sql | 11 - Experiments/StartHere 2021-12-20.sql | 354 ------------------ 4 files changed, 26 insertions(+), 365 deletions(-) create mode 100644 Experiments/MSSQL Defect Try-Catch Invalidates Transaction.sql delete mode 100644 Experiments/StartHere 2021-12-20 B.sql delete mode 100644 Experiments/StartHere 2021-12-20.sql diff --git a/Experiments/Experiments.ssmssqlproj b/Experiments/Experiments.ssmssqlproj index 73eb07cae..7ea928e3d 100644 --- a/Experiments/Experiments.ssmssqlproj +++ b/Experiments/Experiments.ssmssqlproj @@ -66,6 +66,12 @@ LIKE4000.sql + + + + + MSSQL Defect Try-Catch Invalidates Transaction.sql + diff --git a/Experiments/MSSQL Defect Try-Catch Invalidates Transaction.sql b/Experiments/MSSQL Defect Try-Catch Invalidates Transaction.sql new file mode 100644 index 000000000..6497e537a --- /dev/null +++ b/Experiments/MSSQL Defect Try-Catch Invalidates Transaction.sql @@ -0,0 +1,20 @@ +PRINT 'No TRY...CATCH:'; +EXEC ('BEGIN TRAN'); +SELECT XACT_STATE() AS [XACT_STATE()],@@TRANCOUNT AS [@@TRANCOUNT]; +GO +IF(XACT_STATE()<>0)ROLLBACK; +GO + + + +GO +PRINT 'In TRY...CATCH:'; +BEGIN TRY +EXEC ('BEGIN TRAN'); +END TRY +BEGIN CATCH +END CATCH; +SELECT XACT_STATE() AS [XACT_STATE()],@@TRANCOUNT AS [@@TRANCOUNT]; +GO +IF(XACT_STATE()<>0)ROLLBACK; +GO diff --git a/Experiments/StartHere 2021-12-20 B.sql b/Experiments/StartHere 2021-12-20 B.sql deleted file mode 100644 index 2e3252985..000000000 --- a/Experiments/StartHere 2021-12-20 B.sql +++ /dev/null @@ -1,11 +0,0 @@ - -BEGIN TRAN; -SAVE TRAN TranName; - -BEGIN TRY -SELECT XACT_STATE(),@@TRANCOUNT; -EXEC ('BEGIN TRAN'); -END TRY -BEGIN CATCH -SELECT XACT_STATE(),@@TRANCOUNT; -END CATCH; diff --git a/Experiments/StartHere 2021-12-20.sql b/Experiments/StartHere 2021-12-20.sql deleted file mode 100644 index 319b13bd9..000000000 --- a/Experiments/StartHere 2021-12-20.sql +++ /dev/null @@ -1,354 +0,0 @@ - -EXEC tSQLt.NewTestClass 'NotInnerTests'; -GO -CREATE PROCEDURE NotInnerTests.[test brittle transaction test] -AS -BEGIN - BEGIN TRAN; -END; -GO - ---EXEC tSQLt.Private_RunTest @TestName = 'NotInnerTests.[test brittle transaction test]'; - - - --DROP TABLE IF EXISTS #TestMessage; - --DROP TABLE IF EXISTS #ExpectException; - --DROP TABLE IF EXISTS #SkipTest; - --DROP TABLE IF EXISTS #NoTransaction; - --DROP TABLE IF EXISTS #TableBackupLog; - --GO - - DECLARE @TestName NVARCHAR(MAX) = 'NotInnerTests.[test brittle transaction test]'; - --DECLARE @SetUp NVARCHAR(MAX) = NULL; - --DECLARE @CleanUp NVARCHAR(MAX) = NULL; - - --DECLARE @OuterPerimeterTrancount INT = @@TRANCOUNT; - - --DECLARE @Msg NVARCHAR(MAX); SET @Msg = ''; - --DECLARE @Msg2 NVARCHAR(MAX); SET @Msg2 = ''; - --DECLARE @TestClassName NVARCHAR(MAX); SET @TestClassName = ''; - --DECLARE @TestProcName NVARCHAR(MAX); SET @TestProcName = ''; - --DECLARE @Result NVARCHAR(MAX); - DECLARE @TranName CHAR(32) = 'sadfhksajdf'; - --DECLARE @TestResultId INT; - --DECLARE @TestObjectId INT; - --DECLARE @TestEndTime DATETIME2 = NULL; - - --DECLARE @VerboseMsg NVARCHAR(MAX); - --DECLARE @Verbose BIT; - --SET @Verbose = ISNULL((SELECT CAST(Value AS BIT) FROM tSQLt.Private_GetConfiguration('Verbose')),0); - - --TRUNCATE TABLE tSQLt.CaptureOutputLog; - --CREATE TABLE #TestMessage(Msg NVARCHAR(MAX)); - --CREATE TABLE #ExpectException(ExpectException INT,ExpectedMessage NVARCHAR(MAX), ExpectedSeverity INT, ExpectedState INT, ExpectedMessagePattern NVARCHAR(MAX), ExpectedErrorNumber INT, FailMessage NVARCHAR(MAX)); - --CREATE TABLE #SkipTest(SkipTestMessage NVARCHAR(MAX) DEFAULT ''); - --CREATE TABLE #NoTransaction(OrderId INT IDENTITY(1,1),CleanUpProcedureName NVARCHAR(MAX)); - --CREATE TABLE #TableBackupLog(OriginalName NVARCHAR(MAX), BackupName NVARCHAR(MAX)); - - - --SELECT @TestClassName = OBJECT_SCHEMA_NAME(OBJECT_ID(@TestName)), - -- @TestProcName = tSQLt.Private_GetCleanObjectName(@TestName), - -- @TestObjectId = OBJECT_ID(@TestName); - - --INSERT INTO tSQLt.TestResult(Class, TestCase, TranName, Result) - -- SELECT @TestClassName, @TestProcName, @TranName, 'A severe error happened during test execution. Test did not finish.' - -- OPTION(MAXDOP 1); - --SELECT @TestResultId = SCOPE_IDENTITY(); - - --IF(@Verbose = 1) - --BEGIN - -- SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Starting'; - -- EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; - --END; - - - --SET @Result = 'Success'; - --DECLARE @SkipTestFlag BIT = 0; - --DECLARE @NoTransactionFlag BIT = 0; - - --BEGIN TRY - --EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId; - --SET @SkipTestFlag = CASE WHEN EXISTS(SELECT 1 FROM #SkipTest) THEN 1 ELSE 0 END; - --SET @NoTransactionFlag = CASE WHEN EXISTS(SELECT 1 FROM #NoTransaction) THEN 1 ELSE 0 END; - - --IF(@SkipTestFlag = 0) - --BEGIN - -- IF(@NoTransactionFlag = 0) - -- BEGIN - -- EXEC tSQLt.GetNewTranName @TranName OUT; - -- UPDATE tSQLt.TestResult SET TranName = @TranName WHERE Id = @TestResultId; - -- END; - --EXEC tSQLt.Private_RunTest_TestExecution - -- @TestName, - -- @SetUp, - -- @CleanUp, - -- @NoTransactionFlag, - -- @TranName, - -- @Result OUT, - -- @Msg OUT, - -- @TestEndTime OUT; ---IF (1=1) ---BEGIN - --DECLARE @TransactionStartedFlag BIT = 0; - --DECLARE @PreExecTrancount INT = NULL; - DECLARE @TestExecutionCmd NVARCHAR(MAX) = 'EXEC ' + @TestName; - --DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL; - - --BEGIN TRY - - --IF(@NoTransactionFlag = 0) - --BEGIN - BEGIN TRAN; - --SET @TransactionStartedFlag = 1; - SAVE TRAN @TranName; - --END; - --ELSE - --BEGIN - -- SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #BeforeExecutionObjectSnapshot FROM sys.objects; - -- EXEC tSQLt.Private_NoTransactionHandleTables @Action = 'Save'; - --END; - - --SET @PreExecTrancount = @@TRANCOUNT; - - --DECLARE @TmpMsg NVARCHAR(MAX); - --SET @TestEndTime = NULL; - --BEGIN TRY - --IF (@SetUp IS NOT NULL) - --BEGIN - -- EXEC @SetUp; - --END; - - BEGIN TRY - SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; - EXEC (@TestExecutionCmd); - END TRY - BEGIN CATCH - SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; - THROW; - END CATCH; - - --IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) - --BEGIN - -- SET @TmpMsg = COALESCE((SELECT FailMessage FROM #ExpectException)+' ','')+'Expected an error to be raised.'; - -- EXEC tSQLt.Fail @TmpMsg; - --END - --SET @TestEndTime = SYSDATETIME(); - --END TRY - --BEGIN CATCH - --SET @TestEndTime = ISNULL(@TestEndTime,SYSDATETIME()); - --IF ERROR_MESSAGE() LIKE '%tSQLt.Failure%' - --BEGIN - -- SELECT @Msg = Msg FROM #TestMessage; - -- SET @Result = 'Failure'; - --END - --ELSE - --BEGIN - --DECLARE @ErrorInfo NVARCHAR(MAX); - --SELECT @ErrorInfo = FormattedError FROM tSQLt.Private_GetFormattedErrorInfo(); - - --IF(EXISTS(SELECT 1 FROM #ExpectException)) - --BEGIN - -- DECLARE @ExpectException INT; - -- DECLARE @ExpectedMessage NVARCHAR(MAX); - -- DECLARE @ExpectedMessagePattern NVARCHAR(MAX); - -- DECLARE @ExpectedSeverity INT; - -- DECLARE @ExpectedState INT; - -- DECLARE @ExpectedErrorNumber INT; - -- DECLARE @FailMessage NVARCHAR(MAX); - -- SELECT @ExpectException = ExpectException, - -- @ExpectedMessage = ExpectedMessage, - -- @ExpectedSeverity = ExpectedSeverity, - -- @ExpectedState = ExpectedState, - -- @ExpectedMessagePattern = ExpectedMessagePattern, - -- @ExpectedErrorNumber = ExpectedErrorNumber, - -- @FailMessage = FailMessage - -- FROM #ExpectException; - - -- IF(@ExpectException = 1) - -- BEGIN - -- SET @Result = 'Success'; - -- SET @TmpMsg = COALESCE(@FailMessage+' ','')+'Exception did not match expectation!'; - -- IF(ERROR_MESSAGE() <> @ExpectedMessage) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected Message: <'+@ExpectedMessage+'>'+CHAR(13)+CHAR(10)+ - -- 'Actual Message : <'+ERROR_MESSAGE()+'>'; - -- SET @Result = 'Failure'; - -- END - -- IF(ERROR_MESSAGE() NOT LIKE @ExpectedMessagePattern) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected Message to be like <'+@ExpectedMessagePattern+'>'+CHAR(13)+CHAR(10)+ - -- 'Actual Message : <'+ERROR_MESSAGE()+'>'; - -- SET @Result = 'Failure'; - -- END - -- IF(ERROR_NUMBER() <> @ExpectedErrorNumber) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected Error Number: '+CAST(@ExpectedErrorNumber AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - -- 'Actual Error Number : '+CAST(ERROR_NUMBER() AS NVARCHAR(MAX)); - -- SET @Result = 'Failure'; - -- END - -- IF(ERROR_SEVERITY() <> @ExpectedSeverity) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected Severity: '+CAST(@ExpectedSeverity AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - -- 'Actual Severity : '+CAST(ERROR_SEVERITY() AS NVARCHAR(MAX)); - -- SET @Result = 'Failure'; - -- END - -- IF(ERROR_STATE() <> @ExpectedState) - -- BEGIN - -- SET @TmpMsg = @TmpMsg +CHAR(13)+CHAR(10)+ - -- 'Expected State: '+CAST(@ExpectedState AS NVARCHAR(MAX))+CHAR(13)+CHAR(10)+ - -- 'Actual State : '+CAST(ERROR_STATE() AS NVARCHAR(MAX)); - -- SET @Result = 'Failure'; - -- END - -- IF(@Result = 'Failure') - -- BEGIN - -- SET @Msg = @TmpMsg; - -- END - -- END - -- ELSE - -- BEGIN - -- SET @Result = 'Failure'; - -- SET @Msg = - -- COALESCE(@FailMessage+' ','')+ - -- 'Expected no error to be raised. Instead this error was encountered:'+ - -- CHAR(13)+CHAR(10)+ - -- @ErrorInfo; - -- END - --PRINT ''; - --END; - --ELSE - --BEGIN - --SET @Result = 'Error'; - --SET @Msg = @ErrorInfo; - --END; - --END; - --END CATCH; - --END TRY - --BEGIN CATCH - -- --SET @Result = 'Error'; - -- --SET @Msg = ERROR_MESSAGE(); - --END CATCH - - --TODO:NoTran - ---- Compare @@Trancount, throw up arms if it doesn't match - --TODO:NoTran - --BEGIN TRY - -- IF(@TransactionStartedFlag = 1) - -- BEGIN - -- ROLLBACK TRAN @TranName; - -- END; - --END TRY - --BEGIN CATCH - -- DECLARE @PostExecTrancount INT; - -- SET @PostExecTrancount = @PreExecTrancount - @@TRANCOUNT; - -- IF (@@TRANCOUNT > 0) ROLLBACK; - -- BEGIN TRAN; - -- IF( @Result <> 'Success' - -- OR @PostExecTrancount <> 0 - -- ) - -- BEGIN - -- SELECT @Msg = COALESCE(@Msg, '') + ' (There was also a ROLLBACK ERROR --> ' + FormattedError + ')' FROM tSQLt.Private_GetFormattedErrorInfo(); - -- SET @Result = 'Error'; - -- END; - --END CATCH; - --IF (@NoTransactionFlag = 1) - --BEGIN - -- SET @CleanUpProcedureExecutionCmd = ( - -- ( - -- SELECT 'EXEC tSQLt.Private_CleanUpCmdHandler ''EXEC '+ REPLACE(NT.CleanUpProcedureName,'''','''''') +';'', @Result OUT, @Msg OUT;' - -- FROM #NoTransaction NT - -- ORDER BY OrderId - -- FOR XML PATH(''),TYPE - -- ).value('.','NVARCHAR(MAX)') - -- ); - -- IF(@CleanUpProcedureExecutionCmd IS NOT NULL) - -- BEGIN - -- EXEC sys.sp_executesql @CleanUpProcedureExecutionCmd, N'@Result NVARCHAR(MAX) OUTPUT, @Msg NVARCHAR(MAX) OUTPUT', @Result OUT, @Msg OUT; - -- END; - - -- IF(@CleanUp IS NOT NULL) - -- BEGIN - -- EXEC tSQLt.Private_CleanUpCmdHandler @CleanUp, @Result OUT, @Msg OUT; - -- END; - - -- DECLARE @CleanUpErrorMsg NVARCHAR(MAX); - -- EXEC tSQLt.Private_CleanUp @FullTestName = @TestName, @Result = @Result OUT, @ErrorMsg = @CleanUpErrorMsg OUT; - -- SET @Msg = @Msg + ISNULL(' ' + @CleanUpErrorMsg, ''); - - -- SELECT object_id ObjectId, SCHEMA_NAME(schema_id) SchemaName, name ObjectName, type_desc ObjectType INTO #AfterExecutionObjectSnapshot FROM sys.objects; - -- EXEC tSQLt.Private_AssertNoSideEffects - -- @BeforeExecutionObjectSnapshotTableName ='#BeforeExecutionObjectSnapshot', - -- @AfterExecutionObjectSnapshotTableName = '#AfterExecutionObjectSnapshot', - -- @TestResult = @Result OUT, - -- @TestMsg = @Msg OUT - --END; - --IF(@TransactionStartedFlag = 1) - --BEGIN - -- COMMIT; - --END; ---END; - - --END; - --ELSE - --BEGIN - -- DECLARE @TmpMsg NVARCHAR(MAX); - -- SELECT - -- @Result = 'Skipped', - -- @Msg = ST.SkipTestMessage - -- FROM #SkipTest AS ST; - -- SET @TmpMsg = '-->'+@TestName+' skipped: '+@Msg; - -- EXEC tSQLt.Private_Print @Message = @TmpMsg; - -- SET @TestEndTime = SYSDATETIME(); - --END; - --END TRY - --BEGIN CATCH - -- SET @Result = 'Error'; - -- SET @Msg = ISNULL(NULLIF(@Msg,'') + ' ','')+ERROR_MESSAGE(); - -- --SET @TestEndTime = SYSDATETIME(); - --END CATCH; ------------------------------------------------------------------------------------------------- --- If(@Result NOT IN ('Success','Skipped')) --- BEGIN --- SET @Msg2 = @TestName + ' failed: (' + @Result + ') ' + @Msg; --- EXEC tSQLt.Private_Print @Message = @Msg2, @Severity = 0; --- END; --- IF EXISTS(SELECT 1 FROM tSQLt.TestResult WHERE Id = @TestResultId) --- BEGIN --- UPDATE tSQLt.TestResult SET --- Result = @Result, --- Msg = @Msg, --- TestEndTime = @TestEndTime --- WHERE Id = @TestResultId; --- END; --- ELSE --- BEGIN --- INSERT tSQLt.TestResult(Class, TestCase, TranName, Result, Msg) --- SELECT @TestClassName, --- @TestProcName, --- '?', --- 'Error', --- 'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg; --- END; - --- IF(@Verbose = 1) --- BEGIN --- SET @VerboseMsg = 'tSQLt.Run '''+@TestName+'''; --Finished'; --- EXEC tSQLt.Private_Print @Message =@VerboseMsg, @Severity = 0; --- --DECLARE @AsciiArtLine NVARCHAR(MAX) = CASE WHEN @Result<>'Success' THEN REPLICATE(CHAR(168),150)+' '+CHAR(155)+CHAR(155)+' '+@Result + ' ' +CHAR(139)+CHAR(139) ELSE '' END + CHAR(13)+CHAR(10) + CHAR(173); --- --EXEC tSQLt.Private_Print @Message = @AsciiArtLine, @Severity = 0; --- END; - --- IF(@Result = 'FATAL') --- BEGIN --- INSERT INTO tSQLt.Private_Seize VALUES(1); --- RAISERROR('The last test has invalidated the current installation of tSQLt. Please reinstall tSQLt.',16,10); --- END; --- IF(@Result = 'Abort') --- BEGIN --- RAISERROR('Aborting the current execution of tSQLt due to a severe error.', 16, 10); --- END; - --- IF(@OuterPerimeterTrancount != @@TRANCOUNT) RAISERROR('tSQLt is in an invalid state: Stopping Execution. (Mismatching TRANCOUNT: %i <> %i))',16,10,@OuterPerimeterTrancount, @@TRANCOUNT); From 453e269c1f791c3626eb5c3308af8f9ea6d3c3c9 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 20 Dec 2021 06:44:34 -0500 Subject: [PATCH 113/119] Update Run_Methods.sql trying to manually revert back to desired version --- Source/Run_Methods.sql | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Source/Run_Methods.sql b/Source/Run_Methods.sql index 99ce4ff56..2395b8896 100644 --- a/Source/Run_Methods.sql +++ b/Source/Run_Methods.sql @@ -82,14 +82,7 @@ BEGIN EXEC @SetUp; END; - BEGIN TRY - SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; - EXEC (@TestExecutionCmd); - END TRY - BEGIN CATCH - SELECT XACT_STATE(),@@TRANCOUNT,@TestExecutionCmd; - THROW; - END CATCH; + EXEC (@TestExecutionCmd); IF(EXISTS(SELECT 1 FROM #ExpectException WHERE ExpectException = 1)) BEGIN From a6b8260f5842e7e2b3b8386d3f6bb6f2871a242e Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Mon, 20 Dec 2021 07:10:10 -0500 Subject: [PATCH 114/119] Updated documentation and skipped tests in Run_Methods_Tests.class.sql based on learnings around the try-catch w/transactions defect --- Tests/Run_Methods_Tests.class.sql | 43 +++++++++++++++++++------------ 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index a05898d83..36dd3e68d 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2441,6 +2441,7 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO +--[@tSQLt:SkipTest]('TODO: tSQLt should handle this, but does not at the moment because of the issue with transactions started within a try-catch block') CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match] AS BEGIN @@ -2455,37 +2456,47 @@ GO /*-----------------------------------------------------------------------------------------------*/ GO ---[@tSQLt:SkipTest]('TODO: need to review handling of unexpected changes to the tSQLt transaction for NoTransaction tests') +--[@tSQLt:SkipTest]('TODO: tSQLt should handle this, but does not at the moment because of the issue with transactions started within a try-catch block') CREATE PROCEDURE Run_Methods_Tests.[test produces meaningful error when pre and post transactions counts don't match in NoTransaction test] AS BEGIN EXEC tSQLt.NewTestClass 'MyInnerTestsB' EXEC(' --[@'+'tSQLt:NoTransaction](DEFAULT) -CREATE PROCEDURE MyInnerTestsB.[test should execute outside of transaction] AS BEGIN TRAN;SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); +CREATE PROCEDURE MyInnerTestsB.[test should execute outside of transaction] AS BEGIN TRAN; '); EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL; + EXEC tSQLt.Run 'MyInnerTestsB.[test should execute outside of transaction]'; - BEGIN TRY - EXEC tSQLt.Run 'MyInnerTestsB.[test should execute outside of transaction]'; - END TRY - BEGIN CATCH - SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); - END CATCH; + -- FOR FUTURE DEBUGGING + --BEGIN TRY + -- EXEC tSQLt.Run 'MyInnerTestsB.[test should execute outside of transaction]'; + --END TRY + --BEGIN CATCH + -- SELECT * FROM fn_dblog(NULL,NULL) WHERE [Transaction ID] = (SELECT LL.[Transaction ID] FROM fn_dblog(NULL,NULL) LL JOIN sys.dm_tran_current_transaction AS DTCT ON DTCT.transaction_id = LL.[Xact ID]); + --END CATCH; END; GO /*-----------------------------------------------------------------------------------------------*/ GO /*-- - Transaction Tests - - - NoTransaction, but suddenly has transaction - - with transaction, but creates additional transaction - - transaction, but is committed (FATAL) - - what should we do if the original transaction was rolled back and a new one was created? - - what should we do if the original transaction was committed and a new one was created? - - we still need to save the TranName as something somewhere. + Transaction Tests to be considered + + 1. NoTransaction test starts a transaction + 1a. The transaction is commitable (THIS FEELS UNLIKELY GIVEN OUR RESEARCH.) + 1b. The transaction is not commitable + + 2. Transaction test rolls-back tSQLt transaction(s) + 2a. No transaction after test (ROLLBACK, but no new transactions created within the test) + 2b. New transaction after test (ROLLBACK, but new one created within the test) + + 3. Transaction test commits tSQLt transactions(s) + 3a. No transaction after test (COMMIT, but no new transactions created within the test) + 3b. New transaction after test (COMMIT, but new one created within the test) + + 4. Transaction test renders transaction uncommitable (Won't be able to write to anything, including tSQLt.TestResults or a variety of temp tables.) + - do existing tests already cover some of the scenarios described above? --*/ From aedd48de2c3167c5873e1bb623d71a87c78af803 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 21 Dec 2021 07:14:38 -0500 Subject: [PATCH 115/119] More notes to consider when we test transactions; Wrote another test for Private_NoTransactionHandleTable and confirmed that we are done with this functionality (for now). --- ...t.Private_NoTransactionHandleTable.ssp.sql | 3 +- ...te_NoTransactionHandleTableTests.class.sql | 90 +++++++++++-------- Tests/Run_Methods_Tests.class.sql | 3 +- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql index c4b4270cf..2eac91c4b 100644 --- a/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql +++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql @@ -73,8 +73,9 @@ BEGIN BEGIN EXEC('DELETE FROM ' + @FullTableName +';'); END; - ELSE IF (@TableAction IN ('Ignore','Hide')) + ELSE IF (@TableAction IN ('Ignore','Hide')) BEGIN + /* Hidden tables will be restored by UndoTestDoubles. */ RETURN; END; ELSE diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index 3d5ba0f4f..efa22c789 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -577,49 +577,69 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO - - -CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test TODO] +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Hide, @Action Save, Save: do nothing on second save] AS BEGIN - EXEC tSQLt.Fail 'TODO'; ---tSQLt.Run 'Private_NoTransactionHandleTableTests'.[test if @TableAction is Restore, @Action Save, Save: the second Save does nothing]' -/*-- -TODO - -I can rerun them and nothing "bad" happens. But what is "bad"? -What about when I intersperse calls to Save/Reset with UndoTestDoubles? + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Hide'; -Some scenarios to consider -1: Save, Reset -2: Save, Save, Reset -3: Save, Reset, Reset -4: Save, Save, Reset, Reset + SELECT object_id, SCHEMA_NAME(schema_id) [schema_name], name INTO #Before FROM sys.tables; -@TableAction = Restore -5: ?*test* Save (Restore), Save (Restore) --> The second save does nothing, because it checks the #TableBackupLog. -6: Save (Restore), Save (Restore) Eclipsed #TableBackupLog --> The second save takes a new backup because it cannot see #TableBackupLog. -7: ?*test* Save (Restore), Save (Restore) Eclipsed #TableBackupLog, Reset (Restore) Eclipsed #TableBackupLog, Reset (Restore) --> Should be equivalent to scenario 1. -8: ?*test* Save (Restore), Save (Restore), Reset (Restore), Reset (Restore) --> Should be equivalent to scenario 1. -9: ?*test* Save (Restore), Save (Restore) Eclipsed #TableBackupLog, Reset (Restore) --> Should be equivalent to scenario 1. -17: ?*test* Save (Restore), Reset (Restore), Reset (Restore) --> Should be equivalent to scenario 1. + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Hide'; -@TableAction = Hide -10: ?*test* Save (Hide), Save (Hide) --> We can't hide something we can't see. Check to see if the object is already hidden, if so do nothing. If not, throw an error. -11: Save (Hide), Save (Hide) Eclipsed #TableBackupLog --> Same as scenario 10. -12: Save (Hide), Save (Hide) Eclipsed #TableBackupLog, Reset (Hide) Eclipsed #TableBackupLog, Reset (Hide) --> Should be equivalent to Scenario 1. -13: Save (Hide), Save (Hide), Reset (Hide), Reset (Hide) --> Should be equivalent to Scenario 1. -14: Save (Hide), Save (Hide) Eclipsed #TableBackupLog, Reset (Hide) --> Should be equivalent to Scenario 1. -18: Save (Hide), Reset (Hide), Reset (Hide) --> Should be equivalent to scenario 1. + SELECT * INTO #Actual + FROM ( + ( + SELECT 'Extra'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + EXCEPT + SELECT 'Extra'[?],* FROM #Before + ) + UNION ALL + ( + SELECT 'Missing'[?],* FROM #Before + EXCEPT + SELECT 'Missing'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + ) + ) X; + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Hide, @Action Save, Save, Reset, Reset: does nothing in total] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX)); + INSERT INTO Private_NoTransactionHandleTableTests.Table1 VALUES (2,'c'), (3,'a'); -@TableAction = Truncate -15: ?*test* Save (Truncate), Save (Truncate), Reset (Truncate), Save (Truncate), Reset (Truncate), Reset (Truncate) --> Should be idempotent. Any table with TableAction=Truncate should be empty after any number of save, reset actions. + SELECT object_id, SCHEMA_NAME(schema_id) [schema_name], name INTO #Before FROM sys.tables; -@TableAction = Ignore -16: ?*test* Save (Ignore), Save (Ignore), Reset (Ignore), Reset (Ignore) --> No Op. ExpectNoException.s + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Hide'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Hide'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Hide'; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Reset', @FullTableName = '[Private_NoTransactionHandleTableTests].[Table1]', @TableAction = 'Hide'; + EXEC tSQLt.UndoTestDoubles; ---*/ + SELECT * INTO #Actual + FROM ( + ( + SELECT 'Extra'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + EXCEPT + SELECT 'Extra'[?],* FROM #Before + ) + UNION ALL + ( + SELECT 'Missing'[?],* FROM #Before + EXCEPT + SELECT 'Missing'[?],object_id, SCHEMA_NAME(schema_id) [schema_name], name FROM sys.tables + ) + ) X; + + EXEC tSQLt.AssertEmptyTable @TableName = '#Actual'; END; GO /*-----------------------------------------------------------------------------------------------*/ -GO \ No newline at end of file +GO + diff --git a/Tests/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql index 36dd3e68d..cab6f411a 100644 --- a/Tests/Run_Methods_Tests.class.sql +++ b/Tests/Run_Methods_Tests.class.sql @@ -2496,7 +2496,8 @@ GO 3b. New transaction after test (COMMIT, but new one created within the test) 4. Transaction test renders transaction uncommitable (Won't be able to write to anything, including tSQLt.TestResults or a variety of temp tables.) - + + - currently AssertNoSideEffects causes additional problems if executed inside an uncommittable transaction (It tries to write to a new temp table) <-- does this need to be changed? - do existing tests already cover some of the scenarios described above? --*/ From 1025bc64a85aff3d9acfd3bcd6660f3137ff0c07 Mon Sep 17 00:00:00 2001 From: Sebastian Meine Date: Tue, 21 Dec 2021 19:34:53 +0100 Subject: [PATCH 116/119] Update AZ_MainPipeline.yml for Azure Pipelines Updating service principal --- CI/Azure-DevOps/AZ_MainPipeline.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CI/Azure-DevOps/AZ_MainPipeline.yml b/CI/Azure-DevOps/AZ_MainPipeline.yml index 513949bb4..a938278d8 100644 --- a/CI/Azure-DevOps/AZ_MainPipeline.yml +++ b/CI/Azure-DevOps/AZ_MainPipeline.yml @@ -86,8 +86,10 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLt CI Subscription(58c04a99-5b92-410c-9e41-10262f68ca80)' + azureSubscription: 'tSQLt CI Service Principal' KeyVaultName: 'tSQLtSigningKey' + SecretsFilter: '*' + RunAsPreJob: false - task: PowerShell@2 name: CreateResourceGroupName @@ -112,7 +114,7 @@ stages: SQLPORTMINIMUM: $(SqlPortMinimum) SQLPORTMAXIMUM: $(SqlPortMaximum) inputs: - azureSubscription: 'tSQLt CI Subscription(58c04a99-5b92-410c-9e41-10262f68ca80)' + azureSubscription: 'tSQLt CI Service Principal' azurePowerShellVersion: 'LatestVersion' scriptType: ps scriptLocation: inlineScript @@ -199,7 +201,7 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLt CI Subscription(58c04a99-5b92-410c-9e41-10262f68ca80)' + azureSubscription: 'tSQLt CI Service Principal' KeyVaultName: 'tSQLtSigningKey' - task: PowerShell@2 @@ -407,7 +409,7 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLt CI Subscription(58c04a99-5b92-410c-9e41-10262f68ca80)' + azureSubscription: 'tSQLt CI Service Principal' KeyVaultName: 'tSQLtSigningKey' - task: PowerShell@2 @@ -646,7 +648,7 @@ stages: - task: AzureCLI@2 name: DeleteAzureVM inputs: - azureSubscription: 'tSQLt CI Subscription(58c04a99-5b92-410c-9e41-10262f68ca80)' + azureSubscription: 'tSQLt CI Service Principal' azurePowerShellVersion: 'LatestVersion' scriptType: ps scriptLocation: inlineScript From 2558e49fdd4e8956cc0a791f5ebc4e4b1f877023 Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Tue, 21 Dec 2021 17:19:16 -0500 Subject: [PATCH 117/119] Update AZ_MainPipeline.yml for Azure Pipelines updating to test a manually created service principal --- CI/Azure-DevOps/AZ_MainPipeline.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CI/Azure-DevOps/AZ_MainPipeline.yml b/CI/Azure-DevOps/AZ_MainPipeline.yml index a938278d8..3702dfe44 100644 --- a/CI/Azure-DevOps/AZ_MainPipeline.yml +++ b/CI/Azure-DevOps/AZ_MainPipeline.yml @@ -86,7 +86,7 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLt CI Service Principal' + azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' KeyVaultName: 'tSQLtSigningKey' SecretsFilter: '*' RunAsPreJob: false @@ -114,7 +114,7 @@ stages: SQLPORTMINIMUM: $(SqlPortMinimum) SQLPORTMAXIMUM: $(SqlPortMaximum) inputs: - azureSubscription: 'tSQLt CI Service Principal' + azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' azurePowerShellVersion: 'LatestVersion' scriptType: ps scriptLocation: inlineScript @@ -201,7 +201,7 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLt CI Service Principal' + azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' KeyVaultName: 'tSQLtSigningKey' - task: PowerShell@2 @@ -409,7 +409,7 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLt CI Service Principal' + azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' KeyVaultName: 'tSQLtSigningKey' - task: PowerShell@2 @@ -648,7 +648,7 @@ stages: - task: AzureCLI@2 name: DeleteAzureVM inputs: - azureSubscription: 'tSQLt CI Service Principal' + azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' azurePowerShellVersion: 'LatestVersion' scriptType: ps scriptLocation: inlineScript From c1592cd9b4dfdc4a9147c64c394c698f53299b4a Mon Sep 17 00:00:00 2001 From: Liz Baron <10554+lizbaron@users.noreply.github.com> Date: Wed, 22 Dec 2021 06:48:36 -0500 Subject: [PATCH 118/119] Update AZ_MainPipeline.yml for Azure Pipelines Updated service principal again --- CI/Azure-DevOps/AZ_MainPipeline.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CI/Azure-DevOps/AZ_MainPipeline.yml b/CI/Azure-DevOps/AZ_MainPipeline.yml index 3702dfe44..10dae77ca 100644 --- a/CI/Azure-DevOps/AZ_MainPipeline.yml +++ b/CI/Azure-DevOps/AZ_MainPipeline.yml @@ -86,7 +86,7 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' + azureSubscription: 'Azure DevOps Main Pipeline Service Principal' KeyVaultName: 'tSQLtSigningKey' SecretsFilter: '*' RunAsPreJob: false @@ -114,7 +114,7 @@ stages: SQLPORTMINIMUM: $(SqlPortMinimum) SQLPORTMAXIMUM: $(SqlPortMaximum) inputs: - azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' + azureSubscription: 'Azure DevOps Main Pipeline Service Principal' azurePowerShellVersion: 'LatestVersion' scriptType: ps scriptLocation: inlineScript @@ -201,7 +201,7 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' + azureSubscription: 'Azure DevOps Main Pipeline Service Principal' KeyVaultName: 'tSQLtSigningKey' - task: PowerShell@2 @@ -409,7 +409,7 @@ stages: - task: AzureKeyVault@1 inputs: - azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' + azureSubscription: 'Azure DevOps Main Pipeline Service Principal' KeyVaultName: 'tSQLtSigningKey' - task: PowerShell@2 @@ -648,7 +648,7 @@ stages: - task: AzureCLI@2 name: DeleteAzureVM inputs: - azureSubscription: 'tSQLtPipelineServicePrincipal (Manual)' + azureSubscription: 'Azure DevOps Main Pipeline Service Principal' azurePowerShellVersion: 'LatestVersion' scriptType: ps scriptLocation: inlineScript From 70ec9a23b5b57e15da8cadf55dca6b5ad5dafda4 Mon Sep 17 00:00:00 2001 From: mbt1 Date: Fri, 24 Dec 2021 09:04:10 -0500 Subject: [PATCH 119/119] Made this work pre 2017, too --- ...ivate_GetFormattedErrorInfoTests.class.sql | 24 ++++++++++++++++++- ...te_NoTransactionHandleTableTests.class.sql | 16 ++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Tests/Private_GetFormattedErrorInfoTests.class.sql b/Tests/Private_GetFormattedErrorInfoTests.class.sql index 40f8788bd..c57fc1929 100644 --- a/Tests/Private_GetFormattedErrorInfoTests.class.sql +++ b/Tests/Private_GetFormattedErrorInfoTests.class.sql @@ -49,6 +49,28 @@ AS BEGIN DECLARE @FormattedError NVARCHAR(MAX); + BEGIN TRY + EXEC ('/*Line 1*/CREATE PROCEDURE Private_GetFormattedErrorInfoTests.myInnerError + /*Line 2*/AS + /*Line 3*/BEGIN + /*Line 4*/ RAISERROR (13042,14,13); + /*Line 5*/END;'); + EXEC ('Private_GetFormattedErrorInfoTests.myInnerError'); + END TRY + BEGIN CATCH + SET @FormattedError = (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()); + END CATCH; + + EXEC tSQLt.AssertLike @ExpectedPattern = '%| Procedure: %myInnerError (4) |%', @Actual = @FormattedError; +END +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +CREATE PROCEDURE Private_GetFormattedErrorInfoTests.[test returns the correct ERROR procedure name and line number for #tempProcedure] +AS +BEGIN + DECLARE @FormattedError NVARCHAR(MAX); + BEGIN TRY EXEC ('/*Line 1*/CREATE PROCEDURE #myInnerError /*Line 2*/AS @@ -61,7 +83,7 @@ BEGIN SET @FormattedError = (SELECT FormattedError FROM tSQLt.Private_GetFormattedErrorInfo()); END CATCH; - EXEC tSQLt.AssertLike @ExpectedPattern = '%| Procedure: #myInnerError (4) |%', @Actual = @FormattedError; + EXEC tSQLt.AssertLike @ExpectedPattern = '%| Procedure: #myInnerError% (4) |%', @Actual = @FormattedError; END GO /*-----------------------------------------------------------------------------------------------*/ diff --git a/Tests/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql index efa22c789..5030081cb 100644 --- a/Tests/Private_NoTransactionHandleTableTests.class.sql +++ b/Tests/Private_NoTransactionHandleTableTests.class.sql @@ -176,7 +176,21 @@ END; GO /*-----------------------------------------------------------------------------------------------*/ GO -CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test augments any internal error with ' tSQLt is in an unknown state: Stopping execution.'] +--[@tSQLt:MaxSqlMajorVersion](13) +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test augments any internal error with ' tSQLt is in an unknown state: Stopping execution. (<=2016)'] +AS +BEGIN + CREATE TABLE Private_NoTransactionHandleTableTests.SomeTable(i INT); + EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.RemoveObject', @CommandToExecute='RAISERROR(''SOME INTERNAL ERROR.'',15,11)'; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'tSQLt is in an unknown state: Stopping execution. (SOME INTERNAL ERROR. | Procedure: RemoveObject | Line: 1)', @ExpectedSeverity = 15, @ExpectedState = 11; + EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.SomeTable', @TableAction = 'Hide'; +END; +GO +/*-----------------------------------------------------------------------------------------------*/ +GO +--[@tSQLt:MinSqlMajorVersion](14) +CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test augments any internal error with ' tSQLt is in an unknown state: Stopping execution. (>2016)'] AS BEGIN CREATE TABLE Private_NoTransactionHandleTableTests.SomeTable(i INT);