#!/usr/bin/perl -w

# Generate test files for a selection of different dates, in an easily
# extendable and reproducible manner.
#
# When adding result blocks, in vi to append carriage return (aka ^M) use:
#     ^v^m
#
# Make all the dates UTC when we create them, otherwise we need to deal with
# the timezone changes in the past.  NZ used to be +11:39:04?!

use strict;
use v5.10;
use Template;
use Inline::Files;

die "Run in the same directory as the script"
    unless -f '2701-generate-date-tests.pl';

# PostgreSQL allows 4713 BC - 294276 AD for timestamps.
#   https://www.postgresql.org/docs/16/datatype-datetime.html
# To create dates prior to 1 AD in PostgreSQL you need to use 1000-01-01 BC,
# not ISO 8601 style which is -1001-01-01 (-1001 because 0 = -1 BC). We also
# need to allow a minus on all ISO date regexes.
#
# Currently DAViCal only support years < 10,000 due to various regexes that
# assume a maximum of 4 digits for the year. Due to some logic that adds
# 720 days to a year for filtering, the maximum year we realistically
# support is 9997.

my @dates = qw/
    00010102 08040229 50000701 18500820 19010101 20000101 30000101 99970820
    /;

my $collection = 'past-and-future';
my $user = 'user1';

my @files = (
    {
        name   => '[% num %]a-PUT-Event.test',
        handle => 'PUT',
    },
    {
        name   => '[% num %]a-PUT-Event.result',
        handle => 'PUT_RESULT',
    },
    {
        name   => '[% num %]b-REPORT-Event.test',
        handle => 'REPORT',
    },
    {
        name   => '[% num %]b-REPORT-Event.result',
        handle => 'REPORT_RESULT',
    },
    {
        name   => '[% num %]c-FREEBUSY-Event.test',
        handle => 'FREEBUSY',
    },
    {
        name   => '[% num %]c-FREEBUSY-Event.result',
        handle => 'FREEBUSY_RESULT',
    },
);

my $start_number = 2702;

my $tt = new Template;

# Load all the templates into a hash.
for my $file (@files) {
    local $/ = undef;

    my $handle = $file->{handle};
    $file->{template} = <$handle>;
}

for my $date (sort @dates) {
    my $vars = {
        user  => $user,
        date  => $date,
        num   => $start_number++,
        duration   => 'P1H',
        collection => $collection,
    };

    if ($date =~ /^-?(\d{4}\d{2}\d{2})$/) {
        # Just a date
        $vars->{'caldav_date'} = ":${date}T000000Z";
        $vars->{'timestamp'}   = "${date}T000000Z";
        $vars->{'range_end'}   = "${date}T020000Z";
    } else {
        # Timestamp
        $vars->{'caldav_date'} = ":${date}";
        $vars->{'timestamp'}   = $date;
    }

    for my $file (@files) {
        # Template the filename to write to.
        my $filename;
        $tt->process(\$file->{name}, $vars, \$filename)
            || die $tt->error(), "\n";

        # Actually write to the file.
        $tt->process(\$file->{template}, $vars, $filename)
            || die $tt->error(), "\n";
    }
}

__PUT__
#
# PUT an event in [% date %]
#
TYPE=PUT
AUTH=[% user %]:[% user %]
HEADER=User-Agent: test-suite/1.0
HEADER=Content-Type: text/calendar; charset=utf-8
HEAD
#

REPLACE=/^ETag: "[0-9a-f]+"/ETag: "a fine looking etag"/

BEGINDATA
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//artisanal-handcrafted-ICS.com//NONSGML Artisanal.iCal 1.0//EN
BEGIN:VEVENT
CLASS:PUBLIC
DTSTAMP:20240120T034700Z
DTSTART[% caldav_date %]
DURATION:[% duration %]
SUMMARY:Test date in [% date %]
TRANSP:OPAQUE
UID:[% num %]-PUT-Event-[% date %]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
END:VCALENDAR
ENDDATA

URL=http://regression.host/caldav.php/[% user %]/[% collection %]/[% num %]-PUT-Event-[% date %].ics

QUERY
SELECT caldav_data.user_no, caldav_type, logged_user, 
       uid, dtstamp, dtstart at time zone olson_name as dtstart,
       dtend at time zone olson_name as dtend, due, summary, location,
       description, priority, class, transp, rrule, url,
       percent_complete, tz_id, status,
       calendar_item.last_modified,
       caldav_data AS "A1 CalDAV DATA"
FROM caldav_data JOIN calendar_item USING(dav_name) LEFT JOIN timezones ON (tz_id=tzid)
WHERE caldav_data.dav_name ~ '^/[% user %]/[% collection %]/'
  AND uid = '[% num %]-PUT-Event-[% date %]'
ENDQUERY


__PUT_RESULT__
HTTP/1.1 201 Created
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: "a fine looking etag"
Content-Length: 0
Content-Type: text/plain; charset="utf-8"


SQL Query 1 Result:
   A1 CalDAV DATA: >BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//artisanal-handcrafted-ICS.com//NONSGML Artisanal.iCal 1.0//EN
BEGIN:VEVENT
CLASS:PUBLIC
DTSTAMP:20240120T034700Z
DTSTART[% caldav_date %]
DURATION:[% duration %]
SUMMARY:Test date in [% date %]
TRANSP:OPAQUE
UID:[% num %]-PUT-Event-[% date %]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
END:VCALENDAR
<
      caldav_type: >VEVENT<
            class: >PUBLIC<
      description: >NULL<
            dtend: >NULL<
          dtstamp: >2024-01-20 03:47:00<
          dtstart: >NULL<
              due: >NULL<
    last_modified: >2024-01-20 03:47:00<
         location: >NULL<
      logged_user: >10<
 percent_complete: >NULL<
         priority: >NULL<
            rrule: >FREQ=WEEKLY;COUNT=2<
           status: >NULL<
          summary: >Test date in [% date %]<
           transp: >OPAQUE<
            tz_id: >NULL<
              uid: >[% num %]-PUT-Event-[% date %]<
              url: >NULL<
          user_no: >10<

__REPORT__
#
# REPORT on event for [% date %], created by previous test.
#
TYPE=REPORT
AUTH=[% user %]:[% user %]
HEADER=Content-Type: text/xml; charset="UTF-8"
HEADER=Depth: 0
HEAD

REPLACE=/^ETag: "[0-9a-f]+"/ETag: "a fine looking etag"/
REPLACE=/^DTSTAMP:\d+T\d+Z/DTSTAMP:xx/
REPLACE=/^Content-Length: \d+/Content-Length: xx/

BEGINDATA
<?xml version='1.0' encoding='utf-8'?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  <D:prop>
    <C:calendar-data/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:time-range start="[% timestamp %]" end="[% range_end %]"/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
ENDDATA

URL=http://regression.host/caldav.php/[% user %]/[% collection %]/

__REPORT_RESULT__
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: "a fine looking etag"
Content-Length: xx
Content-Type: text/xml; charset="utf-8"

<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
 <response>
  <href>/caldav.php/[% user %]/[% collection %]/[% num %]-PUT-Event-[% date %].ics</href>
  <propstat>
   <prop>
    <C:calendar-data>BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//artisanal-handcrafted-ICS.com//NONSGML Artisanal.iCal 1.0//EN
BEGIN:VEVENT
CLASS:PUBLIC
DTSTAMP:xx
DTSTART:[% timestamp %]
DURATION:P1H
SUMMARY:Test date in [% date %]
TRANSP:OPAQUE
UID:[% num %]-PUT-Event-[% date %]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
END:VCALENDAR
</C:calendar-data>
   </prop>
   <status>HTTP/1.1 200 OK</status>
  </propstat>
 </response>
</multistatus>

__FREEBUSY__
#
# Free/Busy should show two busy periods one that starts at the timestamp
# and another a week later. I haven't added logic to the script which
# generates this file to work out what those other dates are, so as long
# as we have two busy periods, we'll call that done.
#
TYPE=REPORT
AUTH=[% user %]:[% user %]
HEADER=Content-Type: text/xml; charset="UTF-8"
HEADER=Depth: 0
HEAD

REPLACE=/^Content-Length: [0-9]+/Content-Length: xx/
REPLACE=/^DTSTAMP:\d+T\d+Z/DTSTAMP:xx/
REPLACE=/^DTEND:\d+T\d+Z/DTEND:xx/
REPLACE=,^FREEBUSY:[% timestamp %]/\d+T\d+Z,FREEBUSY:[% timestamp %]/xx,
REPLACE=,^FREEBUSY:(?![% timestamp %])\d+T\d+Z/\d+T\d+Z,FREEBUSY:xx/xx,

BEGINDATA
<?xml version="1.0" encoding="utf-8" ?>
<C:free-busy-query xmlns:C="urn:ietf:params:xml:ns:caldav">
  <C:time-range start="[% timestamp %]" />
</C:free-busy-query>
ENDDATA

URL=http://regression.host/caldav.php/[% user %]/[% collection %]/

__FREEBUSY_RESULT__
HTTP/1.1 200 OK
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
Content-Length: xx
Content-Type: text/calendar;charset=UTF-8

BEGIN:VCALENDAR
PRODID:-//davical.org//NONSGML AWL Calendar//EN
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VFREEBUSY
DTSTAMP:xx
DTSTART:[% timestamp %]
DTEND:xx
FREEBUSY:[% timestamp %]/xx
FREEBUSY:xx/xx
END:VFREEBUSY
END:VCALENDAR

