BEGIN;
    -- Load the TAP functions.
    CREATE EXTENSION pgtap;
    SET client_min_messages TO warning;

    -- Plan the tests.

    SELECT plan(175);

    -- Add data

    \i xt/data/42-pg/Base.sql

    -- Validate required tables

    SELECT has_table('lsmb_module');

    -- Validate required functions

    -- Can create role
    SELECT has_function('lsmb__create_role',ARRAY['text','text']);

    -- Check Valid Role creation
    PREPARE test AS SELECT
                 lsmb__create_role('42_working_role', 'role docs');
    SELECT results_eq('test',ARRAY[true],'can create normal role');
    DEALLOCATE test;

    SELECT lsmb__grant_role('42_working_role', 'base_user');

    -- Check with an invalid role name of 86 characters total
    PREPARE test AS SELECT
                 lsmb__create_role('42_failing_with_a_much_too_long_role_that_will_have_more_then_63_bytes', 'role docs');
    SELECT throws_ok('test','42622'); -- 42622 is name_too_long
    DEALLOCATE test;

    -- Can grant function execution
    SELECT has_function('lsmb__grant_exec',ARRAY['text','text']);

    -- Check valid case
    PREPARE test AS SELECT
                 lsmb__grant_exec('42_working_role', 'budget__save_info(integer,date,date,text,text,integer[])');
    SELECT results_eq('test',ARRAY[true],'can grant exec to a defined role');
    DEALLOCATE test;

    -- Validate undefined role
    PREPARE test AS SELECT
                 lsmb__grant_exec('42_working_role_not_defined', 'budget__save_info(integer,date,date,text,text,integer[])');
    SELECT throws_ok('test','0P000');
    DEALLOCATE test;

    -- Validate invalid function
    PREPARE test AS SELECT
                 lsmb__grant_exec('42_working_role', 'budget__save_info_not_defined(integer,date,date,text,text,integer[])');
    SELECT throws_ok('test','42883');
    DEALLOCATE test;

    -- Can grant submenu access
    SELECT has_function('lsmb__grant_menu',ARRAY['text','integer','text']);

    -- Check valid case
    PREPARE test AS SELECT lsmb__grant_menu('42_working_role', 252, 'allow');
    SELECT results_eq('test',ARRAY[true],'can grant submenu to a defined role');
    DEALLOCATE test;

    -- Validate undefined role
    PREPARE test AS SELECT lsmb__grant_menu('42_working_role_not_defined', 252, 'allow');
    SELECT throws_ok('test','0P000');
    DEALLOCATE test;

    -- Validate undefined submenu
    PREPARE test AS SELECT lsmb__grant_menu('42_working_role', -1, 'allow');
    SELECT throws_ok('test','23503');
    DEALLOCATE test;

    -- Validate undefined role
    PREPARE test AS SELECT lsmb__grant_menu('42_working_role', 252, 'allow_special');
    SELECT throws_ok('test','0L000');
    DEALLOCATE test;

    -- Can grant permission with 4 arguments
    SELECT has_function('lsmb__grant_perms',ARRAY['text','text','text','text[]']);

    -- Not used?

    -- Can grant permission
    SELECT has_function('lsmb__grant_perms',ARRAY['text','text','text']);

    -- Valid case
    PREPARE test AS SELECT lsmb__grant_perms('42_working_role', 'budget_info', 'SELECT');
    SELECT results_eq('test',ARRAY[true],'can grant permission to a defined role');
    DEALLOCATE test;

    -- Validate undefined role
    PREPARE test AS SELECT
                 lsmb__grant_perms('42_working_role_not_defined', 'budget_info', 'SELECT');
    SELECT throws_ok('test','0P000');
    DEALLOCATE test;

    -- Validate undefined permission
    PREPARE test AS SELECT
                 lsmb__grant_perms('42_working_role', 'budget_info_not_defined', 'SELECT');
    SELECT throws_ok('test','42P01');
    DEALLOCATE test;

    -- Validate invalid type
    PREPARE test AS SELECT
                 lsmb__grant_perms('42_working_role', 'budget_info', 'SELECT_SPECIAL');
    SELECT throws_ok('test','0L000');
    DEALLOCATE test;

    -- Can grant role
    SELECT has_function('lsmb__grant_role',ARRAY['text','text']);

    -- Check valid case
    PREPARE test AS
                 SELECT lsmb__grant_role('42_working_role', 'budget_view');
    SELECT results_eq('test',ARRAY[true],'can grant defined role to a defined role');
    DEALLOCATE test;

    -- Validate undefined role
    PREPARE test AS
                 SELECT lsmb__grant_role('42_working_role_not_defined', 'budget_view');
    SELECT throws_ok('test','0P000');
    DEALLOCATE test;

    -- Validate undefined role
    PREPARE test AS
                 SELECT lsmb__grant_role('42_working_role', 'budget_view_not_defined');
    SELECT throws_ok('test','0P000');
    DEALLOCATE test;

    -- Can check role allowed
    SELECT has_function('lsmb__is_allowed_role',ARRAY['text[]']);

    -- Check valid case
    PREPARE test AS
                 SELECT lsmb__is_allowed_role(ARRAY['42_working_role']);
    SELECT results_eq('test',ARRAY[true],'can check allowed role');
    DEALLOCATE test;

    -- Validate undefined role
    PREPARE test AS
                 SELECT lsmb__is_allowed_role(ARRAY['42_working_role_not_defined']);
    SELECT throws_ok('test','42704');
    DEALLOCATE test;

    SELECT has_function('quote_ident_array',ARRAY['text[]']);
    SELECT has_function('tg_enforce_perms_eclass','{}'::text[]);

    -- This function is vulnerable to SQL injection but it is transient for the
    -- purposes of these test cases. In particular it is intended only to ensure
    -- that basic permissions are tested.
    --
    -- IT IS THE RESPONSIBILITY OF TEST CASE AUTHORS TO ENSURE THAT THE USAGE OF
    -- THIS FUNCTION IS SAFE.
    CREATE OR REPLACE FUNCTION test__has_select_permission
                                (rolname name, relspec text)
    returns bool language plpgsql as
    $$
    BEGIN
       PERFORM * FROM information_schema.role_table_grants
         WHERE table_schema = 'xyz'
               AND table_name = relspec
               AND privilege_type = 'SELECT';

       RETURN FOUND;
    END;
    $$;

    -- READ PERMISSIONS
    PREPARE test AS SELECT
                 test__has_select_permission('budget_view', 'budget_info');
    SELECT results_eq('test',ARRAY[true],'budget_view can read budget_info');
    DEALLOCATE test;

    PREPARE test AS SELECT
                 test__has_select_permission('budget_view', 'budget_line');
    SELECT results_eq('test',ARRAY[true],'budget_view can read budget_info');
    DEALLOCATE test;

    PREPARE test AS SELECT
            test__has_select_permission('file_read', 'file_base');
    SELECT results_eq('test',ARRAY[true],'file_read can read file_base');
    DEALLOCATE test;

    PREPARE test AS SELECT
            test__has_select_permission('file_read', 'file_links');
    SELECT results_eq('test',ARRAY[true],'file_read can read file_links');
    DEALLOCATE test;

    PREPARE test AS SELECT
            test__has_select_permission('file_read', 'file_secondary_attachment');
    SELECT results_eq('test',ARRAY[true],'file_read can read file_secondary_transaction');
    DEALLOCATE test;

    PREPARE test AS SELECT
            test__has_select_permission('file_read', 'file_order');
    SELECT results_eq('test',ARRAY[true],'file_read can read file_order');
    DEALLOCATE test;

    PREPARE test AS SELECT
            test__has_select_permission('file_read', 'file_part');
    SELECT results_eq('test',ARRAY[true],'file_read can read file_part');
    DEALLOCATE test;

    SELECT results_eq(
        'SELECT test__has_select_permission(''contact_read'', ''' || t || ''')',
        ARRAY[true],'contact_read can read ' || t
    ) FROM unnest(ARRAY['partsvendor'::text, 'partscustomer', 'taxcategory',
              'entity', 'company', 'location', 'entity_to_location',
              'entity_to_contact', 'person', 'entity_credit_account',
              'contact_class', 'eca_tax', 'entity_class', 'entity_note',
              'entity_bank_account', 'entity_other_name', 'location_class',
              'person_to_company', 'eca_to_contact', 'eca_to_location', 'eca_note',
              'pricegroup'
           ]) t;

    SELECT results_eq(
        'SELECT test__has_select_permission(''ar_transaction_list'', ''' || t || ''')',
        ARRAY[true],'ar_transaction_list can read ' || t
    ) FROM unnest(ARRAY['partsvendor'::text, 'partscustomer', 'taxcategory',
              'entity', 'company', 'location', 'entity_to_location',
              'entity_to_contact', 'person', 'entity_credit_account',
              'contact_class', 'eca_tax', 'entity_class', 'entity_note',
              'entity_bank_account', 'entity_other_name', 'location_class',
              'person_to_company', 'eca_to_contact', 'eca_to_location', 'eca_note',
              'ar', 'acc_trans', 'invoice', 'ac_tax_form', 'invoice_tax_form'
           ]) t;

    SELECT results_eq(
        'SELECT test__has_select_permission(''ap_transaction_list'', ''' || t || ''')',
        ARRAY[true],'ap_transaction_list can read ' || t
    ) FROM unnest(ARRAY['partsvendor'::text, 'partscustomer', 'taxcategory',
              'entity', 'company', 'location', 'entity_to_location',
              'entity_to_contact', 'person', 'entity_credit_account',
              'contact_class', 'eca_tax', 'entity_class', 'entity_note',
              'entity_bank_account', 'entity_other_name', 'location_class',
              'person_to_company', 'eca_to_contact', 'eca_to_location', 'eca_note',
              'ap', 'acc_trans', 'invoice', 'ac_tax_form', 'invoice_tax_form'
           ]) t;

    SELECT results_eq(
        'SELECT test__has_select_permission(''sales_order_list'', ''' || t || ''')',
        ARRAY[true],'sales_order_list can read ' || t
    ) FROM unnest(ARRAY['partsvendor'::text, 'partscustomer', 'taxcategory',
              'entity', 'company', 'location', 'entity_to_location',
              'entity_to_contact', 'person', 'entity_credit_account',
              'contact_class', 'eca_tax', 'entity_class', 'entity_note',
              'entity_bank_account', 'entity_other_name', 'location_class',
              'person_to_company', 'eca_to_contact', 'eca_to_location', 'eca_note',
              'oe', 'orderitems'
           ]) t;

    SELECT results_eq(
        'SELECT test__has_select_permission(''purchase_order_list'', ''' || t || ''')',
        ARRAY[true],'purchase_order_list can read ' || t
    ) FROM unnest(ARRAY['partsvendor'::text, 'partscustomer', 'taxcategory',
              'entity', 'company', 'location', 'entity_to_location',
              'entity_to_contact', 'person', 'entity_credit_account',
              'contact_class', 'eca_tax', 'entity_class', 'entity_note',
              'entity_bank_account', 'entity_other_name', 'location_class',
              'person_to_company', 'eca_to_contact', 'eca_to_location', 'eca_note',
              'oe', 'orderitems'
           ]) t;

    SELECT results_eq(
        'SELECT test__has_select_permission(''inventory_reports'', ''' || t || ''')',
        ARRAY[true],'inventory_reports can read ' || t
    ) FROM unnest(array['ar'::text, 'ap', 'warehouse_inventory', 'invoice', 'acc_trans']) t;

    SELECT results_eq(
        'SELECT test__has_select_permission(''gl_reports'', ''' || t || ''')',
        ARRAY[true],'gl_reports can read ' || t
    ) FROM unnest(array['gl'::text, 'acc_trans', 'account_checkpoint', 'ar', 'ap',
                      'entity', 'entity_credit_account'])t;

    SELECT results_eq(
        'SELECT test__has_select_permission(''financial_reports'', ''' || t || ''')',
        ARRAY[true],'financial_reports can read ' || t
    ) FROM unnest(array['gl'::text, 'acc_trans', 'account_checkpoint', 'ar', 'ap',
                      'entity', 'entity_credit_account', 'cash_impact'])t;

    -- Finish the tests and clean up.
    SELECT * FROM finish();

ROLLBACK;
