diff --git a/package/c/hemar/test/test_template_parser.sql b/package/c/hemar/test/test_template_parser.sql index 1db3de1..f406c06 100755 --- a/package/c/hemar/test/test_template_parser.sql +++ b/package/c/hemar/test/test_template_parser.sql @@ -439,6 +439,628 @@ TEXT: " RAISE WARNING 'Test %: Execute tag with braces inside SQL code - FAILED', total_tests; END IF; + -- Test 22: Deeply nested sections with mixed content + total_tests := total_tests + 1; + result := test_template_parse( + $template22${{ for x in outer }} + Level 1: {{ x.name }} + {{ for y in x.items }} + Level 2: {{ y.title }} + {{ for z in y.subitems }} + Level 3: {{ z.label }} - {{ z.value }} + {{ for detail in z.details }} + Details: {{ detail }} + {{ end }} + {{ end }} + {{ end }} +{{ end }}$template22$, + $expected22$Template parsed successfully. Structure: +SECTION: iterator="x", collection="outer" + TEXT: " + Level 1: " + INTERPOLATE: "x.name" + TEXT: " + " + SECTION: iterator="y", collection="x.items" + TEXT: " + Level 2: " + INTERPOLATE: "y.title" + TEXT: " + " + SECTION: iterator="z", collection="y.subitems" + TEXT: " + Level 3: " + INTERPOLATE: "z.label" + TEXT: " - " + INTERPOLATE: "z.value" + TEXT: " + " + SECTION: iterator="detail", collection="z.details" + TEXT: " + Details: " + INTERPOLATE: "detail" + TEXT: " + " + TEXT: " + " + TEXT: " + " + TEXT: " +"$expected22$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Deeply nested sections with mixed content - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Deeply nested sections with mixed content - FAILED', total_tests; + END IF; + + -- Test 23: Multiple tag types mixed with HTML + total_tests := total_tests + 1; + result := test_template_parse( + $template23$
+
{{ page_title }}
+ +
+ {{ include content_template }} + {{ exec SELECT get_footer_text() AS footer_text; }} +
+
$template23$, + $expected23$Template parsed successfully. Structure: +TEXT: "
+
" +INTERPOLATE: "page_title" +TEXT: "
+ +
+ " +INCLUDE: "content_template" +TEXT: " + " +EXECUTE: "SELECT get_footer_text() AS footer_text;" +TEXT: " +
+
"$expected23$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Multiple tag types mixed with HTML - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Multiple tag types mixed with HTML - FAILED', total_tests; + END IF; + + -- Test 24: Section with complex iterator paths + total_tests := total_tests + 1; + result := test_template_parse( + $template24${{ for item.nested[0].value in collection[5].items[2].values }} + {{ item.nested[0].value }} +{{ end }}$template24$, + $expected24$SECTION: iterator="item.nested[0].value", collection="collection[5].items[2].values"$expected24$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Section with complex iterator paths - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Section with complex iterator paths - FAILED', total_tests; + END IF; + + -- Test 25: Interpolation with Unicode characters + total_tests := total_tests + 1; + result := test_template_parse( + $template25${{ unicode_var_ẞαж한글💻🌍 }}$template25$, + $expected25$INTERPOLATE: "unicode_var_ẞαж한글💻🌍"$expected25$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Interpolation with Unicode characters - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Interpolation with Unicode characters - FAILED', total_tests; + END IF; + + -- Test 26: Multiple consecutive sections + total_tests := total_tests + 1; + result := test_template_parse( + $template26${{ for a in list_a }}{{ a }}{{ end }}{{ for b in list_b }}{{ b }}{{ end }}{{ for c in list_c }}{{ c }}{{ end }}$template26$, + $expected26$SECTION: iterator="a", collection="list_a" + INTERPOLATE: "a" +SECTION: iterator="b", collection="list_b" + INTERPOLATE: "b" +SECTION: iterator="c", collection="list_c" + INTERPOLATE: "c"$expected26$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Multiple consecutive sections - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Multiple consecutive sections - FAILED', total_tests; + END IF; + + -- Test 27: Includes with variable paths + total_tests := total_tests + 1; + result := test_template_parse( + $template27${{ include user.preferences.theme_template }} +{{ include system.templates[user.template_index] }}$template27$, + $expected27$INCLUDE: "user.preferences.theme_template" +TEXT: " +" +INCLUDE: "system.templates[user.template_index]"$expected27$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Includes with variable paths - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Includes with variable paths - FAILED', total_tests; + END IF; + + -- Test 28: Extremely long interpolation key + total_tests := total_tests + 1; + result := test_template_parse( + $template28${{ very_long_variable_name_with_many_parts.that_continues_for_a_while.with_multiple_segments.and_keeps_going.for_quite_some_time.until_it_becomes_quite_verbose }}$template28$, + $expected28$INTERPOLATE: "very_long_variable_name_with_many_parts.that_continues_for_a_while.with_multiple_segments.and_keeps_going.for_quite_some_time.until_it_becomes_quite_verbose"$expected28$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Extremely long interpolation key - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Extremely long interpolation key - FAILED', total_tests; + END IF; + + -- Test 29: Tags with extra whitespace + total_tests := total_tests + 1; + result := test_template_parse( + $template29${{ for item in items }} + {{ item.name }} +{{ end }}$template29$, + $expected29$SECTION: iterator="item", collection="items" + TEXT: " + " + INTERPOLATE: "item.name" + TEXT: " +"$expected29$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Tags with extra whitespace - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Tags with extra whitespace - FAILED', total_tests; + END IF; + + -- Test 30: Execute with PL/pgSQL code blocks + total_tests := total_tests + 1; + result := test_template_parse( + $template30${{ exec +DECLARE + temp_var text; + counter int := 0; +BEGIN + FOR i IN 1..10 LOOP + counter := counter + i; + END LOOP; + + IF counter > 50 THEN + temp_var := 'High'; + ELSE + temp_var := 'Low'; + END IF; + + RETURN json_build_object('result', temp_var, 'count', counter); +END; +}}$template30$, + $expected30$EXECUTE: "DECLARE + temp_var text; + counter int := 0; +BEGIN + FOR i IN 1..10 LOOP + counter := counter + i; + END LOOP; + + IF counter > 50 THEN + temp_var := 'High'; + ELSE + temp_var := 'Low'; + END IF; + + RETURN json_build_object('result', temp_var, 'count', counter); +END;"$expected30$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Execute with PL/pgSQL code blocks - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Execute with PL/pgSQL code blocks - FAILED', total_tests; + END IF; + + -- Test 31: Template with mixed indentation and newlines + total_tests := total_tests + 1; + result := test_template_parse( + $template31$
+ {{ for item in items }} + {{ item.name }} + {{ end }} +
$template31$, + $expected31$TEXT: "
+ " +SECTION: iterator="item", collection="items" + TEXT: " + " + INTERPOLATE: "item.name" + TEXT: " + " +TEXT: " +
"$expected31$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Template with mixed indentation and newlines - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Template with mixed indentation and newlines - FAILED', total_tests; + END IF; + + -- Test 32: Execute with window functions and complex SQL + total_tests := total_tests + 1; + result := test_template_parse( + $template32${{ exec +WITH recursive cte AS ( + SELECT id, parent_id, name, 1 AS level + FROM categories + WHERE parent_id IS NULL + UNION ALL + SELECT c.id, c.parent_id, c.name, cte.level + 1 + FROM categories c + JOIN cte ON c.parent_id = cte.id +) +SELECT + id, + repeat(' ', level - 1) || name AS indented_name, + row_number() OVER (PARTITION BY level ORDER BY name) AS row_num +FROM cte +ORDER BY level, name; +}}$template32$, + $expected32$EXECUTE: "WITH recursive cte AS ( + SELECT id, parent_id, name, 1 AS level + FROM categories + WHERE parent_id IS NULL + UNION ALL + SELECT c.id, c.parent_id, c.name, cte.level + 1 + FROM categories c + JOIN cte ON c.parent_id = cte.id +) +SELECT + id, + repeat(' ', level - 1) || name AS indented_name, + row_number() OVER (PARTITION BY level ORDER BY name) AS row_num +FROM cte +ORDER BY level, name;"$expected32$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Execute with window functions and complex SQL - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Execute with window functions and complex SQL - FAILED', total_tests; + END IF; + + -- Test 33: Recursive template includes + total_tests := total_tests + 1; + result := test_template_parse( + $template33${{ include base_template }} +{{ include dynamic_templates[index] }} +{{ for template_name in available_templates }} + {{ include template_name }} +{{ end }}$template33$, + $expected33$INCLUDE: "base_template" +TEXT: " +" +INCLUDE: "dynamic_templates[index]" +TEXT: " +" +SECTION: iterator="template_name", collection="available_templates" + TEXT: " + " + INCLUDE: "template_name" + TEXT: " +"$expected33$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Recursive template includes - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Recursive template includes - FAILED', total_tests; + END IF; + + -- Test 34: Complex JSON manipulation in execute + total_tests := total_tests + 1; + result := test_template_parse( + $template34${{ exec +WITH input_data AS ( + SELECT '{"users": [ + {"id": 1, "name": "Alice", "roles": ["admin", "editor"]}, + {"id": 2, "name": "Bob", "roles": ["viewer"]}, + {"id": 3, "name": "Charlie", "roles": ["editor", "contributor"]} + ]}'::jsonb AS data +) +SELECT + jsonb_agg( + jsonb_build_object( + 'name', user_data->>'name', + 'roles', user_data->'roles', + 'is_admin', user_data->'roles' ? 'admin' + ) + ) AS result +FROM input_data, +jsonb_array_elements(data->'users') AS user_data; +}}$template34$, + $expected34$EXECUTE: "WITH input_data AS ( + SELECT '{"users": [ + {"id": 1, "name": "Alice", "roles": ["admin", "editor"]}, + {"id": 2, "name": "Bob", "roles": ["viewer"]}, + {"id": 3, "name": "Charlie", "roles": ["editor", "contributor"]} + ]}'::jsonb AS data +) +SELECT + jsonb_agg( + jsonb_build_object( + 'name', user_data->>'name', + 'roles', user_data->'roles', + 'is_admin', user_data->'roles' ? 'admin' + ) + ) AS result +FROM input_data, +jsonb_array_elements(data->'users') AS user_data;"$expected34$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Complex JSON manipulation in execute - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Complex JSON manipulation in execute - FAILED', total_tests; + END IF; + + -- Test 35: Tags at start/end without whitespace + total_tests := total_tests + 1; + result := test_template_parse( + $template35${{ var1 }}Text{{ var2 }}$template35$, + $expected35$INTERPOLATE: "var1" +TEXT: "Text" +INTERPOLATE: "var2"$expected35$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Tags at start/end without whitespace - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Tags at start/end without whitespace - FAILED', total_tests; + END IF; + + -- Test 36: Template with special characters in text + total_tests := total_tests + 1; + result := test_template_parse( + $template36$Special chars: <>!@#$%^&*()_+`-=[]{}|;':",./<>?\nAnd {{ variable }} insertion$template36$, + $expected36$TEXT: "Special chars: <>!@#$%^&*()_+`-=[]{}|;':",./<>?\nAnd " +INTERPOLATE: "variable" +TEXT: " insertion"$expected36$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Template with special characters in text - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Template with special characters in text - FAILED', total_tests; + END IF; + + -- Test 37: Complex execute with error handling + total_tests := total_tests + 1; + result := test_template_parse( + $template37${{ exec +BEGIN + RETURN process_data(input_json); +EXCEPTION + WHEN no_data_found THEN + RETURN jsonb_build_object('error', 'No data found', 'code', 404); + WHEN unique_violation THEN + RETURN jsonb_build_object('error', 'Duplicate entry', 'code', 409); + WHEN OTHERS THEN + RETURN jsonb_build_object( + 'error', SQLERRM, + 'code', SQLSTATE, + 'severity', 'CRITICAL' + ); +END; +}}$template37$, + $expected37$EXECUTE: "BEGIN + RETURN process_data(input_json); +EXCEPTION + WHEN no_data_found THEN + RETURN jsonb_build_object('error', 'No data found', 'code', 404); + WHEN unique_violation THEN + RETURN jsonb_build_object('error', 'Duplicate entry', 'code', 409); + WHEN OTHERS THEN + RETURN jsonb_build_object( + 'error', SQLERRM, + 'code', SQLSTATE, + 'severity', 'CRITICAL' + ); +END;"$expected37$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Complex execute with error handling - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Complex execute with error handling - FAILED', total_tests; + END IF; + + -- Test 38: Mixed nested sections and interpolations + total_tests := total_tests + 1; + result := test_template_parse( + $template38${{ for user in users }} + {{ user.name }}'s permissions: + {{ for permission in user.permissions }} + - {{ permission.name }}: {{ permission.status }} + {{ for scope in permission.scopes }} + * {{ scope.area }}: {{ scope.level }} + {{ end }} + {{ end }} +{{ end }}$template38$, + $expected38$SECTION: iterator="user", collection="users" + TEXT: " + " + INTERPOLATE: "user.name" + TEXT: "'s permissions: + " + SECTION: iterator="permission", collection="user.permissions" + TEXT: " + - " + INTERPOLATE: "permission.name" + TEXT: ": " + INTERPOLATE: "permission.status" + TEXT: " + " + SECTION: iterator="scope", collection="permission.scopes" + TEXT: " + * " + INTERPOLATE: "scope.area" + TEXT: ": " + INTERPOLATE: "scope.level" + TEXT: " + " + TEXT: " + " + TEXT: " +"$expected38$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Mixed nested sections and interpolations - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Mixed nested sections and interpolations - FAILED', total_tests; + END IF; + + -- Test 39: Execute with dynamic SQL generation + total_tests := total_tests + 1; + result := test_template_parse( + $template39${{ exec +DECLARE + column_names text[] := ARRAY['id', 'name', 'created_at']; + table_name text := 'users'; + conditions text[] := ARRAY['is_active = true', 'created_at > now() - interval ''1 month''']; + order_clause text := 'last_login DESC'; + query text; +BEGIN + query := 'SELECT ' || array_to_string(column_names, ', ') || + ' FROM ' || table_name; + + IF array_length(conditions, 1) > 0 THEN + query := query || ' WHERE ' || array_to_string(conditions, ' AND '); + END IF; + + IF order_clause IS NOT NULL THEN + query := query || ' ORDER BY ' || order_clause; + END IF; + + EXECUTE query; + RETURN query; +END; +}}$template39$, + $expected39$EXECUTE: "DECLARE + column_names text[] := ARRAY['id', 'name', 'created_at']; + table_name text := 'users'; + conditions text[] := ARRAY['is_active = true', 'created_at > now() - interval ''1 month''']; + order_clause text := 'last_login DESC'; + query text; +BEGIN + query := 'SELECT ' || array_to_string(column_names, ', ') || + ' FROM ' || table_name; + + IF array_length(conditions, 1) > 0 THEN + query := query || ' WHERE ' || array_to_string(conditions, ' AND '); + END IF; + + IF order_clause IS NOT NULL THEN + query := query || ' ORDER BY ' || order_clause; + END IF; + + EXECUTE query; + RETURN query; +END;"$expected39$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Execute with dynamic SQL generation - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Execute with dynamic SQL generation - FAILED', total_tests; + END IF; + + -- Test 40: Complex nested structure with all tag types + total_tests := total_tests + 1; + result := test_template_parse( + $template40$ + + + {{ page.title }} + {{ include meta_tags }} + + +
{{ include header }}
+
+ {{ for section in page.sections }} +
+

{{ section.title }}

+ {{ for item in section.items }} +
+ {{ item.content }} + {{ include item.template }} + {{ exec + -- Get dynamic content for this item + SELECT get_dynamic_content( + '{{ item.id }}', + '{{ section.id }}', + (SELECT context->'user'->'preferences') + ); + }} +
+ {{ end }} +
+ {{ end }} +
+ + +$template40$, + $expected40$TEXT: " + + + " +INTERPOLATE: "page.title" +TEXT: " + " +INCLUDE: "meta_tags" +TEXT: " + + +
" +INCLUDE: "header" +TEXT: "
+
+ " +SECTION: iterator="section", collection="page.sections"$expected40$ + ); + IF result THEN + passed_tests := passed_tests + 1; + RAISE NOTICE 'Test %: Complex nested structure with all tag types - PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Complex nested structure with all tag types - FAILED', total_tests; + END IF; + -- Print summary IF passed_tests = total_tests THEN RAISE NOTICE '------------------------------------';