Skip to content

Commit c6e0f91

Browse files
committed
test:Ensure edits to temporal tables propagate automatically
1 parent d294f76 commit c6e0f91

File tree

2 files changed

+601
-0
lines changed

2 files changed

+601
-0
lines changed
Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
BEGIN;
2+
\i test/setup.sql
3+
-- While the datestyle is set for the database, the pg_regress tool sets the MDY format
4+
-- to ensure consistent date formatting, so we must manually override this
5+
SET datestyle TO 'ISO, DMY';
6+
\if :{?DEBUG}
7+
SET client_min_messages TO debug1;
8+
\else
9+
SET client_min_messages TO NOTICE;
10+
\endif
11+
-- Create temporary function to execute queries as system user
12+
CREATE OR REPLACE FUNCTION test.sudo_exec(
13+
sql text,
14+
OUT results jsonb
15+
) RETURNS jsonb
16+
SECURITY DEFINER LANGUAGE plpgsql AS $sudo_exec$
17+
DECLARE
18+
result_rows jsonb;
19+
BEGIN
20+
-- Check if the SQL starts with common DDL keywords
21+
IF sql ~* '^\s*(CREATE|DROP|ALTER|TRUNCATE|GRANT|REVOKE|ANALYZE)' THEN
22+
-- For DDL statements, execute directly
23+
EXECUTE sql;
24+
results := '[]'::jsonb;
25+
ELSE
26+
-- For DML/queries, wrap in a SELECT to capture results
27+
EXECUTE format('
28+
SELECT COALESCE(
29+
jsonb_agg(row_to_json(t)),
30+
''[]''::jsonb
31+
)
32+
FROM (%s) t',
33+
sql
34+
) INTO result_rows;
35+
results := result_rows;
36+
END IF;
37+
END;
38+
$sudo_exec$;
39+
-- Grant execute to public since this is for testing
40+
GRANT EXECUTE ON FUNCTION test.sudo_exec(text) TO PUBLIC;
41+
\echo Add users for testing purposes
42+
Add users for testing purposes
43+
SELECT * FROM public.user_create('test.admin@statbus.org', 'admin_user'::statbus_role, 'Admin#123!');
44+
email | password
45+
------------------------+------------
46+
test.admin@statbus.org | Admin#123!
47+
(1 row)
48+
49+
SELECT * FROM public.user_create('test.regular@statbus.org', 'regular_user'::statbus_role, 'Regular#123!');
50+
email | password
51+
--------------------------+--------------
52+
test.regular@statbus.org | Regular#123!
53+
(1 row)
54+
55+
SELECT * FROM public.user_create('test.restricted@statbus.org', 'restricted_user'::statbus_role, 'Restricted#123!');
56+
email | password
57+
-----------------------------+-----------------
58+
test.restricted@statbus.org | Restricted#123!
59+
(1 row)
60+
61+
-- This test verifies that an UPDATE on a temporal table correctly triggers
62+
-- the worker to refresh the statistical_unit materialized view.
63+
-- 1. Load initial data using the import system (ported from 306_load_demo_data.sql)
64+
-- 2. Check the initial state of a statistical unit.
65+
-- 3. Perform an UPDATE on a related `activity` record.
66+
-- 4. Verify that a `check_table` task is enqueued in worker.tasks.
67+
-- 5. Process the tasks.
68+
-- 6. Verify that the statistical unit has been updated with the new data.
69+
-- A Super User configures statbus.
70+
CALL test.set_user_from_email('test.admin@statbus.org');
71+
\echo "User selected the Activity Category Standard"
72+
"User selected the Activity Category Standard"
73+
INSERT INTO settings(activity_category_standard_id,only_one_setting)
74+
SELECT id, true FROM activity_category_standard WHERE code = 'isic_v4'
75+
ON CONFLICT (only_one_setting)
76+
DO UPDATE SET
77+
activity_category_standard_id =(SELECT id FROM activity_category_standard WHERE code = 'isic_v4')
78+
WHERE settings.id = EXCLUDED.id;
79+
;
80+
\echo "User uploads the sample activity categories"
81+
"User uploads the sample activity categories"
82+
\copy public.activity_category_available_custom(path,name) FROM 'app/public/demo/activity_custom_isic_demo.csv' WITH (FORMAT csv, DELIMITER ',', QUOTE '"', HEADER true);
83+
\echo "User uploads the sample regions"
84+
"User uploads the sample regions"
85+
\copy public.region_upload(path, name) FROM 'app/public/demo/regions_demo.csv' WITH (FORMAT csv, DELIMITER ',', QUOTE '"', HEADER true);
86+
\echo "User uploads the sample legal forms"
87+
"User uploads the sample legal forms"
88+
\copy public.legal_form_custom_only(code,name) FROM 'app/public/demo/legal_forms_demo.csv' WITH (FORMAT csv, DELIMITER ',', QUOTE '"', HEADER true);
89+
\echo "User uploads the sample sectors"
90+
"User uploads the sample sectors"
91+
\copy public.sector_custom_only(path,name,description) FROM 'app/public/demo/sectors_demo.csv' WITH (FORMAT csv, DELIMITER ',', QUOTE '"', HEADER true);
92+
-- Create Import Job for Legal Units (Demo CSV)
93+
INSERT INTO public.import_job (definition_id, slug, description, note, edit_comment, time_context_ident)
94+
SELECT
95+
(SELECT id FROM public.import_definition WHERE slug = 'legal_unit_job_provided'),
96+
'import_13_lu',
97+
'Import LU Demo CSV (313_update_triggers_refresh.sql)',
98+
'Import job for app/public/demo/legal_units_demo.csv using legal_unit_job_provided definition.',
99+
'Test data load (313_update_triggers_refresh.sql)',
100+
'r_year_curr';
101+
\echo "User uploads the sample legal units (via import job: import_13_lu)"
102+
"User uploads the sample legal units (via import job: import_13_lu)"
103+
\copy public.import_13_lu_upload(tax_ident,stat_ident,name,birth_date,physical_region_code,physical_country_iso_2,primary_activity_category_code,legal_form_code,sector_code,employees,turnover,data_source_code) FROM 'app/public/demo/legal_units_demo.csv' WITH (FORMAT csv, DELIMITER ',', QUOTE '"', HEADER true);
104+
-- Create Import Job for Formal Establishments (Demo CSV)
105+
INSERT INTO public.import_job (definition_id, slug, description, note, edit_comment, time_context_ident)
106+
SELECT
107+
(SELECT id FROM public.import_definition WHERE slug = 'establishment_for_lu_job_provided'),
108+
'import_13_esflu',
109+
'Import Formal ES Demo CSV (313_update_triggers_refresh.sql)',
110+
'Import job for app/public/demo/formal_establishments_units_demo.csv using establishment_for_lu_job_provided definition.',
111+
'Test data load (313_update_triggers_refresh.sql)',
112+
'r_year_curr';
113+
\echo "User uploads the sample formal establishments (via import job: import_13_esflu)"
114+
"User uploads the sample formal establishments (via import job: import_13_esflu)"
115+
\copy public.import_13_esflu_upload(tax_ident,stat_ident,name,physical_region_code,physical_country_iso_2,primary_activity_category_code,employees,turnover,legal_unit_tax_ident,data_source_code) FROM 'app/public/demo/formal_establishments_units_demo.csv' WITH (FORMAT csv, DELIMITER ',', QUOTE '"', HEADER true);
116+
-- Create Import Job for Informal Establishments (Demo CSV)
117+
INSERT INTO public.import_job (definition_id, slug, description, note, edit_comment, time_context_ident)
118+
SELECT
119+
(SELECT id FROM public.import_definition WHERE slug = 'establishment_without_lu_job_provided'),
120+
'import_13_eswlu',
121+
'Import Informal ES Demo CSV (313_update_triggers_refresh.sql)',
122+
'Import job for app/public/demo/informal_establishments_units_demo.csv using establishment_without_lu_job_provided definition.',
123+
'Test data load (313_update_triggers_refresh.sql)',
124+
'r_year_curr';
125+
\echo "User uploads the sample informal establishments (via import job: import_13_eswlu)"
126+
"User uploads the sample informal establishments (via import job: import_13_eswlu)"
127+
\copy public.import_13_eswlu_upload(tax_ident,stat_ident,name,physical_region_code,physical_country_iso_2,primary_activity_category_code,employees,turnover,data_source_code) FROM 'app/public/demo/informal_establishments_units_demo.csv' WITH (FORMAT csv, DELIMITER ',', QUOTE '"', HEADER true);
128+
\echo Run worker processing for import jobs
129+
Run worker processing for import jobs
130+
CALL worker.process_tasks(p_queue => 'import');
131+
\echo Run worker processing for analytics tasks
132+
Run worker processing for analytics tasks
133+
CALL worker.process_tasks(p_queue => 'analytics');
134+
--
135+
SELECT unit_type, external_idents, valid_from, valid_until, name, stats->'employees' as employees FROM public.statistical_unit
136+
WHERE name = 'Statistics Norway'
137+
ORDER BY unit_type, unit_id, valid_from, valid_until;
138+
unit_type | external_idents | valid_from | valid_until | name | employees
139+
---------------+-------------------------------------------------+------------+-------------+-------------------+-----------
140+
establishment | {"tax_ident": "94711111", "stat_ident": "1021"} | 2025-01-01 | infinity | Statistics Norway | 0
141+
legal_unit | {"tax_ident": "4711111", "stat_ident": "230"} | 2025-01-01 | infinity | Statistics Norway | 1
142+
enterprise | {"tax_ident": "4711111", "stat_ident": "230"} | 2025-01-01 | infinity | Statistics Norway |
143+
(3 rows)
144+
145+
\echo "Initial data loaded. All subsequent tests will check the worker response to an UPDATE on a specific temporal table."
146+
"Initial data loaded. All subsequent tests will check the worker response to an UPDATE on a specific temporal table."
147+
--------------------------------------------------------------------------------
148+
\echo "--- TEST 1: UPDATE on stat_for_unit ---"
149+
"--- TEST 1: UPDATE on stat_for_unit ---"
150+
--------------------------------------------------------------------------------
151+
\echo "Before: Checking stats for 'Statistics Norway' legal unit"
152+
"Before: Checking stats for 'Statistics Norway' legal unit"
153+
\x
154+
SELECT external_idents, name, stats->'employees' as employees
155+
FROM public.statistical_unit
156+
WHERE name = 'Statistics Norway' AND unit_type = 'legal_unit'
157+
AND valid_from <= current_date AND current_date < valid_until;
158+
-[ RECORD 1 ]---+----------------------------------------------
159+
external_idents | {"tax_ident": "4711111", "stat_ident": "230"}
160+
name | Statistics Norway
161+
employees | 1
162+
163+
\x
164+
\echo "Action: Updating a stat_for_unit record for 'Statistics Norway' legal unit"
165+
"Action: Updating a stat_for_unit record for 'Statistics Norway' legal unit"
166+
UPDATE public.stat_for_unit
167+
SET value_int = 100
168+
WHERE legal_unit_id = (SELECT id FROM public.legal_unit WHERE name = 'Statistics Norway')
169+
AND stat_definition_id = (SELECT id FROM public.stat_definition WHERE code = 'employees');
170+
\echo "Check: Expect one 'check_table' task for 'stat_for_unit'."
171+
"Check: Expect one 'check_table' task for 'stat_for_unit'."
172+
SELECT command, payload->>'table_name' as table_name, state
173+
FROM worker.tasks
174+
WHERE command = 'check_table' AND state = 'pending';
175+
command | table_name | state
176+
-------------+---------------+---------
177+
check_table | stat_for_unit | pending
178+
(1 row)
179+
180+
\echo "Run worker to process tasks..."
181+
"Run worker to process tasks..."
182+
CALL worker.process_tasks(p_queue => 'analytics'); -- Processes check_table, enqueues derive tasks and process derived tasks
183+
\echo "After: check stats for 'Statistics Norway'. Expect employees to be 100."
184+
"After: check stats for 'Statistics Norway'. Expect employees to be 100."
185+
\x
186+
SELECT external_idents, name, stats->'employees' as employees
187+
FROM public.statistical_unit
188+
WHERE name = 'Statistics Norway' AND unit_type = 'legal_unit'
189+
AND valid_from <= current_date AND current_date < valid_until;
190+
-[ RECORD 1 ]---+----------------------------------------------
191+
external_idents | {"tax_ident": "4711111", "stat_ident": "230"}
192+
name | Statistics Norway
193+
employees | 100
194+
195+
\x
196+
--------------------------------------------------------------------------------
197+
\echo "--- TEST 2: UPDATE on establishment ---"
198+
"--- TEST 2: UPDATE on establishment ---"
199+
--------------------------------------------------------------------------------
200+
\echo "Before: Checking name for establishment 'Statistics Norway'"
201+
"Before: Checking name for establishment 'Statistics Norway'"
202+
SELECT name FROM public.statistical_unit WHERE name = 'Statistics Norway' AND unit_type = 'establishment';
203+
name
204+
-------------------
205+
Statistics Norway
206+
(1 row)
207+
208+
\echo "Action: Updating establishment name"
209+
"Action: Updating establishment name"
210+
UPDATE public.establishment SET name = 'Statistics Norway Updated' WHERE name = 'Statistics Norway';
211+
\echo "Check: Expect one 'check_table' task for 'establishment'."
212+
"Check: Expect one 'check_table' task for 'establishment'."
213+
SELECT command, payload->>'table_name' as table_name, state FROM worker.tasks WHERE command = 'check_table' AND state = 'pending';
214+
command | table_name | state
215+
-------------+---------------+---------
216+
check_table | establishment | pending
217+
(1 row)
218+
219+
\echo "Run worker to process tasks..."
220+
"Run worker to process tasks..."
221+
CALL worker.process_tasks(p_queue => 'analytics');
222+
\echo "After: Checking name for establishment 'Statistics Norway Updated'"
223+
"After: Checking name for establishment 'Statistics Norway Updated'"
224+
SELECT name FROM public.statistical_unit WHERE name = 'Statistics Norway Updated' AND unit_type = 'establishment';
225+
name
226+
---------------------------
227+
Statistics Norway Updated
228+
(1 row)
229+
230+
--------------------------------------------------------------------------------
231+
\echo "--- TEST 3: UPDATE on legal_unit ---"
232+
"--- TEST 3: UPDATE on legal_unit ---"
233+
--------------------------------------------------------------------------------
234+
\echo "Before: Checking name of enterprise for legal_unit 'Statistics Norway'"
235+
"Before: Checking name of enterprise for legal_unit 'Statistics Norway'"
236+
SELECT name FROM public.statistical_unit WHERE unit_type = 'enterprise' AND unit_id = (SELECT enterprise_id FROM public.legal_unit WHERE name = 'Statistics Norway');
237+
name
238+
-------------------
239+
Statistics Norway
240+
(1 row)
241+
242+
\echo "Action: Updating legal_unit short_name"
243+
"Action: Updating legal_unit short_name"
244+
UPDATE public.legal_unit SET short_name = 'GIL' WHERE name = 'Statistics Norway';
245+
\echo "Check: Expect one 'check_table' task for 'legal_unit'."
246+
"Check: Expect one 'check_table' task for 'legal_unit'."
247+
SELECT command, payload->>'table_name' as table_name, state FROM worker.tasks WHERE command = 'check_table' AND state = 'pending';
248+
command | table_name | state
249+
-------------+------------+---------
250+
check_table | legal_unit | pending
251+
(1 row)
252+
253+
\echo "Run worker to process tasks..."
254+
"Run worker to process tasks..."
255+
CALL worker.process_tasks(p_queue => 'analytics');
256+
\echo "After: Checking name of enterprise for legal_unit 'Statistics Norway'. Expect 'GIL'."
257+
"After: Checking name of enterprise for legal_unit 'Statistics Norway'. Expect 'GIL'."
258+
SELECT name FROM public.statistical_unit WHERE unit_type = 'enterprise' AND unit_id = (SELECT enterprise_id FROM public.legal_unit WHERE name = 'Statistics Norway');
259+
name
260+
-------------------
261+
Statistics Norway
262+
(1 row)
263+
264+
--------------------------------------------------------------------------------
265+
\echo "--- TEST 4: UPDATE on location ---"
266+
"--- TEST 4: UPDATE on location ---"
267+
--------------------------------------------------------------------------------
268+
\echo "Before: Checking physical_address_part1 for 'Statistics Norway'"
269+
"Before: Checking physical_address_part1 for 'Statistics Norway'"
270+
SELECT physical_address_part1 FROM public.statistical_unit WHERE name = 'Statistics Norway' AND unit_type = 'legal_unit';
271+
physical_address_part1
272+
------------------------
273+
274+
(1 row)
275+
276+
\echo "Action: Updating location address_part1"
277+
"Action: Updating location address_part1"
278+
UPDATE public.location SET address_part1 = '123 New Street' WHERE legal_unit_id = (SELECT id FROM public.legal_unit WHERE name = 'Statistics Norway');
279+
\echo "Check: Expect one 'check_table' task for 'location'."
280+
"Check: Expect one 'check_table' task for 'location'."
281+
SELECT command, payload->>'table_name' as table_name, state FROM worker.tasks WHERE command = 'check_table' AND state = 'pending';
282+
command | table_name | state
283+
-------------+------------+---------
284+
check_table | location | pending
285+
(1 row)
286+
287+
\echo "Run worker to process tasks..."
288+
"Run worker to process tasks..."
289+
CALL worker.process_tasks(p_queue => 'analytics');
290+
\echo "After: Checking physical_address_part1 for 'Statistics Norway'"
291+
"After: Checking physical_address_part1 for 'Statistics Norway'"
292+
SELECT physical_address_part1 FROM public.statistical_unit WHERE name = 'Statistics Norway' AND unit_type = 'legal_unit';
293+
physical_address_part1
294+
------------------------
295+
123 New Street
296+
(1 row)
297+
298+
--------------------------------------------------------------------------------
299+
\echo "--- TEST 5: UPDATE on activity ---"
300+
"--- TEST 5: UPDATE on activity ---"
301+
--------------------------------------------------------------------------------
302+
\echo "Before: Checking primary_activity_category_path for 'Statistics Norway'"
303+
"Before: Checking primary_activity_category_path for 'Statistics Norway'"
304+
SELECT primary_activity_category_path FROM public.statistical_unit WHERE name = 'Statistics Norway' AND unit_type = 'legal_unit';
305+
primary_activity_category_path
306+
--------------------------------
307+
J.63.1.2.1
308+
(1 row)
309+
310+
\echo "Action: Updating activity category_id"
311+
"Action: Updating activity category_id"
312+
UPDATE public.activity
313+
SET category_id = (SELECT id FROM public.activity_category_available WHERE path = 'A.01')
314+
WHERE legal_unit_id = (SELECT id FROM public.legal_unit WHERE name = 'Statistics Norway')
315+
AND type = 'primary';
316+
\echo "Check: Expect one 'check_table' task for 'activity'."
317+
"Check: Expect one 'check_table' task for 'activity'."
318+
SELECT command, payload->>'table_name' as table_name, state FROM worker.tasks WHERE command = 'check_table' AND state = 'pending';
319+
command | table_name | state
320+
-------------+------------+---------
321+
check_table | activity | pending
322+
(1 row)
323+
324+
\echo "Run worker to process tasks..."
325+
"Run worker to process tasks..."
326+
CALL worker.process_tasks(p_queue => 'analytics');
327+
\echo "After: Checking primary_activity_category_path for 'Statistics Norway'"
328+
"After: Checking primary_activity_category_path for 'Statistics Norway'"
329+
SELECT primary_activity_category_path FROM public.statistical_unit WHERE name = 'Statistics Norway' AND unit_type = 'legal_unit';
330+
primary_activity_category_path
331+
--------------------------------
332+
A.01
333+
(1 row)
334+
335+
--------------------------------------------------------------------------------
336+
\echo "--- TEST 6: UPDATE on contact ---"
337+
"--- TEST 6: UPDATE on contact ---"
338+
--------------------------------------------------------------------------------
339+
\echo "Before: Checking email_address for 'Statistics Norway' (is null as no contact exists yet)"
340+
"Before: Checking email_address for 'Statistics Norway' (is null as no contact exists yet)"
341+
SELECT valid_from, valid_until, email_address FROM public.statistical_unit WHERE name = 'Statistics Norway' AND unit_type = 'legal_unit';
342+
valid_from | valid_until | email_address
343+
------------+-------------+---------------
344+
2025-01-01 | infinity |
345+
(1 row)
346+
347+
\echo "Action: INSERT a new contact and then UPDATE it to test trigger."
348+
"Action: INSERT a new contact and then UPDATE it to test trigger."
349+
-- The demo data does not include contacts, so we insert one to be able to test the UPDATE trigger.
350+
INSERT INTO public.contact (legal_unit_id, email_address, valid_from, valid_to, edit_by_user_id)
351+
SELECT
352+
lu.id,
353+
'test@example.com',
354+
'2025-01-01',
355+
'infinity',
356+
(SELECT u.id FROM auth.user u WHERE u.email = 'test.admin@statbus.org')
357+
FROM public.legal_unit lu WHERE lu.name = 'Statistics Norway';
358+
UPDATE public.contact
359+
SET email_address = 'updated.test@example.com'
360+
WHERE legal_unit_id = (SELECT id FROM public.legal_unit WHERE name = 'Statistics Norway');
361+
\echo "Check: Expect one 'check_table' task for 'contact' (coalesced from INSERT and UPDATE)."
362+
"Check: Expect one 'check_table' task for 'contact' (coalesced from INSERT and UPDATE)."
363+
SELECT command, payload->>'table_name' as table_name, state FROM worker.tasks WHERE command = 'check_table' AND state = 'pending';
364+
command | table_name | state
365+
-------------+------------+---------
366+
check_table | contact | pending
367+
(1 row)
368+
369+
\echo "Run worker to process tasks..."
370+
"Run worker to process tasks..."
371+
CALL worker.process_tasks(p_queue => 'analytics');
372+
\echo "After: Checking email_address for 'Statistics Norway' (should be updated)"
373+
"After: Checking email_address for 'Statistics Norway' (should be updated)"
374+
SELECT email_address FROM public.statistical_unit WHERE name = 'Statistics Norway' AND unit_type = 'legal_unit';
375+
email_address
376+
--------------------------
377+
updated.test@example.com
378+
(1 row)
379+
380+
ROLLBACK;

0 commit comments

Comments
 (0)