From d4504058bd865b256f368740c97297abd1dc4d53 Mon Sep 17 00:00:00 2001 From: Arup Chauhan Date: Sat, 16 May 2026 14:01:07 -0500 Subject: [PATCH 1/4] Added the search implementation files: Main scripts, and test directories Signed-off-by: Arup Chauhan --- ddl/Search/.DS_Store | Bin 0 -> 8196 bytes ddl/Search/codes/01_enable_fuzzy_search.sql | 6 + ddl/Search/codes/02_add_request_search.sql | 150 +++++++++ .../03_add_user_and_volunteer_search.sql | 127 ++++++++ .../04_add_category_and_advanced_search.sql | 86 +++++ ddl/Search/tests/01_search_smoke_test.sql | 275 ++++++++++++++++ ddl/Search/tests/02_search_index_check.sql | 63 ++++ ddl/Search/tests/03_search_function_check.sql | 227 +++++++++++++ ddl/Search/tests/04_instance_clone_check.sql | 86 +++++ .../tests/05_ireland_instance_clone_check.sql | 67 ++++ ddl/Search/tests/QA_VALIDATION.md | 301 ++++++++++++++++++ ddl/Search/tests/README.md | 118 +++++++ ddl/Search/tests/instance_clone/README.md | 75 +++++ .../ireland_reference_instance_clone.sql | 6 + .../ireland_search_instance_clone.sql | 183 +++++++++++ .../virginia_search_instance_clone.sql | 188 +++++++++++ .../01_search_smoke_test.sql | 273 ++++++++++++++++ .../02_search_index_check.sql | 63 ++++ .../03_search_function_check.sql | 225 +++++++++++++ .../04_instance_clone_check.sql | 86 +++++ .../tests/run_ireland_clone_validation.sql | 7 + .../tests/run_ireland_search_migrations.sql | 8 + .../tests/run_ireland_search_validation.sql | 16 + .../tests/run_local_search_validation.sql | 16 + .../tests/run_virginia_search_migrations.sql | 8 + 25 files changed, 2660 insertions(+) create mode 100644 ddl/Search/.DS_Store create mode 100644 ddl/Search/codes/01_enable_fuzzy_search.sql create mode 100644 ddl/Search/codes/02_add_request_search.sql create mode 100644 ddl/Search/codes/03_add_user_and_volunteer_search.sql create mode 100644 ddl/Search/codes/04_add_category_and_advanced_search.sql create mode 100644 ddl/Search/tests/01_search_smoke_test.sql create mode 100644 ddl/Search/tests/02_search_index_check.sql create mode 100644 ddl/Search/tests/03_search_function_check.sql create mode 100644 ddl/Search/tests/04_instance_clone_check.sql create mode 100644 ddl/Search/tests/05_ireland_instance_clone_check.sql create mode 100644 ddl/Search/tests/QA_VALIDATION.md create mode 100644 ddl/Search/tests/README.md create mode 100644 ddl/Search/tests/instance_clone/README.md create mode 100644 ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql create mode 100644 ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql create mode 100644 ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql create mode 100644 ddl/Search/tests/ireland_validation/01_search_smoke_test.sql create mode 100644 ddl/Search/tests/ireland_validation/02_search_index_check.sql create mode 100644 ddl/Search/tests/ireland_validation/03_search_function_check.sql create mode 100644 ddl/Search/tests/ireland_validation/04_instance_clone_check.sql create mode 100644 ddl/Search/tests/run_ireland_clone_validation.sql create mode 100644 ddl/Search/tests/run_ireland_search_migrations.sql create mode 100644 ddl/Search/tests/run_ireland_search_validation.sql create mode 100644 ddl/Search/tests/run_local_search_validation.sql create mode 100644 ddl/Search/tests/run_virginia_search_migrations.sql diff --git a/ddl/Search/.DS_Store b/ddl/Search/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d85cd8a3043848e08742475b6613dfd8b7a33307 GIT binary patch literal 8196 zcmeHMYitx%6u#fIz>FPaS}7D{*_8z((AuR4EfjHS%cIf?vMqg3th+lyJ20K8JF{CJ zK}(FrSK`~mU-&?O5;Ynz27>-FQDQU=i6j!EKg2)&;tv|pbLY;MmPewAF`#pkd(S=R zo^$U#XTCFc?<`{sO?iDeV^xeXnJTAR88z1^e4N*-iWEqu5Cz$@%!oUjv78L?$FI^3 z1z`lj2!s&`BM?R)jKGZ$0XnmJk*{;^i_x$RBM?U5e;EP(euz=!G#SuQLH$<;HU1TV zXeo*PLVb;Qh{gh%4Ctt!G(rW!l&CO8aK(TyCwVN;O9pgQP+`sxTs{!2jNpa>e|4Ha z=BqO#1r6IU0$~KMjsU;Amol9>EX&-9_xH@OWx8G>aRyRJ>6EF{6jHJA@ zmv@`ext*Nra2zXB?58t3Ok<>9o7wExUfQ&coNr+22C4LQnYQkXG&y-&cYWKG7?2fN z>DQ_b9$e9|x;j=@Uw^1NcCev7Q5{>gqVCWkMXrq3Hg50VYYo}X)4~M=uK_mw3YVUl z=#TDZtqkpt>x?^TxrJu&k)kA3bno&8FoiYy?{n`wdJZ&X* zTh1tuhh166$`9FYzgC`2TiLvo_KfwG#g$1m!_DR#%i89+re`{KPq%06^NJHq8tU$K zot)R~Pu8z#dDqx(&|GH0XrUQtstUf^)?q%4yTi!2MB~YX@L4%_>+I;lB{g;RYm*yV z+aDO4p_NytYUQl1g6WzW%jikFS;N*%d!Tc~)V;x83b*Z9yqS^Kb0r%kpyI-gPZM|TTIq~o1)?>LB-+wd5S|CZ%6ny{ zJL?P$ku$E4MVeb=S^NA!zHnRG^|lTh_V|{XH_LLbJDAQHTSo|yMT<6B_8%f!x#7xr|}ie;U}EO&$xi!aS4A&<T zBMnG{!UL(8f4e4oChaGFVyXq-1EpivPPOQ!&8_#TZ8vi zwJDS`f?cdGiE2|RT?D%pwtJOwTE2V4{>^gP6 zsxd_{P0^O9#&b*lr&0bjJIlT&lwV*MF%@Od5XBPQg$6?PMr=ka+R#qO?nV!Gq7McJ zFo->ba}NcKU_TDvF+5Hve-cmOMZAnx@G6er4Z`|SyiG`d7w_SHe1a1=iBtI0$NU(+ z!w>lJ3KX|bMlmi-L~$XNvn|ISAXyRhlhtolu0(9Sir@b?T>tz3&FWsb?=S*k1pYz< zP}-7eX(Gkzz25j;J4W>(s=RQ$Q9*qZYW(XsQU5wlbo>uP>c>cx+av=zDkw>){OcbA X=y&_y|EV3G|Ka)XgVo3Ef1&vkt>kz9 literal 0 HcmV?d00001 diff --git a/ddl/Search/codes/01_enable_fuzzy_search.sql b/ddl/Search/codes/01_enable_fuzzy_search.sql new file mode 100644 index 0000000..0af7d06 --- /dev/null +++ b/ddl/Search/codes/01_enable_fuzzy_search.sql @@ -0,0 +1,6 @@ +-- Enable trigram support for fuzzy search. +-- Idempotent: Can run multiple times +CREATE EXTENSION IF NOT EXISTS pg_trgm; + +-- Verify extension install. +-- SELECT extname FROM pg_extension WHERE extname = 'pg_trgm'; diff --git a/ddl/Search/codes/02_add_request_search.sql b/ddl/Search/codes/02_add_request_search.sql new file mode 100644 index 0000000..549436c --- /dev/null +++ b/ddl/Search/codes/02_add_request_search.sql @@ -0,0 +1,150 @@ +-- Request search. +-- Prerequisite: run 01_enable_fuzzy_search.sql. +-- Set search_path to the target schema before running. + +-- Request-local full-text vector. +ALTER TABLE request +ADD COLUMN IF NOT EXISTS search_vector tsvector +GENERATED ALWAYS AS ( + setweight(to_tsvector('english', coalesce(req_subj, '')), 'A') || + setweight(to_tsvector('english', coalesce(req_desc, '')), 'B') || + setweight(to_tsvector('english', coalesce(req_loc, '')), 'C') +) STORED; + +-- Request indexes. +CREATE INDEX IF NOT EXISTS idx_request_search_vector +ON request USING GIN (search_vector); + +CREATE INDEX IF NOT EXISTS idx_request_subj_trgm +ON request USING GIN (req_subj gin_trgm_ops); + +CREATE INDEX IF NOT EXISTS idx_request_desc_trgm +ON request USING GIN (req_desc gin_trgm_ops); + +CREATE INDEX IF NOT EXISTS idx_request_loc_trgm +ON request USING GIN (req_loc gin_trgm_ops); + +-- Category indexes used by request search. +CREATE INDEX IF NOT EXISTS idx_help_categories_name_fts +ON help_categories +USING GIN (to_tsvector('english', coalesce(cat_name, ''))); + +CREATE INDEX IF NOT EXISTS idx_help_categories_name_trgm +ON help_categories USING GIN (cat_name gin_trgm_ops); + +-- Request search entry point. +CREATE OR REPLACE FUNCTION search_requests( + query_text TEXT, + limit_results INT DEFAULT 20, + requester_user_id VARCHAR(255) DEFAULT NULL, + requester_access_level SMALLINT DEFAULT NULL, + allowed_request_owner_ids VARCHAR(255)[] DEFAULT NULL +) +RETURNS TABLE ( + req_id VARCHAR(255), + req_cat_id VARCHAR(50), + cat_name VARCHAR(100), + req_subj VARCHAR(125), + req_desc VARCHAR(255), + req_loc VARCHAR(125), + req_status_id INT, + req_priority_id INT, + submission_date TIMESTAMP, + relevance_score REAL +) +LANGUAGE plpgsql +SET search_path FROM CURRENT +AS $$ +BEGIN + IF query_text IS NULL OR btrim(query_text) = '' THEN + RETURN; + END IF; + + RETURN QUERY + WITH query_input AS ( + SELECT plainto_tsquery('english', query_text) AS ts_query + ), + ranked AS ( + SELECT + r.req_id, + r.req_cat_id, + hc.cat_name, + r.req_subj, + r.req_desc, + r.req_loc, + r.req_status_id, + r.req_priority_id, + r.submission_date, + ts_rank_cd( + setweight( + to_tsvector('english', replace(coalesce(hc.cat_name, ''), '_', ' ')), + 'A' + ) || + setweight(to_tsvector('english', coalesce(r.req_subj, '')), 'A') || + setweight(to_tsvector('english', coalesce(r.req_desc, '')), 'B') || + setweight(to_tsvector('english', coalesce(r.req_loc, '')), 'C'), + qi.ts_query + ) AS fts_score, + GREATEST( + similarity(replace(coalesce(hc.cat_name, ''), '_', ' '), query_text), + word_similarity(query_text, replace(coalesce(hc.cat_name, ''), '_', ' ')), + similarity(coalesce(r.req_subj, ''), query_text), + word_similarity(query_text, coalesce(r.req_subj, '')), + similarity(coalesce(r.req_desc, ''), query_text), + word_similarity(query_text, coalesce(r.req_desc, '')), + similarity(coalesce(r.req_loc, ''), query_text) + ) AS fuzzy_score + FROM request r + JOIN help_categories hc + ON hc.cat_id = r.req_cat_id + CROSS JOIN query_input qi + WHERE + ( + coalesce(requester_access_level, 0) >= 4 + OR ( + requester_user_id IS NOT NULL + AND r.req_user_id = requester_user_id + ) + OR ( + allowed_request_owner_ids IS NOT NULL + AND r.req_user_id = ANY(allowed_request_owner_ids) + ) + ) + AND + ( + ( + setweight( + to_tsvector('english', replace(coalesce(hc.cat_name, ''), '_', ' ')), + 'A' + ) || + r.search_vector + ) @@ qi.ts_query + OR to_tsvector('english', replace(coalesce(hc.cat_name, ''), '_', ' ')) @@ qi.ts_query + OR coalesce(r.req_subj, '') % query_text + OR coalesce(r.req_desc, '') % query_text + OR coalesce(r.req_loc, '') % query_text + OR replace(coalesce(hc.cat_name, ''), '_', ' ') % query_text + OR word_similarity(query_text, replace(coalesce(hc.cat_name, ''), '_', ' ')) >= 0.45 + OR word_similarity(query_text, coalesce(r.req_subj, '')) >= 0.45 + OR word_similarity(query_text, coalesce(r.req_desc, '')) >= 0.45 + ) + ) + SELECT + ranked.req_id, + ranked.req_cat_id, + ranked.cat_name, + ranked.req_subj, + ranked.req_desc, + ranked.req_loc, + ranked.req_status_id, + ranked.req_priority_id, + ranked.submission_date, + (ranked.fts_score * 0.70 + ranked.fuzzy_score * 0.30)::REAL AS relevance_score + FROM ranked + ORDER BY relevance_score DESC, submission_date DESC NULLS LAST + LIMIT GREATEST(limit_results, 1); +END; +$$; + +-- Example usage: +-- SELECT * FROM search_requests('emergency medical'); diff --git a/ddl/Search/codes/03_add_user_and_volunteer_search.sql b/ddl/Search/codes/03_add_user_and_volunteer_search.sql new file mode 100644 index 0000000..63cd896 --- /dev/null +++ b/ddl/Search/codes/03_add_user_and_volunteer_search.sql @@ -0,0 +1,127 @@ +-- User search. +-- Prerequisite: run 01_enable_fuzzy_search.sql. +-- Set search_path to the target schema before running. + +-- User-local full-text vector. +ALTER TABLE users +ADD COLUMN IF NOT EXISTS search_vector tsvector +GENERATED ALWAYS AS ( + setweight( + to_tsvector( + 'english', + coalesce(full_name, '') || ' ' || + coalesce(first_name, '') || ' ' || + coalesce(last_name, '') + ), + 'A' + ) || + setweight(to_tsvector('simple', coalesce(primary_email_address, '')), 'B') +) STORED; + +-- User indexes. +CREATE INDEX IF NOT EXISTS idx_users_search_vector +ON users USING GIN (search_vector); + +CREATE INDEX IF NOT EXISTS idx_users_full_name_trgm +ON users USING GIN (full_name gin_trgm_ops); + +CREATE INDEX IF NOT EXISTS idx_users_email_trgm +ON users USING GIN (primary_email_address gin_trgm_ops); + +CREATE INDEX IF NOT EXISTS idx_users_email_exact +ON users (primary_email_address); + +-- User search entry point. +CREATE OR REPLACE FUNCTION search_users( + query_text TEXT, + limit_results INT DEFAULT 20, + requester_user_id VARCHAR(255) DEFAULT NULL, + requester_access_level SMALLINT DEFAULT NULL, + allowed_user_ids VARCHAR(255)[] DEFAULT NULL +) +RETURNS TABLE ( + user_id VARCHAR(255), + full_name VARCHAR(255), + primary_email_address VARCHAR(255), + user_category_id INT, + user_status_id INT, + relevance_score REAL +) +LANGUAGE plpgsql +SET search_path FROM CURRENT +AS $$ +BEGIN + IF query_text IS NULL OR btrim(query_text) = '' THEN + RETURN; + END IF; + + RETURN QUERY + WITH query_input AS ( + SELECT + btrim(query_text) AS raw_query, + plainto_tsquery('english', query_text) AS name_ts_query, + plainto_tsquery('simple', query_text) AS email_ts_query + ), + ranked AS ( + SELECT + u.user_id, + u.full_name, + u.primary_email_address, + u.user_category_id, + u.user_status_id, + GREATEST( + ts_rank_cd(u.search_vector, qi.name_ts_query), + ts_rank_cd(u.search_vector, qi.email_ts_query) + ) AS fts_score, + GREATEST( + similarity(coalesce(u.full_name, ''), qi.raw_query), + word_similarity(qi.raw_query, coalesce(u.full_name, '')), + similarity(coalesce(u.primary_email_address, ''), qi.raw_query) + ) AS fuzzy_score, + CASE + WHEN lower(coalesce(u.primary_email_address, '')) = lower(qi.raw_query) THEN 1.0 + ELSE 0.0 + END AS exact_email_score + FROM users u + CROSS JOIN query_input qi + WHERE + ( + coalesce(requester_access_level, 0) >= 4 + OR ( + requester_user_id IS NOT NULL + AND u.user_id = requester_user_id + ) + OR ( + allowed_user_ids IS NOT NULL + AND u.user_id = ANY(allowed_user_ids) + ) + ) + AND + ( + u.search_vector @@ qi.name_ts_query + OR u.search_vector @@ qi.email_ts_query + OR coalesce(u.full_name, '') % qi.raw_query + OR word_similarity(qi.raw_query, coalesce(u.full_name, '')) >= 0.45 + OR similarity(coalesce(u.primary_email_address, ''), qi.raw_query) >= 0.60 + OR lower(coalesce(u.primary_email_address, '')) = lower(qi.raw_query) + ) + ) + SELECT + ranked.user_id, + ranked.full_name, + ranked.primary_email_address, + ranked.user_category_id, + ranked.user_status_id, + ( + ranked.exact_email_score * 1.00 + + ranked.fts_score * 0.60 + + ranked.fuzzy_score * 0.40 + )::REAL AS relevance_score + FROM ranked + ORDER BY relevance_score DESC, full_name ASC NULLS LAST, user_id ASC + LIMIT GREATEST(limit_results, 1); +END; +$$; + +-- Example usage: +-- SELECT * FROM search_users('arup'); diff --git a/ddl/Search/codes/04_add_category_and_advanced_search.sql b/ddl/Search/codes/04_add_category_and_advanced_search.sql new file mode 100644 index 0000000..ebe9001 --- /dev/null +++ b/ddl/Search/codes/04_add_category_and_advanced_search.sql @@ -0,0 +1,86 @@ +-- Organization search. +-- Prerequisite: run 01_enable_fuzzy_search.sql. +-- Set search_path to the target schema before running. + +-- Organization indexes. +CREATE INDEX IF NOT EXISTS idx_organizations_name_trgm +ON organizations USING GIN (org_name gin_trgm_ops); + +CREATE INDEX IF NOT EXISTS idx_organizations_city_trgm +ON organizations USING GIN (city_name gin_trgm_ops); + +-- Organization search entry point. +CREATE OR REPLACE FUNCTION search_organizations( + query_text TEXT, + limit_results INT DEFAULT 20, + requester_access_level SMALLINT DEFAULT NULL, + allowed_org_ids VARCHAR(255)[] DEFAULT NULL +) +RETURNS TABLE ( + org_id VARCHAR(255), + org_name VARCHAR(125), + city_name VARCHAR(100), + state_code VARCHAR(6), + relevance_score REAL +) +LANGUAGE plpgsql +SET search_path FROM CURRENT +AS $$ +BEGIN + IF query_text IS NULL OR btrim(query_text) = '' THEN + RETURN; + END IF; + + RETURN QUERY + WITH query_input AS ( + SELECT btrim(query_text) AS raw_query + ), + ranked AS ( + SELECT + o.org_id, + o.org_name, + o.city_name, + o.state_code, + GREATEST( + similarity(coalesce(o.org_name, ''), qi.raw_query), + word_similarity(qi.raw_query, coalesce(o.org_name, '')) + ) AS org_score, + GREATEST( + similarity(coalesce(o.city_name, ''), qi.raw_query), + word_similarity(qi.raw_query, coalesce(o.city_name, '')) + ) AS city_score + FROM organizations o + CROSS JOIN query_input qi + WHERE + ( + coalesce(requester_access_level, 0) >= 4 + OR ( + allowed_org_ids IS NOT NULL + AND o.org_id = ANY(allowed_org_ids) + ) + ) + AND + ( + coalesce(o.org_name, '') % qi.raw_query + OR coalesce(o.city_name, '') % qi.raw_query + OR word_similarity(qi.raw_query, coalesce(o.org_name, '')) >= 0.45 + OR word_similarity(qi.raw_query, coalesce(o.city_name, '')) >= 0.45 + ) + ) + SELECT + ranked.org_id, + ranked.org_name, + ranked.city_name, + ranked.state_code, + ( + ranked.org_score * 0.70 + + ranked.city_score * 0.30 + )::REAL AS relevance_score + FROM ranked + ORDER BY relevance_score DESC, org_name ASC, org_id ASC + LIMIT GREATEST(limit_results, 1); +END; +$$; + +-- Example usage: +-- SELECT * FROM search_organizations('Saayam Foundation'); diff --git a/ddl/Search/tests/01_search_smoke_test.sql b/ddl/Search/tests/01_search_smoke_test.sql new file mode 100644 index 0000000..b625dff --- /dev/null +++ b/ddl/Search/tests/01_search_smoke_test.sql @@ -0,0 +1,275 @@ +-- Search MVP smoke test. +-- Run after instance clone and ddl/Search/codes/01..04. + +BEGIN; + +SET search_path TO virginia_dev_saayam_rdbms, public; + +INSERT INTO country (country_id, country_name, phone_code, country_code) +VALUES (1, 'United States', '+1', 'US') +ON CONFLICT (country_id) DO NOTHING; + +INSERT INTO state (state_id, country_id, state_name, state_code) +VALUES ('CA', 1, 'California', 'CA') +ON CONFLICT (state_id) DO NOTHING; + +INSERT INTO user_status (user_status_id, user_status, user_status_desc) +VALUES (1, 'active', 'Active user') +ON CONFLICT (user_status_id) DO NOTHING; + +INSERT INTO user_category ( + user_category_id, + user_category, + user_category_desc, + user_access_level, + category_code +) +VALUES + (1, 'requester', 'Requester', 1, 'REQUESTER'), + (4, 'admin', 'Admin', 4, 'ADMIN') +ON CONFLICT (user_category_id) DO NOTHING; + +INSERT INTO users ( + user_id, + state_id, + country_id, + user_status_id, + user_category_id, + full_name, + first_name, + last_name, + primary_email_address, + city_name +) +VALUES + ('user-admin', 'CA', 1, 1, 4, 'Admin Search', 'Admin', 'Search', 'admin@example.test', 'San Jose'), + ('user-alice', 'CA', 1, 1, 1, 'Alice Emergency', 'Alice', 'Emergency', 'alice@example.test', 'San Jose'), + ('user-bob', 'CA', 1, 1, 1, 'Bob Foodhelp', 'Bob', 'Foodhelp', 'bob@example.test', 'Oakland') +ON CONFLICT (user_id) DO NOTHING; + +INSERT INTO help_categories (cat_id, cat_name, cat_desc) +VALUES + ('medical', 'Medical Help', 'Medical and emergency support'), + ('food', 'Food Support', 'Groceries and food help') +ON CONFLICT (cat_id) DO NOTHING; + +INSERT INTO request_status (req_status_id, req_status, req_status_desc) +VALUES (1, 'open', 'Open request') +ON CONFLICT (req_status_id) DO NOTHING; + +INSERT INTO request_priority (req_priority_id, req_priority, req_priority_desc) +VALUES (1, 'high', 'High priority') +ON CONFLICT (req_priority_id) DO NOTHING; + +INSERT INTO request_type (req_type_id, req_type, req_type_desc) +VALUES (1, 'standard', 'Standard request') +ON CONFLICT (req_type_id) DO NOTHING; + +INSERT INTO request_for (req_for_id, req_for, req_for_desc) +VALUES (1, 'self', 'For self') +ON CONFLICT (req_for_id) DO NOTHING; + +INSERT INTO request_isleadvol (req_islead_id, req_islead, req_islead_desc) +VALUES (1, 'no', 'Not lead volunteer') +ON CONFLICT (req_islead_id) DO NOTHING; + +INSERT INTO request ( + req_id, + req_user_id, + req_for_id, + req_islead_id, + req_cat_id, + req_type_id, + req_priority_id, + req_status_id, + req_loc, + iscalamity, + req_subj, + req_desc, + submission_date +) +VALUES + ( + 'req-medical-1', + 'user-alice', + 1, + 1, + 'medical', + 1, + 1, + 1, + 'San Jose', + false, + 'Emergency medical help', + 'Need urgent medical support and transport', + now() + ), + ( + 'req-food-1', + 'user-bob', + 1, + 1, + 'food', + 1, + 1, + 1, + 'Oakland', + false, + 'Food pantry support', + 'Need groceries and food assistance', + now() + ) +ON CONFLICT (req_id) DO NOTHING; + +INSERT INTO organizations ( + org_id, + org_name, + city_name, + state_code, + org_type, + source, + cat_id +) +VALUES + ('org-saayam-1', 'Saayam Foundation', 'San Jose', 'CA', 'non_profit', 'self_registered', 'medical'), + ('org-food-1', 'Community Food Bank', 'Oakland', 'CA', 'non_profit', 'self_registered', 'food') +ON CONFLICT (org_id) DO NOTHING; + +DO $$ +DECLARE + result_count INT; +BEGIN + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_requests( + 'emergancy medical', + 20, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE req_id = 'req-medical-1'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'request fuzzy search failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_requests( + 'food', + 20, + 'user-alice'::VARCHAR(255), + 1::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE req_id = 'req-food-1'; + + IF result_count <> 0 THEN + RAISE EXCEPTION 'request self-scope filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_requests( + 'food', + 20, + NULL::VARCHAR(255), + 1::SMALLINT, + ARRAY['user-bob']::VARCHAR(255)[] + ) + WHERE req_id = 'req-food-1'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'request allowed-owner filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_users( + 'Alic', + 20, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE user_id = 'user-alice'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'user fuzzy search failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_users( + 'bob@example.test', + 20, + 'user-alice'::VARCHAR(255), + 1::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE user_id = 'user-bob'; + + IF result_count <> 0 THEN + RAISE EXCEPTION 'user self-scope filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_users( + 'bob@example.test', + 20, + NULL::VARCHAR(255), + 1::SMALLINT, + ARRAY['user-bob']::VARCHAR(255)[] + ) + WHERE user_id = 'user-bob'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'user allowed-list filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_organizations( + 'Saaym Foundation', + 20, + 4::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE org_id = 'org-saayam-1'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'organization fuzzy search failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_organizations( + 'Food Bank', + 20, + 1::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE org_id = 'org-food-1'; + + IF result_count <> 0 THEN + RAISE EXCEPTION 'organization scope filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_organizations( + 'Food Bank', + 20, + 1::SMALLINT, + ARRAY['org-food-1']::VARCHAR(255)[] + ) + WHERE org_id = 'org-food-1'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'organization allowed-list filter failed'; + END IF; +END $$; + +ROLLBACK; diff --git a/ddl/Search/tests/02_search_index_check.sql b/ddl/Search/tests/02_search_index_check.sql new file mode 100644 index 0000000..1a8437a --- /dev/null +++ b/ddl/Search/tests/02_search_index_check.sql @@ -0,0 +1,63 @@ +-- Search index check. +-- Run after instance clone and ddl/Search/codes/01..04. + +DO $$ +DECLARE + missing_indexes TEXT[]; + invalid_indexes TEXT[]; +BEGIN + SELECT array_agg(expected.index_name ORDER BY expected.index_name) + INTO missing_indexes + FROM ( + VALUES + ('idx_request_search_vector'), + ('idx_request_subj_trgm'), + ('idx_request_desc_trgm'), + ('idx_request_loc_trgm'), + ('idx_help_categories_name_fts'), + ('idx_help_categories_name_trgm'), + ('idx_users_search_vector'), + ('idx_users_full_name_trgm'), + ('idx_users_email_trgm'), + ('idx_users_email_exact'), + ('idx_organizations_name_trgm'), + ('idx_organizations_city_trgm') + ) AS expected(index_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM pg_indexes idx + WHERE idx.schemaname = 'virginia_dev_saayam_rdbms' + AND idx.indexname = expected.index_name + ); + + IF missing_indexes IS NOT NULL THEN + RAISE EXCEPTION 'missing search indexes: %', array_to_string(missing_indexes, ', '); + END IF; + + SELECT array_agg(expected.index_name ORDER BY expected.index_name) + INTO invalid_indexes + FROM ( + VALUES + ('idx_request_search_vector', 'USING gin', 'search_vector'), + ('idx_request_subj_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_request_desc_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_request_loc_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_help_categories_name_fts', 'USING gin', 'to_tsvector'), + ('idx_help_categories_name_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_users_search_vector', 'USING gin', 'search_vector'), + ('idx_users_full_name_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_users_email_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_users_email_exact', 'USING btree', 'primary_email_address'), + ('idx_organizations_name_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_organizations_city_trgm', 'USING gin', 'gin_trgm_ops') + ) AS expected(index_name, required_method, required_expression) + JOIN pg_indexes idx + ON idx.schemaname = 'virginia_dev_saayam_rdbms' + AND idx.indexname = expected.index_name + WHERE idx.indexdef NOT ILIKE '%' || expected.required_method || '%' + OR idx.indexdef NOT ILIKE '%' || expected.required_expression || '%'; + + IF invalid_indexes IS NOT NULL THEN + RAISE EXCEPTION 'invalid search index definitions: %', array_to_string(invalid_indexes, ', '); + END IF; +END $$; diff --git a/ddl/Search/tests/03_search_function_check.sql b/ddl/Search/tests/03_search_function_check.sql new file mode 100644 index 0000000..b81d513 --- /dev/null +++ b/ddl/Search/tests/03_search_function_check.sql @@ -0,0 +1,227 @@ +-- Search function check. +-- Run after instance clone and ddl/Search/codes/01..04. + +BEGIN; + +SET search_path TO virginia_dev_saayam_rdbms, public; + +INSERT INTO country (country_id, country_name, phone_code, country_code) +VALUES (1, 'United States', '+1', 'US') +ON CONFLICT (country_id) DO NOTHING; + +INSERT INTO state (state_id, country_id, state_name, state_code) +VALUES ('CA', 1, 'California', 'CA') +ON CONFLICT (state_id) DO NOTHING; + +INSERT INTO user_status (user_status_id, user_status, user_status_desc) +VALUES (1, 'active', 'Active user') +ON CONFLICT (user_status_id) DO NOTHING; + +INSERT INTO user_category ( + user_category_id, + user_category, + user_category_desc, + user_access_level, + category_code +) +VALUES + (1, 'requester', 'Requester', 1, 'REQUESTER'), + (4, 'admin', 'Admin', 4, 'ADMIN') +ON CONFLICT (user_category_id) DO NOTHING; + +INSERT INTO users ( + user_id, + state_id, + country_id, + user_status_id, + user_category_id, + full_name, + first_name, + last_name, + primary_email_address, + city_name +) +VALUES + ('fn-admin', 'CA', 1, 1, 4, 'Function Admin', 'Function', 'Admin', 'fn-admin@example.test', 'San Jose'), + ('fn-requester', 'CA', 1, 1, 1, 'Maya Medical', 'Maya', 'Medical', 'maya.medical@example.test', 'San Jose'), + ('fn-other', 'CA', 1, 1, 1, 'Noah Grocery', 'Noah', 'Grocery', 'noah.grocery@example.test', 'Oakland') +ON CONFLICT (user_id) DO NOTHING; + +INSERT INTO help_categories (cat_id, cat_name, cat_desc) +VALUES + ('fn-medical', 'Medical Care', 'Medical help category'), + ('fn-food', 'Food Delivery', 'Food help category') +ON CONFLICT (cat_id) DO NOTHING; + +INSERT INTO request_status (req_status_id, req_status, req_status_desc) +VALUES (1, 'open', 'Open request') +ON CONFLICT (req_status_id) DO NOTHING; + +INSERT INTO request_priority (req_priority_id, req_priority, req_priority_desc) +VALUES (1, 'high', 'High priority') +ON CONFLICT (req_priority_id) DO NOTHING; + +INSERT INTO request_type (req_type_id, req_type, req_type_desc) +VALUES (1, 'standard', 'Standard request') +ON CONFLICT (req_type_id) DO NOTHING; + +INSERT INTO request_for (req_for_id, req_for, req_for_desc) +VALUES (1, 'self', 'For self') +ON CONFLICT (req_for_id) DO NOTHING; + +INSERT INTO request_isleadvol (req_islead_id, req_islead, req_islead_desc) +VALUES (1, 'no', 'Not lead volunteer') +ON CONFLICT (req_islead_id) DO NOTHING; + +INSERT INTO request ( + req_id, + req_user_id, + req_for_id, + req_islead_id, + req_cat_id, + req_type_id, + req_priority_id, + req_status_id, + req_loc, + iscalamity, + req_subj, + req_desc, + submission_date +) +VALUES + ( + 'fn-req-medical', + 'fn-requester', + 1, + 1, + 'fn-medical', + 1, + 1, + 1, + 'San Jose', + false, + 'Need medical transportation', + 'Need a ride to medical appointment', + now() + ), + ( + 'fn-req-food', + 'fn-other', + 1, + 1, + 'fn-food', + 1, + 1, + 1, + 'Oakland', + false, + 'Need grocery delivery', + 'Need food delivery this week', + now() + ) +ON CONFLICT (req_id) DO NOTHING; + +INSERT INTO organizations ( + org_id, + org_name, + city_name, + state_code, + org_type, + source, + cat_id +) +VALUES + ('fn-org-medical', 'Medical Transport Network', 'San Jose', 'CA', 'non_profit', 'self_registered', 'fn-medical'), + ('fn-org-food', 'Grocery Relief Group', 'Oakland', 'CA', 'non_profit', 'self_registered', 'fn-food') +ON CONFLICT (org_id) DO NOTHING; + +DO $$ +DECLARE + result_count INT; + top_id TEXT; + score REAL; +BEGIN + SELECT count(*), max(req_id), max(relevance_score) + INTO result_count, top_id, score + FROM virginia_dev_saayam_rdbms.search_requests( + 'medical transportation', + 5, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count = 0 OR top_id <> 'fn-req-medical' OR score <= 0 THEN + RAISE EXCEPTION 'search_requests keyword ranking failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_requests( + '', + 5, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count <> 0 THEN + RAISE EXCEPTION 'search_requests empty query failed'; + END IF; + + SELECT count(*), max(user_id), max(relevance_score) + INTO result_count, top_id, score + FROM virginia_dev_saayam_rdbms.search_users( + 'maya.medical@example.test', + 5, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count = 0 OR top_id <> 'fn-requester' OR score <= 0 THEN + RAISE EXCEPTION 'search_users exact email failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_users( + '', + 5, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count <> 0 THEN + RAISE EXCEPTION 'search_users empty query failed'; + END IF; + + SELECT count(*), max(org_id), max(relevance_score) + INTO result_count, top_id, score + FROM virginia_dev_saayam_rdbms.search_organizations( + 'Medical Transport', + 5, + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count = 0 OR top_id <> 'fn-org-medical' OR score <= 0 THEN + RAISE EXCEPTION 'search_organizations name search failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM virginia_dev_saayam_rdbms.search_organizations( + '', + 5, + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count <> 0 THEN + RAISE EXCEPTION 'search_organizations empty query failed'; + END IF; +END $$; + +ROLLBACK; diff --git a/ddl/Search/tests/04_instance_clone_check.sql b/ddl/Search/tests/04_instance_clone_check.sql new file mode 100644 index 0000000..9a50167 --- /dev/null +++ b/ddl/Search/tests/04_instance_clone_check.sql @@ -0,0 +1,86 @@ +-- Local instance clone check. +-- Run after instance clone and ddl/Search/codes/01..04. + +DO $$ +DECLARE + missing_tables TEXT[]; + missing_columns TEXT[]; + missing_functions TEXT[]; +BEGIN + SELECT array_agg(expected.table_name ORDER BY expected.table_name) + INTO missing_tables + FROM ( + VALUES + ('country'), + ('state'), + ('user_status'), + ('user_category'), + ('users'), + ('help_categories'), + ('request_status'), + ('request_priority'), + ('request_type'), + ('request_for'), + ('request_isleadvol'), + ('request'), + ('organizations'), + ('user_org_map') + ) AS expected(table_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.tables tbl + WHERE tbl.table_schema = 'virginia_dev_saayam_rdbms' + AND tbl.table_name = expected.table_name + ); + + IF missing_tables IS NOT NULL THEN + RAISE EXCEPTION 'missing clone tables: %', array_to_string(missing_tables, ', '); + END IF; + + SELECT array_agg(expected.table_name || '.' || expected.column_name ORDER BY expected.table_name, expected.column_name) + INTO missing_columns + FROM ( + VALUES + ('request', 'search_vector'), + ('users', 'search_vector'), + ('request', 'req_subj'), + ('request', 'req_desc'), + ('request', 'req_loc'), + ('help_categories', 'cat_name'), + ('users', 'full_name'), + ('users', 'primary_email_address'), + ('organizations', 'org_name'), + ('organizations', 'city_name') + ) AS expected(table_name, column_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.columns col + WHERE col.table_schema = 'virginia_dev_saayam_rdbms' + AND col.table_name = expected.table_name + AND col.column_name = expected.column_name + ); + + IF missing_columns IS NOT NULL THEN + RAISE EXCEPTION 'missing clone/search columns: %', array_to_string(missing_columns, ', '); + END IF; + + SELECT array_agg(expected.function_name ORDER BY expected.function_name) + INTO missing_functions + FROM ( + VALUES + ('search_requests'), + ('search_users'), + ('search_organizations') + ) AS expected(function_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.routines routine + WHERE routine.specific_schema = 'virginia_dev_saayam_rdbms' + AND routine.routine_name = expected.function_name + AND routine.routine_type = 'FUNCTION' + ); + + IF missing_functions IS NOT NULL THEN + RAISE EXCEPTION 'missing search functions: %', array_to_string(missing_functions, ', '); + END IF; +END $$; diff --git a/ddl/Search/tests/05_ireland_instance_clone_check.sql b/ddl/Search/tests/05_ireland_instance_clone_check.sql new file mode 100644 index 0000000..42aea6b --- /dev/null +++ b/ddl/Search/tests/05_ireland_instance_clone_check.sql @@ -0,0 +1,67 @@ +-- Ireland local instance clone check. +-- Run after ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql. + +DO $$ +DECLARE + missing_tables TEXT[]; + missing_columns TEXT[]; +BEGIN + SELECT array_agg(expected.table_name ORDER BY expected.table_name) + INTO missing_tables + FROM ( + VALUES + ('country'), + ('state'), + ('user_status'), + ('user_category'), + ('users'), + ('help_categories'), + ('request_status'), + ('request_priority'), + ('request_type'), + ('request_for'), + ('request_isleadvol'), + ('request'), + ('organizations'), + ('user_org_map') + ) AS expected(table_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.tables tbl + WHERE tbl.table_schema = 'proposed_saayam' + AND tbl.table_name = expected.table_name + ); + + IF missing_tables IS NOT NULL THEN + RAISE EXCEPTION 'missing Ireland clone tables: %', array_to_string(missing_tables, ', '); + END IF; + + SELECT array_agg(expected.table_name || '.' || expected.column_name ORDER BY expected.table_name, expected.column_name) + INTO missing_columns + FROM ( + VALUES + ('request', 'req_subj'), + ('request', 'req_desc'), + ('request', 'req_loc'), + ('request', 'req_cat_id'), + ('request', 'req_user_id'), + ('help_categories', 'cat_name'), + ('users', 'full_name'), + ('users', 'first_name'), + ('users', 'last_name'), + ('users', 'primary_email_address'), + ('organizations', 'org_name'), + ('organizations', 'city_name') + ) AS expected(table_name, column_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.columns col + WHERE col.table_schema = 'proposed_saayam' + AND col.table_name = expected.table_name + AND col.column_name = expected.column_name + ); + + IF missing_columns IS NOT NULL THEN + RAISE EXCEPTION 'missing Ireland search columns: %', array_to_string(missing_columns, ', '); + END IF; +END $$; diff --git a/ddl/Search/tests/QA_VALIDATION.md b/ddl/Search/tests/QA_VALIDATION.md new file mode 100644 index 0000000..ee1edd9 --- /dev/null +++ b/ddl/Search/tests/QA_VALIDATION.md @@ -0,0 +1,301 @@ +# Search QA Validation + +## Purpose + +This document explains how to validate the database-side search work locally and later on QA/AWS RDS. + +The local tests prove the SQL compiles and behaves correctly against a Virginia-style clone. +QA validation proves the same search logic works against the real deployed schema, permissions, data shape, and RDS settings. + +## Files Under Test + +Search migration files: + +- `ddl/Search/codes/01_enable_fuzzy_search.sql` +- `ddl/Search/codes/02_add_request_search.sql` +- `ddl/Search/codes/03_add_user_and_volunteer_search.sql` +- `ddl/Search/codes/04_add_category_and_advanced_search.sql` + +Local validation files: + +- `ddl/Search/tests/run_virginia_search_migrations.sql` +- `ddl/Search/tests/run_ireland_search_migrations.sql` +- `ddl/Search/tests/run_local_search_validation.sql` +- `ddl/Search/tests/run_ireland_clone_validation.sql` +- `ddl/Search/tests/run_ireland_search_validation.sql` +- `ddl/Search/tests/01_search_smoke_test.sql` +- `ddl/Search/tests/02_search_index_check.sql` +- `ddl/Search/tests/03_search_function_check.sql` +- `ddl/Search/tests/04_instance_clone_check.sql` +- `ddl/Search/tests/05_ireland_instance_clone_check.sql` +- `ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql` +- `ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql` +- `ddl/Search/tests/ireland_validation/` + +## Local Validation Runbook + +Use this before QA to confirm the current branch works locally. + +```bash +createdb saayam_search_clone_test + +psql -d saayam_search_clone_test \ + -f ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql + +psql -d saayam_search_clone_test \ + -f ddl/Search/tests/run_virginia_search_migrations.sql + +psql -d saayam_search_clone_test \ + -v ON_ERROR_STOP=1 \ + -f ddl/Search/tests/run_local_search_validation.sql +``` + +Expected local result: + +- all scripts complete without errors +- runner prints `Local search validation completed` + +## Ireland Local Clone Validation + +Ireland uses schema `proposed_saayam`. +The production search scripts are schema-agnostic and use the active `search_path`. + +```bash +createdb saayam_ireland_search_clone_test + +psql -d saayam_ireland_search_clone_test \ + -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql + +psql -d saayam_ireland_search_clone_test \ + -v ON_ERROR_STOP=1 \ + -f ddl/Search/tests/run_ireland_clone_validation.sql +``` + +Expected Ireland local result: + +- clone loads without errors +- runner prints `Ireland clone validation completed` +- required Ireland search tables and columns exist under `proposed_saayam` + +## Ireland Full Local Search Validation + +Use this to validate the same search behavior against the Ireland schema name: + +```bash +createdb saayam_ireland_full_search_test + +psql -d saayam_ireland_full_search_test \ + -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql + +psql -d saayam_ireland_full_search_test \ + -f ddl/Search/tests/run_ireland_search_migrations.sql + +psql -d saayam_ireland_full_search_test \ + -v ON_ERROR_STOP=1 \ + -f ddl/Search/tests/run_ireland_search_validation.sql +``` + +Expected Ireland full-search result: + +- scripts compile against `proposed_saayam` +- smoke test passes +- index check passes +- function check passes +- migrated clone check passes +- runner prints `Ireland full search validation completed` + +## QA/RDS Validation Runbook + +Use this only after QA database access is available. + +Connect to QA: + +```bash +psql -h \ + -U \ + -d +``` + +Run search scripts with the correct target schema. + +For Virginia QA: + +```sql +\i ddl/Search/tests/run_virginia_search_migrations.sql +``` + +For Ireland QA: + +```sql +\i ddl/Search/tests/run_ireland_search_migrations.sql +``` + +Then run validation checks. + +Recommended first QA checks: + +```sql +\i ddl/Search/tests/02_search_index_check.sql +\i ddl/Search/tests/04_instance_clone_check.sql +``` + +Run behavior checks only after confirming temporary QA inserts are acceptable: + +```sql +\i ddl/Search/tests/01_search_smoke_test.sql +\i ddl/Search/tests/03_search_function_check.sql +``` + +## QA Safety Notes + +The behavior tests use `BEGIN` and `ROLLBACK`, so their sample data should not persist. + +Still confirm before running on QA: + +- the QA role can create extensions, columns, indexes, and functions +- temporary test inserts are allowed in QA +- QA constraints match the local clone assumptions +- no trigger or audit process has side effects from rolled-back inserts +- the active schema is correct for the target region + +## PR Evidence To Capture + +Capture the following for PR review: + +- output showing `run_virginia_search_migrations.sql` completed locally +- output showing `run_local_search_validation.sql` passed locally for Virginia +- output showing `run_ireland_search_migrations.sql` completed locally +- output showing `run_ireland_search_validation.sql` passed locally for Ireland +- output showing QA index check passed +- output showing QA function/behavior checks passed, if run +- `EXPLAIN` or `EXPLAIN ANALYZE` for representative search queries +- confirmation that unauthorized or missing-scope calls return zero rows + +Local PR evidence already captured: + +- Virginia schema-agnostic validation passed: `saayam_virginia_agnostic_test_1778949545` +- Ireland schema-agnostic validation passed: `saayam_ireland_agnostic_test_1778949579` +- Both validations used the shared production scripts under `ddl/Search/codes/` +- Region selection is handled through `search_path` in the migration runners + +Useful QA evidence queries: + +Virginia examples are shown below. For Ireland, replace `virginia_dev_saayam_rdbms` with `proposed_saayam`. + +```sql +SELECT extname +FROM pg_extension +WHERE extname = 'pg_trgm'; + +SELECT schemaname, tablename, indexname, indexdef +FROM pg_indexes +WHERE schemaname = 'virginia_dev_saayam_rdbms' + AND indexname IN ( + 'idx_request_search_vector', + 'idx_request_subj_trgm', + 'idx_request_desc_trgm', + 'idx_request_loc_trgm', + 'idx_help_categories_name_fts', + 'idx_help_categories_name_trgm', + 'idx_users_search_vector', + 'idx_users_full_name_trgm', + 'idx_users_email_trgm', + 'idx_users_email_exact', + 'idx_organizations_name_trgm', + 'idx_organizations_city_trgm' + ) +ORDER BY tablename, indexname; +``` + +Representative search checks: + +```sql +SELECT * +FROM virginia_dev_saayam_rdbms.search_requests( + 'medical help', + 10, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] +); + +SELECT * +FROM virginia_dev_saayam_rdbms.search_users( + 'test@example.com', + 10, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] +); + +SELECT * +FROM virginia_dev_saayam_rdbms.search_organizations( + 'food bank', + 10, + 4::SMALLINT, + NULL::VARCHAR(255)[] +); +``` + +Missing-scope checks: + +```sql +SELECT count(*) +FROM virginia_dev_saayam_rdbms.search_requests( + 'medical help', + 10, + NULL::VARCHAR(255), + 1::SMALLINT, + NULL::VARCHAR(255)[] +); + +SELECT count(*) +FROM virginia_dev_saayam_rdbms.search_users( + 'test@example.com', + 10, + NULL::VARCHAR(255), + 1::SMALLINT, + NULL::VARCHAR(255)[] +); + +SELECT count(*) +FROM virginia_dev_saayam_rdbms.search_organizations( + 'food bank', + 10, + 1::SMALLINT, + NULL::VARCHAR(255)[] +); +``` + +Expected missing-scope result: + +- count is `0` + +## DevSecOps Items Still Pending + +These are not solved by local SQL validation: + +- final backend-to-DB authorization context contract +- whether to keep function parameters or move to `SET LOCAL app.*` session variables +- whether to add Row-Level Security policies +- AWS IAM database authentication +- `rds.force_ssl = 1` +- pgAudit or RDS audit logging +- CloudWatch monitoring for suspicious search access + +## Current Boundary + +Current state: + +- local code-level search MVP is implemented +- local clone validation passes +- local behavior/index/function tests pass +- Ireland search-shape clone validation passes +- Ireland full local search validation passes +- schema targeting is complete through `search_path` migration runners + +Not yet complete: + +- QA/RDS validation +- performance review with real data +- final RDS security hardening diff --git a/ddl/Search/tests/README.md b/ddl/Search/tests/README.md new file mode 100644 index 0000000..0a4fa73 --- /dev/null +++ b/ddl/Search/tests/README.md @@ -0,0 +1,118 @@ +# Search Test Workspace + +This folder is for search-specific local testing only. + +Do not edit the original schema files under `ddl/Tables/` for search testing. If a local clone of a deployed schema is needed, place the copied or generated schema assets under `instance_clone/`. + +## Intended Flow + +1. Create a local PostgreSQL database. +2. Load a Virginia or Ireland schema clone from `instance_clone/`. +3. Run the search scripts from `../codes/` in order. +4. Run local search validation. + +## Search Scripts Under Test + +- `../codes/01_enable_fuzzy_search.sql` +- `../codes/02_add_request_search.sql` +- `../codes/03_add_user_and_volunteer_search.sql` +- `../codes/04_add_category_and_advanced_search.sql` + +## Smoke Test + +## Run All Local Checks + +Run this after loading the local clone and applying search migrations: + +```bash +psql -d saayam_search_clone_test -f ddl/Search/tests/run_local_search_validation.sql +``` + +This runs the smoke test, index check, function check, and instance clone check in order. + +## Virginia Full Search Check + +```bash +createdb saayam_search_clone_test +psql -d saayam_search_clone_test -f ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/run_virginia_search_migrations.sql +psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_local_search_validation.sql +``` + +## Ireland Clone Check + +The production search scripts are schema-agnostic and rely on `search_path`. +Ireland uses `proposed_saayam`, so the Ireland runner sets that schema before applying the shared scripts. + +```bash +createdb saayam_ireland_search_clone_test +psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql +psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/run_ireland_clone_validation.sql +``` + +This confirms the Ireland-style clone has the tables and columns needed by the search plan. + +## Ireland Full Search Check + +The same production scripts under `ddl/Search/codes/` are used for Ireland. +The Ireland migration runner sets `search_path` to `proposed_saayam`. + +```bash +createdb saayam_ireland_full_search_test +psql -d saayam_ireland_full_search_test -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql +psql -d saayam_ireland_full_search_test -f ddl/Search/tests/run_ireland_search_migrations.sql +psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_ireland_search_validation.sql +``` + +This runs the same behavior, index, function, and migrated-clone checks against the Ireland schema name, `proposed_saayam`. + +## Smoke Test + +Run this after loading the local clone and search scripts: + +```bash +psql -d saayam_search_clone_test -f ddl/Search/tests/01_search_smoke_test.sql +``` + +This inserts temporary sample data, checks search behavior, and rolls the data back. + +## Index Check + +Run this after loading the local clone and search scripts: + +```bash +psql -d saayam_search_clone_test -f ddl/Search/tests/02_search_index_check.sql +``` + +This fails if any expected search index is missing or uses the wrong strategy. +It checks GIN full-text indexes, GIN trigram indexes, and the exact email btree index. + +## Function Check + +Run this after loading the local clone and search scripts: + +```bash +psql -d saayam_search_clone_test -f ddl/Search/tests/03_search_function_check.sql +``` + +This checks the main search functions directly: + +- `search_requests(...)` +- `search_users(...)` +- `search_organizations(...)` + +It verifies keyword matching, exact email matching, organization name matching, relevance scores, and empty-query behavior. + +## Instance Clone Check + +Run this after loading the local clone and search scripts: + +```bash +psql -d saayam_search_clone_test -f ddl/Search/tests/04_instance_clone_check.sql +``` + +This verifies the local Virginia clone has the search-required tables, search columns, and search functions. + +## QA Validation + +See `QA_VALIDATION.md` for the QA/RDS validation runbook, safety notes, and PR evidence checklist. diff --git a/ddl/Search/tests/instance_clone/README.md b/ddl/Search/tests/instance_clone/README.md new file mode 100644 index 0000000..9f0c7b1 --- /dev/null +++ b/ddl/Search/tests/instance_clone/README.md @@ -0,0 +1,75 @@ +# Instance Clone Schemas + +Use this folder for local schema clones that mirror deployed AWS RDS instances. + +Examples: + +- Virginia schema clone +- Ireland schema clone + +These files are test fixtures for search validation. They should be copied or generated from existing deployed/schema sources and kept separate from the original DDL files. + +## Why This Exists + +The search scripts are schema-level migrations. Testing them against a local clone of the Virginia or Ireland RDS schema helps catch: + +- schema-name issues +- missing table or column assumptions +- index creation problems +- function compilation errors +- query behavior differences between minimal test tables and real schema structure + +## Files + +- `virginia_search_instance_clone.sql`: search-scoped Virginia clone for validating the current MVP search scripts. +- `ireland_search_instance_clone.sql`: search-scoped Ireland clone for validating Ireland schema shape without requiring local PostGIS. +- `ireland_reference_instance_clone.sql`: wrapper around the existing combined Ireland script for structure comparison. +- `README.md`: this guide. + +The Virginia clone intentionally includes only the search-relevant tables and dependencies. It is not a replacement for the production DDL. +The Ireland search clone follows the Ireland schema name, `proposed_saayam`, and includes the search-relevant tables/columns only. + +## Suggested Local Test Order + +```bash +createdb saayam_search_clone_test +psql -d saayam_search_clone_test -f ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/run_virginia_search_migrations.sql +psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_local_search_validation.sql +``` + +Add smoke-test SQL files under `ddl/Search/tests/` as the search validation suite grows. + +## Ireland Reference Check + +```bash +createdb saayam_ireland_reference_test +psql -d saayam_ireland_reference_test -f ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql +``` + +Do not run the current Virginia-targeted search scripts against the Ireland reference clone until schema targeting is explicitly handled. + +## Ireland Search Clone Check + +Use this when local PostGIS is not available or when the goal is only to confirm Ireland search shape: + +```bash +createdb saayam_ireland_search_clone_test +psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql +psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/run_ireland_clone_validation.sql +``` + +This confirms the Ireland-style `proposed_saayam` clone has the tables and columns needed by the current search plan. + +## Ireland Full Search Validation + +Use this to validate the same search behavior against the Ireland schema name: + +```bash +createdb saayam_ireland_full_search_test +psql -d saayam_ireland_full_search_test -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql +psql -d saayam_ireland_full_search_test -f ddl/Search/tests/run_ireland_search_migrations.sql +psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_ireland_search_validation.sql +``` + +The same production search scripts are used for Ireland through `run_ireland_search_migrations.sql`. diff --git a/ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql b/ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql new file mode 100644 index 0000000..55eec86 --- /dev/null +++ b/ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql @@ -0,0 +1,6 @@ +-- Ireland reference instance clone. +-- This wraps the existing combined Ireland script for local structure checks. +-- The current MVP search scripts target virginia_dev_saayam_rdbms, so this file +-- is for Ireland shape comparison until search scripts are parameterized. + +\ir ../../../Tables/Scripts/ireland_dev_saayam_rdbms.sql diff --git a/ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql b/ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql new file mode 100644 index 0000000..dc5d4c3 --- /dev/null +++ b/ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql @@ -0,0 +1,183 @@ +-- Search-scoped Ireland instance clone. +-- Use this fixture to validate Ireland search shape locally without editing ddl/Tables. + +DROP SCHEMA IF EXISTS proposed_saayam CASCADE; +CREATE SCHEMA proposed_saayam; +SET search_path TO proposed_saayam, public; + +CREATE TABLE proposed_saayam.country ( + country_id SERIAL PRIMARY KEY, + country_name VARCHAR(100) NOT NULL, + phone_code VARCHAR(5) NOT NULL, + country_code VARCHAR(6) NOT NULL, + last_update_date TIMESTAMP, + UNIQUE (country_id) +); + +CREATE TABLE proposed_saayam.state ( + state_id VARCHAR(50) PRIMARY KEY, + country_id INT NOT NULL, + state_name VARCHAR(100) NOT NULL, + state_code VARCHAR(6), + last_update_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (country_id) REFERENCES proposed_saayam.country (country_id) +); + +CREATE TABLE proposed_saayam.user_status ( + user_status_id SERIAL PRIMARY KEY, + user_status VARCHAR(255) NOT NULL, + user_status_desc VARCHAR(255), + last_update_date TIMESTAMP, + UNIQUE (user_status_id) +); + +CREATE TABLE proposed_saayam.user_category ( + user_category_id SERIAL PRIMARY KEY, + user_category VARCHAR(255) NOT NULL, + user_category_desc VARCHAR(255), + last_update_date TIMESTAMP, + UNIQUE (user_category_id) +); + +CREATE TABLE proposed_saayam.users ( + user_id VARCHAR(255) PRIMARY KEY, + state_id VARCHAR(50) NULL, + country_id INT NULL, + user_status_id INT NULL, + user_category_id INT NULL, + full_name VARCHAR(255) NULL, + first_name VARCHAR(255) NULL, + middle_name VARCHAR(255) NULL, + last_name VARCHAR(255) NULL, + primary_email_address VARCHAR(255) NULL, + primary_phone_number VARCHAR(255) NULL, + addr_ln1 VARCHAR(255) NULL, + addr_ln2 VARCHAR(255) NULL, + addr_ln3 VARCHAR(255) NULL, + city_name VARCHAR(255) NULL, + zip_code VARCHAR(255) NULL, + last_location point, + last_update_date TIMESTAMP, + time_zone VARCHAR(255) NULL, + profile_picture_path VARCHAR(255) NULL, + gender VARCHAR(255) NULL, + language_1 VARCHAR(255) NULL, + language_2 VARCHAR(255) NULL, + language_3 VARCHAR(255) NULL, + promotion_wizard_stage INT NULL, + promotion_wizard_last_update_date TIMESTAMP, + FOREIGN KEY (country_id) REFERENCES proposed_saayam.country (country_id), + FOREIGN KEY (state_id) REFERENCES proposed_saayam.state (state_id), + FOREIGN KEY (user_status_id) REFERENCES proposed_saayam.user_status (user_status_id), + FOREIGN KEY (user_category_id) REFERENCES proposed_saayam.user_category (user_category_id) +); + +CREATE TABLE proposed_saayam.help_categories ( + cat_id VARCHAR(50) PRIMARY KEY, + cat_name VARCHAR(100) NOT NULL, + cat_desc VARCHAR(150) NOT NULL +); + +CREATE TABLE proposed_saayam.request_status ( + req_status_id SERIAL PRIMARY KEY, + req_status VARCHAR(25) NOT NULL, + req_status_desc VARCHAR(125), + last_updated_date TIMESTAMP +); + +CREATE TABLE proposed_saayam.request_priority ( + req_priority_id SERIAL PRIMARY KEY, + req_priority VARCHAR(25) NOT NULL, + req_priority_desc VARCHAR(125), + last_updated_date TIMESTAMP, + UNIQUE (req_priority_id) +); + +CREATE TABLE proposed_saayam.request_type ( + req_type_id SERIAL PRIMARY KEY, + req_type VARCHAR(25), + req_type_desc VARCHAR(125), + last_updated_date TIMESTAMP +); + +CREATE TABLE proposed_saayam.request_for ( + req_for_id SERIAL PRIMARY KEY, + req_for VARCHAR(25) NOT NULL, + req_for_desc VARCHAR(125), + last_updated_date TIMESTAMP, + UNIQUE (req_for_id) +); + +CREATE TABLE proposed_saayam.request_isleadvol ( + req_islead_id SERIAL PRIMARY KEY, + req_islead VARCHAR(15) NOT NULL, + req_islead_desc VARCHAR(100), + last_updated_date TIMESTAMP +); + +CREATE TABLE proposed_saayam.request ( + req_id VARCHAR(255) PRIMARY KEY, + req_user_id VARCHAR(255) NOT NULL, + req_for_id INT NOT NULL, + req_islead_id INT NOT NULL, + req_cat_id VARCHAR(50) NOT NULL, + req_type_id INT NOT NULL, + req_priority_id INT NOT NULL, + req_status_id INT NOT NULL, + req_loc VARCHAR(125), + iscalamity BOOLEAN, + req_subj VARCHAR(125) NOT NULL, + req_desc VARCHAR(255) NOT NULL, + req_doc_link TEXT, + audio_req_desc VARCHAR(255), + submission_date TIMESTAMP, + serviced_date TIMESTAMP, + last_update_date TIMESTAMP, + UNIQUE (req_id), + FOREIGN KEY (req_user_id) REFERENCES proposed_saayam.users (user_id), + FOREIGN KEY (req_status_id) REFERENCES proposed_saayam.request_status (req_status_id), + FOREIGN KEY (req_priority_id) REFERENCES proposed_saayam.request_priority (req_priority_id), + FOREIGN KEY (req_type_id) REFERENCES proposed_saayam.request_type (req_type_id), + FOREIGN KEY (req_cat_id) REFERENCES proposed_saayam.help_categories (cat_id), + FOREIGN KEY (req_for_id) REFERENCES proposed_saayam.request_for (req_for_id), + FOREIGN KEY (req_islead_id) REFERENCES proposed_saayam.request_isleadvol (req_islead_id) +); + +CREATE TYPE org_type_enum AS ENUM ('non_profit', 'for_profit', 'Hybrid', 'Government'); +CREATE TYPE source_enum AS ENUM ('irs', 'self_registered'); + +CREATE TABLE proposed_saayam.organizations ( + org_id VARCHAR(255) PRIMARY KEY, + org_name VARCHAR(125) NOT NULL, + govt_id_num VARCHAR(20) UNIQUE, + street VARCHAR(255), + city_name VARCHAR(100), + state_code VARCHAR(6), + zip_code VARCHAR(10), + mission TEXT, + web_url VARCHAR(255) CHECK (web_url IS NULL OR web_url LIKE 'http%'), + phone VARCHAR(20), + email VARCHAR(255) CHECK (email IS NULL OR email LIKE '%@%'), + org_type org_type_enum, + source source_enum, + cat_id VARCHAR(50), + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + last_updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (cat_id) REFERENCES proposed_saayam.help_categories(cat_id) ON DELETE SET NULL +); + +CREATE INDEX idx_org_city_state ON proposed_saayam.organizations(city_name, state_code); +CREATE INDEX idx_org_state ON proposed_saayam.organizations(state_code); +CREATE INDEX idx_org_cat_id ON proposed_saayam.organizations(cat_id); +CREATE INDEX idx_org_name ON proposed_saayam.organizations(org_name); + +CREATE TABLE proposed_saayam.user_org_map ( + user_id VARCHAR(255) NOT NULL, + org_id VARCHAR(255) NOT NULL, + user_role VARCHAR(50), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + last_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT user_org_map_pk PRIMARY KEY (user_id, org_id), + FOREIGN KEY (user_id) REFERENCES proposed_saayam.users(user_id) ON DELETE CASCADE, + FOREIGN KEY (org_id) REFERENCES proposed_saayam.organizations(org_id) ON DELETE CASCADE +); diff --git a/ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql b/ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql new file mode 100644 index 0000000..b1222a3 --- /dev/null +++ b/ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql @@ -0,0 +1,188 @@ +-- Search-scoped Virginia instance clone. +-- Use this fixture to validate ddl/Search/codes locally without editing ddl/Tables. + +DROP SCHEMA IF EXISTS virginia_dev_saayam_rdbms CASCADE; +CREATE SCHEMA virginia_dev_saayam_rdbms; +SET search_path TO virginia_dev_saayam_rdbms, public; + +CREATE TABLE virginia_dev_saayam_rdbms.country ( + country_id SERIAL PRIMARY KEY, + country_name VARCHAR(100) NOT NULL, + phone_code VARCHAR(5) NOT NULL, + country_code VARCHAR(6) NOT NULL, + last_update_date TIMESTAMP, + is_eu_member BOOLEAN DEFAULT FALSE, + UNIQUE (country_id) +); + +CREATE TABLE virginia_dev_saayam_rdbms.state ( + state_id VARCHAR(50) PRIMARY KEY, + country_id INT NOT NULL, + state_name VARCHAR(100) NOT NULL, + state_code VARCHAR(6), + last_update_date TIMESTAMP, + FOREIGN KEY (country_id) REFERENCES virginia_dev_saayam_rdbms.country (country_id) +); + +CREATE TABLE virginia_dev_saayam_rdbms.user_status ( + user_status_id SERIAL PRIMARY KEY, + user_status VARCHAR(255) NOT NULL, + user_status_desc VARCHAR(255), + last_update_date TIMESTAMP, + UNIQUE (user_status_id) +); + +CREATE TABLE virginia_dev_saayam_rdbms.user_category ( + user_category_id SERIAL PRIMARY KEY, + user_category VARCHAR(255) NOT NULL, + user_category_desc VARCHAR(255), + user_access_level SMALLINT, + category_code VARCHAR(50) UNIQUE, + is_deprecated BOOLEAN DEFAULT FALSE, + permissions JSONB, + last_updated_at TIMESTAMP DEFAULT NOW() +); + +CREATE TABLE virginia_dev_saayam_rdbms.users ( + user_id VARCHAR(255) PRIMARY KEY, + state_id VARCHAR(30) NULL, + country_id INT NULL, + user_status_id INT NULL, + user_category_id INT NULL, + full_name VARCHAR(255) NULL, + first_name VARCHAR(255) NULL, + middle_name VARCHAR(255) NULL, + last_name VARCHAR(255) NULL, + primary_email_address VARCHAR(255) NULL, + primary_phone_number VARCHAR(255) NULL, + addr_ln1 VARCHAR(255) NULL, + addr_ln2 VARCHAR(255) NULL, + addr_ln3 VARCHAR(255) NULL, + city_name VARCHAR(255) NULL, + zip_code VARCHAR(255) NULL, + last_location point, + last_update_date TIMESTAMP, + time_zone VARCHAR(255) NULL, + profile_picture_path VARCHAR(255) NULL, + gender VARCHAR(255) NULL, + language_1 VARCHAR(255) NULL, + language_2 VARCHAR(255) NULL, + language_3 VARCHAR(255) NULL, + promotion_wizard_stage INT NULL, + promotion_wizard_last_update_date TIMESTAMP, + FOREIGN KEY (country_id) REFERENCES virginia_dev_saayam_rdbms.country (country_id) ON DELETE SET NULL, + FOREIGN KEY (state_id) REFERENCES virginia_dev_saayam_rdbms.state (state_id) ON DELETE SET NULL, + FOREIGN KEY (user_status_id) REFERENCES virginia_dev_saayam_rdbms.user_status (user_status_id), + FOREIGN KEY (user_category_id) REFERENCES virginia_dev_saayam_rdbms.user_category (user_category_id) ON DELETE SET NULL +); + +CREATE TABLE virginia_dev_saayam_rdbms.help_categories ( + cat_id VARCHAR(50) PRIMARY KEY, + cat_name VARCHAR(100) NOT NULL, + cat_desc VARCHAR(150) NOT NULL +); + +CREATE TABLE virginia_dev_saayam_rdbms.request_status ( + req_status_id SERIAL PRIMARY KEY, + req_status VARCHAR(25) NOT NULL, + req_status_desc VARCHAR(125), + last_updated_date TIMESTAMP +); + +CREATE TABLE virginia_dev_saayam_rdbms.request_priority ( + req_priority_id SERIAL PRIMARY KEY, + req_priority VARCHAR(25) NOT NULL, + req_priority_desc VARCHAR(125), + last_updated_date TIMESTAMP, + UNIQUE (req_priority_id) +); + +CREATE TABLE virginia_dev_saayam_rdbms.request_type ( + req_type_id SERIAL PRIMARY KEY, + req_type VARCHAR(25), + req_type_desc VARCHAR(125), + last_updated_date TIMESTAMP +); + +CREATE TABLE virginia_dev_saayam_rdbms.request_for ( + req_for_id SERIAL PRIMARY KEY, + req_for VARCHAR(25) NOT NULL, + req_for_desc VARCHAR(125), + last_updated_date TIMESTAMP, + UNIQUE (req_for_id) +); + +CREATE TABLE virginia_dev_saayam_rdbms.request_isleadvol ( + req_islead_id SERIAL PRIMARY KEY, + req_islead VARCHAR(25) NOT NULL, + req_islead_desc VARCHAR(125), + last_updated_date TIMESTAMP, + UNIQUE (req_islead_id) +); + +CREATE TABLE virginia_dev_saayam_rdbms.request ( + req_id VARCHAR(255) PRIMARY KEY, + req_user_id VARCHAR(255) NOT NULL, + req_for_id INT NOT NULL, + req_islead_id INT NOT NULL, + req_cat_id VARCHAR(50) NOT NULL, + req_type_id INT NOT NULL, + req_priority_id INT NOT NULL, + req_status_id INT NOT NULL, + req_loc VARCHAR(125), + iscalamity BOOLEAN, + req_subj VARCHAR(125) NOT NULL, + req_desc VARCHAR(255) NOT NULL, + req_doc_link TEXT, + audio_req_desc VARCHAR(255), + submission_date TIMESTAMP, + serviced_date TIMESTAMP, + last_update_date TIMESTAMP, + UNIQUE (req_id), + FOREIGN KEY (req_user_id) REFERENCES virginia_dev_saayam_rdbms.users (user_id), + FOREIGN KEY (req_status_id) REFERENCES virginia_dev_saayam_rdbms.request_status (req_status_id), + FOREIGN KEY (req_priority_id) REFERENCES virginia_dev_saayam_rdbms.request_priority (req_priority_id), + FOREIGN KEY (req_type_id) REFERENCES virginia_dev_saayam_rdbms.request_type (req_type_id), + FOREIGN KEY (req_cat_id) REFERENCES virginia_dev_saayam_rdbms.help_categories (cat_id), + FOREIGN KEY (req_for_id) REFERENCES virginia_dev_saayam_rdbms.request_for (req_for_id), + FOREIGN KEY (req_islead_id) REFERENCES virginia_dev_saayam_rdbms.request_isleadvol (req_islead_id) +); + +CREATE TYPE org_type_enum AS ENUM ('non_profit', 'for_profit'); +CREATE TYPE source_enum AS ENUM ('irs', 'self_registered'); + +CREATE TABLE virginia_dev_saayam_rdbms.organizations ( + org_id VARCHAR(255) PRIMARY KEY, + org_name VARCHAR(125) NOT NULL, + govt_id_num VARCHAR(20) UNIQUE, + street VARCHAR(255), + city_name VARCHAR(100), + state_code VARCHAR(6), + zip_code VARCHAR(10), + mission TEXT, + web_url VARCHAR(255) CHECK (web_url IS NULL OR web_url LIKE 'http%'), + phone VARCHAR(20), + email VARCHAR(255) CHECK (email IS NULL OR email LIKE '%@%'), + org_type org_type_enum, + source source_enum, + cat_id VARCHAR(50), + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + last_updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (cat_id) REFERENCES virginia_dev_saayam_rdbms.help_categories(cat_id) ON DELETE SET NULL +); + +CREATE INDEX idx_org_city_state ON virginia_dev_saayam_rdbms.organizations(city_name, state_code); +CREATE INDEX idx_org_state ON virginia_dev_saayam_rdbms.organizations(state_code); +CREATE INDEX idx_org_cat_id ON virginia_dev_saayam_rdbms.organizations(cat_id); +CREATE INDEX idx_org_name ON virginia_dev_saayam_rdbms.organizations(org_name); + +CREATE TABLE virginia_dev_saayam_rdbms.user_org_map ( + user_id VARCHAR(255) NOT NULL, + org_id VARCHAR(255) NOT NULL, + user_role VARCHAR(50), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + last_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT user_org_map_pk PRIMARY KEY (user_id, org_id), + FOREIGN KEY (user_id) REFERENCES virginia_dev_saayam_rdbms.users(user_id) ON DELETE CASCADE, + FOREIGN KEY (org_id) REFERENCES virginia_dev_saayam_rdbms.organizations(org_id) ON DELETE CASCADE +); diff --git a/ddl/Search/tests/ireland_validation/01_search_smoke_test.sql b/ddl/Search/tests/ireland_validation/01_search_smoke_test.sql new file mode 100644 index 0000000..2974a82 --- /dev/null +++ b/ddl/Search/tests/ireland_validation/01_search_smoke_test.sql @@ -0,0 +1,273 @@ +-- Search MVP smoke test. +-- Run after instance clone and ddl/Search/codes/01..04. + +BEGIN; + +SET search_path TO proposed_saayam, public; + +INSERT INTO country (country_id, country_name, phone_code, country_code) +VALUES (1, 'United States', '+1', 'US') +ON CONFLICT (country_id) DO NOTHING; + +INSERT INTO state (state_id, country_id, state_name, state_code) +VALUES ('CA', 1, 'California', 'CA') +ON CONFLICT (state_id) DO NOTHING; + +INSERT INTO user_status (user_status_id, user_status, user_status_desc) +VALUES (1, 'active', 'Active user') +ON CONFLICT (user_status_id) DO NOTHING; + +INSERT INTO user_category ( + user_category_id, + user_category, + user_category_desc +) +VALUES + (1, 'requester', 'Requester'), + (4, 'admin', 'Admin') +ON CONFLICT (user_category_id) DO NOTHING; + +INSERT INTO users ( + user_id, + state_id, + country_id, + user_status_id, + user_category_id, + full_name, + first_name, + last_name, + primary_email_address, + city_name +) +VALUES + ('user-admin', 'CA', 1, 1, 4, 'Admin Search', 'Admin', 'Search', 'admin@example.test', 'San Jose'), + ('user-alice', 'CA', 1, 1, 1, 'Alice Emergency', 'Alice', 'Emergency', 'alice@example.test', 'San Jose'), + ('user-bob', 'CA', 1, 1, 1, 'Bob Foodhelp', 'Bob', 'Foodhelp', 'bob@example.test', 'Oakland') +ON CONFLICT (user_id) DO NOTHING; + +INSERT INTO help_categories (cat_id, cat_name, cat_desc) +VALUES + ('medical', 'Medical Help', 'Medical and emergency support'), + ('food', 'Food Support', 'Groceries and food help') +ON CONFLICT (cat_id) DO NOTHING; + +INSERT INTO request_status (req_status_id, req_status, req_status_desc) +VALUES (1, 'open', 'Open request') +ON CONFLICT (req_status_id) DO NOTHING; + +INSERT INTO request_priority (req_priority_id, req_priority, req_priority_desc) +VALUES (1, 'high', 'High priority') +ON CONFLICT (req_priority_id) DO NOTHING; + +INSERT INTO request_type (req_type_id, req_type, req_type_desc) +VALUES (1, 'standard', 'Standard request') +ON CONFLICT (req_type_id) DO NOTHING; + +INSERT INTO request_for (req_for_id, req_for, req_for_desc) +VALUES (1, 'self', 'For self') +ON CONFLICT (req_for_id) DO NOTHING; + +INSERT INTO request_isleadvol (req_islead_id, req_islead, req_islead_desc) +VALUES (1, 'no', 'Not lead volunteer') +ON CONFLICT (req_islead_id) DO NOTHING; + +INSERT INTO request ( + req_id, + req_user_id, + req_for_id, + req_islead_id, + req_cat_id, + req_type_id, + req_priority_id, + req_status_id, + req_loc, + iscalamity, + req_subj, + req_desc, + submission_date +) +VALUES + ( + 'req-medical-1', + 'user-alice', + 1, + 1, + 'medical', + 1, + 1, + 1, + 'San Jose', + false, + 'Emergency medical help', + 'Need urgent medical support and transport', + now() + ), + ( + 'req-food-1', + 'user-bob', + 1, + 1, + 'food', + 1, + 1, + 1, + 'Oakland', + false, + 'Food pantry support', + 'Need groceries and food assistance', + now() + ) +ON CONFLICT (req_id) DO NOTHING; + +INSERT INTO organizations ( + org_id, + org_name, + city_name, + state_code, + org_type, + source, + cat_id +) +VALUES + ('org-saayam-1', 'Saayam Foundation', 'San Jose', 'CA', 'non_profit', 'self_registered', 'medical'), + ('org-food-1', 'Community Food Bank', 'Oakland', 'CA', 'non_profit', 'self_registered', 'food') +ON CONFLICT (org_id) DO NOTHING; + +DO $$ +DECLARE + result_count INT; +BEGIN + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_requests( + 'emergancy medical', + 20, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE req_id = 'req-medical-1'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'request fuzzy search failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_requests( + 'food', + 20, + 'user-alice'::VARCHAR(255), + 1::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE req_id = 'req-food-1'; + + IF result_count <> 0 THEN + RAISE EXCEPTION 'request self-scope filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_requests( + 'food', + 20, + NULL::VARCHAR(255), + 1::SMALLINT, + ARRAY['user-bob']::VARCHAR(255)[] + ) + WHERE req_id = 'req-food-1'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'request allowed-owner filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_users( + 'Alic', + 20, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE user_id = 'user-alice'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'user fuzzy search failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_users( + 'bob@example.test', + 20, + 'user-alice'::VARCHAR(255), + 1::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE user_id = 'user-bob'; + + IF result_count <> 0 THEN + RAISE EXCEPTION 'user self-scope filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_users( + 'bob@example.test', + 20, + NULL::VARCHAR(255), + 1::SMALLINT, + ARRAY['user-bob']::VARCHAR(255)[] + ) + WHERE user_id = 'user-bob'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'user allowed-list filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_organizations( + 'Saaym Foundation', + 20, + 4::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE org_id = 'org-saayam-1'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'organization fuzzy search failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_organizations( + 'Food Bank', + 20, + 1::SMALLINT, + NULL::VARCHAR(255)[] + ) + WHERE org_id = 'org-food-1'; + + IF result_count <> 0 THEN + RAISE EXCEPTION 'organization scope filter failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_organizations( + 'Food Bank', + 20, + 1::SMALLINT, + ARRAY['org-food-1']::VARCHAR(255)[] + ) + WHERE org_id = 'org-food-1'; + + IF result_count <> 1 THEN + RAISE EXCEPTION 'organization allowed-list filter failed'; + END IF; +END $$; + +ROLLBACK; diff --git a/ddl/Search/tests/ireland_validation/02_search_index_check.sql b/ddl/Search/tests/ireland_validation/02_search_index_check.sql new file mode 100644 index 0000000..b0765a4 --- /dev/null +++ b/ddl/Search/tests/ireland_validation/02_search_index_check.sql @@ -0,0 +1,63 @@ +-- Search index check. +-- Run after instance clone and ddl/Search/codes/01..04. + +DO $$ +DECLARE + missing_indexes TEXT[]; + invalid_indexes TEXT[]; +BEGIN + SELECT array_agg(expected.index_name ORDER BY expected.index_name) + INTO missing_indexes + FROM ( + VALUES + ('idx_request_search_vector'), + ('idx_request_subj_trgm'), + ('idx_request_desc_trgm'), + ('idx_request_loc_trgm'), + ('idx_help_categories_name_fts'), + ('idx_help_categories_name_trgm'), + ('idx_users_search_vector'), + ('idx_users_full_name_trgm'), + ('idx_users_email_trgm'), + ('idx_users_email_exact'), + ('idx_organizations_name_trgm'), + ('idx_organizations_city_trgm') + ) AS expected(index_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM pg_indexes idx + WHERE idx.schemaname = 'proposed_saayam' + AND idx.indexname = expected.index_name + ); + + IF missing_indexes IS NOT NULL THEN + RAISE EXCEPTION 'missing search indexes: %', array_to_string(missing_indexes, ', '); + END IF; + + SELECT array_agg(expected.index_name ORDER BY expected.index_name) + INTO invalid_indexes + FROM ( + VALUES + ('idx_request_search_vector', 'USING gin', 'search_vector'), + ('idx_request_subj_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_request_desc_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_request_loc_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_help_categories_name_fts', 'USING gin', 'to_tsvector'), + ('idx_help_categories_name_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_users_search_vector', 'USING gin', 'search_vector'), + ('idx_users_full_name_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_users_email_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_users_email_exact', 'USING btree', 'primary_email_address'), + ('idx_organizations_name_trgm', 'USING gin', 'gin_trgm_ops'), + ('idx_organizations_city_trgm', 'USING gin', 'gin_trgm_ops') + ) AS expected(index_name, required_method, required_expression) + JOIN pg_indexes idx + ON idx.schemaname = 'proposed_saayam' + AND idx.indexname = expected.index_name + WHERE idx.indexdef NOT ILIKE '%' || expected.required_method || '%' + OR idx.indexdef NOT ILIKE '%' || expected.required_expression || '%'; + + IF invalid_indexes IS NOT NULL THEN + RAISE EXCEPTION 'invalid search index definitions: %', array_to_string(invalid_indexes, ', '); + END IF; +END $$; diff --git a/ddl/Search/tests/ireland_validation/03_search_function_check.sql b/ddl/Search/tests/ireland_validation/03_search_function_check.sql new file mode 100644 index 0000000..ef695ce --- /dev/null +++ b/ddl/Search/tests/ireland_validation/03_search_function_check.sql @@ -0,0 +1,225 @@ +-- Search function check. +-- Run after instance clone and ddl/Search/codes/01..04. + +BEGIN; + +SET search_path TO proposed_saayam, public; + +INSERT INTO country (country_id, country_name, phone_code, country_code) +VALUES (1, 'United States', '+1', 'US') +ON CONFLICT (country_id) DO NOTHING; + +INSERT INTO state (state_id, country_id, state_name, state_code) +VALUES ('CA', 1, 'California', 'CA') +ON CONFLICT (state_id) DO NOTHING; + +INSERT INTO user_status (user_status_id, user_status, user_status_desc) +VALUES (1, 'active', 'Active user') +ON CONFLICT (user_status_id) DO NOTHING; + +INSERT INTO user_category ( + user_category_id, + user_category, + user_category_desc +) +VALUES + (1, 'requester', 'Requester'), + (4, 'admin', 'Admin') +ON CONFLICT (user_category_id) DO NOTHING; + +INSERT INTO users ( + user_id, + state_id, + country_id, + user_status_id, + user_category_id, + full_name, + first_name, + last_name, + primary_email_address, + city_name +) +VALUES + ('fn-admin', 'CA', 1, 1, 4, 'Function Admin', 'Function', 'Admin', 'fn-admin@example.test', 'San Jose'), + ('fn-requester', 'CA', 1, 1, 1, 'Maya Medical', 'Maya', 'Medical', 'maya.medical@example.test', 'San Jose'), + ('fn-other', 'CA', 1, 1, 1, 'Noah Grocery', 'Noah', 'Grocery', 'noah.grocery@example.test', 'Oakland') +ON CONFLICT (user_id) DO NOTHING; + +INSERT INTO help_categories (cat_id, cat_name, cat_desc) +VALUES + ('fn-medical', 'Medical Care', 'Medical help category'), + ('fn-food', 'Food Delivery', 'Food help category') +ON CONFLICT (cat_id) DO NOTHING; + +INSERT INTO request_status (req_status_id, req_status, req_status_desc) +VALUES (1, 'open', 'Open request') +ON CONFLICT (req_status_id) DO NOTHING; + +INSERT INTO request_priority (req_priority_id, req_priority, req_priority_desc) +VALUES (1, 'high', 'High priority') +ON CONFLICT (req_priority_id) DO NOTHING; + +INSERT INTO request_type (req_type_id, req_type, req_type_desc) +VALUES (1, 'standard', 'Standard request') +ON CONFLICT (req_type_id) DO NOTHING; + +INSERT INTO request_for (req_for_id, req_for, req_for_desc) +VALUES (1, 'self', 'For self') +ON CONFLICT (req_for_id) DO NOTHING; + +INSERT INTO request_isleadvol (req_islead_id, req_islead, req_islead_desc) +VALUES (1, 'no', 'Not lead volunteer') +ON CONFLICT (req_islead_id) DO NOTHING; + +INSERT INTO request ( + req_id, + req_user_id, + req_for_id, + req_islead_id, + req_cat_id, + req_type_id, + req_priority_id, + req_status_id, + req_loc, + iscalamity, + req_subj, + req_desc, + submission_date +) +VALUES + ( + 'fn-req-medical', + 'fn-requester', + 1, + 1, + 'fn-medical', + 1, + 1, + 1, + 'San Jose', + false, + 'Need medical transportation', + 'Need a ride to medical appointment', + now() + ), + ( + 'fn-req-food', + 'fn-other', + 1, + 1, + 'fn-food', + 1, + 1, + 1, + 'Oakland', + false, + 'Need grocery delivery', + 'Need food delivery this week', + now() + ) +ON CONFLICT (req_id) DO NOTHING; + +INSERT INTO organizations ( + org_id, + org_name, + city_name, + state_code, + org_type, + source, + cat_id +) +VALUES + ('fn-org-medical', 'Medical Transport Network', 'San Jose', 'CA', 'non_profit', 'self_registered', 'fn-medical'), + ('fn-org-food', 'Grocery Relief Group', 'Oakland', 'CA', 'non_profit', 'self_registered', 'fn-food') +ON CONFLICT (org_id) DO NOTHING; + +DO $$ +DECLARE + result_count INT; + top_id TEXT; + score REAL; +BEGIN + SELECT count(*), max(req_id), max(relevance_score) + INTO result_count, top_id, score + FROM proposed_saayam.search_requests( + 'medical transportation', + 5, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count = 0 OR top_id <> 'fn-req-medical' OR score <= 0 THEN + RAISE EXCEPTION 'search_requests keyword ranking failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_requests( + '', + 5, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count <> 0 THEN + RAISE EXCEPTION 'search_requests empty query failed'; + END IF; + + SELECT count(*), max(user_id), max(relevance_score) + INTO result_count, top_id, score + FROM proposed_saayam.search_users( + 'maya.medical@example.test', + 5, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count = 0 OR top_id <> 'fn-requester' OR score <= 0 THEN + RAISE EXCEPTION 'search_users exact email failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_users( + '', + 5, + NULL::VARCHAR(255), + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count <> 0 THEN + RAISE EXCEPTION 'search_users empty query failed'; + END IF; + + SELECT count(*), max(org_id), max(relevance_score) + INTO result_count, top_id, score + FROM proposed_saayam.search_organizations( + 'Medical Transport', + 5, + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count = 0 OR top_id <> 'fn-org-medical' OR score <= 0 THEN + RAISE EXCEPTION 'search_organizations name search failed'; + END IF; + + SELECT count(*) + INTO result_count + FROM proposed_saayam.search_organizations( + '', + 5, + 4::SMALLINT, + NULL::VARCHAR(255)[] + ); + + IF result_count <> 0 THEN + RAISE EXCEPTION 'search_organizations empty query failed'; + END IF; +END $$; + +ROLLBACK; diff --git a/ddl/Search/tests/ireland_validation/04_instance_clone_check.sql b/ddl/Search/tests/ireland_validation/04_instance_clone_check.sql new file mode 100644 index 0000000..e0916d8 --- /dev/null +++ b/ddl/Search/tests/ireland_validation/04_instance_clone_check.sql @@ -0,0 +1,86 @@ +-- Local instance clone check. +-- Run after instance clone and ddl/Search/codes/01..04. + +DO $$ +DECLARE + missing_tables TEXT[]; + missing_columns TEXT[]; + missing_functions TEXT[]; +BEGIN + SELECT array_agg(expected.table_name ORDER BY expected.table_name) + INTO missing_tables + FROM ( + VALUES + ('country'), + ('state'), + ('user_status'), + ('user_category'), + ('users'), + ('help_categories'), + ('request_status'), + ('request_priority'), + ('request_type'), + ('request_for'), + ('request_isleadvol'), + ('request'), + ('organizations'), + ('user_org_map') + ) AS expected(table_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.tables tbl + WHERE tbl.table_schema = 'proposed_saayam' + AND tbl.table_name = expected.table_name + ); + + IF missing_tables IS NOT NULL THEN + RAISE EXCEPTION 'missing clone tables: %', array_to_string(missing_tables, ', '); + END IF; + + SELECT array_agg(expected.table_name || '.' || expected.column_name ORDER BY expected.table_name, expected.column_name) + INTO missing_columns + FROM ( + VALUES + ('request', 'search_vector'), + ('users', 'search_vector'), + ('request', 'req_subj'), + ('request', 'req_desc'), + ('request', 'req_loc'), + ('help_categories', 'cat_name'), + ('users', 'full_name'), + ('users', 'primary_email_address'), + ('organizations', 'org_name'), + ('organizations', 'city_name') + ) AS expected(table_name, column_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.columns col + WHERE col.table_schema = 'proposed_saayam' + AND col.table_name = expected.table_name + AND col.column_name = expected.column_name + ); + + IF missing_columns IS NOT NULL THEN + RAISE EXCEPTION 'missing clone/search columns: %', array_to_string(missing_columns, ', '); + END IF; + + SELECT array_agg(expected.function_name ORDER BY expected.function_name) + INTO missing_functions + FROM ( + VALUES + ('search_requests'), + ('search_users'), + ('search_organizations') + ) AS expected(function_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.routines routine + WHERE routine.specific_schema = 'proposed_saayam' + AND routine.routine_name = expected.function_name + AND routine.routine_type = 'FUNCTION' + ); + + IF missing_functions IS NOT NULL THEN + RAISE EXCEPTION 'missing search functions: %', array_to_string(missing_functions, ', '); + END IF; +END $$; diff --git a/ddl/Search/tests/run_ireland_clone_validation.sql b/ddl/Search/tests/run_ireland_clone_validation.sql new file mode 100644 index 0000000..b7bf6ce --- /dev/null +++ b/ddl/Search/tests/run_ireland_clone_validation.sql @@ -0,0 +1,7 @@ +-- Ireland local clone validation runner. +-- Run after ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql. + +\echo 'Running Ireland instance clone check' +\ir 05_ireland_instance_clone_check.sql + +\echo 'Ireland clone validation completed' diff --git a/ddl/Search/tests/run_ireland_search_migrations.sql b/ddl/Search/tests/run_ireland_search_migrations.sql new file mode 100644 index 0000000..c3c5b61 --- /dev/null +++ b/ddl/Search/tests/run_ireland_search_migrations.sql @@ -0,0 +1,8 @@ +-- Run production search migrations against the Ireland schema. + +SET search_path TO proposed_saayam, public; + +\ir ../codes/01_enable_fuzzy_search.sql +\ir ../codes/02_add_request_search.sql +\ir ../codes/03_add_user_and_volunteer_search.sql +\ir ../codes/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/run_ireland_search_validation.sql b/ddl/Search/tests/run_ireland_search_validation.sql new file mode 100644 index 0000000..fd7cfd4 --- /dev/null +++ b/ddl/Search/tests/run_ireland_search_validation.sql @@ -0,0 +1,16 @@ +-- Ireland full local search validation runner. +-- Run after ireland_search_instance_clone.sql and run_ireland_search_migrations.sql. + +\echo 'Running Ireland search smoke test' +\ir ireland_validation/01_search_smoke_test.sql + +\echo 'Running Ireland search index check' +\ir ireland_validation/02_search_index_check.sql + +\echo 'Running Ireland search function check' +\ir ireland_validation/03_search_function_check.sql + +\echo 'Running Ireland migrated clone check' +\ir ireland_validation/04_instance_clone_check.sql + +\echo 'Ireland full search validation completed' diff --git a/ddl/Search/tests/run_local_search_validation.sql b/ddl/Search/tests/run_local_search_validation.sql new file mode 100644 index 0000000..7ca4e4c --- /dev/null +++ b/ddl/Search/tests/run_local_search_validation.sql @@ -0,0 +1,16 @@ +-- Local search validation runner. +-- Run after instance clone and ddl/Search/codes/01..04. + +\echo 'Running search smoke test' +\ir 01_search_smoke_test.sql + +\echo 'Running search index check' +\ir 02_search_index_check.sql + +\echo 'Running search function check' +\ir 03_search_function_check.sql + +\echo 'Running instance clone check' +\ir 04_instance_clone_check.sql + +\echo 'Local search validation completed' diff --git a/ddl/Search/tests/run_virginia_search_migrations.sql b/ddl/Search/tests/run_virginia_search_migrations.sql new file mode 100644 index 0000000..245ea31 --- /dev/null +++ b/ddl/Search/tests/run_virginia_search_migrations.sql @@ -0,0 +1,8 @@ +-- Run production search migrations against the Virginia schema. + +SET search_path TO virginia_dev_saayam_rdbms, public; + +\ir ../codes/01_enable_fuzzy_search.sql +\ir ../codes/02_add_request_search.sql +\ir ../codes/03_add_user_and_volunteer_search.sql +\ir ../codes/04_add_category_and_advanced_search.sql From bfe03cc172e8b861a3134a6e2e306604fa519c4e Mon Sep 17 00:00:00 2001 From: Arup Chauhan Date: Tue, 19 May 2026 00:11:43 -0500 Subject: [PATCH 2/4] Clean up search test fixtures and ignore generated files Signed-off-by: Arup Chauhan --- .gitignore | 27 ++++++++++++++++++ ddl/Search/.DS_Store | Bin 8196 -> 0 bytes ddl/Search/tests/instance_clone/README.md | 10 ------- .../ireland_reference_instance_clone.sql | 6 ---- 4 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 .gitignore delete mode 100644 ddl/Search/.DS_Store delete mode 100644 ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3439a16 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# macOS metadata +.DS_Store +**/.DS_Store + +# Python generated files +__pycache__/ +*.py[cod] +*$py.class + +# Python virtual environments +.venv/ +venv/ + +# Test/cache folders +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ + +# RDS/Pulumi local generated files +rds-terraform/**/__pycache__/ +rds-terraform/**/*.py[cod] +rds-terraform/**/.pytest_cache/ +rds-terraform/**/.venv/ +rds-terraform/**/venv/ + +# Search local generated files +ddl/Search/**/.DS_Store diff --git a/ddl/Search/.DS_Store b/ddl/Search/.DS_Store deleted file mode 100644 index d85cd8a3043848e08742475b6613dfd8b7a33307..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMYitx%6u#fIz>FPaS}7D{*_8z((AuR4EfjHS%cIf?vMqg3th+lyJ20K8JF{CJ zK}(FrSK`~mU-&?O5;Ynz27>-FQDQU=i6j!EKg2)&;tv|pbLY;MmPewAF`#pkd(S=R zo^$U#XTCFc?<`{sO?iDeV^xeXnJTAR88z1^e4N*-iWEqu5Cz$@%!oUjv78L?$FI^3 z1z`lj2!s&`BM?R)jKGZ$0XnmJk*{;^i_x$RBM?U5e;EP(euz=!G#SuQLH$<;HU1TV zXeo*PLVb;Qh{gh%4Ctt!G(rW!l&CO8aK(TyCwVN;O9pgQP+`sxTs{!2jNpa>e|4Ha z=BqO#1r6IU0$~KMjsU;Amol9>EX&-9_xH@OWx8G>aRyRJ>6EF{6jHJA@ zmv@`ext*Nra2zXB?58t3Ok<>9o7wExUfQ&coNr+22C4LQnYQkXG&y-&cYWKG7?2fN z>DQ_b9$e9|x;j=@Uw^1NcCev7Q5{>gqVCWkMXrq3Hg50VYYo}X)4~M=uK_mw3YVUl z=#TDZtqkpt>x?^TxrJu&k)kA3bno&8FoiYy?{n`wdJZ&X* zTh1tuhh166$`9FYzgC`2TiLvo_KfwG#g$1m!_DR#%i89+re`{KPq%06^NJHq8tU$K zot)R~Pu8z#dDqx(&|GH0XrUQtstUf^)?q%4yTi!2MB~YX@L4%_>+I;lB{g;RYm*yV z+aDO4p_NytYUQl1g6WzW%jikFS;N*%d!Tc~)V;x83b*Z9yqS^Kb0r%kpyI-gPZM|TTIq~o1)?>LB-+wd5S|CZ%6ny{ zJL?P$ku$E4MVeb=S^NA!zHnRG^|lTh_V|{XH_LLbJDAQHTSo|yMT<6B_8%f!x#7xr|}ie;U}EO&$xi!aS4A&<T zBMnG{!UL(8f4e4oChaGFVyXq-1EpivPPOQ!&8_#TZ8vi zwJDS`f?cdGiE2|RT?D%pwtJOwTE2V4{>^gP6 zsxd_{P0^O9#&b*lr&0bjJIlT&lwV*MF%@Od5XBPQg$6?PMr=ka+R#qO?nV!Gq7McJ zFo->ba}NcKU_TDvF+5Hve-cmOMZAnx@G6er4Z`|SyiG`d7w_SHe1a1=iBtI0$NU(+ z!w>lJ3KX|bMlmi-L~$XNvn|ISAXyRhlhtolu0(9Sir@b?T>tz3&FWsb?=S*k1pYz< zP}-7eX(Gkzz25j;J4W>(s=RQ$Q9*qZYW(XsQU5wlbo>uP>c>cx+av=zDkw>){OcbA X=y&_y|EV3G|Ka)XgVo3Ef1&vkt>kz9 diff --git a/ddl/Search/tests/instance_clone/README.md b/ddl/Search/tests/instance_clone/README.md index 9f0c7b1..9f2e1a3 100644 --- a/ddl/Search/tests/instance_clone/README.md +++ b/ddl/Search/tests/instance_clone/README.md @@ -23,7 +23,6 @@ The search scripts are schema-level migrations. Testing them against a local clo - `virginia_search_instance_clone.sql`: search-scoped Virginia clone for validating the current MVP search scripts. - `ireland_search_instance_clone.sql`: search-scoped Ireland clone for validating Ireland schema shape without requiring local PostGIS. -- `ireland_reference_instance_clone.sql`: wrapper around the existing combined Ireland script for structure comparison. - `README.md`: this guide. The Virginia clone intentionally includes only the search-relevant tables and dependencies. It is not a replacement for the production DDL. @@ -40,15 +39,6 @@ psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_loca Add smoke-test SQL files under `ddl/Search/tests/` as the search validation suite grows. -## Ireland Reference Check - -```bash -createdb saayam_ireland_reference_test -psql -d saayam_ireland_reference_test -f ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql -``` - -Do not run the current Virginia-targeted search scripts against the Ireland reference clone until schema targeting is explicitly handled. - ## Ireland Search Clone Check Use this when local PostGIS is not available or when the goal is only to confirm Ireland search shape: diff --git a/ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql b/ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql deleted file mode 100644 index 55eec86..0000000 --- a/ddl/Search/tests/instance_clone/ireland_reference_instance_clone.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Ireland reference instance clone. --- This wraps the existing combined Ireland script for local structure checks. --- The current MVP search scripts target virginia_dev_saayam_rdbms, so this file --- is for Ireland shape comparison until search scripts are parameterized. - -\ir ../../../Tables/Scripts/ireland_dev_saayam_rdbms.sql From 6012aa62a325eacb14b52f1cd2ad2d962b41436a Mon Sep 17 00:00:00 2001 From: Arup Chauhan Date: Wed, 20 May 2026 13:49:58 -0500 Subject: [PATCH 3/4] Updated the file / folder management Signed-off-by: Arup Chauhan --- ddl/Search/tests/QA_VALIDATION.md | 56 +++++++++---------- ddl/Search/tests/README.md | 32 +++++------ .../05_ireland_instance_clone_check.sql | 2 +- .../tests/run_ireland_clone_validation.sql | 7 --- .../tests/run_ireland_search_migrations.sql | 8 --- .../tests/run_local_search_validation.sql | 16 ------ .../tests/run_virginia_search_migrations.sql | 8 --- .../runners/run_ireland_clone_validation.sql | 7 +++ .../runners/run_ireland_search_migrations.sql | 8 +++ .../run_ireland_search_validation.sql | 8 +-- .../run_virginia_search_migrations.sql | 8 +++ .../run_virginia_search_validation.sql | 16 ++++++ .../{instance_clone => test_clones}/README.md | 16 +++--- .../ireland_search_instance_clone.sql | 0 .../virginia_search_instance_clone.sql | 0 .../01_search_smoke_test.sql | 0 .../02_search_index_check.sql | 0 .../03_search_function_check.sql | 0 .../04_instance_clone_check.sql | 0 19 files changed, 95 insertions(+), 97 deletions(-) rename ddl/Search/tests/{ => ireland_validation}/05_ireland_instance_clone_check.sql (96%) delete mode 100644 ddl/Search/tests/run_ireland_clone_validation.sql delete mode 100644 ddl/Search/tests/run_ireland_search_migrations.sql delete mode 100644 ddl/Search/tests/run_local_search_validation.sql delete mode 100644 ddl/Search/tests/run_virginia_search_migrations.sql create mode 100644 ddl/Search/tests/runners/run_ireland_clone_validation.sql create mode 100644 ddl/Search/tests/runners/run_ireland_search_migrations.sql rename ddl/Search/tests/{ => runners}/run_ireland_search_validation.sql (63%) create mode 100644 ddl/Search/tests/runners/run_virginia_search_migrations.sql create mode 100644 ddl/Search/tests/runners/run_virginia_search_validation.sql rename ddl/Search/tests/{instance_clone => test_clones}/README.md (76%) rename ddl/Search/tests/{instance_clone => test_clones}/ireland_search_instance_clone.sql (100%) rename ddl/Search/tests/{instance_clone => test_clones}/virginia_search_instance_clone.sql (100%) rename ddl/Search/tests/{ => virginia_validation}/01_search_smoke_test.sql (100%) rename ddl/Search/tests/{ => virginia_validation}/02_search_index_check.sql (100%) rename ddl/Search/tests/{ => virginia_validation}/03_search_function_check.sql (100%) rename ddl/Search/tests/{ => virginia_validation}/04_instance_clone_check.sql (100%) diff --git a/ddl/Search/tests/QA_VALIDATION.md b/ddl/Search/tests/QA_VALIDATION.md index ee1edd9..1d6a032 100644 --- a/ddl/Search/tests/QA_VALIDATION.md +++ b/ddl/Search/tests/QA_VALIDATION.md @@ -18,18 +18,18 @@ Search migration files: Local validation files: -- `ddl/Search/tests/run_virginia_search_migrations.sql` -- `ddl/Search/tests/run_ireland_search_migrations.sql` -- `ddl/Search/tests/run_local_search_validation.sql` -- `ddl/Search/tests/run_ireland_clone_validation.sql` -- `ddl/Search/tests/run_ireland_search_validation.sql` -- `ddl/Search/tests/01_search_smoke_test.sql` -- `ddl/Search/tests/02_search_index_check.sql` -- `ddl/Search/tests/03_search_function_check.sql` -- `ddl/Search/tests/04_instance_clone_check.sql` -- `ddl/Search/tests/05_ireland_instance_clone_check.sql` -- `ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql` -- `ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql` +- `ddl/Search/tests/runners/run_virginia_search_migrations.sql` +- `ddl/Search/tests/runners/run_ireland_search_migrations.sql` +- `ddl/Search/tests/runners/run_virginia_search_validation.sql` +- `ddl/Search/tests/runners/run_ireland_clone_validation.sql` +- `ddl/Search/tests/runners/run_ireland_search_validation.sql` +- `ddl/Search/tests/virginia_validation/01_search_smoke_test.sql` +- `ddl/Search/tests/virginia_validation/02_search_index_check.sql` +- `ddl/Search/tests/virginia_validation/03_search_function_check.sql` +- `ddl/Search/tests/virginia_validation/04_instance_clone_check.sql` +- `ddl/Search/tests/ireland_validation/05_ireland_instance_clone_check.sql` +- `ddl/Search/tests/test_clones/virginia_search_instance_clone.sql` +- `ddl/Search/tests/test_clones/ireland_search_instance_clone.sql` - `ddl/Search/tests/ireland_validation/` ## Local Validation Runbook @@ -40,20 +40,20 @@ Use this before QA to confirm the current branch works locally. createdb saayam_search_clone_test psql -d saayam_search_clone_test \ - -f ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql + -f ddl/Search/tests/test_clones/virginia_search_instance_clone.sql psql -d saayam_search_clone_test \ - -f ddl/Search/tests/run_virginia_search_migrations.sql + -f ddl/Search/tests/runners/run_virginia_search_migrations.sql psql -d saayam_search_clone_test \ -v ON_ERROR_STOP=1 \ - -f ddl/Search/tests/run_local_search_validation.sql + -f ddl/Search/tests/runners/run_virginia_search_validation.sql ``` Expected local result: - all scripts complete without errors -- runner prints `Local search validation completed` +- runner prints `Virginia search validation completed` ## Ireland Local Clone Validation @@ -64,11 +64,11 @@ The production search scripts are schema-agnostic and use the active `search_pat createdb saayam_ireland_search_clone_test psql -d saayam_ireland_search_clone_test \ - -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql + -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql psql -d saayam_ireland_search_clone_test \ -v ON_ERROR_STOP=1 \ - -f ddl/Search/tests/run_ireland_clone_validation.sql + -f ddl/Search/tests/runners/run_ireland_clone_validation.sql ``` Expected Ireland local result: @@ -85,14 +85,14 @@ Use this to validate the same search behavior against the Ireland schema name: createdb saayam_ireland_full_search_test psql -d saayam_ireland_full_search_test \ - -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql + -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql psql -d saayam_ireland_full_search_test \ - -f ddl/Search/tests/run_ireland_search_migrations.sql + -f ddl/Search/tests/runners/run_ireland_search_migrations.sql psql -d saayam_ireland_full_search_test \ -v ON_ERROR_STOP=1 \ - -f ddl/Search/tests/run_ireland_search_validation.sql + -f ddl/Search/tests/runners/run_ireland_search_validation.sql ``` Expected Ireland full-search result: @@ -121,13 +121,13 @@ Run search scripts with the correct target schema. For Virginia QA: ```sql -\i ddl/Search/tests/run_virginia_search_migrations.sql +\i ddl/Search/tests/runners/run_virginia_search_migrations.sql ``` For Ireland QA: ```sql -\i ddl/Search/tests/run_ireland_search_migrations.sql +\i ddl/Search/tests/runners/run_ireland_search_migrations.sql ``` Then run validation checks. @@ -135,15 +135,15 @@ Then run validation checks. Recommended first QA checks: ```sql -\i ddl/Search/tests/02_search_index_check.sql -\i ddl/Search/tests/04_instance_clone_check.sql +\i ddl/Search/tests/virginia_validation/02_search_index_check.sql +\i ddl/Search/tests/virginia_validation/04_instance_clone_check.sql ``` Run behavior checks only after confirming temporary QA inserts are acceptable: ```sql -\i ddl/Search/tests/01_search_smoke_test.sql -\i ddl/Search/tests/03_search_function_check.sql +\i ddl/Search/tests/virginia_validation/01_search_smoke_test.sql +\i ddl/Search/tests/virginia_validation/03_search_function_check.sql ``` ## QA Safety Notes @@ -163,7 +163,7 @@ Still confirm before running on QA: Capture the following for PR review: - output showing `run_virginia_search_migrations.sql` completed locally -- output showing `run_local_search_validation.sql` passed locally for Virginia +- output showing `run_virginia_search_validation.sql` passed locally for Virginia - output showing `run_ireland_search_migrations.sql` completed locally - output showing `run_ireland_search_validation.sql` passed locally for Ireland - output showing QA index check passed diff --git a/ddl/Search/tests/README.md b/ddl/Search/tests/README.md index 0a4fa73..eb404f5 100644 --- a/ddl/Search/tests/README.md +++ b/ddl/Search/tests/README.md @@ -2,12 +2,12 @@ This folder is for search-specific local testing only. -Do not edit the original schema files under `ddl/Tables/` for search testing. If a local clone of a deployed schema is needed, place the copied or generated schema assets under `instance_clone/`. +Do not edit the original schema files under `ddl/Tables/` for search testing. If a local clone of a deployed schema is needed, place the copied or generated schema assets under `test_clones/`. ## Intended Flow 1. Create a local PostgreSQL database. -2. Load a Virginia or Ireland schema clone from `instance_clone/`. +2. Load a Virginia or Ireland schema clone from `test_clones/`. 3. Run the search scripts from `../codes/` in order. 4. Run local search validation. @@ -18,14 +18,12 @@ Do not edit the original schema files under `ddl/Tables/` for search testing. If - `../codes/03_add_user_and_volunteer_search.sql` - `../codes/04_add_category_and_advanced_search.sql` -## Smoke Test - ## Run All Local Checks Run this after loading the local clone and applying search migrations: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/run_local_search_validation.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/runners/run_virginia_search_validation.sql ``` This runs the smoke test, index check, function check, and instance clone check in order. @@ -34,9 +32,9 @@ This runs the smoke test, index check, function check, and instance clone check ```bash createdb saayam_search_clone_test -psql -d saayam_search_clone_test -f ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql -psql -d saayam_search_clone_test -f ddl/Search/tests/run_virginia_search_migrations.sql -psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_local_search_validation.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/test_clones/virginia_search_instance_clone.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/runners/run_virginia_search_migrations.sql +psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/runners/run_virginia_search_validation.sql ``` ## Ireland Clone Check @@ -46,8 +44,8 @@ Ireland uses `proposed_saayam`, so the Ireland runner sets that schema before ap ```bash createdb saayam_ireland_search_clone_test -psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql -psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/run_ireland_clone_validation.sql +psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql +psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/runners/run_ireland_clone_validation.sql ``` This confirms the Ireland-style clone has the tables and columns needed by the search plan. @@ -59,9 +57,9 @@ The Ireland migration runner sets `search_path` to `proposed_saayam`. ```bash createdb saayam_ireland_full_search_test -psql -d saayam_ireland_full_search_test -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql -psql -d saayam_ireland_full_search_test -f ddl/Search/tests/run_ireland_search_migrations.sql -psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_ireland_search_validation.sql +psql -d saayam_ireland_full_search_test -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql +psql -d saayam_ireland_full_search_test -f ddl/Search/tests/runners/run_ireland_search_migrations.sql +psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/runners/run_ireland_search_validation.sql ``` This runs the same behavior, index, function, and migrated-clone checks against the Ireland schema name, `proposed_saayam`. @@ -71,7 +69,7 @@ This runs the same behavior, index, function, and migrated-clone checks against Run this after loading the local clone and search scripts: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/01_search_smoke_test.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/virginia_validation/01_search_smoke_test.sql ``` This inserts temporary sample data, checks search behavior, and rolls the data back. @@ -81,7 +79,7 @@ This inserts temporary sample data, checks search behavior, and rolls the data b Run this after loading the local clone and search scripts: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/02_search_index_check.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/virginia_validation/02_search_index_check.sql ``` This fails if any expected search index is missing or uses the wrong strategy. @@ -92,7 +90,7 @@ It checks GIN full-text indexes, GIN trigram indexes, and the exact email btree Run this after loading the local clone and search scripts: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/03_search_function_check.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/virginia_validation/03_search_function_check.sql ``` This checks the main search functions directly: @@ -108,7 +106,7 @@ It verifies keyword matching, exact email matching, organization name matching, Run this after loading the local clone and search scripts: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/04_instance_clone_check.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/virginia_validation/04_instance_clone_check.sql ``` This verifies the local Virginia clone has the search-required tables, search columns, and search functions. diff --git a/ddl/Search/tests/05_ireland_instance_clone_check.sql b/ddl/Search/tests/ireland_validation/05_ireland_instance_clone_check.sql similarity index 96% rename from ddl/Search/tests/05_ireland_instance_clone_check.sql rename to ddl/Search/tests/ireland_validation/05_ireland_instance_clone_check.sql index 42aea6b..3608c18 100644 --- a/ddl/Search/tests/05_ireland_instance_clone_check.sql +++ b/ddl/Search/tests/ireland_validation/05_ireland_instance_clone_check.sql @@ -1,5 +1,5 @@ -- Ireland local instance clone check. --- Run after ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql. +-- Run after ddl/Search/tests/test_clones/ireland_search_instance_clone.sql. DO $$ DECLARE diff --git a/ddl/Search/tests/run_ireland_clone_validation.sql b/ddl/Search/tests/run_ireland_clone_validation.sql deleted file mode 100644 index b7bf6ce..0000000 --- a/ddl/Search/tests/run_ireland_clone_validation.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Ireland local clone validation runner. --- Run after ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql. - -\echo 'Running Ireland instance clone check' -\ir 05_ireland_instance_clone_check.sql - -\echo 'Ireland clone validation completed' diff --git a/ddl/Search/tests/run_ireland_search_migrations.sql b/ddl/Search/tests/run_ireland_search_migrations.sql deleted file mode 100644 index c3c5b61..0000000 --- a/ddl/Search/tests/run_ireland_search_migrations.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Run production search migrations against the Ireland schema. - -SET search_path TO proposed_saayam, public; - -\ir ../codes/01_enable_fuzzy_search.sql -\ir ../codes/02_add_request_search.sql -\ir ../codes/03_add_user_and_volunteer_search.sql -\ir ../codes/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/run_local_search_validation.sql b/ddl/Search/tests/run_local_search_validation.sql deleted file mode 100644 index 7ca4e4c..0000000 --- a/ddl/Search/tests/run_local_search_validation.sql +++ /dev/null @@ -1,16 +0,0 @@ --- Local search validation runner. --- Run after instance clone and ddl/Search/codes/01..04. - -\echo 'Running search smoke test' -\ir 01_search_smoke_test.sql - -\echo 'Running search index check' -\ir 02_search_index_check.sql - -\echo 'Running search function check' -\ir 03_search_function_check.sql - -\echo 'Running instance clone check' -\ir 04_instance_clone_check.sql - -\echo 'Local search validation completed' diff --git a/ddl/Search/tests/run_virginia_search_migrations.sql b/ddl/Search/tests/run_virginia_search_migrations.sql deleted file mode 100644 index 245ea31..0000000 --- a/ddl/Search/tests/run_virginia_search_migrations.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Run production search migrations against the Virginia schema. - -SET search_path TO virginia_dev_saayam_rdbms, public; - -\ir ../codes/01_enable_fuzzy_search.sql -\ir ../codes/02_add_request_search.sql -\ir ../codes/03_add_user_and_volunteer_search.sql -\ir ../codes/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/runners/run_ireland_clone_validation.sql b/ddl/Search/tests/runners/run_ireland_clone_validation.sql new file mode 100644 index 0000000..a2de566 --- /dev/null +++ b/ddl/Search/tests/runners/run_ireland_clone_validation.sql @@ -0,0 +1,7 @@ +-- Ireland local clone validation runner. +-- Run after ddl/Search/tests/test_clones/ireland_search_instance_clone.sql. + +\echo 'Running Ireland instance clone check' +\ir ../ireland_validation/05_ireland_instance_clone_check.sql + +\echo 'Ireland clone validation completed' diff --git a/ddl/Search/tests/runners/run_ireland_search_migrations.sql b/ddl/Search/tests/runners/run_ireland_search_migrations.sql new file mode 100644 index 0000000..f4edb37 --- /dev/null +++ b/ddl/Search/tests/runners/run_ireland_search_migrations.sql @@ -0,0 +1,8 @@ +-- Run production search migrations against the Ireland schema. + +SET search_path TO proposed_saayam, public; + +\ir ../../codes/01_enable_fuzzy_search.sql +\ir ../../codes/02_add_request_search.sql +\ir ../../codes/03_add_user_and_volunteer_search.sql +\ir ../../codes/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/run_ireland_search_validation.sql b/ddl/Search/tests/runners/run_ireland_search_validation.sql similarity index 63% rename from ddl/Search/tests/run_ireland_search_validation.sql rename to ddl/Search/tests/runners/run_ireland_search_validation.sql index fd7cfd4..650e535 100644 --- a/ddl/Search/tests/run_ireland_search_validation.sql +++ b/ddl/Search/tests/runners/run_ireland_search_validation.sql @@ -2,15 +2,15 @@ -- Run after ireland_search_instance_clone.sql and run_ireland_search_migrations.sql. \echo 'Running Ireland search smoke test' -\ir ireland_validation/01_search_smoke_test.sql +\ir ../ireland_validation/01_search_smoke_test.sql \echo 'Running Ireland search index check' -\ir ireland_validation/02_search_index_check.sql +\ir ../ireland_validation/02_search_index_check.sql \echo 'Running Ireland search function check' -\ir ireland_validation/03_search_function_check.sql +\ir ../ireland_validation/03_search_function_check.sql \echo 'Running Ireland migrated clone check' -\ir ireland_validation/04_instance_clone_check.sql +\ir ../ireland_validation/04_instance_clone_check.sql \echo 'Ireland full search validation completed' diff --git a/ddl/Search/tests/runners/run_virginia_search_migrations.sql b/ddl/Search/tests/runners/run_virginia_search_migrations.sql new file mode 100644 index 0000000..64b6d25 --- /dev/null +++ b/ddl/Search/tests/runners/run_virginia_search_migrations.sql @@ -0,0 +1,8 @@ +-- Run production search migrations against the Virginia schema. + +SET search_path TO virginia_dev_saayam_rdbms, public; + +\ir ../../codes/01_enable_fuzzy_search.sql +\ir ../../codes/02_add_request_search.sql +\ir ../../codes/03_add_user_and_volunteer_search.sql +\ir ../../codes/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/runners/run_virginia_search_validation.sql b/ddl/Search/tests/runners/run_virginia_search_validation.sql new file mode 100644 index 0000000..a6c559c --- /dev/null +++ b/ddl/Search/tests/runners/run_virginia_search_validation.sql @@ -0,0 +1,16 @@ +-- Virginia search validation runner. +-- Run after Virginia test clone and search migrations. + +\echo 'Running search smoke test' +\ir ../virginia_validation/01_search_smoke_test.sql + +\echo 'Running search index check' +\ir ../virginia_validation/02_search_index_check.sql + +\echo 'Running search function check' +\ir ../virginia_validation/03_search_function_check.sql + +\echo 'Running instance clone check' +\ir ../virginia_validation/04_instance_clone_check.sql + +\echo 'Virginia search validation completed' diff --git a/ddl/Search/tests/instance_clone/README.md b/ddl/Search/tests/test_clones/README.md similarity index 76% rename from ddl/Search/tests/instance_clone/README.md rename to ddl/Search/tests/test_clones/README.md index 9f2e1a3..69c9a7f 100644 --- a/ddl/Search/tests/instance_clone/README.md +++ b/ddl/Search/tests/test_clones/README.md @@ -32,9 +32,9 @@ The Ireland search clone follows the Ireland schema name, `proposed_saayam`, and ```bash createdb saayam_search_clone_test -psql -d saayam_search_clone_test -f ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql -psql -d saayam_search_clone_test -f ddl/Search/tests/run_virginia_search_migrations.sql -psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_local_search_validation.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/test_clones/virginia_search_instance_clone.sql +psql -d saayam_search_clone_test -f ddl/Search/tests/runners/run_virginia_search_migrations.sql +psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/runners/run_virginia_search_validation.sql ``` Add smoke-test SQL files under `ddl/Search/tests/` as the search validation suite grows. @@ -45,8 +45,8 @@ Use this when local PostGIS is not available or when the goal is only to confirm ```bash createdb saayam_ireland_search_clone_test -psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql -psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/run_ireland_clone_validation.sql +psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql +psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/runners/run_ireland_clone_validation.sql ``` This confirms the Ireland-style `proposed_saayam` clone has the tables and columns needed by the current search plan. @@ -57,9 +57,9 @@ Use this to validate the same search behavior against the Ireland schema name: ```bash createdb saayam_ireland_full_search_test -psql -d saayam_ireland_full_search_test -f ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql -psql -d saayam_ireland_full_search_test -f ddl/Search/tests/run_ireland_search_migrations.sql -psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/run_ireland_search_validation.sql +psql -d saayam_ireland_full_search_test -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql +psql -d saayam_ireland_full_search_test -f ddl/Search/tests/runners/run_ireland_search_migrations.sql +psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/runners/run_ireland_search_validation.sql ``` The same production search scripts are used for Ireland through `run_ireland_search_migrations.sql`. diff --git a/ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql b/ddl/Search/tests/test_clones/ireland_search_instance_clone.sql similarity index 100% rename from ddl/Search/tests/instance_clone/ireland_search_instance_clone.sql rename to ddl/Search/tests/test_clones/ireland_search_instance_clone.sql diff --git a/ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql b/ddl/Search/tests/test_clones/virginia_search_instance_clone.sql similarity index 100% rename from ddl/Search/tests/instance_clone/virginia_search_instance_clone.sql rename to ddl/Search/tests/test_clones/virginia_search_instance_clone.sql diff --git a/ddl/Search/tests/01_search_smoke_test.sql b/ddl/Search/tests/virginia_validation/01_search_smoke_test.sql similarity index 100% rename from ddl/Search/tests/01_search_smoke_test.sql rename to ddl/Search/tests/virginia_validation/01_search_smoke_test.sql diff --git a/ddl/Search/tests/02_search_index_check.sql b/ddl/Search/tests/virginia_validation/02_search_index_check.sql similarity index 100% rename from ddl/Search/tests/02_search_index_check.sql rename to ddl/Search/tests/virginia_validation/02_search_index_check.sql diff --git a/ddl/Search/tests/03_search_function_check.sql b/ddl/Search/tests/virginia_validation/03_search_function_check.sql similarity index 100% rename from ddl/Search/tests/03_search_function_check.sql rename to ddl/Search/tests/virginia_validation/03_search_function_check.sql diff --git a/ddl/Search/tests/04_instance_clone_check.sql b/ddl/Search/tests/virginia_validation/04_instance_clone_check.sql similarity index 100% rename from ddl/Search/tests/04_instance_clone_check.sql rename to ddl/Search/tests/virginia_validation/04_instance_clone_check.sql From 10d0f1c54406ba47e6f7c0324ef1cf0b6dfe0fac Mon Sep 17 00:00:00 2001 From: Arup Chauhan Date: Wed, 1 Jul 2026 02:48:04 -0500 Subject: [PATCH 4/4] Finalize DB level full-text search implementation layout --- .../{codes => }/01_enable_fuzzy_search.sql | 0 .../{codes => }/02_add_request_search.sql | 0 .../03_add_user_and_volunteer_search.sql | 0 .../04_add_category_and_advanced_search.sql | 0 .../runners/run_ireland_search_migrations.sql | 8 --- .../run_virginia_search_migrations.sql | 8 --- .../tests => tests/Search}/QA_VALIDATION.md | 64 +++++++++---------- {ddl/Search/tests => tests/Search}/README.md | 38 +++++------ .../01_search_smoke_test.sql | 2 +- .../02_search_index_check.sql | 2 +- .../03_search_function_check.sql | 2 +- .../04_instance_clone_check.sql | 2 +- .../05_ireland_instance_clone_check.sql | 2 +- .../runners/run_ireland_clone_validation.sql | 2 +- .../runners/run_ireland_search_migrations.sql | 8 +++ .../runners/run_ireland_search_validation.sql | 0 .../run_virginia_search_migrations.sql | 8 +++ .../run_virginia_search_validation.sql | 0 .../Search}/test_clones/README.md | 18 +++--- .../ireland_search_instance_clone.sql | 0 .../virginia_search_instance_clone.sql | 2 +- .../01_search_smoke_test.sql | 2 +- .../02_search_index_check.sql | 2 +- .../03_search_function_check.sql | 2 +- .../04_instance_clone_check.sql | 2 +- 25 files changed, 87 insertions(+), 87 deletions(-) rename ddl/Search/{codes => }/01_enable_fuzzy_search.sql (100%) rename ddl/Search/{codes => }/02_add_request_search.sql (100%) rename ddl/Search/{codes => }/03_add_user_and_volunteer_search.sql (100%) rename ddl/Search/{codes => }/04_add_category_and_advanced_search.sql (100%) delete mode 100644 ddl/Search/tests/runners/run_ireland_search_migrations.sql delete mode 100644 ddl/Search/tests/runners/run_virginia_search_migrations.sql rename {ddl/Search/tests => tests/Search}/QA_VALIDATION.md (76%) rename {ddl/Search/tests => tests/Search}/README.md (59%) rename {ddl/Search/tests => tests/Search}/ireland_validation/01_search_smoke_test.sql (99%) rename {ddl/Search/tests => tests/Search}/ireland_validation/02_search_index_check.sql (97%) rename {ddl/Search/tests => tests/Search}/ireland_validation/03_search_function_check.sql (98%) rename {ddl/Search/tests => tests/Search}/ireland_validation/04_instance_clone_check.sql (97%) rename {ddl/Search/tests => tests/Search}/ireland_validation/05_ireland_instance_clone_check.sql (96%) rename {ddl/Search/tests => tests/Search}/runners/run_ireland_clone_validation.sql (71%) create mode 100644 tests/Search/runners/run_ireland_search_migrations.sql rename {ddl/Search/tests => tests/Search}/runners/run_ireland_search_validation.sql (100%) create mode 100644 tests/Search/runners/run_virginia_search_migrations.sql rename {ddl/Search/tests => tests/Search}/runners/run_virginia_search_validation.sql (100%) rename {ddl/Search/tests => tests/Search}/test_clones/README.md (67%) rename {ddl/Search/tests => tests/Search}/test_clones/ireland_search_instance_clone.sql (100%) rename {ddl/Search/tests => tests/Search}/test_clones/virginia_search_instance_clone.sql (98%) rename {ddl/Search/tests => tests/Search}/virginia_validation/01_search_smoke_test.sql (99%) rename {ddl/Search/tests => tests/Search}/virginia_validation/02_search_index_check.sql (97%) rename {ddl/Search/tests => tests/Search}/virginia_validation/03_search_function_check.sql (99%) rename {ddl/Search/tests => tests/Search}/virginia_validation/04_instance_clone_check.sql (97%) diff --git a/ddl/Search/codes/01_enable_fuzzy_search.sql b/ddl/Search/01_enable_fuzzy_search.sql similarity index 100% rename from ddl/Search/codes/01_enable_fuzzy_search.sql rename to ddl/Search/01_enable_fuzzy_search.sql diff --git a/ddl/Search/codes/02_add_request_search.sql b/ddl/Search/02_add_request_search.sql similarity index 100% rename from ddl/Search/codes/02_add_request_search.sql rename to ddl/Search/02_add_request_search.sql diff --git a/ddl/Search/codes/03_add_user_and_volunteer_search.sql b/ddl/Search/03_add_user_and_volunteer_search.sql similarity index 100% rename from ddl/Search/codes/03_add_user_and_volunteer_search.sql rename to ddl/Search/03_add_user_and_volunteer_search.sql diff --git a/ddl/Search/codes/04_add_category_and_advanced_search.sql b/ddl/Search/04_add_category_and_advanced_search.sql similarity index 100% rename from ddl/Search/codes/04_add_category_and_advanced_search.sql rename to ddl/Search/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/runners/run_ireland_search_migrations.sql b/ddl/Search/tests/runners/run_ireland_search_migrations.sql deleted file mode 100644 index f4edb37..0000000 --- a/ddl/Search/tests/runners/run_ireland_search_migrations.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Run production search migrations against the Ireland schema. - -SET search_path TO proposed_saayam, public; - -\ir ../../codes/01_enable_fuzzy_search.sql -\ir ../../codes/02_add_request_search.sql -\ir ../../codes/03_add_user_and_volunteer_search.sql -\ir ../../codes/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/runners/run_virginia_search_migrations.sql b/ddl/Search/tests/runners/run_virginia_search_migrations.sql deleted file mode 100644 index 64b6d25..0000000 --- a/ddl/Search/tests/runners/run_virginia_search_migrations.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Run production search migrations against the Virginia schema. - -SET search_path TO virginia_dev_saayam_rdbms, public; - -\ir ../../codes/01_enable_fuzzy_search.sql -\ir ../../codes/02_add_request_search.sql -\ir ../../codes/03_add_user_and_volunteer_search.sql -\ir ../../codes/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/QA_VALIDATION.md b/tests/Search/QA_VALIDATION.md similarity index 76% rename from ddl/Search/tests/QA_VALIDATION.md rename to tests/Search/QA_VALIDATION.md index 1d6a032..f8db8c1 100644 --- a/ddl/Search/tests/QA_VALIDATION.md +++ b/tests/Search/QA_VALIDATION.md @@ -11,26 +11,26 @@ QA validation proves the same search logic works against the real deployed schem Search migration files: -- `ddl/Search/codes/01_enable_fuzzy_search.sql` -- `ddl/Search/codes/02_add_request_search.sql` -- `ddl/Search/codes/03_add_user_and_volunteer_search.sql` -- `ddl/Search/codes/04_add_category_and_advanced_search.sql` +- `ddl/Search/01_enable_fuzzy_search.sql` +- `ddl/Search/02_add_request_search.sql` +- `ddl/Search/03_add_user_and_volunteer_search.sql` +- `ddl/Search/04_add_category_and_advanced_search.sql` Local validation files: -- `ddl/Search/tests/runners/run_virginia_search_migrations.sql` -- `ddl/Search/tests/runners/run_ireland_search_migrations.sql` -- `ddl/Search/tests/runners/run_virginia_search_validation.sql` -- `ddl/Search/tests/runners/run_ireland_clone_validation.sql` -- `ddl/Search/tests/runners/run_ireland_search_validation.sql` -- `ddl/Search/tests/virginia_validation/01_search_smoke_test.sql` -- `ddl/Search/tests/virginia_validation/02_search_index_check.sql` -- `ddl/Search/tests/virginia_validation/03_search_function_check.sql` -- `ddl/Search/tests/virginia_validation/04_instance_clone_check.sql` -- `ddl/Search/tests/ireland_validation/05_ireland_instance_clone_check.sql` -- `ddl/Search/tests/test_clones/virginia_search_instance_clone.sql` -- `ddl/Search/tests/test_clones/ireland_search_instance_clone.sql` -- `ddl/Search/tests/ireland_validation/` +- `tests/Search/runners/run_virginia_search_migrations.sql` +- `tests/Search/runners/run_ireland_search_migrations.sql` +- `tests/Search/runners/run_virginia_search_validation.sql` +- `tests/Search/runners/run_ireland_clone_validation.sql` +- `tests/Search/runners/run_ireland_search_validation.sql` +- `tests/Search/virginia_validation/01_search_smoke_test.sql` +- `tests/Search/virginia_validation/02_search_index_check.sql` +- `tests/Search/virginia_validation/03_search_function_check.sql` +- `tests/Search/virginia_validation/04_instance_clone_check.sql` +- `tests/Search/ireland_validation/05_ireland_instance_clone_check.sql` +- `tests/Search/test_clones/virginia_search_instance_clone.sql` +- `tests/Search/test_clones/ireland_search_instance_clone.sql` +- `tests/Search/ireland_validation/` ## Local Validation Runbook @@ -40,14 +40,14 @@ Use this before QA to confirm the current branch works locally. createdb saayam_search_clone_test psql -d saayam_search_clone_test \ - -f ddl/Search/tests/test_clones/virginia_search_instance_clone.sql + -f tests/Search/test_clones/virginia_search_instance_clone.sql psql -d saayam_search_clone_test \ - -f ddl/Search/tests/runners/run_virginia_search_migrations.sql + -f tests/Search/runners/run_virginia_search_migrations.sql psql -d saayam_search_clone_test \ -v ON_ERROR_STOP=1 \ - -f ddl/Search/tests/runners/run_virginia_search_validation.sql + -f tests/Search/runners/run_virginia_search_validation.sql ``` Expected local result: @@ -64,11 +64,11 @@ The production search scripts are schema-agnostic and use the active `search_pat createdb saayam_ireland_search_clone_test psql -d saayam_ireland_search_clone_test \ - -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql + -f tests/Search/test_clones/ireland_search_instance_clone.sql psql -d saayam_ireland_search_clone_test \ -v ON_ERROR_STOP=1 \ - -f ddl/Search/tests/runners/run_ireland_clone_validation.sql + -f tests/Search/runners/run_ireland_clone_validation.sql ``` Expected Ireland local result: @@ -85,14 +85,14 @@ Use this to validate the same search behavior against the Ireland schema name: createdb saayam_ireland_full_search_test psql -d saayam_ireland_full_search_test \ - -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql + -f tests/Search/test_clones/ireland_search_instance_clone.sql psql -d saayam_ireland_full_search_test \ - -f ddl/Search/tests/runners/run_ireland_search_migrations.sql + -f tests/Search/runners/run_ireland_search_migrations.sql psql -d saayam_ireland_full_search_test \ -v ON_ERROR_STOP=1 \ - -f ddl/Search/tests/runners/run_ireland_search_validation.sql + -f tests/Search/runners/run_ireland_search_validation.sql ``` Expected Ireland full-search result: @@ -121,13 +121,13 @@ Run search scripts with the correct target schema. For Virginia QA: ```sql -\i ddl/Search/tests/runners/run_virginia_search_migrations.sql +\i tests/Search/runners/run_virginia_search_migrations.sql ``` For Ireland QA: ```sql -\i ddl/Search/tests/runners/run_ireland_search_migrations.sql +\i tests/Search/runners/run_ireland_search_migrations.sql ``` Then run validation checks. @@ -135,15 +135,15 @@ Then run validation checks. Recommended first QA checks: ```sql -\i ddl/Search/tests/virginia_validation/02_search_index_check.sql -\i ddl/Search/tests/virginia_validation/04_instance_clone_check.sql +\i tests/Search/virginia_validation/02_search_index_check.sql +\i tests/Search/virginia_validation/04_instance_clone_check.sql ``` Run behavior checks only after confirming temporary QA inserts are acceptable: ```sql -\i ddl/Search/tests/virginia_validation/01_search_smoke_test.sql -\i ddl/Search/tests/virginia_validation/03_search_function_check.sql +\i tests/Search/virginia_validation/01_search_smoke_test.sql +\i tests/Search/virginia_validation/03_search_function_check.sql ``` ## QA Safety Notes @@ -175,7 +175,7 @@ Local PR evidence already captured: - Virginia schema-agnostic validation passed: `saayam_virginia_agnostic_test_1778949545` - Ireland schema-agnostic validation passed: `saayam_ireland_agnostic_test_1778949579` -- Both validations used the shared production scripts under `ddl/Search/codes/` +- Both validations used the shared production scripts under `ddl/Search/` - Region selection is handled through `search_path` in the migration runners Useful QA evidence queries: diff --git a/ddl/Search/tests/README.md b/tests/Search/README.md similarity index 59% rename from ddl/Search/tests/README.md rename to tests/Search/README.md index eb404f5..a37301c 100644 --- a/ddl/Search/tests/README.md +++ b/tests/Search/README.md @@ -8,22 +8,22 @@ Do not edit the original schema files under `ddl/Tables/` for search testing. If 1. Create a local PostgreSQL database. 2. Load a Virginia or Ireland schema clone from `test_clones/`. -3. Run the search scripts from `../codes/` in order. +3. Run the search scripts from `../` in order. 4. Run local search validation. ## Search Scripts Under Test -- `../codes/01_enable_fuzzy_search.sql` -- `../codes/02_add_request_search.sql` -- `../codes/03_add_user_and_volunteer_search.sql` -- `../codes/04_add_category_and_advanced_search.sql` +- `../01_enable_fuzzy_search.sql` +- `../02_add_request_search.sql` +- `../03_add_user_and_volunteer_search.sql` +- `../04_add_category_and_advanced_search.sql` ## Run All Local Checks Run this after loading the local clone and applying search migrations: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/runners/run_virginia_search_validation.sql +psql -d saayam_search_clone_test -f tests/Search/runners/run_virginia_search_validation.sql ``` This runs the smoke test, index check, function check, and instance clone check in order. @@ -32,9 +32,9 @@ This runs the smoke test, index check, function check, and instance clone check ```bash createdb saayam_search_clone_test -psql -d saayam_search_clone_test -f ddl/Search/tests/test_clones/virginia_search_instance_clone.sql -psql -d saayam_search_clone_test -f ddl/Search/tests/runners/run_virginia_search_migrations.sql -psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/runners/run_virginia_search_validation.sql +psql -d saayam_search_clone_test -f tests/Search/test_clones/virginia_search_instance_clone.sql +psql -d saayam_search_clone_test -f tests/Search/runners/run_virginia_search_migrations.sql +psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f tests/Search/runners/run_virginia_search_validation.sql ``` ## Ireland Clone Check @@ -44,22 +44,22 @@ Ireland uses `proposed_saayam`, so the Ireland runner sets that schema before ap ```bash createdb saayam_ireland_search_clone_test -psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql -psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/runners/run_ireland_clone_validation.sql +psql -d saayam_ireland_search_clone_test -f tests/Search/test_clones/ireland_search_instance_clone.sql +psql -d saayam_ireland_search_clone_test -f tests/Search/runners/run_ireland_clone_validation.sql ``` This confirms the Ireland-style clone has the tables and columns needed by the search plan. ## Ireland Full Search Check -The same production scripts under `ddl/Search/codes/` are used for Ireland. +The same production scripts under `ddl/Search/` are used for Ireland. The Ireland migration runner sets `search_path` to `proposed_saayam`. ```bash createdb saayam_ireland_full_search_test -psql -d saayam_ireland_full_search_test -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql -psql -d saayam_ireland_full_search_test -f ddl/Search/tests/runners/run_ireland_search_migrations.sql -psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/runners/run_ireland_search_validation.sql +psql -d saayam_ireland_full_search_test -f tests/Search/test_clones/ireland_search_instance_clone.sql +psql -d saayam_ireland_full_search_test -f tests/Search/runners/run_ireland_search_migrations.sql +psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f tests/Search/runners/run_ireland_search_validation.sql ``` This runs the same behavior, index, function, and migrated-clone checks against the Ireland schema name, `proposed_saayam`. @@ -69,7 +69,7 @@ This runs the same behavior, index, function, and migrated-clone checks against Run this after loading the local clone and search scripts: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/virginia_validation/01_search_smoke_test.sql +psql -d saayam_search_clone_test -f tests/Search/virginia_validation/01_search_smoke_test.sql ``` This inserts temporary sample data, checks search behavior, and rolls the data back. @@ -79,7 +79,7 @@ This inserts temporary sample data, checks search behavior, and rolls the data b Run this after loading the local clone and search scripts: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/virginia_validation/02_search_index_check.sql +psql -d saayam_search_clone_test -f tests/Search/virginia_validation/02_search_index_check.sql ``` This fails if any expected search index is missing or uses the wrong strategy. @@ -90,7 +90,7 @@ It checks GIN full-text indexes, GIN trigram indexes, and the exact email btree Run this after loading the local clone and search scripts: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/virginia_validation/03_search_function_check.sql +psql -d saayam_search_clone_test -f tests/Search/virginia_validation/03_search_function_check.sql ``` This checks the main search functions directly: @@ -106,7 +106,7 @@ It verifies keyword matching, exact email matching, organization name matching, Run this after loading the local clone and search scripts: ```bash -psql -d saayam_search_clone_test -f ddl/Search/tests/virginia_validation/04_instance_clone_check.sql +psql -d saayam_search_clone_test -f tests/Search/virginia_validation/04_instance_clone_check.sql ``` This verifies the local Virginia clone has the search-required tables, search columns, and search functions. diff --git a/ddl/Search/tests/ireland_validation/01_search_smoke_test.sql b/tests/Search/ireland_validation/01_search_smoke_test.sql similarity index 99% rename from ddl/Search/tests/ireland_validation/01_search_smoke_test.sql rename to tests/Search/ireland_validation/01_search_smoke_test.sql index 2974a82..2d48aaa 100644 --- a/ddl/Search/tests/ireland_validation/01_search_smoke_test.sql +++ b/tests/Search/ireland_validation/01_search_smoke_test.sql @@ -1,5 +1,5 @@ -- Search MVP smoke test. --- Run after instance clone and ddl/Search/codes/01..04. +-- Run after instance clone and ddl/Search/01..04. BEGIN; diff --git a/ddl/Search/tests/ireland_validation/02_search_index_check.sql b/tests/Search/ireland_validation/02_search_index_check.sql similarity index 97% rename from ddl/Search/tests/ireland_validation/02_search_index_check.sql rename to tests/Search/ireland_validation/02_search_index_check.sql index b0765a4..e6fdcf4 100644 --- a/ddl/Search/tests/ireland_validation/02_search_index_check.sql +++ b/tests/Search/ireland_validation/02_search_index_check.sql @@ -1,5 +1,5 @@ -- Search index check. --- Run after instance clone and ddl/Search/codes/01..04. +-- Run after instance clone and ddl/Search/01..04. DO $$ DECLARE diff --git a/ddl/Search/tests/ireland_validation/03_search_function_check.sql b/tests/Search/ireland_validation/03_search_function_check.sql similarity index 98% rename from ddl/Search/tests/ireland_validation/03_search_function_check.sql rename to tests/Search/ireland_validation/03_search_function_check.sql index ef695ce..a90deab 100644 --- a/ddl/Search/tests/ireland_validation/03_search_function_check.sql +++ b/tests/Search/ireland_validation/03_search_function_check.sql @@ -1,5 +1,5 @@ -- Search function check. --- Run after instance clone and ddl/Search/codes/01..04. +-- Run after instance clone and ddl/Search/01..04. BEGIN; diff --git a/ddl/Search/tests/ireland_validation/04_instance_clone_check.sql b/tests/Search/ireland_validation/04_instance_clone_check.sql similarity index 97% rename from ddl/Search/tests/ireland_validation/04_instance_clone_check.sql rename to tests/Search/ireland_validation/04_instance_clone_check.sql index e0916d8..5ceb5d1 100644 --- a/ddl/Search/tests/ireland_validation/04_instance_clone_check.sql +++ b/tests/Search/ireland_validation/04_instance_clone_check.sql @@ -1,5 +1,5 @@ -- Local instance clone check. --- Run after instance clone and ddl/Search/codes/01..04. +-- Run after instance clone and ddl/Search/01..04. DO $$ DECLARE diff --git a/ddl/Search/tests/ireland_validation/05_ireland_instance_clone_check.sql b/tests/Search/ireland_validation/05_ireland_instance_clone_check.sql similarity index 96% rename from ddl/Search/tests/ireland_validation/05_ireland_instance_clone_check.sql rename to tests/Search/ireland_validation/05_ireland_instance_clone_check.sql index 3608c18..99165a2 100644 --- a/ddl/Search/tests/ireland_validation/05_ireland_instance_clone_check.sql +++ b/tests/Search/ireland_validation/05_ireland_instance_clone_check.sql @@ -1,5 +1,5 @@ -- Ireland local instance clone check. --- Run after ddl/Search/tests/test_clones/ireland_search_instance_clone.sql. +-- Run after tests/Search/test_clones/ireland_search_instance_clone.sql. DO $$ DECLARE diff --git a/ddl/Search/tests/runners/run_ireland_clone_validation.sql b/tests/Search/runners/run_ireland_clone_validation.sql similarity index 71% rename from ddl/Search/tests/runners/run_ireland_clone_validation.sql rename to tests/Search/runners/run_ireland_clone_validation.sql index a2de566..9f144a1 100644 --- a/ddl/Search/tests/runners/run_ireland_clone_validation.sql +++ b/tests/Search/runners/run_ireland_clone_validation.sql @@ -1,5 +1,5 @@ -- Ireland local clone validation runner. --- Run after ddl/Search/tests/test_clones/ireland_search_instance_clone.sql. +-- Run after tests/Search/test_clones/ireland_search_instance_clone.sql. \echo 'Running Ireland instance clone check' \ir ../ireland_validation/05_ireland_instance_clone_check.sql diff --git a/tests/Search/runners/run_ireland_search_migrations.sql b/tests/Search/runners/run_ireland_search_migrations.sql new file mode 100644 index 0000000..ee891bc --- /dev/null +++ b/tests/Search/runners/run_ireland_search_migrations.sql @@ -0,0 +1,8 @@ +-- Run production search migrations against the Ireland schema. + +SET search_path TO proposed_saayam, public; + +\ir ../../../ddl/Search/01_enable_fuzzy_search.sql +\ir ../../../ddl/Search/02_add_request_search.sql +\ir ../../../ddl/Search/03_add_user_and_volunteer_search.sql +\ir ../../../ddl/Search/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/runners/run_ireland_search_validation.sql b/tests/Search/runners/run_ireland_search_validation.sql similarity index 100% rename from ddl/Search/tests/runners/run_ireland_search_validation.sql rename to tests/Search/runners/run_ireland_search_validation.sql diff --git a/tests/Search/runners/run_virginia_search_migrations.sql b/tests/Search/runners/run_virginia_search_migrations.sql new file mode 100644 index 0000000..6db9a4f --- /dev/null +++ b/tests/Search/runners/run_virginia_search_migrations.sql @@ -0,0 +1,8 @@ +-- Run production search migrations against the Virginia schema. + +SET search_path TO virginia_dev_saayam_rdbms, public; + +\ir ../../../ddl/Search/01_enable_fuzzy_search.sql +\ir ../../../ddl/Search/02_add_request_search.sql +\ir ../../../ddl/Search/03_add_user_and_volunteer_search.sql +\ir ../../../ddl/Search/04_add_category_and_advanced_search.sql diff --git a/ddl/Search/tests/runners/run_virginia_search_validation.sql b/tests/Search/runners/run_virginia_search_validation.sql similarity index 100% rename from ddl/Search/tests/runners/run_virginia_search_validation.sql rename to tests/Search/runners/run_virginia_search_validation.sql diff --git a/ddl/Search/tests/test_clones/README.md b/tests/Search/test_clones/README.md similarity index 67% rename from ddl/Search/tests/test_clones/README.md rename to tests/Search/test_clones/README.md index 69c9a7f..77b44d6 100644 --- a/ddl/Search/tests/test_clones/README.md +++ b/tests/Search/test_clones/README.md @@ -32,12 +32,12 @@ The Ireland search clone follows the Ireland schema name, `proposed_saayam`, and ```bash createdb saayam_search_clone_test -psql -d saayam_search_clone_test -f ddl/Search/tests/test_clones/virginia_search_instance_clone.sql -psql -d saayam_search_clone_test -f ddl/Search/tests/runners/run_virginia_search_migrations.sql -psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/runners/run_virginia_search_validation.sql +psql -d saayam_search_clone_test -f tests/Search/test_clones/virginia_search_instance_clone.sql +psql -d saayam_search_clone_test -f tests/Search/runners/run_virginia_search_migrations.sql +psql -d saayam_search_clone_test -v ON_ERROR_STOP=1 -f tests/Search/runners/run_virginia_search_validation.sql ``` -Add smoke-test SQL files under `ddl/Search/tests/` as the search validation suite grows. +Add smoke-test SQL files under `tests/Search/` as the search validation suite grows. ## Ireland Search Clone Check @@ -45,8 +45,8 @@ Use this when local PostGIS is not available or when the goal is only to confirm ```bash createdb saayam_ireland_search_clone_test -psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql -psql -d saayam_ireland_search_clone_test -f ddl/Search/tests/runners/run_ireland_clone_validation.sql +psql -d saayam_ireland_search_clone_test -f tests/Search/test_clones/ireland_search_instance_clone.sql +psql -d saayam_ireland_search_clone_test -f tests/Search/runners/run_ireland_clone_validation.sql ``` This confirms the Ireland-style `proposed_saayam` clone has the tables and columns needed by the current search plan. @@ -57,9 +57,9 @@ Use this to validate the same search behavior against the Ireland schema name: ```bash createdb saayam_ireland_full_search_test -psql -d saayam_ireland_full_search_test -f ddl/Search/tests/test_clones/ireland_search_instance_clone.sql -psql -d saayam_ireland_full_search_test -f ddl/Search/tests/runners/run_ireland_search_migrations.sql -psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f ddl/Search/tests/runners/run_ireland_search_validation.sql +psql -d saayam_ireland_full_search_test -f tests/Search/test_clones/ireland_search_instance_clone.sql +psql -d saayam_ireland_full_search_test -f tests/Search/runners/run_ireland_search_migrations.sql +psql -d saayam_ireland_full_search_test -v ON_ERROR_STOP=1 -f tests/Search/runners/run_ireland_search_validation.sql ``` The same production search scripts are used for Ireland through `run_ireland_search_migrations.sql`. diff --git a/ddl/Search/tests/test_clones/ireland_search_instance_clone.sql b/tests/Search/test_clones/ireland_search_instance_clone.sql similarity index 100% rename from ddl/Search/tests/test_clones/ireland_search_instance_clone.sql rename to tests/Search/test_clones/ireland_search_instance_clone.sql diff --git a/ddl/Search/tests/test_clones/virginia_search_instance_clone.sql b/tests/Search/test_clones/virginia_search_instance_clone.sql similarity index 98% rename from ddl/Search/tests/test_clones/virginia_search_instance_clone.sql rename to tests/Search/test_clones/virginia_search_instance_clone.sql index b1222a3..1a7245c 100644 --- a/ddl/Search/tests/test_clones/virginia_search_instance_clone.sql +++ b/tests/Search/test_clones/virginia_search_instance_clone.sql @@ -1,5 +1,5 @@ -- Search-scoped Virginia instance clone. --- Use this fixture to validate ddl/Search/codes locally without editing ddl/Tables. +-- Use this fixture to validate ddl/Search locally without editing ddl/Tables. DROP SCHEMA IF EXISTS virginia_dev_saayam_rdbms CASCADE; CREATE SCHEMA virginia_dev_saayam_rdbms; diff --git a/ddl/Search/tests/virginia_validation/01_search_smoke_test.sql b/tests/Search/virginia_validation/01_search_smoke_test.sql similarity index 99% rename from ddl/Search/tests/virginia_validation/01_search_smoke_test.sql rename to tests/Search/virginia_validation/01_search_smoke_test.sql index b625dff..a86576f 100644 --- a/ddl/Search/tests/virginia_validation/01_search_smoke_test.sql +++ b/tests/Search/virginia_validation/01_search_smoke_test.sql @@ -1,5 +1,5 @@ -- Search MVP smoke test. --- Run after instance clone and ddl/Search/codes/01..04. +-- Run after instance clone and ddl/Search/01..04. BEGIN; diff --git a/ddl/Search/tests/virginia_validation/02_search_index_check.sql b/tests/Search/virginia_validation/02_search_index_check.sql similarity index 97% rename from ddl/Search/tests/virginia_validation/02_search_index_check.sql rename to tests/Search/virginia_validation/02_search_index_check.sql index 1a8437a..3509a5c 100644 --- a/ddl/Search/tests/virginia_validation/02_search_index_check.sql +++ b/tests/Search/virginia_validation/02_search_index_check.sql @@ -1,5 +1,5 @@ -- Search index check. --- Run after instance clone and ddl/Search/codes/01..04. +-- Run after instance clone and ddl/Search/01..04. DO $$ DECLARE diff --git a/ddl/Search/tests/virginia_validation/03_search_function_check.sql b/tests/Search/virginia_validation/03_search_function_check.sql similarity index 99% rename from ddl/Search/tests/virginia_validation/03_search_function_check.sql rename to tests/Search/virginia_validation/03_search_function_check.sql index b81d513..c557153 100644 --- a/ddl/Search/tests/virginia_validation/03_search_function_check.sql +++ b/tests/Search/virginia_validation/03_search_function_check.sql @@ -1,5 +1,5 @@ -- Search function check. --- Run after instance clone and ddl/Search/codes/01..04. +-- Run after instance clone and ddl/Search/01..04. BEGIN; diff --git a/ddl/Search/tests/virginia_validation/04_instance_clone_check.sql b/tests/Search/virginia_validation/04_instance_clone_check.sql similarity index 97% rename from ddl/Search/tests/virginia_validation/04_instance_clone_check.sql rename to tests/Search/virginia_validation/04_instance_clone_check.sql index 9a50167..cd9e73c 100644 --- a/ddl/Search/tests/virginia_validation/04_instance_clone_check.sql +++ b/tests/Search/virginia_validation/04_instance_clone_check.sql @@ -1,5 +1,5 @@ -- Local instance clone check. --- Run after instance clone and ddl/Search/codes/01..04. +-- Run after instance clone and ddl/Search/01..04. DO $$ DECLARE