#!/usr/local/bin/php
<?php
/**
 *
 * $Horde:  $
 *
 * Copyright 2003-2006 Karsten Fourmont <karsten@horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL).  If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Karsten Fourmont <karsten@horde.org>
 * @since   Horde 3.0
 */

@define('AUTH_HANDLER', true);
@define('HORDE_BASE', dirname(__FILE__) . '/..');


@define('TESTUSER','testuser');


/* Set this to true to skip cleanup after tests.
 * Be aware that this results in a test user with
 * a standard password accessible on your system.
 */
$skipcleanup = false;

// Do CLI checks and environment setup first.
require_once HORDE_BASE . '/lib/core.php';
require_once 'Horde/CLI.php';

// Make sure no one runs this from the web.
if (!Horde_CLI::runningFromCLI()) {
    exit("Must be run from the command line\n");
}

// Load the CLI environment - make sure there's no time limit, init
// some variables, etc.
Horde_CLI::init();
$cli = &new Horde_CLI();

require_once HORDE_BASE . '/lib/base.php';


/* Get an Auth object. */
$auth = &Auth::singleton($conf['auth']['driver']);
if (is_a($auth, 'PEAR_Error')) {
    Horde::fatal($auth, __FILE__, __LINE__);
}

/* Create test user */
if (!$auth->exists(TESTUSER)) {

    $o =$auth->addUser(TESTUSER, array('password' => rand()));
    if (is_a($o, 'PEAR_Error')) {
        Horde::fatal($o, __FILE__, __LINE__);
    }
}
$auth->setAuth(TESTUSER, array());

/* The actual testing takes place her: */
testService('tasks');
testService('calendar');
testService('notes');
testService('contacts');

/* Cleanup */
if ($skipcleanup) {
    print "\nSkipping cleanup. Test user remains in the system!\n";
} else {
    print "\nCleaning up: removing test user data and test user...";
    $o = $auth->removeUserData(TESTUSER);
    if (is_a($o, 'PEAR_Error')) {
        Horde::fatal($o, __FILE__, __LINE__);
    }
    $o = $auth->removeUser(TESTUSER);
    if (is_a($o, 'PEAR_Error')) {
        Horde::fatal($o, __FILE__, __LINE__);
    }
    print "OK\n";
}


function testService($db)
{

    global $registry;
    
    print "\nChecking service $db\n";
    
    print "Removing all user data...";
    $o = $registry->call($db . '/removeUserData', array('user' => TESTUSER));
    if (is_a($o, 'PEAR_Error')) {
        Horde::fatal($o, __FILE__, __LINE__);
    }
    print "OK\n";

    print "Checking for empty list...";
    $uids = $registry->call($db . '/list', array());
    if (is_a($uids, 'PEAR_Error')) {
        Horde::fatal($uids, __FILE__, __LINE__);
    }
    if (count($uids) != 0) {
        Horde::fatal("Error! Expected empty list but got entries: " . count($uids), __FILE__, __LINE__);
    }
    print "OK\n";

    print "Creating two test entries...";
    $id1 = $registry->call($db . '/import', createTestData($db));
    if (empty($id1) || is_a($id1, 'PEAR_Error')) {
        Horde::fatal($id1, __FILE__, __LINE__);
    }
    
    $id2 = $registry->call($db . '/import', createTestData($db));
    if (empty($id2) || is_a($id2, 'PEAR_Error')) {
        Horde::fatal($id1, __FILE__, __LINE__);
    }
    print "OK\n";

    print "Checking timestamps...";
    $ts1 = $registry->call($db . '/getActionTimestamp', array($id1, 'add'));
    if (is_a($ts1, 'PEAR_Error') || $ts1<100) {
        Horde::fatal($ts1, __FILE__, __LINE__);
    }
    $ts2 = $registry->call($db . '/getActionTimestamp', array($id2, 'add'));
    if (is_a($ts2, 'PEAR_Error') || $ts2<100) {
        Horde::fatal($ts2, __FILE__, __LINE__);
    }
    print "OK\n";

    print "Retrieving content...";
    $c1 = $registry->call($db . '/export', array($id1, getMimeType($db)));
    if (is_a($c1, 'PEAR_Error')) {
        Horde::fatal($c1, __FILE__, __LINE__);
    }
    $c2 = $registry->call($db . '/export', array($id2, getMimeType($db)));
    if (is_a($c2, 'PEAR_Error')) {
        Horde::fatal($c2, __FILE__, __LINE__);
    }
    if ($c1 == $c2 || strlen($c1)<100 || strlen($c2)<100) {
        Horde::fatal("Problems with entry contents!", __FILE__, __LINE__);
    }
    print "OK\n";

    print "Replacing entry two with content from entry one...";
    stripUID($c1);
    $x = $registry->call($db . '/replace', array($id2, $c1, getMimeType($db)));
    if (is_a($x, 'PEAR_Error')) {
        Horde::fatal($x, __FILE__, __LINE__);
    }
    $c2 = $registry->call($db . '/export', array($id2, getMimeType($db)));
    if (is_a($c2, 'PEAR_Error')) {
        Horde::fatal($c2, __FILE__, __LINE__);
    }
    print "OK\n";

    print "Deleting entry one...";
    $x = $registry->call($db . '/delete', array($id1));
    if (is_a($x, 'PEAR_Error')) {
        Horde::fatal($x, __FILE__, __LINE__);
    }
    print "OK\n";

    print "Checking for one element list...";
    $uids = $registry->call($db . '/list', array());
    if (is_a($o, 'PEAR_Error')) {
        Horde::fatal($o, __FILE__, __LINE__);
    }
    if (count($uids) != 1) {
        Horde::fatal("Error! Expected one element list but got entries: " . count($uids), __FILE__, __LINE__);
    }
    print "OK\n";


    print "Checking history...";
    $o = $registry->call($db. '/listBy', array('action' => 'add', 'timestamp' => 0));
    if (is_a($o, 'PEAR_Error')) {
        Horde::fatal($o, __FILE__, __LINE__);
    }
    /* The history must comprise to entries: $id1 and $id2 */
    $o2 = array_flip($o);
    if (count($o) != 2 || !isset($o2[$id1]) || !isset($o2[$id2])) {
        print "Add history:\n"; 
        var_dump($o);
        Horde::fatal("Unexpected values in add history!\n" .
                     "Expected $id1 and $id2.\n", __FILE__, __LINE__);
    }
    $o = $registry->call($db. '/listBy', array('action' => 'modify', 'timestamp' => 0));
    if (is_a($o, 'PEAR_Error')) {
        Horde::fatal($o, __FILE__, __LINE__);
    }
    if (count($o) != 1 || $o[0] !== $id2) {
        Horde::fatal("Unexpected values in modify history!", __FILE__, __LINE__);
    }
    $o = $registry->call($db. '/listBy', array('action' => 'delete', 'timestamp' => 0));
    if (is_a($o, 'PEAR_Error')) {
        Horde::fatal($o, __FILE__, __LINE__);
    }
    if (count($o) != 1 || $o[0] !== $id1) {
        print "Delete history:\n"; var_dump($o);
        Horde::fatal("Unexpected values in delete history!", __FILE__, __LINE__);
    }
    print "OK\n";
    
    return true; // all tests passed
}

/* Function to create some dummy test data */
function createTestData($db)
{
    switch ($db) {
    case 'contacts':
        return array('content' => 'BEGIN:VCARD
VERSION:2.1
REV:20040523T195308Z
UID:05e9cc080c5ccb80-00e0b8cfeee61637-106
N:Lastname;Firstname' . rand(). ';;;
TEL;WORK;X-SYNCMLREF1:0251-981588612
FN:Astrid Kaczmarek
END:VCARD
',
'contentType' => getMimeType($db));

    case 'tasks':
        return array('content' => 'BEGIN:VCALENDAR
VERSION:1.0
BEGIN:VTODO
SUMMARY:Task Testentry' . rand(). '
DESCRIPTION: description
PRIORITY:1
STATUS:NEEDS ACTION
END:VTODO
END:VCALENDAR
',
'contentType' => getMimeType($db));
    
    case 'calendar':
        return array('content' => 'BEGIN:VCALENDAR
VERSION:1.0
BEGIN:VEVENT
SUMMARY:Calendar Testentry' . rand(). '
DTSTART:20020605T070000Z
DTEND:20020605T215900Z
END:VEVENT
END:VCALENDAR
',
'contentType' => getMimeType($db));

    case 'notes':
        return array('content' => 'BEGIN:VNOTE
VERSION:1.1
BODY:Note Testentry' . rand(). '
END:VNOTE
',
'contentType' => getMimeType($db));

    }
}

function getMimeType($db)
{
    switch ($db) {
    case 'tasks':
        return 'text/x-vcalendar';
    case 'calendar':
        return 'text/x-vcalendar';
    case 'notes':
        return 'text/x-vnote';
    case 'contacts':
        return 'text/x-vcard';
    }
}

/*
 * Strip UID from content to avoid confusion when copying content data
 * from one entry to another.
 */
function stripUID(&$content)
{
    $content = preg_replace('/(\r\n|\r|\n)UID:.*?(\r\n|\r|\n)/', '\1', $content, 1);
    return $content;
}

