Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5179a88
mock and fake functions
Dec 31, 2024
463c367
Add files via upload
v-maliutin Dec 31, 2024
4c29b12
there are several functions to fake a table and mock a function
v-maliutin Jan 1, 2025
65d0f50
Merge branch 'fake_and_mock_functions' of https://github.com/v-maliut…
Jan 2, 2025
9ab57ca
added example how to use mock and fake functions
Jan 2, 2025
69aa765
mock_func is able to mock plpgsql and sql functionality, + some enhan…
v-maliutin Jan 2, 2025
0f8c32d
fixed tap_funky and implemented call_count
v-maliutin Jan 4, 2025
c733367
fixed returns column in tap_funky and added a regression test
v-maliutin Jan 6, 2025
401bcc2
wrote self-tests on faking functionality
v-maliutin Jan 8, 2025
c958455
Prettying up examples
Mar 13, 2025
19c700a
Leave a single version
Mar 13, 2025
2231bec
Merge pull request #1 from v-maliutin/fake_and_mock_functions
v-maliutin Mar 13, 2025
3f8d7eb
Merge branch 'main' into main
v-maliutin Mar 17, 2025
8dce627
Created a new function to print a query instead of a table. Just more…
Aug 24, 2025
80fdb8a
No modifications in unpackaged
Aug 24, 2025
1604a83
Merge remote-tracking branch 'origin/main'
Aug 24, 2025
6226807
No modifications in unpackaged
Aug 24, 2025
e660e9d
Merge branch 'fake_and_mock_functions'
Aug 24, 2025
f57c7e0
Function instead of procedures + format function instead of ||
Sep 23, 2025
a2af1dd
Documentation on fake_table function
Oct 12, 2025
6de5e8a
Documentation on mock_func function
Oct 19, 2025
edf0f9a
Updated pgtap.sql.in
Oct 20, 2025
2273ba2
Add ability to mock a view + some fixes
Oct 21, 2025
4ae191b
Fixes, new tests, new documentation
Oct 24, 2025
5baf8c7
fixed a couple of typos
Oct 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
359 changes: 359 additions & 0 deletions examples/mocking_and_faking.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,359 @@
/*
DROP EXTENSION pgtap;

CREATE EXTENSION pgtap schema tap;
*/

create schema if not exists tap;
create schema if not exists pgconf;
create schema if not exists tests;

set search_path to public, tap, pgconf;
set track_functions = 'all' ;

drop function if exists pgconf.get_osv_slice(int, int, int);
drop function if exists pgconf.get_tree_of(int);
drop function if exists pgconf.time_machine_now();
drop procedure if exists pgconf.make_osv_report();
drop procedure if exists tests.create_test_data();
drop function if exists tests.test_osv_on_time();
drop function if exists tests.test_osv_not_on_time();
drop function if exists tests.test_get_tree_of_called_times();

drop table if exists pgconf.osv;
drop table if exists pgconf.transactions;
drop table if exists pgconf.analytic;
drop table if exists pgconf.account;

create table pgconf.account(
id int generated always as identity primary key
, parent_id int
, num text
, constraint account_01_fk foreign key(parent_id) references pgconf.account(id)
);

create table pgconf.analytic(
id int generated always as identity primary key
, subconto text
);

create table pgconf.transactions(
account_num text
, subconto_1 text
, subconto_2 text
, subconto_3 text
, amount_dt numeric(15, 2)
, amount_ct numeric(15, 2)
);

create table pgconf.osv(
account_id int
, subconto_1_id int
, subconto_2_id int
, subconto_3_id int
, amount_dt numeric(15, 2)
, amount_ct numeric(15, 2)
, constraint osv_01_fk foreign key(account_id) references pgconf.account(id)
, constraint osv_02_fk foreign key(subconto_1_id) references pgconf.analytic(id)
, constraint osv_03_fk foreign key(subconto_2_id) references pgconf.analytic(id)
, constraint osv_04_fk foreign key(subconto_3_id) references pgconf.analytic(id)
);

create or replace function pgconf.get_osv_slice(_account_id int, _subc_1 int, _subc_2 int)
returns table(
ordercol text[]
, account_num text
, subconto_1_id int
, subconto_1 text
, subconto_2_id int
, subconto_2 text
, subconto_3 text
, amount_dt numeric(15, 2)
, amount_ct numeric(15, 2)
)
language plpgsql
as $$
begin
return query
with recursive account_tree as (
select id, parent_id, num
from pgconf.account
where (id = _account_id or _account_id is null)
and parent_id is null
union all
select a.id, a.parent_id, a.num
from account_tree t
join pgconf.account a
on a.parent_id = t.id
)search depth first by num set account_order
select
t.account_order::text[]
, t.num as account_num
, a1.id as subconto_1_id
, a1.subconto as subconto_1
, a2.id as subconto_2_id
, a2.subconto as subconto_2
, a3.subconto as subconto_3
, o.amount_dt
, o.amount_ct
from account_tree t
left join pgconf.osv o
on o.account_id = t.id
left join pgconf.analytic a1
on a1.id = o.subconto_1_id
left join pgconf.analytic a2
on a2.id = o.subconto_2_id
left join pgconf.analytic a3
on a3.id = o.subconto_3_id
where
pgconf.time_machine_now() between '12:00'::time and '15:00'::time
and (_account_id is null and o.subconto_1_id is null and o.subconto_2_id is null and o.subconto_3_id is null)
order by t.account_order
;
end;
$$;

create or replace function pgconf.get_tree_of(_account_id int)
returns table(id int, parent_id int, num text, lev int, is_folder bool)
language sql
as $function$
with recursive acc as (
select id, parent_id, num, 1 as l, True as is_folder from pgconf.account
where (id = _account_id and _account_id is not null) or (_account_id is null and parent_id is null)
union all
select a2.id, a2.parent_id, a2.num, a1.l + 1 as l
, exists(select from pgconf.account ca where ca.parent_id = a2.id) as is_folder
from acc a1
join pgconf.account a2
on a1.id = a2.parent_id
)
select * from acc a
$function$
;

create or replace function pgconf.time_machine_now()
returns time
language sql
as $$
select now()::time;
$$;

create or replace procedure pgconf.make_osv_report()
language plpgsql
as $$
begin
insert into pgconf.osv(account_id, subconto_1_id, subconto_2_id, subconto_3_id, amount_dt, amount_ct)
select a.id, a1.id, a2.id, a3.id, sum(amount_dt), sum(amount_ct)
from pgconf.account a
join pgconf.transactions t
on t.account_num = a.num
left join pgconf.analytic a1
on a1.subconto = t.subconto_1
left join pgconf.analytic a2
on a2.subconto = t.subconto_2
left join pgconf.analytic a3
on a3.subconto = t.subconto_3
group by a.id, grouping sets(
(a.id, a1.id, a2.id, a3.id)
, (a.id, a1.id, a2.id)
, (a.id, a1.id)
, (a.id)
);

insert into pgconf.osv(account_id, subconto_1_id, subconto_2_id, subconto_3_id, amount_dt, amount_ct)
select
acc.id
, null, null, null
, agg.amount_dt
, agg.amount_ct
from pgconf.get_tree_of(null) as acc
left join lateral(
select
sum(osv.amount_dt) as amount_dt
, sum(osv.amount_ct) as amount_ct
from pgconf.osv
where osv.account_id in (select id from pgconf.get_tree_of(acc.id) t where not t.is_folder)
and subconto_1_id is null and subconto_2_id is null and subconto_3_id is null
) as agg on true
where
acc.is_folder;
end;
$$;

create or replace procedure tests.create_test_data()
language plpgsql
as $$
begin
call tap.fake_table(
_table_ident => '{pgconf.account, pgconf.analytic, pgconf.osv, pgconf.transactions}'::text[],
_make_table_empty => true,
_leave_primary_key => false,
_drop_not_null => false,
_drop_collation => false
);

insert into pgconf.account(parent_id, num) values(null, '02');

insert into pgconf.account(parent_id, num)
select id, '02.01'
from pgconf.account where num = '02';

insert into pgconf.account(parent_id, num) values(null, '01');

insert into pgconf.account(parent_id, num)
select id, '03.01'
from pgconf.account where num = '01';

insert into pgconf.account(parent_id, num)
select id, '01.01.01'
from pgconf.account where num = '03.01';

insert into pgconf.account(parent_id, num)
select id, '01.01.02'
from pgconf.account where num = '03.01';

insert into pgconf.account(parent_id, num)
select id, '01.01.01.01'
from pgconf.account where num = '01.01.01';

insert into pgconf.account(parent_id, num)
select id, '01.01.01.02'
from pgconf.account where num = '01.01.01';

insert into pgconf.account(parent_id, num)
select id, '01.01.02.01'
from pgconf.account where num = '01.01.02';

insert into pgconf.analytic(subconto) values('Суб_1'), ('Суб_2'), ('Суб_3'), ('Суб_4'), ('Суб_5'), ('Суб_6'), ('Суб_7');

insert into pgconf.transactions(account_num, subconto_1, subconto_2, subconto_3, amount_dt, amount_ct) values
('01.01.01.01', 'Суб_1', 'Суб_2', 'Суб_4', 10, 0)
, ('01.01.01.01', 'Суб_1', 'Суб_2', 'Суб_5', 10, 0)
, ('01.01.01.01', 'Суб_1', 'Суб_3', 'Суб_6', 10, 0)
, ('01.01.01.02', 'Суб_1', 'Суб_3', 'Суб_7', 10, 0)
, ('01.01.02.01', 'Суб_1', 'Суб_2', 'Суб_4', 10, 0)
, ('02.01', 'Суб_1', 'Суб_3', 'Суб_5', 0, 10)
, ('02.01', 'Суб_1', 'Суб_2', 'Суб_4', 0, 10);

call pgconf.make_osv_report();
end;
$$;

create or replace function tests.test_osv_ordered_in_depth()
returns setof text
language plpgsql
as $$
begin
-- GIVEN
call tests.create_test_data();
call tap.mock_func('pgconf', 'time_machine_now', '()'
, _return_scalar_value => '13:00'::time);

-- WHEN
perform tap.drop_prepared_statement('{expected, returned}'::text[]);

prepare expected as
select num::text
from (values ('01', 1), ('03.01', 2), ('01.01.01', 2), ('01.01.01.01', 3), ('01.01.01.02', 4)
, ('01.01.02', 5), ('01.01.02.01', 6)
, ('02', 7), ('02.01', 8)) as t(num, id)
order by id;

prepare returned as
select account_num from pgconf.get_osv_slice(null, null, null);

-- THEN
return query
select tap.results_eq(
'returned',
'expected',
'Accounts must be sorted in depth first.'
);


create table pgconf.slice as
select * from pgconf.get_osv_slice(null, null, null);

call tap.print_table_as_json('pgconf', 'slice');
call tap.print_table_as_json('pgconf', 'account');

-- WHEN
perform tap.drop_prepared_statement('{expected, returned}'::text[]);
end;
$$;

create or replace function tests.test_osv_on_time()
returns setof text
language plpgsql
as $$
begin
-- GIVEN
call tests.create_test_data();
call tap.mock_func('pgconf', 'time_machine_now', '()'
, _return_scalar_value => '15:01'::time);

create table pgconf.x as select * from pgconf.time_machine_now();
call tap.print_table_as_json('pgconf', 'x');

-- WHEN
perform tap.drop_prepared_statement('{returned}'::text[]);

prepare returned as
select * from pgconf.get_osv_slice(null, null, null);

-- THEN
return query
select tap.is_empty(
'returned',
'It is not good time to make osv report.'
);

perform tap.drop_prepared_statement('{returned}'::text[]);
end;
$$;

create or replace function tests.test_osv_not_on_time()
returns setof text
language plpgsql
as $$
begin
-- GIVEN
call tests.create_test_data();
call tap.mock_func('pgconf', 'time_machine_now', '()'
, _return_set_value => null, _return_scalar_value => '13:00'::time);

-- WHEN
perform tap.drop_prepared_statement('{returned}'::text[]);

prepare returned as
select * from pgconf.get_osv_slice(null, null, null);

-- THEN
return query
select tap.isnt_empty(
'returned',
'Time has come. We can make osv report.'
);

perform tap.drop_prepared_statement('{expected}'::text[]);
end;
$$;

create or replace function tests.test_get_tree_of_called_times()
returns setof text
language plpgsql
as $$
begin
-- GIVEN
call tests.create_test_data();

-- THEN
return query
select tap.call_count(6, 'pgconf'
, 'get_tree_of', '{int}'::name[]);
end;
$$;

select * from tap.runtests('tests', '^test_');

select * from tap.runtests('tests', 'test_osv_on_time');
Loading