-- packages/acs-events/sql/postgres/test/time_interval-test.sql
--
-- Regression tests for time_interval API
--
-- @author jowell@jsabino.com
-- @creation-date 2001-06-26
--
-- $Id: time_intervals-test.sql,v 1.4 2018/03/27 12:22:17 hectorr Exp $

-- Note: These tests use the semi-ported utPLSQL regression package
\i utest-create.sql

-- Set-up the regression test
CREATE OR REPLACE FUNCTION ut__setup() RETURNS integer AS $$
BEGIN

	raise notice 'Setting up time_intervals test...';

	-- create copies of the tables (shadow tables) to verify API operations
	-- No need for execute here?
	create table ut_time_intervals as select * from time_intervals;

	-- For testing purposes, both tables should still be empty
	PERFORM ut_assert__eqtable ('Comparing copied data for time interval',
				    'time_intervals',
				    'ut_time_intervals'
				    );

	-- Store keys that are in the table prior to the regression test
	create table ut_interval_ids as select interval_id from time_intervals;

	return 0;

END;
$$ LANGUAGE plpgsql;


-- Clean up the mess that regression testing did
CREATE OR REPLACE FUNCTION ut__teardown() RETURNS integer AS $$
BEGIN

	raise notice 'Tearing down time_intervals test...';

	-- Delete intervals added by tests
	-- cascade delete in timespans should delete corresponding entries in that table
	-- Note that we exclude deleting rows that existed prior to regression test
	delete from ut_time_intervals
	where interval_id not in (select interval_id
			          from ut_interval_ids);

				

	-- Drop test tables
	-- cascade option does not work?
	drop table ut_time_intervals;
	drop table ut_interval_ids;

	return 0;

END;
$$ LANGUAGE plpgsql;


-- Postgres has this weird behavior that you cannot change a row twice
-- within a transaction.  


-- We test the creation of a time interval entry


-- added
select define_function_args('ut__new','date1,date2');

--
-- procedure ut__new/2
--
CREATE OR REPLACE FUNCTION ut__new(
   new__date1 timestamptz,
   new__date2 timestamptz
) RETURNS integer AS $$
DECLARE
	new__interval_id    time_intervals.interval_id%TYPE;
	v_result	    integer;
BEGIN

        raise notice 'Testing time_interval__new...';

	-- create a time interval, and check if entry is made
	v_result :=  ut_assert__isnotnull ('Creating a new test time interval:',
		 		           time_interval__new(new__date1, new__date2)
				           );

	-- Verify that the API does the correct insert by manually entering
	-- an entry in the shadow table
	-- Note that we did not port the currval in the timepsan_seq view
	select currval('timespan_sequence') into new__interval_id;
	insert into ut_time_intervals(interval_id, start_date, end_date)
        values(new__interval_id, new__date1, new__date2);
	
	PERFORM ut_assert__eqtable ('Comparing created data for time interval :',
			    'time_intervals',
			    'ut_time_intervals'
			    );

	-- If successful, interval id is correct
	return new__interval_id;

END;
$$ LANGUAGE plpgsql;


-- Check the deletion of a time interval


-- added
select define_function_args('ut__delete','interval_id');

--
-- procedure ut__delete/1
--
CREATE OR REPLACE FUNCTION ut__delete(
   delete__interval_id integer
) RETURNS integer AS $$
DECLARE
BEGIN

	raise notice 'Testing time interval delete...';

	-- Delete entry from shadow table
	delete from ut_time_intervals
	where interval_id = delete__interval_id;

	-- Delete the row from actual table
	PERFORM time_interval__delete(delete__interval_id);


	-- Verify time interval not there.
	PERFORM ut_assert__eqtable ('Delete verification',
				    'ut_time_intervals',
				    'time_intervals'
				    );

	-- If successful, interval id is correct
	return 0;

END;
$$ LANGUAGE plpgsql;



-- added
select define_function_args('ut__edit','interval_id,start_date,end_date');

--
-- procedure ut__edit/3
--
CREATE OR REPLACE FUNCTION ut__edit(
   edit__interval_id integer,
   edit__start_date timestamptz,
   edit__end_date timestamptz
) RETURNS integer AS $$
DECLARE
BEGIN
        raise notice 'Testing time_interval__edit...';

	-- Edit the time interval
	PERFORM time_interval__edit(edit__interval_id,edit__start_date,edit__end_date);

	-- Verify
	if edit__start_date is not null and edit__end_date is not null
	then
	update ut_time_intervals
		set start_date = edit__start_date,
		    end_date = edit__end_date
		where interval_id = edit__interval_id;
	end if;

	if edit__start_date is null and edit__end_date is not null
	then
	update ut_time_intervals
		set  end_date = edit__end_date
		where interval_id = edit__interval_id;
	end if;

	if edit__start_date is not null and edit__end_date is null
	then
	update ut_time_intervals
		set start_date = edit__start_date
		where interval_id = edit__interval_id;
	end if;

	PERFORM ut_assert__eqtable ('Comparing edited data for time interval',
			    'time_intervals',
			    'ut_time_intervals'
			    );

	return 0;

END;
$$ LANGUAGE plpgsql;



-- added
select define_function_args('ut__eq','msg,interval_id_1,interval_id_2,result');

--
-- procedure ut__eq/4
--
CREATE OR REPLACE FUNCTION ut__eq(
   eq__msg varchar,
   eq__interval_id_1 integer,
   eq__interval_id_2 integer,
   eq__result boolean
) RETURNS integer AS $$
DECLARE

BEGIN

	PERFORM ut_assert__eq (eq__msg,
			       time_interval__eq(eq__interval_id_1, eq__interval_id_2),
			       eq__result
			       );
	return 0;

END;
$$ LANGUAGE plpgsql;




-- added
select define_function_args('ut__shift','interval_id,offset_1,offset_2,date1,date2');

--
-- procedure ut__shift/5
--
CREATE OR REPLACE FUNCTION ut__shift(
   shift__interval_id integer,
   shift__offset_1 integer,
   shift__offset_2 integer,
   shift__date1 timestamptz,
   shift__date2 timestamptz
) RETURNS integer AS $$
DECLARE
BEGIN

	raise notice 'Testing shift...';
		
	-- Shift the time interval
	PERFORM time_interval__shift(shift__interval_id, shift__offset_1, shift__offset_2);

	-- Verify
	update ut_time_intervals 
	        set start_date = shift__date1,
		    end_date = shift__date2
                where interval_id = shift__interval_id;

	PERFORM ut_assert__eqtable ('Comparing shifted data for time intervals',
				    'time_intervals',
				    'ut_time_intervals'
				    );
		
	return 0;

END;
$$ LANGUAGE plpgsql;




-- added

--
-- procedure ut__overlaps_p/4
--
CREATE OR REPLACE FUNCTION ut__overlaps_p(
   overlaps_p__msg varchar,
   overlaps_p__interval_id_1 integer,
   overlaps_p__interval_id_2 integer,
   overlaps_p__result boolean
) RETURNS integer AS $$
DECLARE
BEGIN
	-- Test the time interval
	PERFORM	ut_assert__eq (overlaps_p__msg,
			       time_interval__overlaps_p(overlaps_p__interval_id_1, overlaps_p__interval_id_2),
			       overlaps_p__result
			       );

	return 0;

END;
$$ LANGUAGE plpgsql;



-- added

--
-- procedure ut__overlaps_p/5
--
CREATE OR REPLACE FUNCTION ut__overlaps_p(
   overlaps_p__msg varchar,
   overlaps_p__interval_id integer,
   overlaps_p__start_date timestamptz,
   overlaps_p__end_date timestamptz,
   overlaps_p__result boolean
) RETURNS integer AS $$
DECLARE
BEGIN
	-- Test the time interval
	PERFORM	ut_assert__eq (overlaps_p__msg,
			       time_interval__overlaps_p(overlaps_p__interval_id, 
							 overlaps_p__start_date,
							 overlaps_p__end_date),
			       overlaps_p__result
			       );

	return 0;

END;
$$ LANGUAGE plpgsql;



-- added
select define_function_args('ut__overlaps_p','msg,start_date_1,end_date_1,start_date_2,end_date_2,result');

--
-- procedure ut__overlaps_p/6
--
CREATE OR REPLACE FUNCTION ut__overlaps_p(
   overlaps_p__msg varchar,
   overlaps_p__start_date_1 timestamptz,
   overlaps_p__end_date_1 timestamptz,
   overlaps_p__start_date_2 timestamptz,
   overlaps_p__end_date_2 timestamptz,
   overlaps_p__result boolean
) RETURNS integer AS $$
DECLARE
BEGIN
	-- Test the time interval
	PERFORM	ut_assert__eq (overlaps_p__msg,
			       time_interval__overlaps_p(overlaps_p__start_date_1,
							 overlaps_p__end_date_1,
							 overlaps_p__start_date_2,
							 overlaps_p__end_date_2),
			       overlaps_p__result
			       );

	return 0;

END;
$$ LANGUAGE plpgsql;




-- added
select define_function_args('ut__copy','interval_id,offset');

--
-- procedure ut__copy/2
--
CREATE OR REPLACE FUNCTION ut__copy(
   copy__interval_id integer,
   copy__offset integer
) RETURNS integer AS $$
DECLARE
	v_interval_id		time_intervals.interval_id%TYPE;
	interval_row		record;
BEGIN

	raise notice 'Testing time_interval__copy...';
		
		
	-- Copy the time interval
	v_interval_id := time_interval__copy(copy__interval_id,copy__offset);

	-- Get the copied start and end dates, before the offset
	select * into interval_row
	from time_intervals
	where interval_id = copy__interval_id;

	-- Insert for testing
	insert into ut_time_intervals (interval_id, start_date, end_date)
	values (v_interval_id, interval_row.start_date + copy__offset, interval_row.end_date + copy__offset);

	-- Verify copies
	PERFORM	ut_assert__eqtable ('Comparing copied data for time intervals',
			            'time_intervals',
			            'ut_time_intervals'
				    );

	return v_interval_id;

END;
$$ LANGUAGE plpgsql;




--
-- procedure ut__regression1/0
--
CREATE OR REPLACE FUNCTION ut__regression1(

) RETURNS integer AS $$
DECLARE
	v_result	 integer := 0;
	v_interval_id	 time_intervals.interval_id%TYPE;
	v_interval_id_ck time_intervals.interval_id%TYPE;
BEGIN

	raise notice 'Regression test, part 1 (creates and edits).';

	-- We first check if the creation of time intervals work
	v_interval_id := ut__new(timestamptz '2001-01-01',timestamptz '2001-01-02');

	-- Try to edit, putting new values for start date and end dates
	PERFORM ut__edit(v_interval_id,timestamptz '2001-01-02',timestamptz '2001-01-30');

	-- Edit, but this time, change only the start date
	PERFORM ut__edit(v_interval_id,timestamptz '2001-01-07',null);

	-- Edit, but this time, change only the end date
	PERFORM ut__edit(v_interval_id,null,timestamptz '2001-01-08');

	-- We now test equality of (identical) intervals
	PERFORM ut__eq('Equal (same) intervals',v_interval_id,v_interval_id,true);

	-- Create another interval for comparison
	v_interval_id_ck := ut__new(timestamptz '2001-01-07',timestamptz '2001-01-08');

	-- We now test equality of (nonidentical) intervals
	PERFORM ut__eq('Equal (distinct) intervals',v_interval_id,v_interval_id_ck,true);

	-- Shift the second interval start date by one day, the end date by two days
	PERFORM ut__shift(v_interval_id_ck,1,2,timestamptz '2001-01-08', timestamptz '2001-01-10');

	-- Now test inequality of time intervals
	PERFORM ut__eq('Unequal (distinct) intervals',v_interval_id,v_interval_id_ck,false);

	-- Shift the second interval start date BACK by one day, the end date same
	PERFORM ut__shift(v_interval_id_ck,-1,0,timestamptz '2001-01-07', timestamptz '2001-01-10');

	-- Now test inequality of time intervals
	PERFORM ut__eq('Unequal (distinct) intervals: start date equal',v_interval_id,v_interval_id_ck,false);

	-- Shift the second interval, start date same, but the end date BACK by two days
	PERFORM ut__shift(v_interval_id_ck,0,-2,timestamptz '2001-01-07', timestamptz '2001-01-08');

	-- Should be equal again
	PERFORM ut__eq('Equal again, (distinct) intervals',v_interval_id,v_interval_id_ck,true);

	-- For fun, shift start date BACK by two days, the end date BACK by 1 day
	PERFORM ut__shift(v_interval_id_ck,-2,-1,timestamptz '2001-01-05', timestamptz '2001-01-07');

	-- Should be unequal again
	PERFORM ut__eq('For fun, unequal (distinct) intervals',v_interval_id,v_interval_id_ck,false);

	-- Note that at this point, interval pointed to by v_interval_id is from 2001-01-07 through 2001-01-08
	-- while interval pointed to by v_interval_id_ck is from 2001-01-05 through 2001-01-07.
	-- They overlap.
	PERFORM ut__overlaps_p('Overlapping intervals',v_interval_id,v_interval_id_ck,true);

	-- Ok, shift the dtart and end dates by one so that intervals do not overlap
	PERFORM ut__shift(v_interval_id_ck,-1,-1,timestamptz '2001-01-04', timestamptz '2001-01-06');

	-- They should not overlap now.
	PERFORM ut__overlaps_p('Non-overlapping intervals',v_interval_id,v_interval_id_ck,false);


	-- We test the overloaded function definitions of time_interval__overlaps_p
	-- Note that we are comparing with 2001-01-07 through 2001-01-08
	PERFORM ut__overlaps_p('Overlapping intervals',
			       v_interval_id,
			       timestamptz '2001-01-06',
			       timestamptz '2001-01-09',
			       true);	

	-- How about an interval next month?
	PERFORM ut__overlaps_p('Non-overlapping intervals',
			       v_interval_id,
			       timestamptz '2001-02-06',
			       timestamptz '2001-02-09',
			       false);	

	-- Try a null starting interval
	PERFORM ut__overlaps_p('Overlapping intervals (null start)',
			       v_interval_id,
			       null,
			       timestamptz '2001-01-09',
			       true);	

	-- Try a null starting interval
	PERFORM ut__overlaps_p('Overlapping intervals (null end)',
			       v_interval_id,
			       timestamptz '2001-01-06',
			       null,
			       true);	

	-- What if the interval is not an allowable interval?
	-- By definition, any interval should be non-overlapping with a non-existent interval
	PERFORM ut__overlaps_p('Non-overlapping intervals (non-allowed interval, outside month)',
			       v_interval_id,
			       timestamptz '2001-02-09',
			       timestamptz '2001-02-06',
			       false);				     

	-- What if the interval is not an allowable interval?
	-- By definition, any interval should be non-overlapping with a non-existent interval
	PERFORM ut__overlaps_p('Non-overlapping intervals (non-allowed interval, in month)',
			       v_interval_id,
			       timestamptz '2001-01-09',
			       timestamptz '2001-01-06',
			       false);				     

	-- Yet another overloaded definition
	PERFORM ut__overlaps_p('Overlapping intervals (not in time_intervals)',
			       timestamptz '2001-01-06',
			       timestamptz '2001-01-09',
			       timestamptz '2001-01-07',
			       timestamptz '2001-01-08',
			       true);				     


	-- Yet another overloaded definition
	PERFORM ut__overlaps_p('Overlapping intervals (not in time_intervals)',
			       timestamptz '2001-01-06',
			       timestamptz '2001-01-09',
			       timestamptz '2001-01-09',
			       timestamptz '2001-01-10',
			       true);				     

	-- Yet another overloaded definition
	PERFORM ut__overlaps_p('Overlapping intervals (not in time_intervals)',
			       timestamptz '2001-01-06',
			       timestamptz '2001-01-09',
			       null,
			       timestamptz '2001-01-10',
			       true);				     
	PERFORM ut__overlaps_p('Overlapping intervals (not in time_intervals)',
			       timestamptz '2001-01-06',
			       timestamptz '2001-01-09',
			       timestamptz '2001-01-10',
			       null,
			       false);				     

	-- Yet another overloaded definition
	PERFORM ut__overlaps_p('Non-overlapping intervals (not in time_intervals)',
			       timestamptz '2001-02-06',
			       timestamptz '2001-02-09',
			       timestamptz '2001-01-07',
			       timestamptz '2001-01-08',
			       false);				     



	-- Overwrite the check interval a copy, with zero offset
	v_interval_id_ck := ut__copy(v_interval_id,0);


	-- Should be equal
	-- Now test equality of time intervals
	PERFORM ut__eq('Copied intervals (zero offset)',v_interval_id,v_interval_id_ck,true);

	-- Overwrite the check interval a copy, with non-zero offset
	v_interval_id_ck := ut__copy(v_interval_id,1);


	-- Should be unequal
	-- Now test inequality of time intervals
	PERFORM ut__eq('Copied intervals (non-zero offset)',v_interval_id,v_interval_id_ck,false);


	-- We will improve the regression test so there is reporting 
	-- of individual test results.  For now, reaching this far is
	-- enough to declare success.
       	return v_result;

END;
$$ LANGUAGE plpgsql;



--
-- procedure ut__regression2/0
--
CREATE OR REPLACE FUNCTION ut__regression2(

) RETURNS integer AS $$
DECLARE
	v_result	 integer := 0;
	rec_interval	 record;
BEGIN

	raise notice 'Regression test, part 2 (deletes).';

	-- Remove all entries made by regression test
	-- This also tests the deletion mechanism
	FOR rec_interval IN 
	   select * from time_intervals
	   where interval_id not in (select interval_id from ut_interval_ids)
        LOOP
		PERFORM ut__delete(rec_interval.interval_id);
	END LOOP;

	-- We will improve the regression test so there is reporting 
	-- of individual test results.  For now, reaching this far is
	-- enough to declare success.
       	return v_result;

END;
$$ LANGUAGE plpgsql;

--------------------------------------------------------------------------------
-- Main regression test.  PostgreSQL does not allow multiple changes made to a 
-- primary key inside a transaction if the primary key is referenced by another 
-- table (e.g., insert and delete). As a fix, we break down the regression test 
-- so that row creations and edits are separate from row deletions
--------------------------------------------------------------------------------
select (case when ut__setup() = 0
             then
	         'Regression test properly set up.'
	     end) as setup_result;

select (case when ut__regression1() = 0
             then
	         'Regression test, part 1 successful.'
	     end) as test_result;

select (case when ut__regression2() = 0
             then
	         'Regression test, part 2 successful.'
	     end) as test_result;

select (case when ut__teardown() = 0
             then
	         'Regression test properly torn down.'
	     end) as teardown_result;

-- Clean up created functions.
-- This depends on openacs4 installed.
select drop_package('ut');

--------------------------------------------------------------------------------
-- End of regression test
--------------------------------------------------------------------------------
\i utest-drop.sql








