diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d2403d14..00513c3a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -157,3 +157,22 @@ test_ldap:
- cd testing && su testrunner -c 'IS_CI=yes ALLSUITES="ldap" ./run_regressions.sh all x'
after_script:
- testing/gitlab_ci_after_script.sh
+
+# Only test if the memcache test passes
+test_memcache_and_ldap:
+ stage: testldap
+ image: debian:unstable
+ artifacts:
+ paths:
+ - testing/report.xml
+ - apache2_log/*
+ - davical_log/*
+ reports:
+ junit: testing/report.xml
+ when:
+ always
+ script:
+ - testing/gitlab_ci_script.sh ldap_memcache_auth
+ - cd testing && su testrunner -c 'IS_CI=yes ALLSUITES="ldap_memcache" ./run_regressions.sh all x'
+ after_script:
+ - testing/gitlab_ci_after_script.sh
diff --git a/testing/gitlab_ci_script.sh b/testing/gitlab_ci_script.sh
index b86541c1..8928a147 100755
--- a/testing/gitlab_ci_script.sh
+++ b/testing/gitlab_ci_script.sh
@@ -17,9 +17,16 @@ ldap=0
memcache=0
interop=0
-if [[ $mode =~ "ldap" ]]; then
+if [[ $mode =~ "ldap_memcache_auth" ]]; then
ldap=1
memcache=1
+ memcache_auth=1
+elif [[ $mode =~ "ldap" ]]; then
+ ldap=1
+ memcache=1
+elif [[ $mode =~ "memcache_auth" ]]; then
+ memcache=1
+ memcache_auth=1
elif [[ $mode =~ "memcache" ]]; then
memcache=1
elif [[ $mode =~ "interop" ]]; then
@@ -109,6 +116,10 @@ if [ $memcache == 1 ]; then
conf_filter_memcache="| sed 's.//memcache ..g'"
fi
+if [ $memcache_auth == 1 ]; then
+ conf_filter_memcache="$conf_filter_memcache| sed 's.//memcache_auth ..g'"
+fi
+
###
### Prepare PHP for LDAP if we're using it.
###
diff --git a/testing/regression-conf.php.example b/testing/regression-conf.php.example
index 00d20355..435eed69 100644
--- a/testing/regression-conf.php.example
+++ b/testing/regression-conf.php.example
@@ -24,7 +24,9 @@
// if testing memcache
//memcache $c->memcache_servers[] = '127.0.0.1,11211';
- //memcache $c->auth_cache = true;
+
+ // if testing cached of auth with memcache
+ //memcache_auth $c->auth_cache = true;
// if testing LDAP
//ldap $c->authenticate_hook['call'] = 'LDAP_check';
diff --git a/testing/tests/ldap_memcache/0000-clear-memcached.result b/testing/tests/ldap_memcache/0000-clear-memcached.result
new file mode 100644
index 00000000..e178f823
--- /dev/null
+++ b/testing/tests/ldap_memcache/0000-clear-memcached.result
@@ -0,0 +1 @@
+OK
diff --git a/testing/tests/ldap_memcache/0000-clear-memcached.test b/testing/tests/ldap_memcache/0000-clear-memcached.test
new file mode 100644
index 00000000..dd88e094
--- /dev/null
+++ b/testing/tests/ldap_memcache/0000-clear-memcached.test
@@ -0,0 +1,3 @@
+# Ensure that memcached has nothing cached.
+
+SCRIPT=echo flush_all | nc -N 127.0.0.1 11211
diff --git a/testing/tests/ldap_memcache/0000-confirm-no-ldap1-user.result b/testing/tests/ldap_memcache/0000-confirm-no-ldap1-user.result
new file mode 100644
index 00000000..eef88daf
--- /dev/null
+++ b/testing/tests/ldap_memcache/0000-confirm-no-ldap1-user.result
@@ -0,0 +1 @@
+SQL Query 1 Result:
diff --git a/testing/tests/ldap_memcache/0000-confirm-no-ldap1-user.test b/testing/tests/ldap_memcache/0000-confirm-no-ldap1-user.test
new file mode 100644
index 00000000..8bd8893f
--- /dev/null
+++ b/testing/tests/ldap_memcache/0000-confirm-no-ldap1-user.test
@@ -0,0 +1,6 @@
+# Check that no usr record exists
+QUERY
+SELECT active ,email, fullname, last_used, password, username, user_no
+FROM usr
+WHERE username = 'ldap1';
+ENDQUERY
diff --git a/testing/tests/ldap_memcache/0001-test-ldap.result b/testing/tests/ldap_memcache/0001-test-ldap.result
new file mode 100644
index 00000000..b6bfbf33
--- /dev/null
+++ b/testing/tests/ldap_memcache/0001-test-ldap.result
@@ -0,0 +1,70 @@
+HTTP/1.1 207 Multi-Status
+Date: Dow, 01 Jan 2000 00:00:00 GMT
+DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
+DAV: extended-mkcol, bind, addressbook, calendar-auto-schedule, calendar-proxy
+ETag: "ed58709591152964fd4a584af5b55d79"
+Content-Length: 942
+Content-Type: text/xml; charset="utf-8"
+
+
+
+
+ /caldav.php/
+
+
+
+
+
+
+ HTTP/1.1 200 OK
+
+
+
+ /caldav.php/resource1/
+
+
+
+
+
+
+
+ HTTP/1.1 200 OK
+
+
+
+ /caldav.php/resource2/
+
+
+
+
+
+
+
+ HTTP/1.1 200 OK
+
+
+
+ /caldav.php/ldap1/
+
+
+
+
+
+
+
+ HTTP/1.1 200 OK
+
+
+
+
+SQL Query 1 Result:
+ active: >1<
+ email: >ldap1@example.com<
+ fullname: >ldap1<
+ last_used: >NULL<
+ password: >NULL<
+ user_no: >1001<
+ username: >ldap1<
+
+No salt for ldap1 found, passed
+No cached credentials found, passed
diff --git a/testing/tests/ldap_memcache/0001-test-ldap.test b/testing/tests/ldap_memcache/0001-test-ldap.test
new file mode 100644
index 00000000..dd96acc8
--- /dev/null
+++ b/testing/tests/ldap_memcache/0001-test-ldap.test
@@ -0,0 +1,101 @@
+BEGINPERL
+if ($debug) { $ENV{'LDAP_DEBUG'} = 1 };
+
+use Net::LDAP::Server::Test;
+use Net::LDAP;
+use IO::Socket::INET;
+
+#my $port = find_idle_port();
+
+#my $ldap_port = RT::Test->find_idle_port;
+my $ldap_port = 21394;
+my $ldap_socket = IO::Socket::INET->new(
+ Listen => 5,
+ Proto => 'tcp',
+ Reuse => 1,
+ LocalPort => $ldap_port,
+);
+
+# Keep it around after this block exits.
+$evaled{'ldap_server'} = Net::LDAP::Server::Test->new( $ldap_socket, auto_schema => 1 );
+
+my $ldap = Net::LDAP->new("localhost:$ldap_port") || die "Failed to instantiate Net::LDAP: $!";
+$ldap->bind();
+my $username = "ldap1";
+my $base = "dc=example,dc=com";
+my $dn = "uid=$username,ou=users,$base";
+my $entry = {
+ cn => $username,
+ mail => "$username\@example.com",
+ uid => $username,
+ objectClass => 'User',
+ userPassword => 'ldap1',
+};
+$ldap->add( $base );
+$ldap->add( $dn, attr => [%$entry] );
+
+# We need to keep the client around, otherwise the test server will exit.
+$evaled{'ldap_client'} = $ldap;
+#sleep 100;
+ENDPERL
+
+TYPE=PROPFIND
+HEADER=Content-Type: text/xml
+HEADER=Depth: 1
+AUTH=ldap1:ldap1
+HEAD
+
+BEGINDATA
+
+
+
+
+
+
+ENDDATA
+
+URL=http://regression_ldap.host/caldav.php/
+
+# Check that a usr record has been created.
+QUERY
+SELECT active, email, fullname, last_used, password, username, user_no
+FROM usr
+WHERE username = 'ldap1';
+ENDQUERY
+
+# Check log file to make sure expected lines are present.
+BEGINPERL
+my $log_file = '/var/log/apache2/regression-error.log';
+open(my $log, "< $log_file")
+ || die "Failed to open $log_file for reading: $!";
+
+my $no_salt = 0;
+my $cached_creds = 0;
+
+if (defined $request_id) {
+ while (<$log>) {
+ if (/davical: $request_id: ALL: HTTPAuthLogin:CheckCache: (.*)/) {
+ my $msg = $1;
+ if ($msg =~ /^No salt, assuming no cached credentials/) {
+ $no_salt = 1;
+ } elsif ($msg =~ /^Cached credentials for ldap1 are good/) {
+ $cached_creds = 1;
+ }
+ }
+ }
+
+ if ($no_salt) {
+ print "No salt for ldap1 found, passed\n";
+ } else {
+ print "Salt unexpectedly found for ldap1, failed\n";
+ }
+
+ if ($cached_creds) {
+ print "Cached credentials found, unexpected, failed\n";
+ } else {
+ print "No cached credentials found, passed\n";
+ }
+} else {
+ print "No request_id found, can't check log file, failed\n";
+}
+ENDPERL
diff --git a/testing/tests/ldap_memcache/0003-test-ldap.result b/testing/tests/ldap_memcache/0003-test-ldap.result
new file mode 100644
index 00000000..4c8bfdf5
--- /dev/null
+++ b/testing/tests/ldap_memcache/0003-test-ldap.result
@@ -0,0 +1,68 @@
+HTTP/1.1 207 Multi-Status
+Date: Dow, 01 Jan 2000 00:00:00 GMT
+DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
+DAV: extended-mkcol, bind, addressbook, calendar-auto-schedule, calendar-proxy
+ETag: "ed58709591152964fd4a584af5b55d79"
+Content-Length: 942
+Content-Type: text/xml; charset="utf-8"
+
+
+
+
+ /caldav.php/
+
+
+
+
+
+
+ HTTP/1.1 200 OK
+
+
+
+ /caldav.php/resource1/
+
+
+
+
+
+
+
+ HTTP/1.1 200 OK
+
+
+
+ /caldav.php/resource2/
+
+
+
+
+
+
+
+ HTTP/1.1 200 OK
+
+
+
+ /caldav.php/ldap1/
+
+
+
+
+
+
+
+ HTTP/1.1 200 OK
+
+
+
+
+SQL Query 1 Result:
+ active: >1<
+ email: >ldap1@example.com<
+ fullname: >ldap1<
+ last_used: >NULL<
+ password: >NULL<
+ user_no: >1001<
+ username: >ldap1<
+
diff --git a/testing/tests/ldap_memcache/0003-test-ldap.test b/testing/tests/ldap_memcache/0003-test-ldap.test
new file mode 100644
index 00000000..411fbf48
--- /dev/null
+++ b/testing/tests/ldap_memcache/0003-test-ldap.test
@@ -0,0 +1,103 @@
+BEGINPERL
+if ($debug) { $ENV{'LDAP_DEBUG'} = 1 };
+
+use Net::LDAP::Server::Test;
+use Net::LDAP;
+use IO::Socket::INET;
+
+#my $port = find_idle_port();
+
+#my $ldap_port = RT::Test->find_idle_port;
+my $ldap_port = 21394;
+my $ldap_socket = IO::Socket::INET->new(
+ Listen => 5,
+ Proto => 'tcp',
+ Reuse => 1,
+ LocalPort => $ldap_port,
+);
+
+# Keep it around after this block exits.
+$evaled{'ldap_server'} = Net::LDAP::Server::Test->new( $ldap_socket, auto_schema => 1 );
+
+my $ldap = Net::LDAP->new("localhost:$ldap_port") || die "Failed to instantiate Net::LDAP: $!";
+$ldap->bind();
+my $username = "ldap1";
+my $base = "dc=example,dc=com";
+my $dn = "uid=$username,ou=users,$base";
+my $entry = {
+ cn => $username,
+ mail => "$username\@example.com",
+ uid => $username,
+ objectClass => 'User',
+ userPassword => 'ldap1',
+};
+$ldap->add( $base );
+$ldap->add( $dn, attr => [%$entry] );
+
+# We need to keep the client around, otherwise the test server will exit.
+$evaled{'ldap_client'} = $ldap;
+#sleep 100;
+ENDPERL
+
+TYPE=PROPFIND
+HEADER=Content-Type: text/xml
+HEADER=Depth: 1
+AUTH=ldap1:ldap1
+HEAD
+
+BEGINDATA
+
+
+
+
+
+
+ENDDATA
+
+URL=http://regression_ldap.host/caldav.php/
+
+# Check that a usr record has been created.
+QUERY
+SELECT active, email, fullname, last_used, password, username, user_no
+FROM usr
+WHERE username = 'ldap1';
+ENDQUERY
+
+# Check to see if the log line for cached credentials being valid is
+# present. That is only issued if we successfully fetch valid credentials
+# from our cache.
+BEGINPERL
+my $log_file = '/var/log/apache2/regression-error.log';
+open(my $log, "< $log_file")
+ || die "Failed to open $log_file for reading: $!";
+
+my $no_salt = 0;
+my $cached_creds = 0;
+
+if (defined $request_id) {
+ while (<$log>) {
+ if (/davical: $request_id: ALL: HTTPAuthLogin:CheckCache: (.*)/) {
+ my $msg = $1;
+ if ($msg =~ /^No salt, assuming no cached credentials/) {
+ $no_salt = 1;
+ } elsif ($msg =~ /^Cached credentials for ldap1 are good/) {
+ $cached_creds = 1;
+ }
+ }
+ }
+
+ if ($no_salt) {
+ print "No salt for ldap1 found, failed\n";
+ } else {
+ print "Salt found for ldap1, passed\n";
+ }
+
+ if ($cached_creds) {
+ print "Cached credentials found, passed\n";
+ } else {
+ print "No cached credentials found, failed\n";
+ }
+} else {
+ print "No request_id found, can't check log file, failed\n";
+}
+ENDPERL
diff --git a/testing/tests/ldap_memcache/0005-test-invalid-user.result b/testing/tests/ldap_memcache/0005-test-invalid-user.result
new file mode 100644
index 00000000..22901a86
--- /dev/null
+++ b/testing/tests/ldap_memcache/0005-test-invalid-user.result
@@ -0,0 +1,9 @@
+HTTP/1.1 401 Unauthorized
+Date: Dow, 01 Jan 2000 00:00:00 GMT
+WWW-Authenticate: Basic realm="DAViCal CalDAV Server"
+Content-Length: 40
+Content-Type: text/plain; ; charset="utf-8"
+
+Please log in for access to this system.
+SQL Query 1 Result:
+No cached credentials found, passed
diff --git a/testing/tests/ldap_memcache/0005-test-invalid-user.test b/testing/tests/ldap_memcache/0005-test-invalid-user.test
new file mode 100644
index 00000000..1a07d3ae
--- /dev/null
+++ b/testing/tests/ldap_memcache/0005-test-invalid-user.test
@@ -0,0 +1,93 @@
+BEGINPERL
+if ($debug) { $ENV{'LDAP_DEBUG'} = 1 };
+
+use Net::LDAP::Server::Test;
+use Net::LDAP;
+use IO::Socket::INET;
+
+#my $port = find_idle_port();
+
+#my $ldap_port = RT::Test->find_idle_port;
+my $ldap_port = 21394;
+my $ldap_socket = IO::Socket::INET->new(
+ Listen => 5,
+ Proto => 'tcp',
+ Reuse => 1,
+ LocalPort => $ldap_port,
+);
+
+# Keep it around after this block exits.
+$evaled{'ldap_server'} = Net::LDAP::Server::Test->new( $ldap_socket, auto_schema => 1 );
+
+my $ldap = Net::LDAP->new("localhost:$ldap_port") || die "Failed to instantiate Net::LDAP: $!";
+$ldap->bind();
+my $username = "ldap1";
+my $base = "dc=example,dc=com";
+my $dn = "uid=$username,ou=users,$base";
+my $entry = {
+ cn => $username,
+ mail => "$username\@example.com",
+ uid => $username,
+ objectClass => 'User',
+ userPassword => 'ldap1',
+};
+$ldap->add( $base );
+$ldap->add( $dn, attr => [%$entry] );
+
+# We need to keep the client around, otherwise the test server will exit.
+$evaled{'ldap_client'} = $ldap;
+#sleep 100;
+ENDPERL
+
+TYPE=PROPFIND
+HEADER=Content-Type: text/xml
+HEADER=Depth: 1
+AUTH=ldap2:ldap2
+HEAD
+
+BEGINDATA
+
+
+
+
+
+
+ENDDATA
+
+URL=http://regression_ldap.host/caldav.php/
+
+# Check that no usr record has been created.
+QUERY
+SELECT active, email, fullname, last_used, password, username, user_no
+FROM usr
+WHERE username = 'ldap2';
+ENDQUERY
+
+# Make sure there are no cached credentials present.
+BEGINPERL
+my $log_file = '/var/log/apache2/regression-error.log';
+open(my $log, "< $log_file")
+ || die "Failed to open $log_file for reading: $!";
+
+my $cached_creds = 0;
+
+if (defined $request_id) {
+ while (<$log>) {
+ if (/davical: $request_id: ALL: HTTPAuthLogin:CheckCache: (.*)/) {
+ my $msg = $1;
+
+ if ($msg =~ /^Cached credentials for ldap2/) {
+ $cached_creds = 1;
+ }
+ }
+ }
+
+ if ($cached_creds) {
+ print "Cached credentials found, unexpected, failed\n";
+ } else {
+ print "No cached credentials found, passed\n";
+ }
+} else {
+ print "No request_id found, can't check log file, failed\n";
+}
+ENDPERL
diff --git a/testing/tests/ldap_memcache/0007-test-invalid-user.result b/testing/tests/ldap_memcache/0007-test-invalid-user.result
new file mode 100644
index 00000000..7f26297f
--- /dev/null
+++ b/testing/tests/ldap_memcache/0007-test-invalid-user.result
@@ -0,0 +1,11 @@
+HTTP/1.1 401 Unauthorized
+Date: Dow, 01 Jan 2000 00:00:00 GMT
+WWW-Authenticate: Basic realm="DAViCal CalDAV Server"
+Content-Length: 40
+Content-Type: text/plain; ; charset="utf-8"
+
+Please log in for access to this system.
+SQL Query 1 Result:
+Salt found for ldap2, passed
+Cached credentials found (invalid), correct, passed
+No connection to LDAP, auth bailed out due to cached credentials, passed
diff --git a/testing/tests/ldap_memcache/0007-test-invalid-user.test b/testing/tests/ldap_memcache/0007-test-invalid-user.test
new file mode 100644
index 00000000..8feac916
--- /dev/null
+++ b/testing/tests/ldap_memcache/0007-test-invalid-user.test
@@ -0,0 +1,115 @@
+# Test again for an invalid user to check that the failed credentials are
+# cached.
+BEGINPERL
+if ($debug) { $ENV{'LDAP_DEBUG'} = 1 };
+
+use Net::LDAP::Server::Test;
+use Net::LDAP;
+use IO::Socket::INET;
+
+#my $port = find_idle_port();
+
+#my $ldap_port = RT::Test->find_idle_port;
+my $ldap_port = 21394;
+my $ldap_socket = IO::Socket::INET->new(
+ Listen => 5,
+ Proto => 'tcp',
+ Reuse => 1,
+ LocalPort => $ldap_port,
+);
+
+# Keep it around after this block exits.
+$evaled{'ldap_server'} = Net::LDAP::Server::Test->new( $ldap_socket, auto_schema => 1 );
+
+my $ldap = Net::LDAP->new("localhost:$ldap_port") || die "Failed to instantiate Net::LDAP: $!";
+$ldap->bind();
+my $username = "ldap1";
+my $base = "dc=example,dc=com";
+my $dn = "uid=$username,ou=users,$base";
+my $entry = {
+ cn => $username,
+ mail => "$username\@example.com",
+ uid => $username,
+ objectClass => 'User',
+ userPassword => 'ldap1',
+};
+$ldap->add( $base );
+$ldap->add( $dn, attr => [%$entry] );
+
+# We need to keep the client around, otherwise the test server will exit.
+$evaled{'ldap_client'} = $ldap;
+#sleep 100;
+ENDPERL
+
+TYPE=PROPFIND
+HEADER=Content-Type: text/xml
+HEADER=Depth: 1
+AUTH=ldap2:ldap2
+HEAD
+
+BEGINDATA
+
+
+
+
+
+
+ENDDATA
+
+URL=http://regression_ldap.host/caldav.php/
+
+# Check that no usr record has been created.
+QUERY
+SELECT active, email, fullname, last_used, password, username, user_no
+FROM usr
+WHERE username = 'ldap2';
+ENDQUERY
+
+# Check to see if the log line for cached credentials being invalid is
+# present. That is only issued if we've fetch a fail for the credentials
+# from our cache.
+BEGINPERL
+my $log_file = '/var/log/apache2/regression-error.log';
+open(my $log, "< $log_file")
+ || die "Failed to open $log_file for reading: $!";
+
+my $no_salt = 0;
+my $cached_creds = 0;
+my $ldap_conn = 0;
+
+if (defined $request_id) {
+ while (<$log>) {
+ if (/davical: $request_id: ALL: (HTTPAuthLogin:CheckCache|LDAP:drivers_ldap ): (.*)/) {
+ my $msg = $2;
+ if ($msg =~ /^No salt, assuming no cached credentials/) {
+ $no_salt = 1;
+ } elsif ($msg =~ /^Cached credentials are good and invalid/) {
+ $cached_creds = 1;
+ } elsif ($msg =~ /^Connected to LDAP server/) {
+ $ldap_conn = 1;
+ }
+ }
+ }
+
+ if ($no_salt) {
+ print "No salt for ldap2 found, failed\n";
+ } else {
+ print "Salt found for ldap2, passed\n";
+ }
+
+ if ($cached_creds) {
+ print "Cached credentials found (invalid), correct, passed\n";
+ } else {
+ print "No cached credentials found, unexpected, failed\n";
+ }
+
+ if ($ldap_conn) {
+ print "Conencted to LDAP server, unexpected, failed\n";
+ } else {
+ print "No connection to LDAP, auth bailed out due to cached credentials, passed\n";
+ }
+} else {
+ print "No request_id found, can't check log file, failed\n";
+}
+ENDPERL
+
diff --git a/testing/tests/ldap_memcache/Create-Database.result b/testing/tests/ldap_memcache/Create-Database.result
new file mode 100644
index 00000000..3ce079b7
--- /dev/null
+++ b/testing/tests/ldap_memcache/Create-Database.result
@@ -0,0 +1,22 @@
+
+
+Supported locales updated.
+Updated view: dav_principal.sql applied.
+CalDAV functions updated.
+RRULE functions updated.
+Database permissions updated.
+The database is version XX currently at revision 1.3.5.
+Applying patch 1.3.6.sql ... succeeded.
+Successfully applied 1 patches.
+Supported locales updated.
+Updated view: dav_principal.sql applied.
+CalDAV functions updated.
+RRULE functions updated.
+Database permissions updated.
+NOTE
+====
+* The password for the 'admin' user has been set to 'nimda'
+
+Thanks for trying DAViCal! Check the configuration in /etc/davical/config.php.
+For help, look at our website and wiki, or visit #davical on irc.oftc.net.
+
diff --git a/testing/tests/ldap_memcache/Dump-Database.result b/testing/tests/ldap_memcache/Dump-Database.result
new file mode 100644
index 00000000..e69de29b
diff --git a/testing/tests/ldap_memcache/Load-Sample-Data.result b/testing/tests/ldap_memcache/Load-Sample-Data.result
new file mode 100644
index 00000000..b86d6212
--- /dev/null
+++ b/testing/tests/ldap_memcache/Load-Sample-Data.result
@@ -0,0 +1,15 @@
+ setval
+--------
+ 1000
+(1 row)
+
+ setval
+--------
+ 1000
+(1 row)
+
+ setval
+--------
+ 10
+(1 row)
+
diff --git a/testing/tests/ldap_memcache/Really-Upgrade-Database.result b/testing/tests/ldap_memcache/Really-Upgrade-Database.result
new file mode 100644
index 00000000..ddf131d0
--- /dev/null
+++ b/testing/tests/ldap_memcache/Really-Upgrade-Database.result
@@ -0,0 +1,7 @@
+The database is version XX currently at revision 1.3.6.
+No patches were applied.
+Supported locales updated.
+Updated view: dav_principal.sql applied.
+CalDAV functions updated.
+RRULE functions updated.
+Database permissions updated.
diff --git a/testing/tests/ldap_memcache/Restore-Database.result b/testing/tests/ldap_memcache/Restore-Database.result
new file mode 100644
index 00000000..67afac31
--- /dev/null
+++ b/testing/tests/ldap_memcache/Restore-Database.result
@@ -0,0 +1,140 @@
+ set_config
+------------
+ public
+(1 row)
+
+ setval
+--------
+ 1013
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 4
+(1 row)
+
+ setval
+--------
+ 10
+(1 row)
+
+ setval
+--------
+ 10
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1
+(1 row)
+
+ setval
+--------
+ 1000
+(1 row)
+
diff --git a/testing/tests/ldap_memcache/Upgrade-Database.result b/testing/tests/ldap_memcache/Upgrade-Database.result
new file mode 100644
index 00000000..a34c672b
--- /dev/null
+++ b/testing/tests/ldap_memcache/Upgrade-Database.result
@@ -0,0 +1,5 @@
+Supported locales updated.
+Updated view: dav_principal.sql applied.
+CalDAV functions updated.
+RRULE functions updated.
+Database permissions updated.
diff --git a/testing/tests/ldap_memcache/sample-data.sql b/testing/tests/ldap_memcache/sample-data.sql
new file mode 100644
index 00000000..89ed56b6
--- /dev/null
+++ b/testing/tests/ldap_memcache/sample-data.sql
@@ -0,0 +1,126 @@
+-- Some sample data to prime the database...
+-- base-data.sql should be processed before this
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 2, TRUE, current_date, current_date, 'andrew', '**x', 'Andrew McMillan', 'andrew@catalyst.net.nz' );
+INSERT INTO role_member (user_no, role_no) VALUES( 2, 1);
+
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 10, TRUE, current_date, current_date, 'user1', '**user1', 'User 1', 'user1@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 11, TRUE, current_date, current_date, 'user2', '**user2', 'User 2', 'user2@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 12, TRUE, current_date, current_date, 'user3', '**user3', 'User 3', 'user3@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 13, TRUE, current_date, current_date, 'user4', '**user4', 'User 4', 'user4@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 14, TRUE, current_date, current_date, 'user5', '**user5', 'User 5', 'user5@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 15, TRUE, current_date, current_date, 'User Six', '**user6', 'User 6', 'user6@example.net' );
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 20, TRUE, current_date, current_date, 'manager1', '**manager1', 'Manager 1', 'manager1@example.net' );
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 30, TRUE, current_date, current_date, 'assistant1', '**assistant1', 'Assistant 1', 'assistant1@example.net' );
+
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 100, TRUE, current_date, current_date, 'resource1', '*salt*unpossible', 'Resource 1', 'resource1@example.net' );
+INSERT INTO role_member (user_no, role_no) VALUES( 100, 4);
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 101, TRUE, current_date, current_date, 'resource2', '*salt*unpossible', 'Resource 2', 'resource2@example.net' );
+INSERT INTO role_member (user_no, role_no) VALUES( 101, 4);
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 200, TRUE, current_date, current_date, 'resmgr1', '*salt*unpossible', 'Resource Managers', 'resource-managers@example.net' );
+INSERT INTO role_member (user_no, role_no) VALUES( 200, 2);
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+ VALUES( 300, TRUE, current_date, current_date, 'teamclient1', '*salt*unpossible', 'Team for Client1', 'team-client1@example.net' );
+INSERT INTO role_member (user_no, role_no) VALUES( 300, 2);
+
+SELECT setval('usr_user_no_seq', 1000);
+SELECT setval('dav_id_seq', 1000);
+
+UPDATE usr SET joined = '2009-06-01', updated = '2009-06-02';
+
+INSERT INTO collection (user_no, parent_container, dav_name, dav_etag,
+ dav_displayname, is_calendar, created, modified,
+ public_events_only, publicly_readable, collection_id, resourcetypes )
+ SELECT user_no, '/' || username || '/', '/' || username || '/home/', md5(username),
+ username || ' home', TRUE, '2009-06-03', '2009-06-04',
+ FALSE, FALSE, user_no + 150, ''
+ FROM usr ORDER BY user_no;
+
+INSERT INTO collection (user_no, parent_container, dav_name, dav_etag,
+ dav_displayname, is_calendar, is_addressbook, created, modified,
+ public_events_only, publicly_readable, collection_id, resourcetypes )
+ SELECT user_no, '/' || username || '/', '/' || username || '/addresses/', md5(username),
+ username || ' addresses', FALSE, TRUE, '1957-07-26', '1998-03-16',
+ FALSE, FALSE, user_no + 450, ''
+ FROM usr ORDER BY user_no;
+
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+ SELECT 1, user_no, fullname, privilege_to_bits(ARRAY['read-free-busy','schedule-send','schedule-deliver']) FROM usr
+ WHERE NOT EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Group' AND role_member.user_no = usr.user_no)
+ AND NOT EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Resource' AND role_member.user_no = usr.user_no)
+ AND NOT EXISTS(SELECT 1 FROM principal WHERE principal.user_no = usr.user_no) ORDER BY user_no;
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+ SELECT 2, user_no, fullname, privilege_to_bits(ARRAY['read','schedule-send','schedule-deliver']) FROM usr
+ WHERE EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Resource' AND role_member.user_no = usr.user_no)
+ AND NOT EXISTS(SELECT 1 FROM principal WHERE principal.user_no = usr.user_no) ORDER BY user_no;
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+ SELECT 3, user_no, fullname, privilege_to_bits(ARRAY['read-free-busy','schedule-send','schedule-deliver']) FROM usr
+ WHERE EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Group' AND role_member.user_no = usr.user_no)
+ AND NOT EXISTS(SELECT 1 FROM principal WHERE principal.user_no = usr.user_no) ORDER BY user_no;
+
+-- Set the insert sequence to the next number, with a minimum of 1000
+SELECT setval('relationship_type_rt_id_seq', (SELECT 10 UNION SELECT rt_id FROM relationship_type ORDER BY 1 DESC LIMIT 1) );
+
+-- The resources for meetings
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 200, 100, 1 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 200, 101, 1 );
+
+-- The people who administer meetings
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 10, 200, 1 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 11, 200, 1 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30, 200, 1 );
+
+-- Between a PA and their Manager
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30, 20, 2 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30, 10, 2 );
+
+
+-- Between a team
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 20, 300, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 10, 300, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30, 300, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 300, 20, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 300, 10, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 300, 30, 3 );
+
+-- Granting explicit free/busy permission
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 11, 10, 4 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 10, 11, 4 );
+
+
+UPDATE relationship r SET confers = (SELECT bit_confers FROM relationship_type rt WHERE rt.rt_id=r.rt_id);
+
+INSERT INTO group_member ( group_id, member_id)
+ SELECT g.principal_id, m.principal_id
+ FROM relationship JOIN principal g ON(to_user=g.user_no AND g.type_id = 3) -- Group
+ JOIN principal m ON(from_user=m.user_no AND m.type_id IN (1,2)) ORDER BY 1, 2; -- Person | Resource
+
+INSERT INTO grants ( by_principal, to_principal, privileges, is_group )
+ SELECT pby.principal_id AS by_principal, pto.principal_id AS to_principal,
+ confers AS privileges, pto.type_id > 2 AS is_group
+ FROM relationship r JOIN usr f ON(f.user_no=r.from_user)
+ JOIN usr t ON(t.user_no=r.to_user)
+ JOIN principal pby ON(t.user_no=pby.user_no)
+ JOIN principal pto ON(pto.user_no=f.user_no)
+ WHERE rt_id < 4 AND pby.type_id < 3 ORDER BY 1, 2;