diff --git a/CI/Azure-DevOps/AZ_MainPipeline.yml b/CI/Azure-DevOps/AZ_MainPipeline.yml
index 6319c306d..10dae77ca 100644
--- a/CI/Azure-DevOps/AZ_MainPipeline.yml
+++ b/CI/Azure-DevOps/AZ_MainPipeline.yml
@@ -88,6 +88,8 @@ stages:
inputs:
azureSubscription: 'Azure DevOps Main Pipeline Service Principal'
KeyVaultName: 'tSQLtSigningKey'
+ SecretsFilter: '*'
+ RunAsPreJob: false
- task: PowerShell@2
name: CreateResourceGroupName
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/Debugging tSQLt.tdf b/Experiments/Debugging tSQLt.tdf
new file mode 100644
index 000000000..86a69c6de
Binary files /dev/null and b/Experiments/Debugging tSQLt.tdf differ
diff --git a/Experiments/Experiments.ssmssqlproj b/Experiments/Experiments.ssmssqlproj
index 4bf7c06d0..7ea928e3d 100644
--- a/Experiments/Experiments.ssmssqlproj
+++ b/Experiments/Experiments.ssmssqlproj
@@ -36,6 +36,12 @@
CertificateTest.sql
+
+
+
+
+ CollationTests.sql
+
@@ -60,6 +66,12 @@
LIKE4000.sql
+
+
+
+
+ MSSQL Defect Try-Catch Invalidates Transaction.sql
+
@@ -102,6 +114,12 @@
SQLCmdTest.1.sql
+
+
+
+
+ TestingTransactionNames.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/SM query - read trace table.sql b/Experiments/SM query - read trace table.sql
new file mode 100644
index 000000000..13d2094ca
--- /dev/null
+++ b/Experiments/SM query - read trace table.sql
@@ -0,0 +1,61 @@
+SELECT
+ R.EventSequence,
+ 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.RowNumber,
+ R.EventClass,
+ R.EventSubClass,
+ R.ApplicationName,
+ R.ClientProcessID,
+ R.DatabaseID,
+ R.DatabaseName,
+ 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.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 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
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
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;
diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt
index e70ba5f24..df1c46aeb 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
@@ -41,6 +42,8 @@ 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_HandleMessageAndResult.sfn.sql
tSQLt.Private_CleanTestResult.ssp.sql
tSQLt.Private_ListTestAnnotations.sfn.sql
tSQLt.Private_ProcessTestAnnotations.ssp.sql
@@ -50,8 +53,16 @@ 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
+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
tSQLt.Private_GetFullTypeName.sfn.sql
@@ -60,6 +71,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,17 +80,16 @@ 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
+tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql
tSQLt.SpyProcedure.ssp.sql
tSQLt.Private_GetCommaSeparatedColumnList.sfn.sql
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
@@ -94,7 +105,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
@@ -105,6 +115,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 b67d36bf9..2395b8896 100644
--- a/Source/Run_Methods.sql
+++ b/Source/Run_Methods.sql
@@ -1,5 +1,6 @@
-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_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;
@@ -24,116 +25,83 @@ 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
+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 @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); EXEC tSQLt.GetNewTranName @TranName OUT;
- DECLARE @TestResultId INT;
- DECLARE @PreExecTrancount INT;
- 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 #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 '');
-
- 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)), --tSQLt.Private_GetCleanSchemaName('', @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';
- BEGIN TRAN;
- SAVE TRAN @TranName;
-
- SET @PreExecTrancount = @@TRANCOUNT;
-
-
- TRUNCATE TABLE tSQLt.TestMessage;
+ DECLARE @TransactionStartedFlag BIT = 0;
+ DECLARE @PreExecTrancount INT = NULL;
+ DECLARE @TestExecutionCmd NVARCHAR(MAX) = 'EXEC ' + @TestName;
+ DECLARE @CleanUpProcedureExecutionCmd NVARCHAR(MAX) = NULL;
BEGIN TRY
- EXEC tSQLt.Private_ProcessTestAnnotations @TestObjectId=@TestObjectId;
+ 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);
- DECLARE @TestEndTime DATETIME2; SET @TestEndTime = NULL;
+ SET @TestEndTime = NULL;
BEGIN TRY
- IF(NOT EXISTS(SELECT 1 FROM #SkipTest))
+ IF (@SetUp IS NOT NULL)
BEGIN
- IF (@SetUp IS NOT NULL) EXEC @SetUp;
- 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
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
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
@@ -220,8 +188,14 @@ BEGIN
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;
@@ -232,17 +206,151 @@ 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 --> ' + 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;
+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(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
@@ -260,14 +368,27 @@ BEGIN
'Error',
'TestResult entry is missing; Original outcome: ' + @Result + ', ' + @Msg;
END;
- COMMIT;
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')
+ 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);
+
END;
GO
@@ -278,28 +399,20 @@ 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 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;
+ DECLARE @CleanUpProcName NVARCHAR(MAX);
+ EXEC tSQLt.Private_GetClassHelperProcedureName @TestClassId, @SetupProcName OUT, @CleanUpProcName OUT;
- 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')+', '+ISNULL(''''+REPLACE(@CleanUpProcName,'''','''''')+'''','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
@@ -316,7 +429,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,
@@ -334,9 +447,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;
@@ -353,34 +467,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 '''+REPLACE(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
@@ -393,16 +501,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
@@ -846,3 +951,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 9bf6e3537..85f3316e9 100644
--- a/Source/Source.ssmssqlproj
+++ b/Source/Source.ssmssqlproj
@@ -2,6 +2,8 @@
+
+
@@ -18,9 +20,6 @@
ExecutePrepareServer.sql
-
-
-
Run_Methods.sql
@@ -35,6 +34,12 @@
tSQLt.(at)tSQLt_MinSqlMajorVersion.sfn.sql
+
+
+
+
+ tSQLt.(at)tSQLt_NoTransaction.sfn.sql
+
@@ -77,12 +82,6 @@
tSQLt.AssertEqualsTableSchema.ssp.sql
-
-
-
-
- tSQLt.AssertEqualsTableSchema.tables.sql
-
@@ -209,12 +208,27 @@
tSQLt.PrepareServer.ssp.sql
+
+ tSQLt.Private_AssertNoSideEffects.ssp.sql
+
+
+ tSQLt.Private_AssertNoSideEffects_GenerateCommand.sfn.sql
+
tSQLt.Private_CleanTestResult.ssp.sql
+
+
+
+
+ tSQLt.Private_CleanUp.ssp.sql
+
+
+ tSQLt.Private_CleanUpCmdHandler.ssp.sql
+
@@ -329,6 +343,12 @@
tSQLt.Private_GetForeignKeyDefinition.sfn.sql
+
+
+
+
+ tSQLt.Private_GetFormattedErrorInfo.sfn.sql
+
@@ -353,6 +373,12 @@
tSQLt.Private_GetUniqueConstraintDefinition.sfn.sql
+
+
+
+
+ tSQLt.Private_HandleMessageAndResult.sfn.sql
+
@@ -366,9 +392,6 @@
tSQLt.Private_HostPlatform.svw.sql
-
-
-
tSQLt.Private_Init.ssp.sql
@@ -389,6 +412,12 @@
tSQLt.Private_ListTestAnnotations.sfn.sql
+
+
+
+
+ tSQLt.Private_Lock.tbl.sql
+
@@ -413,12 +442,6 @@
tSQLt.Private_NewTestClassList.tbl.sql
-
-
-
-
- tSQLt.Private_NullCellTable.tbl.sql
-
@@ -479,12 +502,42 @@
tSQLt.Private_ResetNewTestClassList.ssp.sql
+
+
+
+
+ tSQLt.Private_NoTransactionHandleTable.ssp.sql
+
+
+
+
+
+ tSQLt.Private_NoTransactionTableAction.view.sql
+
+
+
+
+
+ tSQLt.Private_NoTransactionHandleTables.ssp.sql
+
+
+
+
+
+ tSQLt.Private_Results.view.sql
+
tSQLt.Private_ScriptIndex.sfn.sql
+
+
+
+
+ tSQLt.Private_Seize.tbl.sql
+
@@ -539,6 +592,12 @@
tSQLt.Private_ValidateObjectsCompatibleWithFakeFunction.ssp.sql
+
+
+
+
+ tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure.ssp.sql
+
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
new file mode 100644
index 000000000..60bb2c6af
--- /dev/null
+++ b/Source/tSQLt.(at)tSQLt_NoTransaction.sfn.sql
@@ -0,0 +1,18 @@
+IF OBJECT_ID('tSQLt.[@tSQLt:NoTransaction]') IS NOT NULL DROP FUNCTION tSQLt.[@tSQLt:NoTransaction];
+GO
+---Build+
+GO
+CREATE FUNCTION tSQLt.[@tSQLt:NoTransaction](@CleanUpProcedureName NVARCHAR(MAX) = NULL)
+RETURNS TABLE
+AS
+RETURN
+ SELECT
+ 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-
+GO
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.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
diff --git a/Source/tSQLt.DropClass.ssp.sql b/Source/tSQLt.DropClass.ssp.sql
index 5c3af0989..86c8ce058 100644
--- a/Source/tSQLt.DropClass.ssp.sql
+++ b/Source/tSQLt.DropClass.ssp.sql
@@ -8,13 +8,32 @@ BEGIN
/*SnipStart: CreateDropClassStatement.ps1*/
DECLARE @Cmd NVARCHAR(MAX);
- WITH ObjectInfo(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
+ JOIN SchemaInfo SI ON SI.SchemaId = O.schema_id
+ 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)
+ JOIN SchemaInfo SI ON SI.SchemaId = O.schema_id
+ AND O.type NOT IN ('F')
),
TypeInfo(FullName, ItemType) AS
(
@@ -22,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
(
@@ -30,25 +49,20 @@ 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
(
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.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.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_AssertNoSideEffects.ssp.sql b/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql
new file mode 100644
index 000000000..8febaac61
--- /dev/null
+++ b/Source/tSQLt.Private_AssertNoSideEffects.ssp.sql
@@ -0,0 +1,15 @@
+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 @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/Source/tSQLt.Private_CleanUp.ssp.sql b/Source/tSQLt.Private_CleanUp.ssp.sql
new file mode 100644
index 000000000..c262958d5
--- /dev/null
+++ b/Source/tSQLt.Private_CleanUp.ssp.sql
@@ -0,0 +1,27 @@
+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),
+ @Result NVARCHAR(MAX) OUTPUT,
+ @ErrorMsg NVARCHAR(MAX) OUTPUT
+AS
+BEGIN
+
+ EXEC tSQLt.Private_CleanUpCmdHandler
+ @CleanUpCmd = 'EXEC tSQLt.Private_NoTransactionHandleTables @Action=''Reset'';',
+ @TestResult = @Result OUT,
+ @TestMsg = @ErrorMsg OUT,
+ @ResultInCaseOfError = 'FATAL';
+
+ EXEC tSQLt.Private_CleanUpCmdHandler
+ @CleanUpCmd = 'EXEC tSQLt.UndoTestDoubles @Force = 0;',
+ @TestResult = @Result OUT,
+ @TestMsg = @ErrorMsg OUT,
+ @ResultInCaseOfError = 'Abort';
+
+END;
+GO
+---Build-
+GO
diff --git a/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql
new file mode 100644
index 000000000..e8fdd04c0
--- /dev/null
+++ b/Source/tSQLt.Private_CleanUpCmdHandler.ssp.sql
@@ -0,0 +1,19 @@
+IF OBJECT_ID('tSQLt.Private_CleanUpCmdHandler') IS NOT NULL DROP PROCEDURE tSQLt.Private_CleanUpCmdHandler;
+GO
+---Build+
+CREATE PROCEDURE tSQLt.Private_CleanUpCmdHandler
+ @CleanUpCmd NVARCHAR(MAX),
+ @TestResult NVARCHAR(MAX) OUTPUT,
+ @TestMsg NVARCHAR(MAX) OUTPUT,
+ @ResultInCaseOfError NVARCHAR(MAX) = 'Error'
+AS
+BEGIN
+ BEGIN TRY
+ EXEC(@CleanUpCmd);
+ END TRY
+ BEGIN CATCH
+ 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_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_CreateResultTableForCompareTables.ssp.sql b/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql
index 17055733f..5f629bab2 100644
--- a/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql
+++ b/Source/tSQLt.Private_CreateResultTableForCompareTables.ssp.sql
@@ -10,11 +10,10 @@ 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);
+ EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = @ResultTable, @ObjectType = N'TABLE';
END
GO
---Build-
diff --git a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql
index b0b17d4bc..6176baf30 100644
--- a/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql
+++ b/Source/tSQLt.Private_GenerateCreateProcedureSpyStatement.ssp.sql
@@ -4,28 +4,28 @@ GO
CREATE PROCEDURE tSQLt.Private_GenerateCreateProcedureSpyStatement
@ProcedureObjectId INT,
@OriginalProcedureName NVARCHAR(MAX),
+ @UnquotedNewNameOfProcedure NVARCHAR(MAX) = NULL,
@LogTableName NVARCHAR(MAX),
@CommandToExecute NVARCHAR(MAX),
+ @CallOriginal BIT,
@CreateProcedureStatement NVARCHAR(MAX) OUTPUT,
@CreateLogTableStatement NVARCHAR(MAX) OUTPUT
AS
BEGIN
- DECLARE @ProcParmList 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 @SeparatorWithoutCursor CHAR(1) = '';
+ DECLARE @SeparatorWithCursor 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 = '',
- @ProcParmList = '', @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
@@ -39,13 +39,13 @@ BEGIN
BEGIN
IF @IsCursorRef = 0
BEGIN
- SELECT @ProcParmList = @ProcParmList + @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,
- @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 +60,21 @@ 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 AND @IsCursorRef <> 1
+ THEN ' OUT'
+ ELSE ''
+ END;
+ SELECT @SeparatorWithCursor = ',';
+
FETCH NEXT FROM Parameters INTO @ParamName, @TypeName, @IsOutput, @IsCursorRef, @IsTableType;
END;
@@ -78,7 +84,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,9 +93,16 @@ BEGIN
'CREATE PROCEDURE ' + @OriginalProcedureName + ' ' + @ProcParmTypeList +
' AS BEGIN ' +
ISNULL(@InsertStmt,'') +
+ 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 @SpyProcedureOriginalObjectName ' + @ProcParmListForCall + ';'
+ ELSE ''
+ END +
' RETURN;' +
' END;';
+ --RAISERROR(@CreateProcedureStatement, 0, 1) WITH NOWAIT;
RETURN;
END;
diff --git a/Source/tSQLt.Private_GetDropItemCmd.sfn.sql b/Source/tSQLt.Private_GetDropItemCmd.sfn.sql
index 7148c9929..bd6d6ef8d 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 QUOTENAME(SCHEMA_NAME(schema_id))+'.'+QUOTENAME(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 QUOTENAME(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_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/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql
new file mode 100644
index 000000000..2379fa0e5
--- /dev/null
+++ b/Source/tSQLt.Private_HandleMessageAndResult.sfn.sql
@@ -0,0 +1,22 @@
+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 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+
+ 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_Init.ssp.sql b/Source/tSQLt.Private_Init.ssp.sql
index ddf1efb2c..4190cc741 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
@@ -19,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_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/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/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql
new file mode 100644
index 000000000..2eac91c4b
--- /dev/null
+++ b/Source/tSQLt.Private_NoTransactionHandleTable.ssp.sql
@@ -0,0 +1,104 @@
+IF OBJECT_ID('tSQLt.Private_NoTransactionHandleTable') IS NOT NULL DROP PROCEDURE tSQLt.Private_NoTransactionHandleTable;
+GO
+---Build+
+GO
+CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTable
+@Action NVARCHAR(MAX),
+@FullTableName NVARCHAR(MAX),
+@TableAction NVARCHAR(MAX)
+AS
+BEGIN
+ DECLARE @cmd NVARCHAR(MAX);
+ BEGIN TRY
+ IF (OBJECT_ID(@FullTableName) IS NULL AND @TableAction <> 'Hide')
+ BEGIN
+ RAISERROR('Table %s does not exist.',16,10,@FullTableName);
+ END;
+ IF (@Action = 'Save')
+ BEGIN
+ IF (@TableAction = 'Restore')
+ BEGIN
+ 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 = 'Hide')
+ BEGIN
+ 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
+ RETURN;
+ END;
+ ELSE
+ BEGIN
+ RAISERROR('Invalid @TableAction parameter value.',16,10);
+ END;
+ END;
+ ELSE IF (@Action = 'Reset')
+ BEGIN
+ IF (@TableAction = 'Restore')
+ BEGIN
+ 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 +'(';
+ 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)+';';
+ EXEC(@cmd);
+ END;
+ COMMIT;
+ END;
+ ELSE IF (@TableAction = 'Truncate')
+ BEGIN
+ EXEC('DELETE FROM ' + @FullTableName +';');
+ END;
+ ELSE IF (@TableAction IN ('Ignore','Hide'))
+ BEGIN
+ /* Hidden tables will be restored by UndoTestDoubles. */
+ RETURN;
+ END;
+ ELSE
+ BEGIN
+ RAISERROR('Invalid @TableAction parameter value.', 16, 10);
+ END;
+ END;
+ ELSE
+ BEGIN
+ RAISERROR('Invalid @Action parameter value.',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
+--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--XX--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_NoTransactionHandleTables.ssp.sql b/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql
new file mode 100644
index 000000000..87a8af3c9
--- /dev/null
+++ b/Source/tSQLt.Private_NoTransactionHandleTables.ssp.sql
@@ -0,0 +1,17 @@
+IF OBJECT_ID('tSQLt.Private_NoTransactionHandleTables') IS NOT NULL DROP PROCEDURE tSQLt.Private_NoTransactionHandleTables;
+GO
+---Build+
+GO
+CREATE PROCEDURE tSQLt.Private_NoTransactionHandleTables
+ @Action NVARCHAR(MAX)
+AS
+BEGIN
+ DECLARE @cmd NVARCHAR(MAX) = (
+ SELECT 'EXEC tSQLt.Private_NoTransactionHandleTable @Action = '''+@Action+''', @FullTableName = '''+X.Name+''', @TableAction = '''+X.Action+''';'
+ FROM tSQLt.Private_NoTransactionTableAction X
+ WHERE X.Action <> 'Ignore'
+ FOR XML PATH(''),TYPE
+ ).value('.','NVARCHAR(MAX)');
+ EXEC(@cmd);
+END;
+GO
\ No newline at end of file
diff --git a/Source/tSQLt.Private_NoTransactionTableAction.view.sql b/Source/tSQLt.Private_NoTransactionTableAction.view.sql
new file mode 100644
index 000000000..3b80a0673
--- /dev/null
+++ b/Source/tSQLt.Private_NoTransactionTableAction.view.sql
@@ -0,0 +1,18 @@
+IF OBJECT_ID('tSQLt.Private_NoTransactionTableAction') IS NOT NULL DROP VIEW tSQLt.Private_NoTransactionTableAction;
+GO
+---Build+
+GO
+CREATE VIEW tSQLt.Private_NoTransactionTableAction
+AS
+SELECT CAST(Name AS NVARCHAR(MAX)) Name, CAST(Action AS NVARCHAR(MAX)) Action
+ FROM(
+ VALUES('[tSQLt].[Private_NewTestClassList]','Hide'),
+ ('[tSQLt].[Run_LastExecution]','Hide'),
+ ('[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_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/Source/tSQLt.Private_RenameObject.ssp.sql b/Source/tSQLt.Private_RenameObject.ssp.sql
index e464b7a52..7dd62db87 100644
--- a/Source/tSQLt.Private_RenameObject.ssp.sql
+++ b/Source/tSQLt.Private_RenameObject.ssp.sql
@@ -9,8 +9,8 @@ AS
BEGIN
DECLARE @RenameCmd NVARCHAR(MAX);
SET @RenameCmd = 'EXEC sp_rename ''' +
- @SchemaName + '.' + @ObjectName + ''', ''' +
- @NewName + ''',''OBJECT'';';
+ REPLACE(@SchemaName + '.' + @ObjectName, '''', '''''') + ''', ''' +
+ REPLACE(@NewName, '''', '''''') + ''',''OBJECT'';';
EXEC tSQLt.SuppressOutput @RenameCmd;
END;
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.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/Source/tSQLt.Private_Seize.tbl.sql b/Source/tSQLt.Private_Seize.tbl.sql
new file mode 100644
index 000000000..eb17bb3d7
--- /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 [Private_Seize:PK] PRIMARY KEY CONSTRAINT [Private_Seize:CHK] CHECK(Kaput=1)
+);
+GO
+CREATE TABLE tSQLt.Private_Seize_NoTruncate(
+ 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
+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/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 4740b47f9..ea05ed842 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,23 +18,30 @@ BEGIN
DECLARE @CreateProcedureStatement NVARCHAR(MAX);
DECLARE @CreateLogTableStatement NVARCHAR(MAX);
+ 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 @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(@CreateLogTableStatement);
+ EXEC tSQLt.Private_RenameObjectToUniqueNameUsingObjectId @ProcedureObjectId, @NewName = @NewNameOfOriginalObject OUTPUT;
EXEC(@CreateProcedureStatement);
EXEC tSQLt.Private_MarktSQLtTempObject @ProcedureName, N'PROCEDURE', @NewNameOfOriginalObject;
+ EXEC tSQLt.Private_MarktSQLtTempObject @LogTableName, N'TABLE', NULL;
RETURN 0;
END;
diff --git a/Source/tSQLt.UndoTestDoubles.ssp.sql b/Source/tSQLt.UndoTestDoubles.ssp.sql
index 050e7c84e..59a7bb63a 100644
--- a/Source/tSQLt.UndoTestDoubles.ssp.sql
+++ b/Source/tSQLt.UndoTestDoubles.ssp.sql
@@ -7,17 +7,12 @@ CREATE PROCEDURE tSQLt.UndoTestDoubles
AS
BEGIN
DECLARE @cmd NVARCHAR(MAX);
+ DECLARE @ErrorMessageTableList 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;
- SELECT @cmd = REPLACE(@cmd,'%s',Collisions.List)
+ /*-- Two non-temp objects, the first of which should be renamed to the second --*/
+ 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
@@ -39,7 +34,92 @@ BEGIN
).value('.','NVARCHAR(MAX)'),
1,2,'')
) Collisions(List)
- EXEC(@cmd);
+
+ /*-- Attempting to rename two or more non-temp objects to the same name --*/
+
+ IF(EXISTS(
+ SELECT O.schema_id, 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 O.schema_id, ROL.OriginalName
+ HAVING COUNT(1)>1
+ ))
+ BEGIN
+ WITH S AS(
+ SELECT
+ C.Id,
+ C.OriginalName,
+ C.CurrentName,
+ C.SchemaName
+ FROM(
+ 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
+ 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
+ ),
+ ErrorTableLists AS(
+ 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 (
+ STUFF(
+ (
+ 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)'),
+ 1,2,'')
+ ) CList
+ )C
+ )
+ 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);
+ 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;
+
+
+
SELECT TOP(0)A.* INTO #RenamedObjects FROM tSQLt.Private_RenamedObjectLog A RIGHT JOIN tSQLt.Private_RenamedObjectLog X ON 1=0;
@@ -49,6 +129,57 @@ 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)');
+ 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 O.schema_id, 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
@@ -98,31 +229,9 @@ 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)')
+ ).value('.','NVARCHAR(MAX)');
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/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/tSQLt.class.sql b/Source/tSQLt.class.sql
index 3e6b92874..cbd5533f3 100644
--- a/Source/tSQLt.class.sql
+++ b/Source/tSQLt.class.sql
@@ -1,29 +1,23 @@
---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)),
- 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(),
TestEndTime DATETIME2 NULL
);
GO
-CREATE TABLE tSQLt.TestMessage(
- Msg NVARCHAR(MAX)
-);
-GO
CREATE TABLE tSQLt.Run_LastExecution(
TestName NVARCHAR(MAX),
SessionId INT,
LoginTime DATETIME
);
GO
-CREATE TABLE tSQLt.Private_ExpectException(i INT);
-GO
CREATE PROCEDURE tSQLt.Private_Print
@Message NVARCHAR(MAX),
@Severity INT = 0
@@ -127,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)
@@ -182,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/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/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.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/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/AnnotationNoTransactionTests.class.sql b/Tests/AnnotationNoTransactionTests.class.sql
new file mode 100644
index 000000000..798b7748b
--- /dev/null
+++ b/Tests/AnnotationNoTransactionTests.class.sql
@@ -0,0 +1,1581 @@
+EXEC tSQLt.NewTestClass 'AnnotationNoTransactionTests';
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test runs test without transaction]
+AS
+BEGIN
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+--[@'+'tSQLt:NoTransaction](DEFAULT)
+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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test transaction name is NULL in TestResults table]
+AS
+BEGIN
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+--[@'+'tSQLt:NoTransaction](DEFAULT)
+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
+/*-----------------------------------------------------------------------------------------------*/
+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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test succeeding test gets correct entry in TestResults table]
+AS
+BEGIN
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+--[@'+'tSQLt:NoTransaction](DEFAULT)
+CREATE PROCEDURE MyInnerTests.[test should execute outside of transaction] AS RETURN;
+ ');
+
+ EXEC tSQLt.Run 'MyInnerTests.[test should execute outside of transaction]';
+ 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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test failing test gets correct entry in TestResults table]
+AS
+BEGIN
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+--[@'+'tSQLt:NoTransaction](DEFAULT)
+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 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('Failure','Some Obscure Reason');
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+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)
+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','42424242:Some Obscure Recoverable Error');
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is executed]
+AS
+BEGIN
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+--[@'+'tSQLt:NoTransaction](DEFAULT)
+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;
+
+ 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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if test is not annotated and passing]
+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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if test is 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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is not called if test is 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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp error message is appended to tSQLt.TestResult.Msg]
+AS
+BEGIN
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+--[@'+'tSQLt:NoTransaction](DEFAULT)
+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);
+
+ EXEC tSQLt.AssertLike @ExpectedPattern = '% ', @Actual = @Actual;
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt.Private_CleanUp is called before the test result message is printed]
+AS
+BEGIN
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+--[@'+'tSQLt:NoTransaction](DEFAULT)
+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]';
+
+ DECLARE @Actual NVARCHAR(MAX) = (SELECT Message FROM tSQLt.Private_Print_SpyProcedureLog WHERE Message LIKE '%%');
+
+ 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](DEFAULT)
+ CREATE PROCEDURE MyInnerTests.[test1]
+ AS
+ BEGIN
+ INSERT INTO #Actual SELECT Action FROM tSQLt.Private_NoTransactionHandleTables_SpyProcedureLog;
+ 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]';
+
+ 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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test using SkipTest and NoTransaction annotation skips the test]
+AS
+BEGIN
+ CREATE TABLE #SkippedTestExecutionLog (Id INT);
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+ --[@'+'tSQLt:NoTransaction](DEFAULT)
+ --[@'+'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
+/*-----------------------------------------------------------------------------------------------*/
+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](DEFAULT)
+ --[@'+'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
+/*-----------------------------------------------------------------------------------------------*/
+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
+RETURN
+ SELECT @TestName TestName
+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
+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 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 ('CREATE SCHEMA MyInnerTests AUTHORIZATION [tSQLt.TestClass];');
+ EXEC('
+--[@'+'tSQLt:NoTransaction](DEFAULT)
+CREATE PROCEDURE MyInnerTests.[test should cause unrecoverable error] AS PRINT CAST(''Some obscure string'' AS INT);
+ ');
+
+ /*******************************************************************************************************************************/
+ /************************* 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';
+
+ DECLARE @Actual NVARCHAR(MAX) = (SELECT ISNULL(Result,'')+'<><><>'+ISNULL(Msg,'') FROM tSQLt.TestResult WHERE Name = '[MyInnerTests].[test should cause unrecoverable error]');
+
+ EXEC tSQLt.AssertLike @ExpectedPattern = 'Error<><><>%Conversion failed when converting the varchar value ''Some obscure string'' to data type int.%', @Actual = @Actual;
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp is executed 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
+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 'MyInner''Tests'
+ EXEC('
+ CREATE PROCEDURE [MyInner''Tests].[UserClean''Up1]
+ AS
+ BEGIN
+ INSERT INTO #Actual VALUES (''UserClean''''Up1'');
+ END;
+ ');
+ EXEC('
+ --[@'+'tSQLt:NoTransaction](''[MyInner''''Tests].[UserClean''''Up1]'')
+ CREATE PROCEDURE [MyInner''Tests].[test''1]
+ AS
+ BEGIN
+ RETURN
+ END;
+ ');
+
+ CREATE TABLE #Actual (col1 NVARCHAR(MAX));
+
+
+ EXEC tSQLt.Run '[MyInner''Tests].[test''1]';
+
+ 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 annotation throws appropriate error if specified Test-CleanUp does not exist]
+AS
+BEGIN
+ EXEC tSQLt.NewTestClass 'MyInnerTests'
+ EXEC('
+ --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[UserCleanUpDoesNotExist]'')
+ CREATE PROCEDURE MyInnerTests.[test1]
+ AS
+ BEGIN
+ RETURN
+ END;
+ ');
+
+
+ EXEC tSQLt.Run 'MyInnerTests.[test1]', @TestResultFormatter = 'tSQLt.NullTestResultFormatter';
+
+ 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 annotation throws appropriate error if specified Test-CleanUp 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;
+ ');
+
+ 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 annotation does not throw error if specified Test-CleanUp 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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test tSQLt executes multiple Test-CleanUp 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 Schema-CleanUp is executed 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 Schema-CleanUp is executed 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 Schema-CleanUp is executed 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 Schema-CleanUp is executed 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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Schema-CleanUp error causes test result to be 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 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.SpyProcedure @ProcedureName = 'tSQLt.Private_CleanUp';
+ EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_NoTransactionHandleTables';
+ 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.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:Error during clean up: (42134213)','Error');
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+
+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
+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 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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp appends all individual error messages]
+AS
+BEGIN
+ 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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes failing test to be set to Error]
+AS
+BEGIN
+ 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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error causes passing test to be set to Error]
+AS
+BEGIN
+ 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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Test-CleanUp error and test error still results in Error]
+AS
+BEGIN
+ 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
+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.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.AssertLike @ExpectedPattern = 'Error during clean up: (%This is another error ;)%Procedure: %)', @Actual = @FriendlyMsg;
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp error stops execution of all subsequent tests]
+AS
+BEGIN
+ 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 = 'SET @Result = ''Abort''; SET @ErrorMsg = ''Error in Private_CleanUp'';';
+ EXEC tSQLt.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects';
+ 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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Private_CleanUp @Result OUTPUT gets written to tSQLt.TestResult before tSQLt stops]
+AS
+BEGIN
+ 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.SpyProcedure @ProcedureName = 'tSQLt.Private_AssertNoSideEffects';
+
+ 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
+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('
+ --[@'+'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](''[MyInnerTests].[Test-CleanUp1]'')
+ --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp2]'')
+ --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp3]'')
+ 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%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.Test-Cleanup(s)';
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+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].[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.CleanUpA'')
+ --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUp'')
+ --[@'+'tSQLt:NoTransaction](''MyInnerTests.CleanUpB'')
+ CREATE PROCEDURE [MyInnerTests].[test1]
+ AS
+ BEGIN
+ RETURN;
+ END;
+ ');
+
+ 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,'CleanUpA'),(2,'CleanUp'),(3,'CleanUpB'),(4,'CleanUp');
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+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].[Test-CleanUp1] AS BEGIN INSERT INTO #Actual DEFAULT VALUES; END;');
+ EXEC('
+ --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp1]'')
+ --[@'+'tSQLt:NoTransaction](''[MyInnerTests].[Test-CleanUp1]'')
+ --[@'+'tSQLt:NoTransaction](''MyInnerTests.[Test-CleanUp1]'')
+ 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),(1),(1);
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+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
+ 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
+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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Cleanup is executed after the outer-most test execution try catch and before writing to TestResult]
+AS
+BEGIN
+ 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 there are new/missing/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
+CREATE PROCEDURE AnnotationNoTransactionTests.[test Private-CleanUp when @Result = FATAL, it is not overwritten by another Result]
+AS
+BEGIN
+ 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;
+ 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);
+
+ EXEC tSQLt.AssertEqualsString @Expected = 'FATAL', @Actual = @Actual;
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+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.NewTestClass 'MyInnerTests'
+ EXEC('
+ --[@'+'tSQLt:NoTransaction](DEFAULT)
+ --[@'+'tSQLt:SkipTest](DEFAULT)
+ CREATE PROCEDURE MyInnerTests.[test1]
+ AS
+ BEGIN
+ RETURN;
+ END;
+ ');
+
+ 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 --<--*/
+
+ 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
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/AssertEqualsTableSchemaTests.class.sql b/Tests/AssertEqualsTableSchemaTests.class.sql
index 71d79aac5..4215285c3 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 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
+ 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/AssertEqualsTableTests.class.sql b/Tests/AssertEqualsTableTests.class.sql
index daff47a8f..08fb5b046 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
@@ -785,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
+
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/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';
diff --git a/Tests/DropClassTests.class.sql b/Tests/DropClassTests.class.sql
index a397a6c5e..4d4005976 100644
--- a/Tests/DropClassTests.class.sql
+++ b/Tests/DropClassTests.class.sql
@@ -249,5 +249,88 @@ 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);');
+
+ 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
+/*-----------------------------------------------------------------------------------------------*/
+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);');
+
+ 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 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/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/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/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/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/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_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/Private_CleanUpCmdHandlerTests.class.sql b/Tests/Private_CleanUpCmdHandlerTests.class.sql
new file mode 100644
index 000000000..a2caa9613
--- /dev/null
+++ b/Tests/Private_CleanUpCmdHandlerTests.class.sql
@@ -0,0 +1,44 @@
+EXEC tSQLt.NewTestClass 'Private_CleanUpCmdHandlerTests';
+GO
+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.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_CleanUpTests.class.sql b/Tests/Private_CleanUpTests.class.sql
new file mode 100644
index 000000000..2a07b968e
--- /dev/null
+++ b/Tests/Private_CleanUpTests.class.sql
@@ -0,0 +1,123 @@
+EXEC tSQLt.NewTestClass 'Private_CleanUpTests';
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+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, @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;
+ INSERT INTO #Expected VALUES(1,0);
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+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, @Result = NULL, @ErrorMsg = NULL;
+
+ 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, '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 OUT;
+
+ EXEC tSQLt.AssertLike @ExpectedPattern = 'previous error%some cleanup error%', @Actual = @ErrorMsg;
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE Private_CleanUpTests.[test UndoTestDoubles error causes @Result to be set to Abort]
+AS
+BEGIN
+
+ 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 = 'Abort', @Actual = @Result;
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE Private_CleanUpTests.[test HandleTables error is appended to @ErrorMsg]
+AS
+BEGIN
+
+ 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
+CREATE PROCEDURE Private_CleanUpTests.[test HandleTables error causes @Result to be set to FATAL]
+AS
+BEGIN
+
+ 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
+/*-----------------------------------------------------------------------------------------------*/
+GO
diff --git a/Tests/Private_GetFormattedErrorInfoTests.class.sql b/Tests/Private_GetFormattedErrorInfoTests.class.sql
new file mode 100644
index 000000000..c57fc1929
--- /dev/null
+++ b/Tests/Private_GetFormattedErrorInfoTests.class.sql
@@ -0,0 +1,90 @@
+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 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
+ /*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
new file mode 100644
index 000000000..ca0bef266
--- /dev/null
+++ b/Tests/Private_HandleMessageAndResultTests.class.sql
@@ -0,0 +1,174 @@
+EXEC tSQLt.NewTestClass 'Private_HandleMessageAndResultTests';
+GO
+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 ('another random message', NULL, NULL, DEFAULT));
+
+ EXEC tSQLt.AssertEqualsString @Expected = 'another random message [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 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 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 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 only the @NewMessage if @PrevMessage is NULL and @PrevResult is Success]
+AS
+BEGIN
+ 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 returns @NewResult if @PrevResult is NULL]
+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, NULL, DEFAULT, 'SomeNewResult'));
+ EXEC tSQLt.AssertEqualsString @Expected = 'SomeNewResult', @Actual = @Result;
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+
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_MarktSQLtTempObjectTests.class.sql b/Tests/Private_MarktSQLtTempObjectTests.class.sql
index 205caa035..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;
@@ -110,6 +111,103 @@ 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
+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
\ No newline at end of file
+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
+/*-----------------------------------------------------------------------------------------------*/
+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/Private_NoTransactionHandleTableTests.class.sql b/Tests/Private_NoTransactionHandleTableTests.class.sql
new file mode 100644
index 000000000..5030081cb
--- /dev/null
+++ b/Tests/Private_NoTransactionHandleTableTests.class.sql
@@ -0,0 +1,659 @@
+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 @ExpectedMessagePattern = '%Invalid @Action parameter value.%', @ExpectedSeverity = 16, @ExpectedState = 10;
+
+ 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
+ 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
+/*-----------------------------------------------------------------------------------------------*/
+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
+/*-----------------------------------------------------------------------------------------------*/
+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
+/*-----------------------------------------------------------------------------------------------*/
+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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+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 = '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;
+ INSERT INTO #Expected
+ 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 Hide]
+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 = 'Hide';
+
+ 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
+/*-----------------------------------------------------------------------------------------------*/
+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 @ExpectedMessagePattern = '%Invalid @TableAction parameter value.%', @ExpectedSeverity = 16, @ExpectedState = 10;
+
+ EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.SomeTable', @TableAction = 'Unacceptable';
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+--[@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);
+ 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 = 'Hide';
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test errors if the table does not exist]
+AS
+BEGIN
+
+ 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;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test can restore table with computed column]
+AS
+BEGIN
+ 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 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');
+
+ 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;
+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 Hide]
+AS
+BEGIN
+ CREATE TABLE Private_NoTransactionHandleTableTests.Table1 (Id INT, col1 NVARCHAR(MAX));
+ EXEC tSQLt.Private_NoTransactionHandleTable @Action = 'Save', @FullTableName = 'Private_NoTransactionHandleTableTests.Table1', @TableAction = 'Hide';
+
+ 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 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
+ 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
+CREATE PROCEDURE Private_NoTransactionHandleTableTests.[test if @TableAction is Hide, @Action Save, Save: do nothing on second save]
+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 = 'Hide';
+
+ 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 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');
+
+ 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';
+ 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
+
diff --git a/Tests/Private_NoTransactionHandleTablesTests.class.sql b/Tests/Private_NoTransactionHandleTablesTests.class.sql
new file mode 100644
index 000000000..977e8b348
--- /dev/null
+++ b/Tests/Private_NoTransactionHandleTablesTests.class.sql
@@ -0,0 +1,152 @@
+EXEC tSQLt.NewTestClass 'Private_NoTransactionHandleTablesTests';
+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_NoTransactionHandleTable';
+ EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction';
+
+ EXEC tSQLt.Private_NoTransactionHandleTables @Action='Reset';
+
+ EXEC tSQLt.AssertEmptyTable @TableName = 'tSQLt.Private_NoTransactionHandleTable_SpyProcedureLog';
+
+END;
+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_NoTransactionHandleTable';
+ EXEC tSQLt.FakeTable @TableName = 'tSQLt.Private_NoTransactionTableAction';
+ EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl1'',''Restore'');')
+
+ 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');
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+END;
+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_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='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'),('tbl2'),('tbl3');
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+END;
+GO
+CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test does not call tSQLt.Private_NoTransactionHandleTable for 'Ignore' tables]
+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'',''Ignore'');')
+ EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl3'',''Restore'');')
+ EXEC('INSERT INTO tSQLt.Private_NoTransactionTableAction VALUES(''tbl4'',''Other'');')
+
+ 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'),('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');
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+END;
+GO
+CREATE PROCEDURE Private_NoTransactionHandleTablesTests.[test is rerunnable (though it still needs some help from UndoTestDoubles)]
+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.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
+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
diff --git a/Tests/Private_NoTransactionTableActionTests.class.sql b/Tests/Private_NoTransactionTableActionTests.class.sql
new file mode 100644
index 000000000..35850d49a
--- /dev/null
+++ b/Tests/Private_NoTransactionTableActionTests.class.sql
@@ -0,0 +1,33 @@
+EXEC tSQLt.NewTestClass 'Private_NoTransactionTableActionTests';
+GO
+CREATE PROCEDURE Private_NoTransactionTableActionTests.[test contains all tSQLt tables]
+AS
+BEGIN
+ 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
+ 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_NoTransactionTableActionTests.[test has the correct actions for all tSQLt tables]
+AS
+BEGIN
+ 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
+ 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
+ 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;
+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
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
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/Private_SeizeTests.class.sql b/Tests/Private_SeizeTests.class.sql
new file mode 100644
index 000000000..ae0f18982
--- /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 = '%"Private_Seize:CHK"%';
+ 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/Run_Methods_Tests.class.sql b/Tests/Run_Methods_Tests.class.sql
index 8022d5b06..cab6f411a 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
@@ -1450,29 +1458,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 +1479,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 +2220,284 @@ 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
+/* ----------------------------------------------------------------------------------------------*/
+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](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';
+ EXEC('INSERT INTO tSQLt.TestClasses VALUES(''a class with a '''' in the middle'',12321);');
+
+ EXEC tSQLt.SetSummaryError 0;
+
+ 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](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.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](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.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](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.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](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';
+
+ 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.SetSummaryError 0;
+
+ 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
+/*-----------------------------------------------------------------------------------------------*/
+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: 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
+ 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: 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;
+ ');
+
+ EXEC tSQLt.ExpectException @ExpectedMessage = 'SOMETHING RATHER', @ExpectedSeverity = NULL, @ExpectedState = NULL;
+ EXEC tSQLt.Run 'MyInnerTestsB.[test should execute outside of transaction]';
+
+ -- 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 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.)
+
+ - 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?
+
+--*/
diff --git a/Tests/SpyProcedureTests.class.sql b/Tests/SpyProcedureTests.class.sql
index 12d830d50..d3e138cc5 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
@@ -682,9 +706,10 @@ 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,
@CreateProcedureStatement = @CreateProcedureStatement OUT,
@CreateLogTableStatement = @CreateLogTableStatement OUT;
@@ -695,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');
@@ -704,9 +729,10 @@ 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,
@CreateProcedureStatement = @CreateProcedureStatement OUT,
@CreateLogTableStatement = @CreateLogTableStatement OUT;
@@ -723,7 +749,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;
@@ -755,12 +782,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 +796,355 @@ 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
+/*-----------------------------------------------------------------------------------------------*/
+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
+/*-----------------------------------------------------------------------------------------------*/
+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
+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 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
+ 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
+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 @FirstInt INT;
+ DECLARE @SecondInt INT;
+ OPEN @Cursor1;
+ FETCH NEXT FROM @Cursor1 INTO @FirstInt;
+ CLOSE @Cursor1;
+ DEALLOCATE @Cursor1;
+ OPEN @Cursor2;
+ FETCH NEXT FROM @Cursor2 INTO @SecondInt;
+ CLOSE @Cursor2;
+ 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; SET @Cursor11 = CURSOR FOR SELECT 36;
+ DECLARE @Cursor22 CURSOR; SET @Cursor22 = CURSOR FOR SELECT 42;
+ DECLARE @ProcedureNameVariableSoWeDoNotGetAWarning NVARCHAR(MAX) = 'SpyProcedureTests.TempProcedure1';
+
+ EXEC @ProcedureNameVariableSoWeDoNotGetAWarning @Cursor11, @InputOnlyInt, @Cursor22;
+
+ SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0;
+ INSERT INTO #Expected VALUES(36, 42, 17);
EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+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
+ 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);
+
+ EXEC tSQLt.SpyProcedure @ProcedureName = 'SpyProcedureTests.TempProcedure1', @CallOriginal = 1;
+
+ EXEC('
+ DECLARE @InputOnlyInt INT = 17;
+ 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'';
+ 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(36, 42, 17);
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+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 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;'
+ );
+
+ CREATE TABLE #Actual (Msg NVARCHAR(MAX), I INT);
+
+ EXEC tSQLt.SpyProcedure @ProcedureName = '[I''nn''er Test.Schema 1].[T''emp Proc.edure 1]', @CommandToExecute='EXEC @SpyProcedureOriginalObjectName 42;';
+
+
+ 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('[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;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj
index c3a19eb54..66968e619 100644
--- a/Tests/Tests.ssmssqlproj
+++ b/Tests/Tests.ssmssqlproj
@@ -2,16 +2,26 @@
-
+
+
+
+
+
+
+ _ExploratoryTests.class.sql
+
AnnotationHostPlatformTests.class.sql
+
+ AnnotationNoTransactionTests.class.sql
+
@@ -132,12 +142,6 @@
ExpectNoExceptionTests.class.sql
-
-
-
-
- ExploratoryTests.class.sql
-
@@ -180,6 +184,21 @@
NewTestClassTests.class.sql
+
+ Private_AssertNoSideEffectsTests.class.sql
+
+
+
+
+
+ Private_CleanUpCmdHandlerTests.class.sql
+
+
+
+
+
+ Private_CleanUpTests.class.sql
+
@@ -192,6 +211,12 @@
Private_GetAnnotationListTests.class.sql
+
+
+
+
+ Private_GetFormattedErrorInfoTests.class.sql
+
@@ -204,6 +229,12 @@
Private_GetSQLProductMajorVersionTests.class.sql
+
+
+
+
+ Private_HandleMessageAndResultTests.class.sql
+
@@ -222,11 +253,23 @@
Private_MarktSQLtTempObjectTests.class.sql
-
+
+
+
+
+ Private_NoTransactionHandleTableTests.class.sql
+
+
+
+
+
+ Private_NoTransactionTableActionTests.class.sql
+
+
- Private_NullCellTableTests.class.sql
+ Private_NoTransactionHandleTablesTests.class.sql
@@ -258,12 +301,24 @@
Private_ResetNewTestClassListTests.class.sql
+
+
+
+
+ Private_ResultsTests.class.sql
+
Private_ScriptIndexTests.class.sql
+
+
+
+
+ Private_SeizeTests.class.sql
+
@@ -307,9 +362,6 @@
ResultSetFilterTests.class.sql
-
-
-
Run_Methods_Tests.class.sql
diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql
index 81cdac500..8040ac3d3 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;
@@ -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;
@@ -481,12 +485,14 @@ 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';
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
@@ -519,7 +525,37 @@ 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;
+
+ CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT);
+ 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
+ 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 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
@@ -527,8 +563,12 @@ BEGIN
FROM sys.objects O;
CREATE TABLE UndoTestDoublesTests.SimpleTable1 (i INT);
+ CREATE TABLE UndoTestDoublesTests.SimpleTable2 (i INT);
+ CREATE TABLE UndoTestDoublesTests.SimpleTable3 (i INT);
EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = 'UndoTestDoublesTests.SimpleTable1', @ObjectType = 'TABLE', @NewNameOfOriginalObject = NULL;
+ EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = 'UndoTestDoublesTests.SimpleTable2', @ObjectType = 'TABLE', @NewNameOfOriginalObject = NULL;
+ EXEC tSQLt.Private_MarktSQLtTempObject @ObjectName = 'UndoTestDoublesTests.SimpleTable3', @ObjectType = 'TABLE', @NewNameOfOriginalObject = NULL;
EXEC tSQLt.UndoTestDoubles;
@@ -546,21 +586,222 @@ 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 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;
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.SimpleTable1';
+ EXEC tSQLt.RemoveObject @ObjectName = 'UndoTestDoublesTests.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
+/** ------------------------------------------------------------------------------------------- **/
+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 @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
+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
+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;');
+
+ 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 can handle the kitchen sink]
+AS
+BEGIN
+ 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
@@ -575,5 +816,9 @@ BEGIN
SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A
) T;
EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty';
+
END;
GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+
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
diff --git a/Tests/_ExploratoryTests.class.sql b/Tests/_ExploratoryTests.class.sql
new file mode 100644
index 000000000..633043507
--- /dev/null
+++ b/Tests/_ExploratoryTests.class.sql
@@ -0,0 +1,366 @@
+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 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
+/*-----------------------------------------------------------------------------------------------*/
+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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+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''),NULL;');
+ DECLARE @ACursor CURSOR;
+ INSERT INTO #Actual SELECT 'Variable defined (DECLARE)', CURSOR_STATUS('variable','@ACursor'), NULL;
+ SET @ACursor = CURSOR FOR SELECT 1;
+ INSERT INTO #Actual SELECT 'Variable allocated (SET)', CURSOR_STATUS('variable','@ACursor'), NULL;
+ OPEN @ACursor;
+ 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'), NULL;
+ DEALLOCATE @ACursor;
+ 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,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,NULL),
+ ('Cursor deallocated',-2,NULL);
+
+ EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
+
+END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+CREATE PROCEDURE [_ExploratoryTests].[test CURSOR will not be passed out through OUTPUT @parameter if it has not been opened (CURSOR_STATUS = -1)]
+AS
+BEGIN
+ 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 CURSOR can be passed out through OUTPUT @parameter if it has been opened (CURSOR_STATUS = 1)]
+AS
+BEGIN
+ 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 cannot be passed to a proc with OUTPUT specified in the call 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.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 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
+/*-----------------------------------------------------------------------------------------------*/
+GO
+--CREATE PROCEDURE [_ExploratoryTests].[test TBD]
+--AS
+--BEGIN
+-- EXEC tSQLt.Fail 'TemplateTest';
+--END;
+GO
+/*-----------------------------------------------------------------------------------------------*/
+GO
+
+--EXEC tSQLt.Run [_ExploratoryTests]
\ No newline at end of file
diff --git a/Tests/tSQLt_test.class.sql b/Tests/tSQLt_test.class.sql
index 31fa05afd..5107cdb49 100644
--- a/Tests/tSQLt_test.class.sql
+++ b/Tests/tSQLt_test.class.sql
@@ -95,30 +95,60 @@ GO
CREATE PROC tSQLt_test.test_Run_handles_uncommitable_transaction
AS
+BEGIN
+ 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';
@@ -1310,7 +1340,7 @@ BEGIN
SELECT '{' + Msg + '}' AS BracedMsg
INTO #actual
- FROM tSQLt.TestMessage;
+ FROM #TestMessage;
SELECT TOP(0) *
INTO #expected
@@ -1333,7 +1363,7 @@ BEGIN
SELECT '{' + Msg + '}' AS BracedMsg
INTO #actual
- FROM tSQLt.TestMessage;
+ FROM #TestMessage;
SELECT TOP(0) *
INTO #expected
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
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 @@
+