#!/usr/bin/env perl # -*-mode:cperl; indent-tabs-mode: nil-*- ## Test of customcode to handle exceptions use 5.008003; use strict; use warnings; use Data::Dumper; use lib 't','.'; use DBD::Pg; use Test::More; use vars qw/ $dbhX $dbhA $dbhB $dbhC $res $command $t $SQL %pkey %sth %sql $sth $count $val /; use BucardoTesting; my $bct = BucardoTesting->new({location => 'postgres'}) or BAIL_OUT "Creation of BucardoTesting object failed\n"; #plan tests => 9999; END { $bct and $bct->stop_bucardo($dbhX); $dbhX and $dbhX->disconnect(); $dbhA and $dbhA->disconnect(); $dbhB and $dbhB->disconnect(); $dbhC and $dbhC->disconnect(); } ## Get A, B, and C created, emptied out, and repopulated with sample data $dbhA = $bct->repopulate_cluster('A'); $dbhB = $bct->repopulate_cluster('B'); $dbhC = $bct->repopulate_cluster('C'); ## Store into hashes for convenience my %dbh = (A=>$dbhA, B=>$dbhB, C=>$dbhC); ## Create a bucardo database, and install Bucardo into it $dbhX = $bct->setup_bucardo('A'); ## Teach Bucardo about three databases for my $name (qw/ A B C /) { $t = "Adding database from cluster $name works"; my ($dbuser,$dbport,$dbhost) = $bct->add_db_args($name); $command = "bucardo add db $name dbname=bucardo_test user=$dbuser port=$dbport host=$dbhost"; $res = $bct->ctl($command); like ($res, qr/Added database "$name"/, $t); } ## Create a table with a non-primary key unique constraint $SQL = q{DROP TABLE IF EXISTS employee CASCADE}; $dbhA->do($SQL); $dbhB->do($SQL); $dbhC->do($SQL); $SQL = q{DROP TABLE IF EXISTS employee_conflict CASCADE}; $dbhA->do($SQL); $dbhB->do($SQL); $dbhC->do($SQL); $SQL = q{ CREATE TABLE employee ( id SERIAL PRIMARY KEY, fullname TEXT, email TEXT UNIQUE ); }; $dbhA->do($SQL); $dbhB->do($SQL); $dbhC->do($SQL); $SQL = q{CREATE TABLE employee_conflict (LIKE employee)}; $dbhA->do($SQL); $dbhB->do($SQL); $dbhC->do($SQL); $dbhA->commit();$dbhB->commit();$dbhC->commit(); ## Create a new herd for the table $t = q{Adding tables to new herd 'exherd' works}; $res = $bct->ctl(q{bucardo add table employee herd=exherd}); like ($res, qr/Created the relgroup named "exherd".*are now part of/s, $t); ## Create a new dbgroup going from A to B to C $t = q{Created a new dbgroup exabc for A <=> B <=> C}; $res = $bct->ctl('bucardo add dbgroup exabc A:source B:source C:source'); like ($res, qr/Created dbgroup "exabc"/, $t); ## Create a new sync $t = q{Created a new sync for dbgroup exabc}; $res = $bct->ctl('bucardo add sync exabc relgroup=exherd dbs=exabc status=active autokick=false'); like ($res, qr/Added sync "exabc"/, $t); ## Start listening for a syncdone message $dbhX->do('LISTEN bucardo_syncdone_exabc'); $dbhX->commit(); ## Start up Bucardo $bct->restart_bucardo($dbhX); ## Add some rows and verify that basic replication is working $SQL = 'INSERT INTO employee (id,fullname,email) VALUES (?,?,?)'; my $insert_ea = $dbhA->prepare($SQL); my $insert_eb = $dbhB->prepare($SQL); my $insert_ec = $dbhC->prepare($SQL); $insert_ea->execute(100, 'Alice', 'alice@acme' ); $insert_eb->execute(101, 'Bob', 'bob@acme' ); $dbhA->commit(); $dbhB->commit(); $dbhC->commit(); $bct->ctl('bucardo kick sync exabc 0'); ## We cool? $SQL = 'SELECT id FROM employee ORDER BY id'; for my $db (qw/ A B C /) { my $dbh = $dbh{$db}; my $result = $dbh->selectall_arrayref($SQL); $t = qq{Database $db has expected rows}; is_deeply ($result, [[100],[101]], $t); } ## Cause a unique index violation and confirm the sync dies $insert_eb->execute(102, 'Mallory1', 'mallory@acme' ); $insert_ec->execute(103, 'Mallory2', 'mallory@acme' ); $dbhA->commit(); $dbhB->commit(); $dbhC->commit(); $bct->ctl('bucardo kick sync exabc 0'); ## Check the status - should be bad $res = $bct->ctl('bucardo status exabc'); $t = q{Sync exabc is marked as bad after a failed run}; like ($res, qr{Current state\s+:\s+Bad}, $t); $t = q{Sync exabc shows a duplicate key violation}; like ($res, qr{ERROR.*employee_email_key}, $t); ## Add in a customcode exception handler $res = $bct->ctl('bucardo add customcode email_exception whenrun=exception src_code=t/customcode.exception.bucardotest.pl sync=exabc getdbh=1'); $t = q{Customcode exception handler was added for sync exabc}; like ($res, qr{Added customcode "email_exception"}, $t); # $res = $bct->ctl('update customcode email_exception status=inactive'); # diag Dumper $res; # exit; ## Reload the sync and verify the exception handler allows the sync to continue $bct->ctl('bucardo reload exabc'); $bct->ctl('bucardo kick sync exabc 0'); ## Status should now be good $res = $bct->ctl('bucardo status exabc'); $t = q{Sync exabc is marked as good after a exception-handled run}; like ($res, qr{Current state\s+:\s+Good}, $t); ## Make sure all the rows are as we expect inside employee ## We cool? $SQL = 'SELECT id,email,fullname FROM employee ORDER BY id'; for my $db (qw/ A B C /) { my $dbh = $dbh{$db}; my $result = $dbh->selectall_arrayref($SQL); $t = qq{Database $db has expected rows in employee}; is_deeply ($result,[ [100,'alice@acme','Alice'], [101,'bob@acme','Bob'], [102,'mallory@acme','Mallory1'] ],$t); } ## Make sure all the rows are as we expect inside employee_conflict $SQL = 'SELECT id,email,fullname FROM employee_conflict'; for my $db (qw/ C /) { my $dbh = $dbh{$db}; my $result = $dbh->selectall_arrayref($SQL); $t = qq{Database $db has expected rows in employee_conflict}; is_deeply ($result,[ [103,'mallory@acme','Mallory2'] ],$t); } ## Test disabling the customcode like $bct->ctl('update customcode email_exception status=inactive'), qr/\QChanged customcode "email_exception" status from 'active' to 'inactive'/, 'Change email_exception status to "inactive"'; ## Test goat-level customcode done_testing(); exit;