Successfully reported this slideshow.
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

3,620 views

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
  • Be the first to comment

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