Test Driven
           Database Development
                                               David E. Wheeler
              ...
Test Driven
           Database Development
                                                  ✘
                          ...
David E. Wheeler



                                                         OSCON 2010
                                  ...
David E. Wheeler



                                                       OSCON 2010
                                    ...
Build My VC-Funded App



                                                                        ✔
        for Me for Fre...
This is Genius
This is Genius
I had this idea
This is Genius
I had this idea

Social networking is hot
This is Genius
I had this idea

Social networking is hot

Has been for too long
This is Genius
I had this idea

Social networking is hot

Has been for too long

The backlash is overdue
This is Genius
I had this idea

Social networking is hot

Has been for too long

The backlash is overdue

Getting ahead of...
This is Genius
I had this idea

Social networking is hot

Has been for too long

The backlash is overdue

Getting ahead of...
http://flic.kr/p/8j5gG8 © 2010 Strongrrl. All rights reserved. Used with permission.
antisocial   network

http://flic.kr/p/8j5gG8 © 2010 Strongrrl. All rights reserved. Used with permission.
How it Works




antisocial   network
How it Works
                       Microblogging platform




antisocial   network
How it Works
                       Microblogging platform

                       Everyone follows you




antisocial   n...
How it Works
                       Microblogging platform

                       Everyone follows you

                 ...
How it Works
                       Microblogging platform

                       Everyone follows you

                 ...
How it Works
                       Microblogging platform

                       Everyone follows you

                 ...
How it Works
                       Microblogging platform

                       Everyone follows you

                 ...
Your Task




antisocial   network
Your Task
                       Create the database




antisocial   network
Your Task
                       Create the database

                       Use TDDD to make it right




antisocial   ne...
Your Task
                       Create the database

                       Use TDDD to make it right

                  ...
Your Task
                       Create the database

                       Use TDDD to make it right

                  ...
Your Task
                       Create the database

                       Use TDDD to make it right

                  ...
http://flic.kr/p/2honiQ © 2007 James Duncan Davidson. All rights reserved. Used with permission.
antisocial   network
TDDD
antisocial   network
                       WTF?
We’ll get there



antisocial   network
But first…



antisocial   network
NDA
antisocial   network
Okay, now that that’s out
                             of the way…



antisocial   network
Please organize into pairs



antisocial   network
Yes, that’s right



antisocial   network
You’re gonna do pair
                       database programming



antisocial   network
App developers partner
                             with DBAs



antisocial   network
I’m waiting…



antisocial   network
Okay, Ready?



antisocial   network
Time to Install



antisocial   network
Install PostgreSQL




antisocial   network
Install PostgreSQL
                       http:/
                            /www.postgresql.org/download/


             ...
Install PostgreSQL
                       http:/
                            /www.postgresql.org/download/

              ...
Install PostgreSQL
                       http:/
                            /www.postgresql.org/download/

              ...
Install PostgreSQL
                       http:/
                            /www.postgresql.org/download/

              ...
Install PostgreSQL
                       http:/
                            /www.postgresql.org/download/

              ...
Install PostgreSQL
                       http:/
                            /www.postgresql.org/download/

              ...
Install PostgreSQL
                       http:/
                            /www.postgresql.org/download/

              ...
Install PostgreSQL
                       http:/
                            /www.postgresql.org/download/

              ...
Install Test::Harness
Install Test::Harness

% cpan Test::Harness
Install Test::Harness

% cpan Test::Harness
          Or…
Install Test::Harness

% cpan Test::Harness
          Or…
% wget http://bit.ly/test-harness-321
Install Test::Harness

% cpan Test::Harness
          Or…
% wget http://bit.ly/test-harness-321
% tar zxf Test-Harness-3.2...
Install Test::Harness

% cpan Test::Harness
          Or…
% wget http://bit.ly/test-harness-321
% tar zxf Test-Harness-3.2...
Install Test::Harness

% cpan Test::Harness
            Or…
%   wget http://bit.ly/test-harness-321
%   tar zxf Test-Harne...
Install Test::Harness

% cpan Test::Harness
            Or…
%   wget http://bit.ly/test-harness-321
%   tar zxf Test-Harne...
Install Test::Harness

% cpan Test::Harness
            Or…
%   wget http://bit.ly/test-harness-321
%   tar zxf Test-Harne...
Install Test::Harness

% cpan Test::Harness
            Or…
%   wget http://bit.ly/test-harness-321
%   tar zxf Test-Harne...
Install pgTAP
Install pgTAP

% tar jxf pgtap-0.24.tar.bz2
Install pgTAP

% tar jxf pgtap-0.24.tar.bz2
% cd pgtap-0.24
Install pgTAP

% tar jxf pgtap-0.24.tar.bz2
% cd pgtap-0.24
% make TAPSCHEMA=tap Separate
                     schema
Install pgTAP

%   tar jxf pgtap-0.24.tar.bz2
%   cd pgtap-0.24
%   make TAPSCHEMA=tap
%   sudo make install
Install pgTAP

%   tar jxf pgtap-0.24.tar.bz2
%   cd pgtap-0.24
%   make TAPSCHEMA=tap
%   sudo make install
%   make inst...
Install pgTAP

%   tar jxf pgtap-0.24.tar.bz2
%   cd pgtap-0.24
%   make TAPSCHEMA=tap
%   sudo make install
%   make inst...
Install pgTAP

%   tar jxf pgtap-0.24.tar.bz2
%   cd pgtap-0.24
%   make TAPSCHEMA=tap
%   sudo make install
%   make inst...
Install pgTAP

%   tar jxf pgtap-0.24.tar.bz2
%   cd pgtap-0.24
%   make TAPSCHEMA=tap
%   sudo make install
%   make inst...
We good?



antisocial   network
First Po^^Test!



antisocial   network
pgTAP Basics




001-schema.pg
pgTAP Basics

SET search_path = public,tap;

BEGIN;

-- Plan the tests.
SELECT plan( 1 );

SELECT has_table( 'users' );

-...
pgTAP Basics

SET search_path = public,tap;

BEGIN;

-- Plan the tests.
SELECT plan( 1 );

SELECT has_table( 'users' );

-...
pgTAP Basics

SET search_path = public,tap;

BEGIN;

-- Plan the tests.
SELECT plan( 1 );

SELECT has_table( 'users' );

-...
pgTAP Basics

SET search_path = public,tap;

BEGIN;

-- Plan the tests.
SELECT plan( 1 );

SELECT has_table( 'users' );

-...
pgTAP Basics

SET search_path = public,tap;

BEGIN;

-- Plan the tests.
SELECT plan( 1 );

SELECT has_table( 'users' );

-...
pgTAP Basics

SET search_path = public,tap;

BEGIN;

-- Plan the tests.
SELECT plan( 1 );

SELECT has_table( 'users' );

-...
pgTAP Basics

SET search_path = public,tap;

BEGIN;

-- Plan the tests.
SELECT plan( 1 );

SELECT has_table( 'users' );

-...
Run the Test
Run the Test
% pg_prove -vd flipr tests/001-schema.pg
tests/001-schema.pg .. 1/1
not ok 1 - Table users should exist
# Fail...
Run the Test
% pg_prove -vd flipr tests/001-schema.pg
tests/001-schema.pg .. 1/1
not ok 1 - Table users should exist
# Fail...
Run the Test
% pg_prove -vd flipr tests/001-schema.pg
tests/001-schema.pg .. 1/1
not ok 1 - Table users should exist
# Fail...
Run the Test
% pg_prove -vd flipr tests/001-schema.pg
tests/001-schema.pg .. 1/1
not ok 1 - Table users should exist
# Fail...
Create Users Table




001-users.sql
Create Users Table

CREATE TABLE users (
   id INT
);




 001-users.sql
Create Users Table

CREATE TABLE users (
   id INT
);




                 Bare minimum
 001-users.sql
First Pass
First Pass
% psql -d flipr -f sql/001-users.sql
% pg_prove -vd flipr tests/001-schema.pg
tests/1-schema.pg ..
1..1
ok 1 - Ta...
First Pass
% psql -d flipr -f sql/001-users.sql
% pg_prove -vd flipr tests/001-schema.pg
tests/1-schema.pg ..
1..1
ok 1 - Ta...
First Pass
% psql -d flipr -f sql/001-users.sql
% pg_prove -vd flipr tests/001-schema.pg
tests/1-schema.pg ..
1..1
ok 1 - Ta...
First Pass
% psql -d flipr -f sql/001-users.sql
% pg_prove -vd flipr tests/001-schema.pg
tests/1-schema.pg ..
1..1
ok 1 - Ta...
Now…Iterate!



antisocial   network
What Columns?




antisocial   network
What Columns?
                       Nickname




antisocial   network
What Columns?
                       Nickname

                       Password




antisocial   network
What Columns?
                       Nickname

                       Password

                       Timestamp




antis...
Column Testing




002-schema.pg
Column Testing

SET search_path = public,tap;

BEGIN;
SELECT plan( 5 );

SELECT tables_are( 'public', ARRAY[ 'users' ] );
...
Column Testing

SET search_path = public,tap;

BEGIN;
SELECT plan( 5 );

SELECT tables_are( 'public', ARRAY[ 'users' ] );
...
Column Testing

SET search_path = public,tap;

BEGIN;
SELECT plan( 5 );

SELECT tables_are( 'public', ARRAY[ 'users' ] );
...
Column Testing

SET search_path = public,tap;

BEGIN;
SELECT plan( 5 );

SELECT tables_are( 'public', ARRAY[ 'users' ] );
...
Column Testing

SET search_path = public,tap;

BEGIN;
SELECT plan( 5 );

SELECT tables_are( 'public', ARRAY[ 'users' ] );
...
Column Testing

SET search_path = public,tap;

BEGIN;
SELECT plan( 5 );

SELECT tables_are( 'public', ARRAY[ 'users' ] );
...
Run ’Em
Run ’Em
% pg_prove -vd flipr tests/002-schema.pg
tests/002-schema.pg ..
1..5
ok 1 - Schema public should have the correct t...
Run ’Em
% pg_prove -vd flipr tests/002-schema.pg
tests/002-schema.pg ..
1..5
ok 1 - Schema public should have the correct t...
Add the Columns




002-users.sql
Add the Columns
DROP TABLE IF EXISTS users;
CREATE TABLE users (
   nickname TEXT      PRIMARY KEY,
   password TEXT      ...
Add the Columns
DROP TABLE IF EXISTS users;
CREATE TABLE users (
   nickname TEXT      PRIMARY KEY,
   password TEXT      ...
Run Again
Run Again
% psql -d flipr -f sql/002-users.sql
% pg_prove -vd flipr tests/002-schema.pg
tests/002-schema.pg ..
1..5
ok 1 - S...
Run Again
% psql -d flipr -f sql/002-users.sql
% pg_prove -vd flipr tests/002-schema.pg
tests/002-schema.pg ..
1..5
ok 1 - S...
003-schmea.pg
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT tables_are( 'public', ARRAY[ 'u...
Files
Files
git clone git:/
              /ec2.dagolden.com/git/oscon-tddd.git
Files
git clone git:/
              /ec2.dagolden.com/git/oscon-tddd.git

http://roadie.show.local/oscon/
Files
git clone git:/
              /ec2.dagolden.com/git/oscon-tddd.git

http://roadie.show.local/oscon/
http:/ 10.16.2/o...
Files
git clone git:/
              /ec2.dagolden.com/git/oscon-tddd.git

http://roadie.show.local/oscon/
http:/ 10.16.2/o...
Files
git clone git:/
              /ec2.dagolden.com/git/oscon-tddd.git

http://roadie.show.local/oscon/
http:/ 10.16.2/o...
Go!
Go!
% pg_prove -d flipr tests/003-schema.pg
tests/003-schema.pg .. ok
All tests successful.
Files=1, Tests=16, 0 wallclock ...
Go!
% pg_prove -d flipr tests/003-schema.pg
tests/003-schema.pg .. ok
All tests successful.
Files=1, Tests=16, 0 wallclock ...
Go!
% pg_prove -d flipr tests/003-schema.pg
tests/003-schema.pg .. ok
All tests successful.
Files=1, Tests=16, 0 wallclock ...
antisocial   network



Your Turn
antisocial   network



Your Turn
Create users table
antisocial   network



      Your Turn
       Create users table
          Project Repo:
http://github.com/theory/tddd/
So Far So Good




antisocial   network
So Far So Good
                       Appdev begins




antisocial   network
So Far So Good
                       Appdev begins

                         Web site




antisocial   network
So Far So Good
                       Appdev begins

                         Web site

                         API




a...
So Far So Good
                       Appdev begins

                         Web site

                         API

    ...
So Far So Good
                       Appdev begins

                         Web site

                         API

    ...
So Far So Good
                       Appdev begins

                         Web site

                         API

    ...
Data Inconsistencies
Data Inconsistencies
flipr=# select nickname, password from users;
 nickname |          password
-----------
+-------------...
Data Inconsistencies
flipr=# select nickname, password from users;
 nickname |          password
-----------
+-------------...
Data Inconsistencies
flipr=# select nickname, password from users;
 nickname |          password
-----------
+-------------...
Data Inconsistencies
flipr=# select nickname, password from users;
 nickname |          password
-----------
+-------------...
Data Inconsistencies
flipr=# select nickname, password from users;
 nickname |          password
-----------
+-------------...
Data Inconsistencies
flipr=# select nickname, password from users;
 nickname |          password
-----------
+-------------...
Data Inconsistencies
flipr=# select nickname, password from users;
 nickname |          password
-----------
+-------------...
What Encryption?




antisocial   network
What Encryption?
                       Multiple interfaces




antisocial   network
What Encryption?
                       Multiple interfaces

                       Data must be consistent




antisocial...
What Encryption?
                       Multiple interfaces

                       Data must be consistent

             ...
What Encryption?
                       Multiple interfaces

                       Data must be consistent

             ...
What Encryption?
                       Multiple interfaces

                       Data must be consistent

             ...
Test For New Function




004-userfunc.pg
Test For New Function
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT has_funct...
Test For New Function
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT has_funct...
Test For New Function
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT has_funct...
Test For New Function
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT has_funct...
Run ’Em
Run ’Em
% pg_prove -d flipr tests/004-userfunc.pg
tests/004-userfunc.pg .. 1/?
not ok 1 - Function ins_user() should exist
...
Run ’Em
% pg_prove -d flipr tests/004-userfunc.pg
tests/004-userfunc.pg .. 1/?
not ok 1 - Function ins_user() should exist
...
Create It!




004-userfunc.sql
Create It!

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS '';



...
Create It!

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS '';



...
…To make ’em pass
…To make ’em pass

% psql -d flipr -f sql/004-userfunc.sql
CREATE FUNCTION
%
…To make ’em pass

% psql -d flipr -f sql/004-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/004-userfunc.pg
tests/0...
…To make ’em pass

% psql -d flipr -f sql/004-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/004-userfunc.pg
tests/0...
Make sure it does
                  something.
SELECT has_function('ins_user');
SELECT has_function('ins_user', ARRAY['tex...
Make sure it does
                  something.
SELECT has_function('ins_user');
SELECT has_function('ins_user', ARRAY['tex...
Make sure it does
                  something.
SELECT has_function('ins_user');
SELECT has_function('ins_user', ARRAY['tex...
Make sure it does
                  something.
SELECT has_function('ins_user');
SELECT has_function('ins_user', ARRAY['tex...
Make sure it does
                  something.
SELECT has_function('ins_user');
SELECT has_function('ins_user', ARRAY['tex...
Make sure it does
                  something.
SELECT has_function('ins_user');
SELECT has_function('ins_user', ARRAY['tex...
What does that get us?
What does that get us?
% pg_prove -d flipr tests/005-userfunc.pg
tests/005-userfunc.pg .. 1/?
not ok 6 - Should now have on...
What does that get us?
% pg_prove -d flipr tests/005-userfunc.pg
tests/005-userfunc.pg .. 1/?
not ok 6 - Should now have on...
Modify for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL A...
Modify for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
                   $$
) RETUR...
Modify for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
                   $$
) RETUR...
…To make ’em pass
…To make ’em pass

% psql -d flipr -f sql/005-userfunc.sql
CREATE FUNCTION
%
…To make ’em pass

% psql -d flipr -f sql/005-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/005-userfunc.pg
tests/0...
…To make ’em pass

% psql -d flipr -f sql/005-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/005-userfunc.pg
tests/0...
Add More Assertions
SELECT lives_ok(
   $$ SELECT ins_user('theory', 'wet blanket') $$,
   'Execution of ins_user() should...
Add More Assertions
SELECT lives_ok(
   $$ SELECT ins_user('theory', 'wet blanket') $$,
   'Execution of ins_user() should...
Add More Assertions
SELECT lives_ok(
   $$ SELECT ins_user('theory', 'wet blanket') $$,
   'Execution of ins_user() should...
Add More Assertions
SELECT lives_ok(
   $$ SELECT ins_user('theory', 'wet blanket') $$,
   'Execution of ins_user() should...
Run ’Em
Run ’Em
% pg_prove -d flipr tests/006-userfunc.pg
tests/006-userfunc.pg .. 1/?
not ok 7 - Password should not be clear text...
Run ’Em
% pg_prove -d flipr tests/006-userfunc.pg
tests/006-userfunc.pg .. 1/?
not ok 7 - Password should not be clear text...
Modify for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL A...
Modify for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL A...
Modify for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL A...
…To make ’em pass
…To make ’em pass

% psql -d flipr -f sql/006-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/006-userfunc.pg
tests/0...
…To make ’em pass

% psql -d flipr -f sql/006-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/006-userfunc.pg
tests/0...
Sanity Check
SELECT isnt(
   password, 'wet blanket',
   'Password should not be clear text'
) FROM users WHERE nickname =...
Sanity Check
SELECT isnt(
   password, 'wet blanket',
   'Password should not be clear text'
) FROM users WHERE nickname =...
Sanity Check
SELECT isnt(
   password, 'wet blanket',
   'Password should not be clear text'
) FROM users WHERE nickname =...
Sanity Check
SELECT isnt(
   password, 'wet blanket',
   'Password should not be clear text'
) FROM users WHERE nickname =...
Are We Insane?
Are We Insane?
pg_prove -d flipr tests/007-userfunc.pg
tests/007-userfunc.pg .. 1/?
not ok 8 - Password should not be nickn...
Are We Insane?
pg_prove -d flipr tests/007-userfunc.pg
tests/007-userfunc.pg .. 1/?
not ok 8 - Password should not be nickn...
Change for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL A...
Change for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL A...
Change for the Test

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL A...
See ’em Pass
See ’em Pass

% psql -d flipr -f sql/007-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/007-userfunc.pg
tests/007-us...
See ’em Pass

% psql -d flipr -f sql/007-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/007-userfunc.pg
tests/007-us...
Add Another User…
SELECT isnt(
   password, 'theory',
   'Password should not be nickname'
) FROM users WHERE nickname = '...
Add Another User…
SELECT isnt(
   password, 'theory',
   'Password should not be nickname'
) FROM users WHERE nickname = '...
Add Another User…
SELECT isnt(
   password, 'theory',
   'Password should not be nickname'
) FROM users WHERE nickname = '...
Add Another User…
SELECT isnt(
   password, 'theory',
   'Password should not be nickname'
) FROM users WHERE nickname = '...
Add Another User…
SELECT isnt(
   password, 'theory',
   'Password should not be nickname'
) FROM users WHERE nickname = '...
Let Us Be Inconsistent
Let Us Be Inconsistent
pg_prove -d flipr tests/008-userfunc.pg
tests/008-userfunc.pg .. 1/?
not ok 11 - Same password shoul...
Let Us Be Inconsistent
pg_prove -d flipr tests/008-userfunc.pg
tests/008-userfunc.pg .. 1/?
not ok 11 - Same password shoul...
Cheat Death

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   ...
Cheat Death

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   ...
Cheat Death

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   ...
Take a Pass
Take a Pass

% psql -d flipr -f sql/008-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/008-userfunc.pg
tests/008-use...
Take a Pass

% psql -d flipr -f sql/008-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/008-userfunc.pg
tests/008-use...
Let’s Get Serious
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM user...
Let’s Get Serious
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM user...
Let’s Get Serious
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM user...
Let’s Get Serious
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM user...
Let’s Get Serious
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM user...
Let’s Get Serious
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM user...
% pg_prove -d flipr tests/009-userfunc.pg
tests/009-userfunc.pg .. 1/?
not ok 12 - Password should not contain nickname
# F...
% pg_prove -d flipr tests/009-userfunc.pg
tests/009-userfunc.pg .. 1/?
not ok 12 - Password should not contain nickname
# F...
% pg_prove -d flipr tests/009-userfunc.pg
tests/009-userfunc.pg .. 1/?
not ok 12 - Password should not contain nickname
# F...
Try MD5

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   INSE...
Try MD5

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   INSE...
Try MD5

CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   INSE...
Here We Go!
Here We Go!
% psql -d flipr sql/009-userfunc.sql
% pg_prove -d flipr tests/009-userfunc.pg
tests/009-userfunc.pg .. 1/?
not ...
Here We Go!
% psql -d flipr sql/009-userfunc.sql
% pg_prove -d flipr tests/009-userfunc.pg
tests/009-userfunc.pg .. 1/?
not ...
Here We Go!
% psql -d flipr sql/009-userfunc.sql
% pg_prove -d flipr tests/009-userfunc.pg
tests/009-userfunc.pg .. 1/?
not ...
Whaaaaa?
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM users WHERE n...
Whaaaaa?
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM users WHERE n...
Whaaaaa?
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM users WHERE n...
Whaaaaa?
SELECT isnt(
   (SELECT password FROM users WHERE nickname = 'strongrrl'),
   (SELECT password FROM users WHERE n...
Where Are We?




antisocial   network
Where Are We?
                       Function adds user




antisocial   network
Where Are We?
                       Function adds user

                       Nickname not in password




antisocial   ...
Where Are We?
                       Function adds user

                       Nickname not in password

                ...
Where Are We?
                       Function adds user

                       Nickname not in password

                ...
Where Are We?
                       Function adds user

                       Nickname not in password

                ...
Where Are We?
                       Function adds user

                       Nickname not in password

                ...
Test for pg_crypto
SELECT col_has_default( 'users', 'timestamp' );
SELECT col_default_is( 'users', 'timestamp', 'now()' );...
Test for pg_crypto
SELECT col_has_default( 'users', 'timestamp' );
SELECT col_default_is( 'users', 'timestamp', 'now()' );...
See Test Fail
See Test Fail
% pg_prove -d flipr tests/010-schema.pg
tests/010-schema.pg .. 1/?
not ok 17 - Function crypt() should exist
...
Install pg_crypto
Install pg_crypto
% export PG=/usr/local/pgsql
% psql -d flipr -f $PG/share/contrib/pgcrypto.sql
%
Install pg_crypto
% export PG=/usr/local/pgsql
% psql -d flipr -f $PG/share/contrib/pgcrypto.sql
% pg_prove -d flipr tests/0...
Install pg_crypto
% export PG=/usr/local/pgsql
% psql -d flipr -f $PG/share/contrib/pgcrypto.sql
% pg_prove -d flipr tests/0...
Install pg_crypto
% export PG=/usr/local/pgsql
% psql -d flipr -f $PG/share/contrib/pgcrypto.sql
% pg_prove -d flipr tests/0...
Use pg_crypt
CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   ...
Use pg_crypt
CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   ...
Use pg_crypt
CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS $$
   ...
Cryptonomicon!
Cryptonomicon!

% psql -d flipr -f sql/010-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/009-userfunc.pg
tests/009-...
Cryptonomicon!

% psql -d flipr -f sql/010-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/009-userfunc.pg
tests/009-...
Sanity Check
SELECT unalike(
   password,
   '%wet blanket%',
   'Password should not contain clear text'
) FROM users;


...
Sanity Check
SELECT unalike(
   password,
   '%wet blanket%',
   'Password should not contain clear text'
) FROM users;

S...
Sanity Check
SELECT unalike(
   password,
   '%wet blanket%',
   'Password should not contain clear text'
) FROM users;

S...
Sanity Check
SELECT unalike(
   password,
   '%wet blanket%',
   'Password should not contain clear text'
) FROM users;

S...
Are We Sane?
Are We Sane?
% pg_prove -d flipr tests/011-userfunc.pg
tests/011-userfunc.pg .. 1/?
not ok 16 - Password should match crypt...
Are We Sane?
% pg_prove -d flipr tests/011-userfunc.pg
tests/011-userfunc.pg .. 1/?
not ok 16 - Password should match crypt...
Are We Sane?
% pg_prove -d flipr tests/011-userfunc.pg
tests/011-userfunc.pg .. 1/?
not ok 16 - Password should match crypt...
What’d We Do Wrong?
CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS...
What’d We Do Wrong?
CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS...
What’d We Do Wrong?
CREATE OR REPLACE FUNCTION ins_user(
   nickname TEXT,
   password TEXT
) RETURNS VOID LANGUAGE SQL AS...
And…
And…

% psql -d flipr -f sql/011-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/011-userfunc.pg
tests/011-userfunc.p...
And…

% psql -d flipr -f sql/011-userfunc.sql
CREATE FUNCTION
% pg_prove -d flipr tests/011-userfunc.pg
tests/011-userfunc.p...
Next: upd_pass()




antisocial   network
Next: upd_pass()
                       Need password updating




antisocial   network
Next: upd_pass()
                       Need password updating

                       Things to consider:




antisocial ...
Next: upd_pass()
                       Need password updating

                       Things to consider:

              ...
Next: upd_pass()
                       Need password updating

                       Things to consider:

              ...
Next: upd_pass()
                       Need password updating

                       Things to consider:

              ...
antisocial   network



Your Turn
antisocial   network



Your Turn
Create upd_pass()
antisocial   network



      Your Turn
     Create upd_pass()
          Project Repo:
http://github.com/theory/tddd/
How’d We Do?




012-userfunc.sql
How’d We Do?
SELECT    has_language('plpgsql');
SELECT    has_function('upd_pass');
SELECT    has_function('upd_pass', ARR...
How’d We Do?
SELECT    has_language('plpgsql');
SELECT    has_function('upd_pass');
SELECT    has_function('upd_pass', ARR...
How’d We Do?
SELECT    has_language('plpgsql');
SELECT    has_function('upd_pass');
SELECT    has_function('upd_pass', ARR...
How’d We Do?
SELECT    has_language('plpgsql');
SELECT    has_function('upd_pass');
SELECT    has_function('upd_pass', ARR...
How’d We Do?
SELECT    has_language('plpgsql');
SELECT    has_function('upd_pass');
SELECT    has_function('upd_pass', ARR...
How’d We Do?
SELECT    has_language('plpgsql');
SELECT    has_function('upd_pass');
SELECT    has_function('upd_pass', ARR...
How’d We Do?
SELECT     has_language('plpgsql');
SELECT     has_function('upd_pass');
SELECT     has_function('upd_pass', ...
How’d We Do?
SELECT     has_language('plpgsql');
SELECT     has_function('upd_pass');
SELECT     has_function('upd_pass', ...
How’d We Do?
SELECT     has_language('plpgsql');
SELECT     has_function('upd_pass');
SELECT     has_function('upd_pass', ...
How’d We Do?
SELECT     has_language('plpgsql');
SELECT     has_function('upd_pass');
SELECT     has_function('upd_pass', ...
How’d We Do?




012-userfunc.sql
How’d We Do?
SELECT ok(
   upd_pass('theory', 'wet blanket', 'pgtap rulez'),
   'upd_pass() should return true for proper ...
How’d We Do?
SELECT ok(
   upd_pass('theory', 'wet blanket', 'pgtap rulez'),
   'upd_pass() should return true for proper ...
How’d We Do?
SELECT ok(
   upd_pass('theory', 'wet blanket', 'pgtap rulez'),
   'upd_pass() should return true for proper ...
What Does it Look Like?




013-privs.sql
What Does it Look Like?
CREATE OR REPLACE FUNCTION upd_pass(
   nick TEXT,
   oldpass TEXT,
   newpass TEXT
) RETURNS BOOL...
What Does it Look Like?
CREATE OR REPLACE FUNCTION upd_pass(
   nick TEXT,
   oldpass TEXT,
   newpass TEXT
) RETURNS BOOL...
What About Privileges?




antisocial   network
What About Privileges?
                       Got solid functions




antisocial   network
What About Privileges?
                       Got solid functions

                       Apps still using tables




anti...
What About Privileges?
                       Got solid functions

                       Apps still using tables

       ...
What About Privileges?
                       Got solid functions

                       Apps still using tables

       ...
What About Privileges?
                       Got solid functions

                       Apps still using tables

       ...
What About Privileges?
                       Got solid functions

                       Apps still using tables

       ...
Test Privileges




013-privs.pg
Test Privileges
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT has_role('flipra...
Test Privileges
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT has_role('flipra...
Test Privileges
SET search_path = public,tap;

BEGIN;
--SELECT plan( 5 );
SELECT * FROM no_plan();

SELECT has_role('flipra...
Check Privileges
Check Privileges
% pg_prove -d flipr tests/013-privs.pg
tests/013-privs.pg .. 1/?
not ok 1 - Role fliprapp should exist
# Fa...
Check Privileges
% pg_prove -d flipr tests/013-privs.pg
tests/013-privs.pg .. 1/?
not ok 1 - Role fliprapp should exist
# Fa...
Check Privileges
% pg_prove -d flipr tests/013-privs.pg
tests/013-privs.pg .. 1/?
not ok 1 - Role fliprapp should exist
# Fa...
Add a User




013-privs.sql
Add a User

CREATE USER fliprapp
 WITH LOGIN;




 013-privs.sql
Add a User

CREATE USER fliprapp
 WITH LOGIN;




                  Easy, right?
 013-privs.sql
User Test
User Test
% psql -d flipr -f sql/013-privs.sql
CREATE ROLE
% pg_prove -d flipr tests/013-privs.pg
tests/013-privs.pg .. ok
A...
User Test
% psql -d flipr -f sql/013-privs.sql
CREATE ROLE
% pg_prove -d flipr tests/013-privs.pg
tests/013-privs.pg .. ok
A...
Sanity Check
SELECT has_role('fliprapp');
SELECT isnt_superuser('fliprapp');




 014-userfunc.pg
Sanity Check
SELECT has_role('fliprapp');
SELECT isnt_superuser('fliprapp');

SELECT volatility_is('ins_user', 'volatile');
...
Sanity Check
SELECT has_role('fliprapp');
SELECT isnt_superuser('fliprapp');

SELECT volatility_is('ins_user', 'volatile');
...
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Test Driven Database Development
Upcoming SlideShare
Loading in...5
×

Test Driven Database Development

2,947

Published on

reating a database schema means creating an interface for applications to use to manage their data. But how do you know how well that interface works until you’ve tried it?

Well, by trying it before you create it.

This talk introduces the concept of test-driven development to database administrators. We’ll use pgTAP to work through a real-world example creating a database design with an intuitive, useful interface for managing application data. Derive more intuitive table structures! Keep an eye to the beauty of your views! Let your procedures make your application more productive! Feel younger and more clever, all through the power of TDDD!

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,947
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
113
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide
  • TODO:
    • Add more privilege stuff?
    • Add example of renaming `flips.timestamp`?
  • TODO:
    • Add more privilege stuff?
    • Add example of renaming `flips.timestamp`?
  • TODO:
    • Add more privilege stuff?
    • Add example of renaming `flips.timestamp`?
  • TODO:
    • Add more privilege stuff?
    • Add example of renaming `flips.timestamp`?

































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































  • Test Driven Database Development

    1. 1. Test Driven Database Development David E. Wheeler PostgreSQL Experts, Inc. OSCON 2010 Portland OR USA Text: Attribution-Noncommercial-Share Alike 3.0 United States: http://creativecommons.org/licenses/by-nc-sa/3.0/us/ Images licensed independently and © Their respective owners.
    2. 2. Test Driven Database Development ✘ David E. Wheeler PostgreSQL Experts, Inc. OSCON 2010 Portland OR USA Text: Attribution-Noncommercial-Share Alike 3.0 United States: http://creativecommons.org/licenses/by-nc-sa/3.0/us/ Images licensed independently and © Their respective owners.
    3. 3. David E. Wheeler OSCON 2010 Portland OR USA Text: Attribution-Noncommercial-Share Alike 3.0 United States: http://creativecommons.org/licenses/by-nc-sa/3.0/us/ Images licensed independently and © Their respective owners.
    4. 4. David E. Wheeler OSCON 2010 Portland OR USA License: Attribution-Noncommercial-Share Alike 3.0 United States: http://creativecommons.org/licenses/by-nc-sa/3.0/us/
    5. 5. Build My VC-Funded App ✔ for Me for Free David E. Wheeler CEO, Data Manager FASN Enterprises, Inc. OSCON 2010 Portland OR USA License: Attribution-Noncommercial-Share Alike 3.0 United States: http://creativecommons.org/licenses/by-nc-sa/3.0/us/
    6. 6. This is Genius
    7. 7. This is Genius I had this idea
    8. 8. This is Genius I had this idea Social networking is hot
    9. 9. This is Genius I had this idea Social networking is hot Has been for too long
    10. 10. This is Genius I had this idea Social networking is hot Has been for too long The backlash is overdue
    11. 11. This is Genius I had this idea Social networking is hot Has been for too long The backlash is overdue Getting ahead of the curve
    12. 12. This is Genius I had this idea Social networking is hot Has been for too long The backlash is overdue Getting ahead of the curve Introducing…
    13. 13. http://flic.kr/p/8j5gG8 © 2010 Strongrrl. All rights reserved. Used with permission.
    14. 14. antisocial network http://flic.kr/p/8j5gG8 © 2010 Strongrrl. All rights reserved. Used with permission.
    15. 15. How it Works antisocial network
    16. 16. How it Works Microblogging platform antisocial network
    17. 17. How it Works Microblogging platform Everyone follows you antisocial network
    18. 18. How it Works Microblogging platform Everyone follows you New users follow everyone antisocial network
    19. 19. How it Works Microblogging platform Everyone follows you New users follow everyone Goal: Alienate your followers antisocial network
    20. 20. How it Works Microblogging platform Everyone follows you New users follow everyone Goal: Alienate your followers Get them to unfollow you antisocial network
    21. 21. How it Works Microblogging platform Everyone follows you New users follow everyone Goal: Alienate your followers Get them to unfollow you Leaderboard: Those with fewest followers antisocial network
    22. 22. Your Task antisocial network
    23. 23. Your Task Create the database antisocial network
    24. 24. Your Task Create the database Use TDDD to make it right antisocial network
    25. 25. Your Task Create the database Use TDDD to make it right Contribute to VC-funded Corp antisocial network
    26. 26. Your Task Create the database Use TDDD to make it right Contribute to VC-funded Corp Profit! antisocial network
    27. 27. Your Task Create the database Use TDDD to make it right Contribute to VC-funded Corp Profit! For VC-funded Corp antisocial network
    28. 28. http://flic.kr/p/2honiQ © 2007 James Duncan Davidson. All rights reserved. Used with permission.
    29. 29. antisocial network
    30. 30. TDDD antisocial network WTF?
    31. 31. We’ll get there antisocial network
    32. 32. But first… antisocial network
    33. 33. NDA antisocial network
    34. 34. Okay, now that that’s out of the way… antisocial network
    35. 35. Please organize into pairs antisocial network
    36. 36. Yes, that’s right antisocial network
    37. 37. You’re gonna do pair database programming antisocial network
    38. 38. App developers partner with DBAs antisocial network
    39. 39. I’m waiting… antisocial network
    40. 40. Okay, Ready? antisocial network
    41. 41. Time to Install antisocial network
    42. 42. Install PostgreSQL antisocial network
    43. 43. Install PostgreSQL http:/ /www.postgresql.org/download/ http://roadie.show.local/oscon/ http:/ 10.16.2/oscon/ /10. antisocial network
    44. 44. Install PostgreSQL http:/ /www.postgresql.org/download/ Distribution package http://roadie.show.local/oscon/ http:/ 10.16.2/oscon/ /10. antisocial network
    45. 45. Install PostgreSQL http:/ /www.postgresql.org/download/ Distribution package Mac/BSD Ports http://roadie.show.local/oscon/ http:/ 10.16.2/oscon/ /10. antisocial network
    46. 46. Install PostgreSQL http:/ /www.postgresql.org/download/ Distribution package Mac/BSD Ports http://roadie.show.local/oscon/ Need postgresql-dev too! http:/ 10.16.2/oscon/ /10. antisocial network
    47. 47. Install PostgreSQL http:/ /www.postgresql.org/download/ Distribution package Mac/BSD Ports http://roadie.show.local/oscon/ Need postgresql-dev too! Source: http:/ 10.16.2/oscon/ /10. antisocial network
    48. 48. Install PostgreSQL http:/ /www.postgresql.org/download/ Distribution package Mac/BSD Ports http://roadie.show.local/oscon/ Need postgresql-dev too! Source: http:/ 10.16.2/oscon/ /10. ./configure && make && make install antisocial network
    49. 49. Install PostgreSQL http:/ /www.postgresql.org/download/ Distribution package Mac/BSD Ports http://roadie.show.local/oscon/ Need postgresql-dev too! Source: http:/ 10.16.2/oscon/ /10. ./configure && make && make install cd contrib && make && make install antisocial network
    50. 50. Install PostgreSQL http:/ /www.postgresql.org/download/ Distribution package Mac/BSD Ports http://roadie.show.local/oscon/ Need postgresql-dev too! Source: http:/ 10.16.2/oscon/ /10. ./configure && make && make install cd contrib && make && make install initdb -D /usr/local/pgsql/data antisocial network
    51. 51. Install Test::Harness
    52. 52. Install Test::Harness % cpan Test::Harness
    53. 53. Install Test::Harness % cpan Test::Harness Or…
    54. 54. Install Test::Harness % cpan Test::Harness Or… % wget http://bit.ly/test-harness-321
    55. 55. Install Test::Harness % cpan Test::Harness Or… % wget http://bit.ly/test-harness-321 % tar zxf Test-Harness-3.21.tar.gz
    56. 56. Install Test::Harness % cpan Test::Harness Or… % wget http://bit.ly/test-harness-321 % tar zxf Test-Harness-3.21.tar.gz % cd Test-Harness-3.21
    57. 57. Install Test::Harness % cpan Test::Harness Or… % wget http://bit.ly/test-harness-321 % tar zxf Test-Harness-3.21.tar.gz % cd Test-Harness-3.21 % perl Makefile.PL
    58. 58. Install Test::Harness % cpan Test::Harness Or… % wget http://bit.ly/test-harness-321 % tar zxf Test-Harness-3.21.tar.gz % cd Test-Harness-3.21 % perl Makefile.PL % make
    59. 59. Install Test::Harness % cpan Test::Harness Or… % wget http://bit.ly/test-harness-321 % tar zxf Test-Harness-3.21.tar.gz % cd Test-Harness-3.21 % perl Makefile.PL % make % make test
    60. 60. Install Test::Harness % cpan Test::Harness Or… % wget http://bit.ly/test-harness-321 % tar zxf Test-Harness-3.21.tar.gz % cd Test-Harness-3.21 % perl Makefile.PL % make % make test % sudo make install
    61. 61. Install pgTAP
    62. 62. Install pgTAP % tar jxf pgtap-0.24.tar.bz2
    63. 63. Install pgTAP % tar jxf pgtap-0.24.tar.bz2 % cd pgtap-0.24
    64. 64. Install pgTAP % tar jxf pgtap-0.24.tar.bz2 % cd pgtap-0.24 % make TAPSCHEMA=tap Separate schema
    65. 65. Install pgTAP % tar jxf pgtap-0.24.tar.bz2 % cd pgtap-0.24 % make TAPSCHEMA=tap % sudo make install
    66. 66. Install pgTAP % tar jxf pgtap-0.24.tar.bz2 % cd pgtap-0.24 % make TAPSCHEMA=tap % sudo make install % make installcheck
    67. 67. Install pgTAP % tar jxf pgtap-0.24.tar.bz2 % cd pgtap-0.24 % make TAPSCHEMA=tap % sudo make install % make installcheck % createdb -U postgres flipr
    68. 68. Install pgTAP % tar jxf pgtap-0.24.tar.bz2 % cd pgtap-0.24 % make TAPSCHEMA=tap % sudo make install % make installcheck % createdb -U postgres flipr % createlang -U postgres -d flipr plpgsql
    69. 69. Install pgTAP % tar jxf pgtap-0.24.tar.bz2 % cd pgtap-0.24 % make TAPSCHEMA=tap % sudo make install % make installcheck % createdb -U postgres flipr % createlang -U postgres -d flipr plpgsql % psql -U postgres -d flipr -f pgtap.sql
    70. 70. We good? antisocial network
    71. 71. First Po^^Test! antisocial network
    72. 72. pgTAP Basics 001-schema.pg
    73. 73. pgTAP Basics SET search_path = public,tap; BEGIN; -- Plan the tests. SELECT plan( 1 ); SELECT has_table( 'users' ); -- Clean up. SELECT finish(); ROLLBACK; 001-schema.pg
    74. 74. pgTAP Basics SET search_path = public,tap; BEGIN; -- Plan the tests. SELECT plan( 1 ); SELECT has_table( 'users' ); -- Clean up. SELECT finish(); ROLLBACK; 001-schema.pg
    75. 75. pgTAP Basics SET search_path = public,tap; BEGIN; -- Plan the tests. SELECT plan( 1 ); SELECT has_table( 'users' ); -- Clean up. SELECT finish(); ROLLBACK; 001-schema.pg
    76. 76. pgTAP Basics SET search_path = public,tap; BEGIN; -- Plan the tests. SELECT plan( 1 ); SELECT has_table( 'users' ); -- Clean up. SELECT finish(); ROLLBACK; 001-schema.pg
    77. 77. pgTAP Basics SET search_path = public,tap; BEGIN; -- Plan the tests. SELECT plan( 1 ); SELECT has_table( 'users' ); -- Clean up. SELECT finish(); ROLLBACK; 001-schema.pg
    78. 78. pgTAP Basics SET search_path = public,tap; BEGIN; -- Plan the tests. SELECT plan( 1 ); SELECT has_table( 'users' ); -- Clean up. SELECT finish(); ROLLBACK; 001-schema.pg
    79. 79. pgTAP Basics SET search_path = public,tap; BEGIN; -- Plan the tests. SELECT plan( 1 ); SELECT has_table( 'users' ); -- Clean up. SELECT finish(); ROLLBACK; 001-schema.pg
    80. 80. Run the Test
    81. 81. Run the Test % pg_prove -vd flipr tests/001-schema.pg tests/001-schema.pg .. 1/1 not ok 1 - Table users should exist # Failed test 1: "Table users should exist" # Looks like you failed 1 test of 1 tests/001-schema.pg .. Failed 1/1 subtests Test Summary Report ------------------- tests/001-schema.pg (Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL
    82. 82. Run the Test % pg_prove -vd flipr tests/001-schema.pg tests/001-schema.pg .. 1/1 not ok 1 - Table users should exist # Failed test 1: "Table users should exist" # Looks like you failed 1 test of 1 tests/001-schema.pg .. Failed 1/1 subtests Test Summary Report ------------------- tests/001-schema.pg (Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL
    83. 83. Run the Test % pg_prove -vd flipr tests/001-schema.pg tests/001-schema.pg .. 1/1 not ok 1 - Table users should exist # Failed test 1: "Table users should exist" # Looks like you failed 1 test of 1 tests/001-schema.pg .. Failed 1/1 subtests Test Summary Report ------------------- tests/001-schema.pg (Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL
    84. 84. Run the Test % pg_prove -vd flipr tests/001-schema.pg tests/001-schema.pg .. 1/1 not ok 1 - Table users should exist # Failed test 1: "Table users should exist" # Looks like you failed 1 test of 1 tests/001-schema.pg .. Failed 1/1 subtests Test Summary Report ------------------- tests/001-schema.pg (Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL
    85. 85. Create Users Table 001-users.sql
    86. 86. Create Users Table CREATE TABLE users ( id INT ); 001-users.sql
    87. 87. Create Users Table CREATE TABLE users ( id INT ); Bare minimum 001-users.sql
    88. 88. First Pass
    89. 89. First Pass % psql -d flipr -f sql/001-users.sql % pg_prove -vd flipr tests/001-schema.pg tests/1-schema.pg .. 1..1 ok 1 - Table users should exist ok All tests successful. Files=1, Tests=1, 1 wallclock secs Result: PASS
    90. 90. First Pass % psql -d flipr -f sql/001-users.sql % pg_prove -vd flipr tests/001-schema.pg tests/1-schema.pg .. 1..1 ok 1 - Table users should exist ok All tests successful. Files=1, Tests=1, 1 wallclock secs Result: PASS
    91. 91. First Pass % psql -d flipr -f sql/001-users.sql % pg_prove -vd flipr tests/001-schema.pg tests/1-schema.pg .. 1..1 ok 1 - Table users should exist ok All tests successful. Files=1, Tests=1, 1 wallclock secs Result: PASS
    92. 92. First Pass % psql -d flipr -f sql/001-users.sql % pg_prove -vd flipr tests/001-schema.pg tests/1-schema.pg .. 1..1 ok 1 - Table users should exist ok All tests successful. Files=1, Tests=1, 1 wallclock secs Result: PASS W00t!
    93. 93. Now…Iterate! antisocial network
    94. 94. What Columns? antisocial network
    95. 95. What Columns? Nickname antisocial network
    96. 96. What Columns? Nickname Password antisocial network
    97. 97. What Columns? Nickname Password Timestamp antisocial network
    98. 98. Column Testing 002-schema.pg
    99. 99. Column Testing SET search_path = public,tap; BEGIN; SELECT plan( 5 ); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT finish(); ROLLBACK; 002-schema.pg
    100. 100. Column Testing SET search_path = public,tap; BEGIN; SELECT plan( 5 ); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT finish(); ROLLBACK; 002-schema.pg
    101. 101. Column Testing SET search_path = public,tap; BEGIN; SELECT plan( 5 ); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT finish(); ROLLBACK; 002-schema.pg
    102. 102. Column Testing SET search_path = public,tap; BEGIN; SELECT plan( 5 ); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT finish(); ROLLBACK; 002-schema.pg
    103. 103. Column Testing SET search_path = public,tap; BEGIN; SELECT plan( 5 ); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT finish(); ROLLBACK; 002-schema.pg
    104. 104. Column Testing SET search_path = public,tap; BEGIN; SELECT plan( 5 ); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT finish(); ROLLBACK; 002-schema.pg
    105. 105. Run ’Em
    106. 106. Run ’Em % pg_prove -vd flipr tests/002-schema.pg tests/002-schema.pg .. 1..5 ok 1 - Schema public should have the correct tables ok 2 - Table users should exist not ok 3 - Column users.nickname should exist # Failed test 3: "Column users.nickname should exist" not ok 4 - Column users.password should exist # Failed test 4: "Column users.password should exist" not ok 5 - Column users."timestamp" should exist # Failed test 5: "Column users."timestamp" should exist" # Looks like you failed 3 tests of 5 tests/002-schema.pg .. Failed 3/5 subtests Test Summary Report ------------------- tests/002-schema.pg (Tests: 5 Failed: 2) Failed tests: 3-5 Files=1, Tests=5, 0 wallclock secs Result: FAIL
    107. 107. Run ’Em % pg_prove -vd flipr tests/002-schema.pg tests/002-schema.pg .. 1..5 ok 1 - Schema public should have the correct tables ok 2 - Table users should exist not ok 3 - Column users.nickname should exist # Failed test 3: "Column users.nickname should exist" not ok 4 - Column users.password should exist # Failed test 4: "Column users.password should exist" not ok 5 - Column users."timestamp" should exist # Failed test 5: "Column users."timestamp" should exist" # Looks like you failed 3 tests of 5 As tests/002-schema.pg .. Failed 3/5 subtests Test Summary Report ------------------- tests/002-schema.pg (Tests: 5 Failed: 2) Expected Failed tests: 3-5 Files=1, Tests=5, 0 wallclock secs Result: FAIL
    108. 108. Add the Columns 002-users.sql
    109. 109. Add the Columns DROP TABLE IF EXISTS users; CREATE TABLE users ( nickname TEXT PRIMARY KEY, password TEXT NOT NULL, timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW() ); 002-users.sql
    110. 110. Add the Columns DROP TABLE IF EXISTS users; CREATE TABLE users ( nickname TEXT PRIMARY KEY, password TEXT NOT NULL, timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW() ); 002-users.sql Simple.
    111. 111. Run Again
    112. 112. Run Again % psql -d flipr -f sql/002-users.sql % pg_prove -vd flipr tests/002-schema.pg tests/002-schema.pg .. 1..5 ok 1 - Schema public should have the correct tables ok 2 - Table users should exist ok 3 - Column users.nickname should exist ok 4 - Column users.password should exist ok 5 - Column users."timestamp" should exist ok All tests successful. Files=1, Tests=5, 0 wallclock secs Result: PASS
    113. 113. Run Again % psql -d flipr -f sql/002-users.sql % pg_prove -vd flipr tests/002-schema.pg tests/002-schema.pg .. 1..5 ok 1 - Schema public should have the correct tables ok 2 - Table users should exist ok 3 - Column users.nickname should exist ok 4 - Column users.password should exist ok 5 - Column users."timestamp" should exist ok All tests successful. Files=1, Tests=5, 0 wallclock secs Result: PASS Rockin.
    114. 114. 003-schmea.pg
    115. 115. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    116. 116. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    117. 117. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    118. 118. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    119. 119. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    120. 120. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    121. 121. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    122. 122. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    123. 123. SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT tables_are( 'public', ARRAY[ 'users' ] ); SELECT has_table( 'users' ); SELECT has_pk( 'users' ); SELECT has_column( 'users', 'nickname' ); SELECT col_type_is( 'users', 'nickname', 'text' ); SELECT col_hasnt_default( 'users', 'nickname' ); SELECT col_is_pk( 'users', 'nickname' ); SELECT has_column( 'users', 'password' ); SELECT col_type_is( 'users', 'password', 'text' ); SELECT col_not_null( 'users', 'password' ); SELECT col_hasnt_default( 'users', 'password' ); SELECT has_column( 'users', 'timestamp' ); SELECT col_type_is('users', 'timestamp', 'timestamp with time zone'); SELECT col_not_null( 'users', 'timestamp' ); SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT finish(); ROLLBACK; 003-schmea.pg
    124. 124. Files
    125. 125. Files git clone git:/ /ec2.dagolden.com/git/oscon-tddd.git
    126. 126. Files git clone git:/ /ec2.dagolden.com/git/oscon-tddd.git http://roadie.show.local/oscon/
    127. 127. Files git clone git:/ /ec2.dagolden.com/git/oscon-tddd.git http://roadie.show.local/oscon/ http:/ 10.16.2/oscon/ /10.
    128. 128. Files git clone git:/ /ec2.dagolden.com/git/oscon-tddd.git http://roadie.show.local/oscon/ http:/ 10.16.2/oscon/ /10. http:/ /github.com/theory/tddd/
    129. 129. Files git clone git:/ /ec2.dagolden.com/git/oscon-tddd.git http://roadie.show.local/oscon/ http:/ 10.16.2/oscon/ /10. http:/ /github.com/theory/tddd/ pg_prove -vd flipr -U postgres tests/002-schema.pg
    130. 130. Go!
    131. 131. Go! % pg_prove -d flipr tests/003-schema.pg tests/003-schema.pg .. ok All tests successful. Files=1, Tests=16, 0 wallclock secs Result: PASS
    132. 132. Go! % pg_prove -d flipr tests/003-schema.pg tests/003-schema.pg .. ok All tests successful. Files=1, Tests=16, 0 wallclock secs Result: PASS
    133. 133. Go! % pg_prove -d flipr tests/003-schema.pg tests/003-schema.pg .. ok All tests successful. Files=1, Tests=16, 0 wallclock secs Result: PASS Kick ass.
    134. 134. antisocial network Your Turn
    135. 135. antisocial network Your Turn Create users table
    136. 136. antisocial network Your Turn Create users table Project Repo: http://github.com/theory/tddd/
    137. 137. So Far So Good antisocial network
    138. 138. So Far So Good Appdev begins antisocial network
    139. 139. So Far So Good Appdev begins Web site antisocial network
    140. 140. So Far So Good Appdev begins Web site API antisocial network
    141. 141. So Far So Good Appdev begins Web site API Ruh-roh antisocial network
    142. 142. So Far So Good Appdev begins Web site API Ruh-roh Two code paths antisocial network
    143. 143. So Far So Good Appdev begins Web site API Ruh-roh Two code paths Just look at this data! antisocial network
    144. 144. Data Inconsistencies
    145. 145. Data Inconsistencies flipr=# select nickname, password from users; nickname | password ----------- +---------------------------------- agliodbs | 72b302bf297a228a75730123efef7c41 anna | May 13, 2005 strongrrl | 008c5926ca861023c1d2a36653fd88e2 theory | 008c5926ca861023c1d2a36653fd88e2 (4 rows)
    146. 146. Data Inconsistencies flipr=# select nickname, password from users; nickname | password ----------- +---------------------------------- agliodbs | 72b302bf297a228a75730123efef7c41 anna | May 13, 2005 strongrrl | 008c5926ca861023c1d2a36653fd88e2 theory | 008c5926ca861023c1d2a36653fd88e2 (4 rows)
    147. 147. Data Inconsistencies flipr=# select nickname, password from users; nickname | password ----------- +---------------------------------- agliodbs | 72b302bf297a228a75730123efef7c41 ✔ anna | May 13, 2005 strongrrl | 008c5926ca861023c1d2a36653fd88e2 theory | 008c5926ca861023c1d2a36653fd88e2 (4 rows)
    148. 148. Data Inconsistencies flipr=# select nickname, password from users; nickname | password ----------- +---------------------------------- agliodbs | 72b302bf297a228a75730123efef7c41 ✔ anna | May 13, 2005 strongrrl | 008c5926ca861023c1d2a36653fd88e2 theory | 008c5926ca861023c1d2a36653fd88e2 (4 rows)
    149. 149. Data Inconsistencies flipr=# select nickname, password from users; nickname | password ----------- +---------------------------------- ✘✘✘✘✘✘✘✘ Bad! agliodbs | 72b302bf297a228a75730123efef7c41 ✔ anna | May 13, 2005 strongrrl | 008c5926ca861023c1d2a36653fd88e2 theory | 008c5926ca861023c1d2a36653fd88e2 (4 rows)
    150. 150. Data Inconsistencies flipr=# select nickname, password from users; nickname | password ----------- +---------------------------------- ✘✘✘✘✘✘✘✘ Bad! agliodbs | 72b302bf297a228a75730123efef7c41 ✔ anna | May 13, 2005 strongrrl | 008c5926ca861023c1d2a36653fd88e2 theory | 008c5926ca861023c1d2a36653fd88e2 (4 rows)
    151. 151. Data Inconsistencies flipr=# select nickname, password from users; nickname | password ----------- +---------------------------------- ✘✘✘✘✘✘✘✘ Bad! agliodbs | 72b302bf297a228a75730123efef7c41 ✔ anna 〷〷〷〷〷〷〷〷〷 | May 13, 2005 strongrrl | 008c5926ca861023c1d2a36653fd88e2 theory | 008c5926ca861023c1d2a36653fd88e2 We can do better! (4 rows)
    152. 152. What Encryption? antisocial network
    153. 153. What Encryption? Multiple interfaces antisocial network
    154. 154. What Encryption? Multiple interfaces Data must be consistent antisocial network
    155. 155. What Encryption? Multiple interfaces Data must be consistent Encrypt passwords antisocial network
    156. 156. What Encryption? Multiple interfaces Data must be consistent Encrypt passwords Obscure password duplication antisocial network
    157. 157. What Encryption? Multiple interfaces Data must be consistent Encrypt passwords Obscure password duplication Solution: Database functions antisocial network
    158. 158. Test For New Function 004-userfunc.pg
    159. 159. Test For New Function SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT finish(); ROLLBACK; 004-userfunc.pg
    160. 160. Test For New Function SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT finish(); ROLLBACK; 004-userfunc.pg
    161. 161. Test For New Function SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT finish(); ROLLBACK; 004-userfunc.pg
    162. 162. Test For New Function SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT finish(); ROLLBACK; 004-userfunc.pg
    163. 163. Run ’Em
    164. 164. Run ’Em % pg_prove -d flipr tests/004-userfunc.pg tests/004-userfunc.pg .. 1/? not ok 1 - Function ins_user() should exist # Failed test 1: "Function ins_user() should exist" not ok 2 - Function ins_user(text, text) should exist # Failed test 2: "Function ins_user(text, text) should exist" not ok 3 - Function ins_user() should return void # Failed test 3: "Function ins_user() should return void" # Function ins_user() does not exist # Looks like you failed 3 tests of 3 tests/004-userfunc.pg .. Failed 3/3 subtests Test Summary Report ------------------- tests/004-userfunc.pg (Wstat: 0 Tests: 3 Failed: 3) Failed tests: 1-3 Files=1, Tests=3, 0 wallclock secs Result: FAIL
    165. 165. Run ’Em % pg_prove -d flipr tests/004-userfunc.pg tests/004-userfunc.pg .. 1/? not ok 1 - Function ins_user() should exist # Failed test 1: "Function ins_user() should exist" not ok 2 - Function ins_user(text, text) should exist # Failed test 2: "Function ins_user(text, text) should exist" not ok 3 - Function ins_user() should return void # Failed test 3: "Function ins_user() should return void" # Function ins_user() does not exist # Looks like you failed 3 tests of 3 tests/004-userfunc.pg .. Failed 3/3 subtests Test Summary Report ------------------- tests/004-userfunc.pg (Wstat: 0 Tests: 3 Failed: 3) Failed tests: 1-3 Files=1, Tests=3, 0 wallclock secs Result: FAIL
    166. 166. Create It! 004-userfunc.sql
    167. 167. Create It! CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS ''; 004-userfunc.sql
    168. 168. Create It! CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS ''; Just enough… 004-userfunc.sql
    169. 169. …To make ’em pass
    170. 170. …To make ’em pass % psql -d flipr -f sql/004-userfunc.sql CREATE FUNCTION %
    171. 171. …To make ’em pass % psql -d flipr -f sql/004-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/004-userfunc.pg tests/004-userfunc.pg .. ok All tests successful. Files=1, Tests=3, 0 wallclock secs Result: PASS
    172. 172. …To make ’em pass % psql -d flipr -f sql/004-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/004-userfunc.pg tests/004-userfunc.pg .. ok All tests successful. Files=1, Tests=3, 0 wallclock secs Result: PASS Yay!
    173. 173. Make sure it does something. SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); 005-userfunc.pg
    174. 174. Make sure it does something. SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT is(COUNT(*)::int, 0, 'Should have no users') FROM users; SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; 005-userfunc.pg
    175. 175. Make sure it does something. SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT is(COUNT(*)::int, 0, 'Should have no users') FROM users; SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; 005-userfunc.pg
    176. 176. Make sure it does something. SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT is(COUNT(*)::int, 0, 'Should have no users') FROM users; SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; 005-userfunc.pg
    177. 177. Make sure it does something. SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT is(COUNT(*)::int, 0, 'Should have no users') FROM users; SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; 005-userfunc.pg
    178. 178. Make sure it does something. SELECT has_function('ins_user'); SELECT has_function('ins_user', ARRAY['text', 'text']); SELECT function_returns('ins_user', 'void'); SELECT is(COUNT(*)::int, 0, 'Should have no users') FROM users; SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; 005-userfunc.pg
    179. 179. What does that get us?
    180. 180. What does that get us? % pg_prove -d flipr tests/005-userfunc.pg tests/005-userfunc.pg .. 1/? not ok 6 - Should now have one user # Failed test 6: "Should now have one user" # have: 0 # want: 1 # Looks like you failed 1 test of 6 tests/005-userfunc.pg .. Failed 1/6 subtests Test Summary Report ------------------- tests/005-userfunc.pg (Wstat: 0 Tests: 6 Failed: 1) Failed test: 6 Files=1, Tests=6, 1 wallclock secs Result: FAIL
    181. 181. What does that get us? % pg_prove -d flipr tests/005-userfunc.pg tests/005-userfunc.pg .. 1/? not ok 6 - Should now have one user # Failed test 6: "Should now have one user" # have: 0 # want: 1 # Looks like you failed 1 test of 6 tests/005-userfunc.pg .. Failed 1/6 subtests Test Summary Report ------------------- tests/005-userfunc.pg (Wstat: 0 Tests: 6 Failed: 1) Failed test: 6 Files=1, Tests=6, 1 wallclock secs Result: FAIL
    182. 182. Modify for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS''; 005-userfunc.sql
    183. 183. Modify for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT $$ ) RETURNS VOID LANGUAGE SQL AS INSERT INTO users values($1, $2); $$; 005-userfunc.sql
    184. 184. Modify for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT $$ ) RETURNS VOID LANGUAGE SQL AS INSERT INTO users values($1, $2); $$; Bare minimum 005-userfunc.sql
    185. 185. …To make ’em pass
    186. 186. …To make ’em pass % psql -d flipr -f sql/005-userfunc.sql CREATE FUNCTION %
    187. 187. …To make ’em pass % psql -d flipr -f sql/005-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/005-userfunc.pg tests/005-userfunc.pg .. ok All tests successful. Files=1, Tests=6, 0 wallclock secs Result: PASS
    188. 188. …To make ’em pass % psql -d flipr -f sql/005-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/005-userfunc.pg tests/005-userfunc.pg .. ok All tests successful. Files=1, Tests=6, 0 wallclock secs Result: PASS Great!
    189. 189. Add More Assertions SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; 006-userfunc.pg
    190. 190. Add More Assertions SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; SELECT isnt( password, 'wet blanket', 'Password should not be clear text' ) FROM users WHERE nickname = 'theory'; 006-userfunc.pg
    191. 191. Add More Assertions SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; SELECT isnt( password, 'wet blanket', 'Password should not be clear text' ) FROM users WHERE nickname = 'theory'; 006-userfunc.pg
    192. 192. Add More Assertions SELECT lives_ok( $$ SELECT ins_user('theory', 'wet blanket') $$, 'Execution of ins_user() should not die' ); SELECT is(COUNT(*)::int, 1, 'Should now have one user') FROM users; SELECT isnt( password, 'wet blanket', 'Password should not be clear text' ) FROM users WHERE nickname = 'theory'; 006-userfunc.pg
    193. 193. Run ’Em
    194. 194. Run ’Em % pg_prove -d flipr tests/006-userfunc.pg tests/006-userfunc.pg .. 1/? not ok 7 - Password should not be clear text # Failed test 7: "Password should not be clear text" # have: wet blanket # want: anything else # Looks like you failed 1 test of 7 tests/006-userfunc.pg .. Failed 1/7 subtests Test Summary Report ------------------- tests/006-userfunc.pg (Wstat: 0 Tests: 7 Failed: 1) Failed test: 7 Files=1, Tests=7, 0 wallclock secs Result: FAIL
    195. 195. Run ’Em % pg_prove -d flipr tests/006-userfunc.pg tests/006-userfunc.pg .. 1/? not ok 7 - Password should not be clear text # Failed test 7: "Password should not be clear text" # have: wet blanket # want: anything else # Looks like you failed 1 test of 7 tests/006-userfunc.pg .. Failed 1/7 subtests Test Summary Report ------------------- tests/006-userfunc.pg (Wstat: 0 Tests: 7 Failed: 1) Failed test: 7 Files=1, Tests=7, 0 wallclock secs Result: FAIL
    196. 196. Modify for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, $ ); 2 $$; 006-userfunc.sql
    197. 197. Modify for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, $ ); 1 $$; 006-userfunc.sql
    198. 198. Modify for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, $ ); 1 $$; I’m not kidding 006-userfunc.sql
    199. 199. …To make ’em pass
    200. 200. …To make ’em pass % psql -d flipr -f sql/006-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/006-userfunc.pg tests/006-userfunc.pg .. ok All tests successful. Files=1, Tests=7, 1 wallclock secs Result: PASS
    201. 201. …To make ’em pass % psql -d flipr -f sql/006-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/006-userfunc.pg tests/006-userfunc.pg .. ok All tests successful. Files=1, Tests=7, 1 wallclock secs Result: PASS So what?
    202. 202. Sanity Check SELECT isnt( password, 'wet blanket', 'Password should not be clear text' ) FROM users WHERE nickname = 'theory'; 007-userfunc.pg
    203. 203. Sanity Check SELECT isnt( password, 'wet blanket', 'Password should not be clear text' ) FROM users WHERE nickname = 'theory'; SELECT isnt( password, 'theory', 'Password should not be nickname' ) FROM users WHERE nickname = 'theory'; 007-userfunc.pg
    204. 204. Sanity Check SELECT isnt( password, 'wet blanket', 'Password should not be clear text' ) FROM users WHERE nickname = 'theory'; SELECT isnt( password, 'theory', 'Password should not be nickname' ) FROM users WHERE nickname = 'theory'; 007-userfunc.pg
    205. 205. Sanity Check SELECT isnt( password, 'wet blanket', 'Password should not be clear text' ) FROM users WHERE nickname = 'theory'; SELECT isnt( password, 'theory', 'Password should not be nickname' ) FROM users WHERE nickname = 'theory'; 007-userfunc.pg
    206. 206. Are We Insane?
    207. 207. Are We Insane? pg_prove -d flipr tests/007-userfunc.pg tests/007-userfunc.pg .. 1/? not ok 8 - Password should not be nickname # Failed test 8: "Password should not be nickname" # have: theory # want: anything else # Looks like you failed 1 test of 8 tests/007-userfunc.pg .. Failed 1/8 subtests Test Summary Report ------------------- tests/007-userfunc.pg (Wstat: 0 Tests: 8 Failed: 1) Failed test: 8 Files=1, Tests=8, 0 wallclock secs Result: FAIL
    208. 208. Are We Insane? pg_prove -d flipr tests/007-userfunc.pg tests/007-userfunc.pg .. 1/? not ok 8 - Password should not be nickname # Failed test 8: "Password should not be nickname" # have: theory # want: anything else # Looks like you failed 1 test of 8 tests/007-userfunc.pg .. Failed 1/8 subtests Test Summary Report ------------------- tests/007-userfunc.pg (Wstat: 0 Tests: 8 Failed: 1) Failed test: 8 Files=1, Tests=8, 0 wallclock secs Result: FAIL
    209. 209. Change for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, $1); $$; 007-userfunc.sql
    210. 210. Change for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, 'whatever'); $$; 007-userfunc.sql
    211. 211. Change for the Test CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, 'whatever'); $$; That’ll do it. 007-userfunc.sql
    212. 212. See ’em Pass
    213. 213. See ’em Pass % psql -d flipr -f sql/007-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/007-userfunc.pg tests/007-userfunc.pg .. ok All tests successful. Files=1, Tests=8, 0 wallclock secs Result: PASS
    214. 214. See ’em Pass % psql -d flipr -f sql/007-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/007-userfunc.pg tests/007-userfunc.pg .. ok All tests successful. Files=1, Tests=8, 0 wallclock secs Result: PASS Exciting, no?
    215. 215. Add Another User… SELECT isnt( password, 'theory', 'Password should not be nickname' ) FROM users WHERE nickname = 'theory'; 008-userfunc.pg
    216. 216. Add Another User… SELECT isnt( password, 'theory', 'Password should not be nickname' ) FROM users WHERE nickname = 'theory'; SELECT lives_ok( $$ SELECT ins_user('strongrrl', 'wet blanket') $$, 'Insert user "strongrrl"' ); SELECT is(COUNT(*)::int, 2, 'Should now have two users') FROM users; SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); 008-userfunc.pg