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,231 views
3,147 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
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,231
On SlideShare
0
From Embeds
0
Number of Embeds
78
Actions
Shares
0
Downloads
115
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
    217. 217. 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
    218. 218. 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
    219. 219. 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
    220. 220. Let Us Be Inconsistent
    221. 221. Let Us Be Inconsistent pg_prove -d flipr tests/008-userfunc.pg tests/008-userfunc.pg .. 1/? not ok 11 - Same password should not match # Failed test 11: "Same password should not match" # have: whatever # want: anything else # Looks like you failed 1 test of 11 tests/008-userfunc.pg .. Failed 1/11 subtests Test Summary Report ------------------- tests/008-userfunc.pg (Wstat: 0 Tests: 11 Failed: 1) Failed test: 11 Files=1, Tests=11, 0 wallclock secs Result: FAIL
    222. 222. Let Us Be Inconsistent pg_prove -d flipr tests/008-userfunc.pg tests/008-userfunc.pg .. 1/? not ok 11 - Same password should not match # Failed test 11: "Same password should not match" # have: whatever # want: anything else # Looks like you failed 1 test of 11 tests/008-userfunc.pg .. Failed 1/11 subtests Test Summary Report ------------------- tests/008-userfunc.pg (Wstat: 0 Tests: 11 Failed: 1) Failed test: 11 Files=1, Tests=11, 0 wallclock secs Result: FAIL
    223. 223. Cheat Death CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, 'whatever'); $$; 008-userfunc.sql
    224. 224. Cheat Death CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, $1 || $2); $$; 008-userfunc.sql
    225. 225. Cheat Death CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, $1 || $2); $$; No, seriously. 008-userfunc.sql
    226. 226. Take a Pass
    227. 227. Take a Pass % psql -d flipr -f sql/008-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/008-userfunc.pg tests/008-userfunc.pg .. ok All tests successful. Files=1, Tests=11, 0 wallclock secs Result: PASS
    228. 228. Take a Pass % psql -d flipr -f sql/008-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/008-userfunc.pg tests/008-userfunc.pg .. ok All tests successful. Files=1, Tests=11, 0 wallclock secs Result: PASS Not unexpected.
    229. 229. Let’s Get Serious SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); 009-userfunc.pg
    230. 230. Let’s Get Serious SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); SELECT unalike( password, '%' || nickname || '%', 'Password should not contain nickname' ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    231. 231. Let’s Get Serious SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); SELECT unalike( password, '%' || nickname || '%', 'Password should not contain nickname' ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    232. 232. Let’s Get Serious SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); SELECT unalike( password, '%' || nickname || '%', 'Password should not contain nickname' ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    233. 233. Let’s Get Serious SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); SELECT unalike( password, '%' || nickname || '%', 'Password should not contain nickname' ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    234. 234. Let’s Get Serious SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); SELECT unalike( password, '%' || nickname || '%', 'Password should not contain nickname' ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    235. 235. % pg_prove -d flipr tests/009-userfunc.pg tests/009-userfunc.pg .. 1/? not ok 12 - Password should not contain nickname # Failed test 12: "Password should not contain nickname" # 'theorywet blanket' # matches: '%theory%' not ok 13 - Password should not contain nickname # Failed test 13: "Password should not contain nickname" # 'strongrrlwet blanket' # matches: '%strongrrl%' not ok 14 - Password should not contain clear text # Failed test 14: "Password should not contain clear text" # 'theorywet blanket' # matches: '%wet blanket%' not ok 15 - Password should not contain clear text # Failed test 15: "Password should not contain clear text" # 'strongrrlwet blanket' # matches: '%wet blanket%' # Looks like you failed 4 tests of 15 tests/009-userfunc.pg .. Failed 4/15 subtests Test Summary Report ------------------- tests/009-userfunc.pg (Wstat: 0 Tests: 15 Failed: 4) Failed tests: 12-15 Files=1, Tests=15, 0 wallclock secs Result: FAIL
    236. 236. % pg_prove -d flipr tests/009-userfunc.pg tests/009-userfunc.pg .. 1/? not ok 12 - Password should not contain nickname # Failed test 12: "Password should not contain nickname" # 'theorywet blanket' # matches: '%theory%' not ok 13 - Password should not contain nickname # Failed test 13: "Password should not contain nickname" # 'strongrrlwet blanket' # matches: '%strongrrl%' not ok 14 - Password should not contain clear text # Failed test 14: "Password should not contain clear text" # 'theorywet blanket' # matches: '%wet blanket%' not ok 15 - Password should not contain clear text # Failed test 15: "Password should not contain clear text" # 'strongrrlwet blanket' # matches: '%wet blanket%' # Looks like you failed 4 tests of 15 tests/009-userfunc.pg .. Failed 4/15 subtests Test Summary Report ------------------- tests/009-userfunc.pg (Wstat: 0 Tests: 15 Failed: 4) Failed tests: 12-15 Files=1, Tests=15, 0 wallclock secs Result: FAIL
    237. 237. % pg_prove -d flipr tests/009-userfunc.pg tests/009-userfunc.pg .. 1/? not ok 12 - Password should not contain nickname # Failed test 12: "Password should not contain nickname" # 'theorywet blanket' # matches: '%theory%' not ok 13 - Password should not contain nickname # Failed test 13: "Password should not contain nickname" # 'strongrrlwet blanket' # matches: '%strongrrl%' not ok 14 - Password should not contain clear text # Failed test 14: "Password should not contain clear text" # 'theorywet blanket' # matches: '%wet blanket%' not ok 15 - Password should not contain clear text # Failed test 15: "Password should not contain clear text" # 'strongrrlwet blanket' # matches: '%wet blanket%' # Looks like you failed 4 tests of 15 tests/009-userfunc.pg .. Failed 4/15 subtests Test Summary Report ------------------- tests/009-userfunc.pg (Wstat: 0 Tests: 15 Failed: 4) Failed tests: 12-15 Files=1, Tests=15, 0 wallclock secs Result: FAIL
    238. 238. Try MD5 CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, $1 || $2); $$; 009-userfunc.sql
    239. 239. Try MD5 CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, md5($2)); $$; 009-userfunc.sql
    240. 240. Try MD5 CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, md5($2)); $$; It’s hashish. 009-userfunc.sql
    241. 241. Here We Go!
    242. 242. Here We Go! % psql -d flipr sql/009-userfunc.sql % pg_prove -d flipr tests/009-userfunc.pg tests/009-userfunc.pg .. 1/? not ok 11 - Same password should not match # Failed test 11: "Same password should not match" # have: 3c908e08d874ad9ec39bffc93b5fb983 # want: anything else # Looks like you failed 1 test of 15 tests/009-userfunc.pg .. Failed 1/15 subtests Test Summary Report ------------------- tests/009-userfunc.pg (Wstat: 0 Tests: 15 Failed: 1) Failed test: 11 Files=1, Tests=15, 0 wallclock secs Result: FAIL
    243. 243. Here We Go! % psql -d flipr sql/009-userfunc.sql % pg_prove -d flipr tests/009-userfunc.pg tests/009-userfunc.pg .. 1/? not ok 11 - Same password should not match # Failed test 11: "Same password should not match" # have: 3c908e08d874ad9ec39bffc93b5fb983 # want: anything else # Looks like you failed 1 test of 15 tests/009-userfunc.pg .. Failed 1/15 subtests Test Summary Report ------------------- Wait, what? tests/009-userfunc.pg (Wstat: 0 Tests: 15 Failed: 1) Failed test: 11 Files=1, Tests=15, 0 wallclock secs Result: FAIL
    244. 244. Here We Go! % psql -d flipr sql/009-userfunc.sql % pg_prove -d flipr tests/009-userfunc.pg tests/009-userfunc.pg .. 1/? not ok 11 - Same password should not match # Failed test 11: "Same password should not match" # have: 3c908e08d874ad9ec39bffc93b5fb983 # want: anything else # Looks like you failed 1 test of 15 tests/009-userfunc.pg .. Failed 1/15 subtests Test Summary Report ------------------- Wait, what? tests/009-userfunc.pg (Wstat: 0 Tests: 15 Failed: 1) Failed test: 11 Files=1, Tests=15, 0 wallclock secs Result: FAIL
    245. 245. Whaaaaa? SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); SELECT unalike( password, '%' || nickname || '%', 'Password should not contain nickname' ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    246. 246. Whaaaaa? SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); SELECT unalike( password, '%' || nickname || '%', 'Password should not contain nickname' ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    247. 247. Whaaaaa? SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); SELECT unalike( password, '%' || nickname || '%', 'Password should not contain nickname' Regression! ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    248. 248. Whaaaaa? SELECT isnt( (SELECT password FROM users WHERE nickname = 'strongrrl'), (SELECT password FROM users WHERE nickname = 'theory'), 'Same password should not match' ); Must SELECT unalike( password, obscure '%' || nickname || '%', 'Password should not contain nickname' dupes. Regression! ) FROM users; SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 009-userfunc.pg
    249. 249. Where Are We? antisocial network
    250. 250. Where Are We? Function adds user antisocial network
    251. 251. Where Are We? Function adds user Nickname not in password antisocial network
    252. 252. Where Are We? Function adds user Nickname not in password No clear text password antisocial network
    253. 253. Where Are We? Function adds user Nickname not in password No clear text password Must obscure duplicates antisocial network
    254. 254. Where Are We? Function adds user Nickname not in password No clear text password Must obscure duplicates How? antisocial network
    255. 255. Where Are We? Function adds user Nickname not in password No clear text password Must obscure duplicates How? Solution: pg_crypto antisocial network
    256. 256. Test for pg_crypto SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); 010-schema.pg
    257. 257. Test for pg_crypto SELECT col_has_default( 'users', 'timestamp' ); SELECT col_default_is( 'users', 'timestamp', 'now()' ); SELECT has_function('crypt'); SELECT has_function('gen_salt'); 010-schema.pg
    258. 258. See Test Fail
    259. 259. See Test Fail % pg_prove -d flipr tests/010-schema.pg tests/010-schema.pg .. 1/? not ok 17 - Function crypt() should exist # Failed test 17: "Function crypt() should exist" not ok 18 - Function gen_salt() should exist # Failed test 18: "Function gen_salt() should exist" # Looks like you failed 2 tests of 18 tests/010-schema.pg .. Failed 2/18 subtests Test Summary Report ------------------- tests/010-schema.pg (Wstat: 0 Tests: 18 Failed: 2) Failed tests: 17-18 Files=1, Tests=18, 0 wallclock secs Result: FAIL
    260. 260. Install pg_crypto
    261. 261. Install pg_crypto % export PG=/usr/local/pgsql % psql -d flipr -f $PG/share/contrib/pgcrypto.sql %
    262. 262. Install pg_crypto % export PG=/usr/local/pgsql % psql -d flipr -f $PG/share/contrib/pgcrypto.sql % pg_prove -d flipr tests/004-schema.pg tests/004-schema.pg .. ok All tests successful. Files=1, Tests=18, 1 wallclock secs Result: PASS
    263. 263. Install pg_crypto % export PG=/usr/local/pgsql % psql -d flipr -f $PG/share/contrib/pgcrypto.sql % pg_prove -d flipr tests/004-schema.pg tests/004-schema.pg .. ok All tests successful. Files=1, Tests=18, 1 wallclock secs Result: PASS Great.
    264. 264. Install pg_crypto % export PG=/usr/local/pgsql % psql -d flipr -f $PG/share/contrib/pgcrypto.sql % pg_prove -d flipr tests/004-schema.pg tests/004-schema.pg .. ok All tests successful. Files=1, Tests=18, 1 wallclock secs Result: PASS Great. Let’s use them!
    265. 265. Use pg_crypt CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, md5($2)); $$; 010-userfunc.sql
    266. 266. Use pg_crypt CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, crypt($1, gen_salt('md5'))); $$; 010-userfunc.sql
    267. 267. Use pg_crypt CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO users values($1, crypt($1, gen_salt('md5'))); $$; Cryptonite! 010-userfunc.sql
    268. 268. Cryptonomicon!
    269. 269. Cryptonomicon! % psql -d flipr -f sql/010-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/009-userfunc.pg tests/009-userfunc.pg .. ok All tests successful. Files=1, Tests=15, 0 wallclock secs Result: PASS
    270. 270. Cryptonomicon! % psql -d flipr -f sql/010-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/009-userfunc.pg tests/009-userfunc.pg .. ok All tests successful. Files=1, Tests=15, 0 wallclock secs Result: PASS Finally!
    271. 271. Sanity Check SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; 011-userfunc.pg
    272. 272. Sanity Check SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; SELECT is( password, crypt('wet blanket', password), 'Password should match crypted password' ) FROM users; 011-userfunc.pg
    273. 273. Sanity Check SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; SELECT is( password, crypt('wet blanket', password), 'Password should match crypted password' ) FROM users; 011-userfunc.pg
    274. 274. Sanity Check SELECT unalike( password, '%wet blanket%', 'Password should not contain clear text' ) FROM users; SELECT is( password, crypt('wet blanket', password), 'Password should match crypted password' ) FROM users; 011-userfunc.pg
    275. 275. Are We Sane?
    276. 276. Are We Sane? % pg_prove -d flipr tests/011-userfunc.pg tests/011-userfunc.pg .. 1/? not ok 16 - Password should match crypted password # Failed test 16: "Password should match crypted password" # have: $1$PmBl0CzG$sUwGMzxCKqovvMKaBhyXK. # want: $1$PmBl0CzG$E/uNgzObrQHl3Wp.C6/TX0 not ok 17 - Password should match crypted password # Failed test 17: "Password should match crypted password" # have: $1$KCxoeJAK$APSOlalADk1akGrItOASx. # want: $1$KCxoeJAK$DrhZ5wT4aUzJE.RydnJLy0 # Looks like you failed 2 tests of 17 tests/011-userfunc.pg .. Failed 2/17 subtests Test Summary Report ------------------- tests/011-userfunc.pg (Wstat: 0 Tests: 17 Failed: 2) Failed tests: 16-17 Files=1, Tests=17, 0 wallclock secs Result: FAIL
    277. 277. Are We Sane? % pg_prove -d flipr tests/011-userfunc.pg tests/011-userfunc.pg .. 1/? not ok 16 - Password should match crypted password # Failed test 16: "Password should match crypted password" # have: $1$PmBl0CzG$sUwGMzxCKqovvMKaBhyXK. # want: $1$PmBl0CzG$E/uNgzObrQHl3Wp.C6/TX0 not ok 17 - Password should match crypted password # Failed test 17: "Password should match crypted password" # have: $1$KCxoeJAK$APSOlalADk1akGrItOASx. # want: $1$KCxoeJAK$DrhZ5wT4aUzJE.RydnJLy0 # Looks like you failed 2 tests of 17 tests/011-userfunc.pg .. Failed 2/17 subtests Test Summary Report ------------------- Say what? tests/011-userfunc.pg (Wstat: 0 Tests: 17 Failed: 2) Failed tests: 16-17 Files=1, Tests=17, 0 wallclock secs Result: FAIL
    278. 278. Are We Sane? % pg_prove -d flipr tests/011-userfunc.pg tests/011-userfunc.pg .. 1/? not ok 16 - Password should match crypted password # Failed test 16: "Password should match crypted password" # have: $1$PmBl0CzG$sUwGMzxCKqovvMKaBhyXK. # want: $1$PmBl0CzG$E/uNgzObrQHl3Wp.C6/TX0 not ok 17 - Password should match crypted password # Failed test 17: "Password should match crypted password" # have: $1$KCxoeJAK$APSOlalADk1akGrItOASx. # want: $1$KCxoeJAK$DrhZ5wT4aUzJE.RydnJLy0 # Looks like you failed 2 tests of 17 tests/011-userfunc.pg .. Failed 2/17 subtests Test Summary Report ------------------- Say what? tests/011-userfunc.pg (Wstat: 0 Tests: 17 Failed: 2) Failed tests: 16-17 Files=1, Tests=17, 0 wallclock secs Result: FAIL
    279. 279. What’d We Do Wrong? CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ 1 INSERT INTO users values($1, crypt($ , gen_salt('md5'))); $$; 011-userfunc.sql
    280. 280. What’d We Do Wrong? CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ 2 INSERT INTO users values($1, crypt($ , gen_salt('md5'))); $$; 011-userfunc.sql
    281. 281. What’d We Do Wrong? CREATE OR REPLACE FUNCTION ins_user( nickname TEXT, password TEXT ) RETURNS VOID LANGUAGE SQL AS $$ 2 INSERT INTO users values($1, crypt($ , gen_salt('md5'))); $$; Cryptonite! 011-userfunc.sql
    282. 282. And…
    283. 283. And… % psql -d flipr -f sql/011-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/011-userfunc.pg tests/011-userfunc.pg .. ok All tests successful. Files=1, Tests=17, 1 wallclock secs Result: PASS
    284. 284. And… % psql -d flipr -f sql/011-userfunc.sql CREATE FUNCTION % pg_prove -d flipr tests/011-userfunc.pg tests/011-userfunc.pg .. ok All tests successful. Files=1, Tests=17, 1 wallclock secs Result: PASS We’re there!
    285. 285. Next: upd_pass() antisocial network
    286. 286. Next: upd_pass() Need password updating antisocial network
    287. 287. Next: upd_pass() Need password updating Things to consider: antisocial network
    288. 288. Next: upd_pass() Need password updating Things to consider: Update nonexistent user? antisocial network
    289. 289. Next: upd_pass() Need password updating Things to consider: Update nonexistent user? Update with invalid password? antisocial network
    290. 290. Next: upd_pass() Need password updating Things to consider: Update nonexistent user? Update with invalid password? Was password updated? antisocial network
    291. 291. antisocial network Your Turn
    292. 292. antisocial network Your Turn Create upd_pass()
    293. 293. antisocial network Your Turn Create upd_pass() Project Repo: http://github.com/theory/tddd/
    294. 294. How’d We Do? 012-userfunc.sql
    295. 295. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); 012-userfunc.sql
    296. 296. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); 012-userfunc.sql
    297. 297. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); 012-userfunc.sql
    298. 298. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); 012-userfunc.sql
    299. 299. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); 012-userfunc.sql
    300. 300. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); 012-userfunc.sql
    301. 301. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); SELECT ok( NOT upd_pass('nobody', 'foo', 'bar'), 'upd_pass() should return false for nonexistent user' ); SELECT ok( NOT upd_pass('theory', 'foo', 'bar'), 'upd_pass() should return false for invalid old pass' ); SELECT is( password, crypt('wet blanket', password), 'Password should be unchanged' ) FROM users WHERE nickname = 'theory'; 012-userfunc.sql
    302. 302. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); SELECT ok( NOT upd_pass('nobody', 'foo', 'bar'), 'upd_pass() should return false for nonexistent user' ); SELECT ok( NOT upd_pass('theory', 'foo', 'bar'), 'upd_pass() should return false for invalid old pass' ); SELECT is( password, crypt('wet blanket', password), 'Password should be unchanged' ) FROM users WHERE nickname = 'theory'; 012-userfunc.sql
    303. 303. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); SELECT ok( NOT upd_pass('nobody', 'foo', 'bar'), 'upd_pass() should return false for nonexistent user' ); SELECT ok( NOT upd_pass('theory', 'foo', 'bar'), 'upd_pass() should return false for invalid old pass' ); SELECT is( password, crypt('wet blanket', password), 'Password should be unchanged' ) FROM users WHERE nickname = 'theory'; 012-userfunc.sql
    304. 304. How’d We Do? SELECT has_language('plpgsql'); SELECT has_function('upd_pass'); SELECT has_function('upd_pass', ARRAY['text', 'text', 'text']); SELECT function_returns('upd_pass', 'boolean'); SELECT function_lang_is('upd_pass', 'plpgsql'); SELECT ok( NOT upd_pass('nobody', 'foo', 'bar'), 'upd_pass() should return false for nonexistent user' ); SELECT ok( NOT upd_pass('theory', 'foo', 'bar'), 'upd_pass() should return false for invalid old pass' ); SELECT is( password, crypt('wet blanket', password), 'Password should be unchanged' ) FROM users WHERE nickname = 'theory'; 012-userfunc.sql
    305. 305. How’d We Do? 012-userfunc.sql
    306. 306. How’d We Do? SELECT ok( upd_pass('theory', 'wet blanket', 'pgtap rulez'), 'upd_pass() should return true for proper args' ); SELECT is( password, crypt('pgtap rulez', password), 'Password should now be changed' ) FROM users WHERE nickname = 'theory'; 012-userfunc.sql
    307. 307. How’d We Do? SELECT ok( upd_pass('theory', 'wet blanket', 'pgtap rulez'), 'upd_pass() should return true for proper args' ); SELECT is( password, crypt('pgtap rulez', password), 'Password should now be changed' ) FROM users WHERE nickname = 'theory'; 012-userfunc.sql
    308. 308. How’d We Do? SELECT ok( upd_pass('theory', 'wet blanket', 'pgtap rulez'), 'upd_pass() should return true for proper args' ); SELECT is( password, crypt('pgtap rulez', password), 'Password should now be changed' ) FROM users WHERE nickname = 'theory'; 012-userfunc.sql
    309. 309. What Does it Look Like? 013-privs.sql
    310. 310. What Does it Look Like? CREATE OR REPLACE FUNCTION upd_pass( nick TEXT, oldpass TEXT, newpass TEXT ) RETURNS BOOLEAN LANGUAGE plpgsql AS $$ BEGIN UPDATE users SET password = crypt($3, gen_salt('md5')) WHERE nickname = $1 AND password = crypt($2, password); RETURN FOUND; END; $$; 013-privs.sql
    311. 311. What Does it Look Like? CREATE OR REPLACE FUNCTION upd_pass( nick TEXT, oldpass TEXT, newpass TEXT ) RETURNS BOOLEAN LANGUAGE plpgsql AS $$ BEGIN UPDATE users SET password = crypt($3, gen_salt('md5')) WHERE nickname = $1 AND password = crypt($2, password); RETURN FOUND; END; $$; 013-privs.sql Good, eh?
    312. 312. What About Privileges? antisocial network
    313. 313. What About Privileges? Got solid functions antisocial network
    314. 314. What About Privileges? Got solid functions Apps still using tables antisocial network
    315. 315. What About Privileges? Got solid functions Apps still using tables How to prevent that? antisocial network
    316. 316. What About Privileges? Got solid functions Apps still using tables How to prevent that? Privileges! antisocial network
    317. 317. What About Privileges? Got solid functions Apps still using tables How to prevent that? Privileges! Deny access to table antisocial network
    318. 318. What About Privileges? Got solid functions Apps still using tables How to prevent that? Privileges! Deny access to table Allow access to functions antisocial network
    319. 319. Test Privileges 013-privs.pg
    320. 320. Test Privileges SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT has_role('fliprapp'); SELECT isnt_superuser('fliprapp'); SELECT finish(); ROLLBACK; 013-privs.pg
    321. 321. Test Privileges SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT has_role('fliprapp'); SELECT isnt_superuser('fliprapp'); SELECT finish(); ROLLBACK; 013-privs.pg
    322. 322. Test Privileges SET search_path = public,tap; BEGIN; --SELECT plan( 5 ); SELECT * FROM no_plan(); SELECT has_role('fliprapp'); SELECT isnt_superuser('fliprapp'); SELECT finish(); ROLLBACK; 013-privs.pg
    323. 323. Check Privileges
    324. 324. Check Privileges % pg_prove -d flipr tests/013-privs.pg tests/013-privs.pg .. 1/? not ok 1 - Role fliprapp should exist # Failed test 1: "Role fliprapp should exist" not ok 2 - User fliprapp should not be a super user # Failed test 2: "User fliprapp should not be a super user" # User fliprapp does not exist # Looks like you failed 2 tests of 2 tests/013-privs.pg .. Failed 2/2 subtests Test Summary Report ------------------- tests/013-privs.pg (Wstat: 0 Tests: 2 Failed: 2) Failed tests: 1-2 Files=1, Tests=2, 0 wallclock secs Result: FAIL
    325. 325. Check Privileges % pg_prove -d flipr tests/013-privs.pg tests/013-privs.pg .. 1/? not ok 1 - Role fliprapp should exist # Failed test 1: "Role fliprapp should exist" not ok 2 - User fliprapp should not be a super user # Failed test 2: "User fliprapp should not be a super user" # User fliprapp does not exist # Looks like you failed 2 tests of 2 tests/013-privs.pg .. Failed 2/2 subtests Test Summary Report ------------------- tests/013-privs.pg (Wstat: 0 Tests: 2 Failed: 2) Failed tests: 1-2 Files=1, Tests=2, 0 wallclock secs Result: FAIL
    326. 326. Check Privileges % pg_prove -d flipr tests/013-privs.pg tests/013-privs.pg .. 1/? not ok 1 - Role fliprapp should exist # Failed test 1: "Role fliprapp should exist" not ok 2 - User fliprapp should not be a super user # Failed test 2: "User fliprapp should not be a super user" # User fliprapp does not exist # Looks like you failed 2 tests of 2 tests/013-privs.pg .. Failed 2/2 subtests Test Summary Report ------------------- tests/013-privs.pg (Wstat: 0 Tests: 2 Failed: 2) Failed tests: 1-2 Files=1, Tests=2, 0 wallclock secs Result: FAIL
    327. 327. Add a User 013-privs.sql
    328. 328. Add a User CREATE USER fliprapp WITH LOGIN; 013-privs.sql
    329. 329. Add a User CREATE USER fliprapp WITH LOGIN; Easy, right? 013-privs.sql
    330. 330. User Test
    331. 331. 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 All tests successful. Files=1, Tests=2, 0 wallclock secs Result: PASS
    332. 332. 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 All tests successful. Files=1, Tests=2, 0 wallclock secs Result: PASS Riiiiight.
    333. 333. Sanity Check SELECT has_role('fliprapp'); SELECT isnt_superuser('fliprapp'); 014-userfunc.pg
    334. 334. Sanity Check SELECT has_role('fliprapp'); SELECT isnt_superuser('fliprapp'); SELECT volatility_is('ins_user', 'volatile'); SELECT volatility_is('upd_pass', 'volatile'); SELECT is_definer('ins_user'); SELECT is_definer('upd_pass'); 014-userfunc.pg
    335. 335. Sanity Check SELECT has_role('fliprapp'); SELECT isnt_superuser('fliprapp'); SELECT volatility_is('ins_user', 'volatile'); SELECT volatility_is('upd_pass', 'volatile'); SELECT is_definer('ins_user'); SELECT is_definer('upd_pass'); 014-userfunc.pg

    ×