Monday, April 9, 2018

Bubbling up SQL Script errors within Jenkins CI Server using sqlcmd -b argument

We use Jenkins Continuous Integration server at work and frankly its so dug into our work pipeline, its hard to remember how we worked without Jenkins.  I bet a lot of people say that once they become addicted to Jenkins.

One thing I wanted to do recently was: Schedule a SQL Server script to run against our dev server that would test one of our key functions (it not central to the point here but the function takes numbers and rounds them to various rounding rules).  This thing really needs to return correct numbers consistently and so we have hundreds of assertions to make sure its working.  To assert pass or fail, the end of the script selects against rows in a seed table and returns back any failed assertions.

Pseudo code:

select * from dbo.rounding_test where expected_val <> rounded_val;

Previously a human would run this script manually during our release procedures and if no rows were returned it passes, rows returned a bug is logged.  Obviously this is a perfect scenario to automate.

Problems: I could not get Jenkins to detect when a script had SQL errors and I didn't know how to inform Jenkins that there were rows in that assertion table which would indicate the function was suddenly failing testing.

There are many ways one could do this but there are some cool nuggets in here, so here'a what we did.

First I decided that I would raise a true SQL error in my SQL Server test script when rows existed in my assertion table (this indicates a fail of the test).  To do this the end of my script already has this for human eyes:

select * from dbo.rounding_test where expected_val <> rounded_val;

But I added this for Jenkins:

if exists (select * from dbo.rounding_test where expected_val <> rounded_val)
 select 1/0; -- this is designed to intentionally raise an error in Jenkins
go

So when a human runs this and its failing you will see rows and a big ugly divide by zero error.

Next, let's automate this in Jenkins so I don't have to remember to do this manually... I'll schedule it to run periodically in Jenkins and have Jenkins send me an email when it fails.

In Jenkins I create a Freestyle project and add a build step "Execute Windows Batch Command".  The command I add will be:

sqlcmd -S localhost -d mydatabase -U user -P pw -i "C:\SQL Server_tests\roundingrules.sql"

Save and run the Jenkins job.  It passes.

Now, edit rounding rules in a way so that it intentionally fails one or more of the tests (I just put a bogus value into expected_val seed data).  Then save and run the Jenkins job.  It passes.  Examine the console log and review it for errors.  Indeed when I see the console log it references a divide by zero error there but Jenkins reports SUCCESS.  To be sure edit the script again and change the table name to something that doesn't exists, such as:

... select * from dbo.FRED

Save the script and re-run the job.  The script will raise an error but Jenkins will still pass.

This is because sqlcmd is not being run so that errors bubble up.  To do this you want to change your command in Jenkins to use the -b argument and also detect the error and echo it up in your Jenkins Windows Batch Command as follows:

sqlcmd -b -S localhost -d mydatabase -U user -P pw -i "C:\SQL Server_tests\roundingrules.sql"
IF %ERRORLEVEL% NEQ 0 ECHO "Error"

Save the Jenkins job and run it.  Now, I see Jenkins is failing the job and I get an email about the build being broken.  Awesome.

Next, I correct my sql script (change the table name back to the correct one, and correct the intentional rounding failure in expected_val) and re-run the Jenkins job.  It passes.  I get an email from Jenkins saying the build is back to normal.

Now I can rely on this script running faithfully in Jenkins to inform me when this key area of our app is broken by someone, and I don't have to remember anything.  We will build upon this approach and start automating the testing of other areas as well.

Next, I have to figure out how to do the same with Oracle's sqlplus because we support Oracle as well and thus we need the same test coverage on Oracle as we do on SQL Server.

Hello there

Howdy!

Jeff here.  I'm a coder.  Hard to believe because I always thought I'd be an astronaut (silly dreams these 5 year olds have) but I stumbled into the tech field over 25 years ago and have grown to really love coding.  So, no complaints here... based on my family ancestry, I could have been in a coal mine.

Historically I code mainly on the Microsoft stack because that is what a lot of our customers tend to have in their enterprise (Windows PCs and Windows servers). As a result my focus over the past 15 years has been rattling around within ASP.NET environment (VB.NET and C#) and coding in SQL and tuning SQL engines such as SQL Server and Oracle.  These aren't exactly bleeding edge but they provide a mature framework for creating production systems running at hundreds of customer sites.  In other words, its a living.  I am an expert on none but capable on all.

In the last few years, my focus has been moving towards more modern technologies such as Javascript frameworks (jQuery, but also reviewing Angular, React, etc) and getting away from the stale tasting server-side coding model of ASP.NET.  I have also enjoyed coding some small scale Bootstrap based pages that are responsive on all clients. Cloud servers and Azure are also a part time musing.

We are also trying to fully implement Agile/XP at the workplace. We have the beginnings of a TDD mentality but we need to automate it more from the manual system we have today.  We adopted the  concept of CI using Jenkins but we need to tie in the testing into the CI pipeline better and we need to towards the goal of Continuous Delivery and up the pace a bit.  We have automated testing in place using Telerik Test Studio but we are hounded by its finicky nature and high cost and intend to dump that in favor of Katalon and Selenium.

Going forward, I'm going to blog on whatever comes to mind.  It will probably be a case study of something neato I discovered roughly in the areas described above.  But whatever the topic is, this blog will be a potpouri of coding tips, hacks and bodges.  If one person gets a benefit from this area, I guess its all worthwhile.  Worst case is I will have a place to refer back to once I forget what I have done.

When I am not coding I am traveling or walking dogs with my wife, riding bicycles or snowboards, at the dojo or settling in for the night with a malted beverage infused with lupulin.  Cheers.

Bubbling up SQL Script errors within Jenkins CI Server using sqlcmd -b argument

We use Jenkins Continuous Integration server at work and frankly its so dug into our work pipeline, its hard to remember how we worked witho...