Files
util.nix/package/c/hemar/test/test_template_parser.sql

460 lines
15 KiB
PL/PgSQL
Executable File

-- Test file for hemar template parser
-- Run with: psql -f test_template_parser.sql
-- Load extension if not already loaded
-- CREATE EXTENSION IF NOT EXISTS hemar;
-- SAFETY(yukkop): !!! If you fix identation, you will ruin the tests.
-- Create test function to validate template parsing
CREATE OR REPLACE FUNCTION test_template_parse(template_text text, expected_structure text) RETURNS boolean AS $$
DECLARE
parsed_result text;
passed boolean;
BEGIN
BEGIN
parsed_result := hemar.parse(template_text);
IF parsed_result IS NULL THEN
RAISE WARNING 'Parser returned NULL for template: %', template_text;
RETURN false;
END IF;
passed := position(expected_structure in parsed_result) > 0;
IF NOT passed THEN
RAISE WARNING 'Template parsing test failed!';
RAISE WARNING 'Template: %', template_text;
RAISE WARNING 'Expected to find: %', expected_structure;
RAISE WARNING 'Actual result: %', parsed_result;
END IF;
RETURN passed;
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'Exception during parsing: % (state: %)', SQLERRM, SQLSTATE;
RAISE WARNING 'Template: %', template_text;
RETURN false;
END;
END;
$$ LANGUAGE plpgsql;
-- Run the tests
DO $$
DECLARE
total_tests integer := 0;
passed_tests integer := 0;
result boolean;
BEGIN
PERFORM pg_sleep(2);
RAISE NOTICE 'Starting template parser tests...';
-- Test 1: Simple interpolation
total_tests := total_tests + 1;
result := test_template_parse(
$hemar1${{ simple_var }}$hemar1$,
$expected1$INTERPOLATE: "simple_var"$expected1$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Simple interpolation - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Simple interpolation - FAILED', total_tests;
END IF;
-- Test 2: Interpolation with surrounding text
total_tests := total_tests + 1;
result := test_template_parse(
$hemar2$Hello, {{ name }}!$hemar2$,
$expected2$TEXT: "Hello, "
INTERPOLATE: "name"
TEXT: "!"$expected2$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Interpolation with surrounding text - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Interpolation with surrounding text - FAILED', total_tests;
END IF;
-- Test 3: Simple section (for loop)
total_tests := total_tests + 1;
result := test_template_parse(
$hemar3${{ for item in items }}{{ item }}{{ end }}$hemar3$,
$expected3$SECTION: iterator="item", collection="items"$expected3$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Simple section (for loop) - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Simple section (for loop) - FAILED', total_tests;
END IF;
-- Test 4: Section with nested interpolation
total_tests := total_tests + 1;
result := test_template_parse(
$hemar4${{ for item in items }}Name: {{ item.name }}{{ end }}$hemar4$,
$expected4$SECTION: iterator="item", collection="items"
TEXT: "Name: "
INTERPOLATE: "item.name"$expected4$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Section with nested interpolation - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Section with nested interpolation - FAILED', total_tests;
END IF;
-- Test 5: Nested sections
total_tests := total_tests + 1;
result := test_template_parse(
$hemar5${{ for item in items }}{{ for subitem in item.subitems }}{{ subitem }}{{ end }}{{ end }}$hemar5$,
$expected5$SECTION: iterator="item", collection="items"
SECTION: iterator="subitem", collection="item.subitems"
INTERPOLATE: "subitem"$expected5$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Nested sections - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Nested sections - FAILED', total_tests;
END IF;
-- Test 6: Include tag
total_tests := total_tests + 1;
result := test_template_parse(
$hemar6${{ include template_name }}$hemar6$,
$expected6$INCLUDE: "template_name"$expected6$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Include tag - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Include tag - FAILED', total_tests;
END IF;
-- Test 7: Execute tag
total_tests := total_tests + 1;
result := test_template_parse(
$hemar7${{ exec RETURN my_function(arg1, arg2) }}$hemar7$,
$expected7$EXECUTE: "RETURN my_function(arg1, arg2)"$expected7$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Execute tag - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Execute tag - FAILED', total_tests;
END IF;
-- Test 8: Complex mixed template
total_tests := total_tests + 1;
result := test_template_parse(
$hemar8$<div>{{ for item in items }}<p>{{ item.name }}</p>{{ include item.template }}{{ end }}</div>$hemar8$,
$expected8$TEXT: "<div>"
SECTION: iterator="item", collection="items"
TEXT: "<p>"
INTERPOLATE: "item.name"
TEXT: "</p>"
INCLUDE: "item.template"
TEXT: "</div>"$expected8$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Complex mixed template - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Complex mixed template - FAILED', total_tests;
END IF;
-- Test 9: Execute tag with complex SQL
total_tests := total_tests + 1;
result := test_template_parse(
'{{ exec SELECT 123 AS number; }}',
'EXECUTE: "SELECT 123 AS number;"'
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Execute tag with complex SQL - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Execute tag with complex SQL - FAILED', total_tests;
END IF;
-- Test 10: Whitespace handling
total_tests := total_tests + 1;
result := test_template_parse(
$hemar10${{ spaced_var }}$hemar10$,
$expected10$INTERPOLATE: "spaced_var"$expected10$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Whitespace handling - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Whitespace handling - FAILED', total_tests;
END IF;
-- Test 11: Multiple consecutive tags
total_tests := total_tests + 1;
result := test_template_parse(
$hemar11${{ var1 }}{{ var2 }}{{ var3 }}$hemar11$,
$expected11$INTERPOLATE: "var1"
INTERPOLATE: "var2"
INTERPOLATE: "var3"$expected11$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Multiple consecutive tags - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Multiple consecutive tags - FAILED', total_tests;
END IF;
-- Test 12: Section with multiple nested elements
total_tests := total_tests + 1;
result := test_template_parse(
$hemar12${{ for item in items }}
<h2>{{ item.title }}</h2>
<p>{{ item.description }}</p>
{{ include item.footer }}
{{ end }}$hemar12$,
$expected12$SECTION: iterator="item", collection="items"
TEXT: "
<h2>"
INTERPOLATE: "item.title"
TEXT: "</h2>
<p>"
INTERPOLATE: "item.description"
TEXT: "</p>
"
INCLUDE: "item.footer"
TEXT: "
"$expected12$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Section with multiple nested elements - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Section with multiple nested elements - FAILED', total_tests;
END IF;
-- Test 13: Empty template
total_tests := total_tests + 1;
result := test_template_parse(
'',
'TEXT: ""'
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Empty template - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Empty template - FAILED', total_tests;
END IF;
-- Test 14: Just text, no tags
total_tests := total_tests + 1;
result := test_template_parse(
$hemar14$Just plain text, no tags here.$hemar14$,
$expected14$TEXT: "Just plain text, no tags here."$expected14$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Just text, no tags - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Just text, no tags - FAILED', total_tests;
END IF;
-- Test 15: Complex example from documentation
total_tests := total_tests + 1;
result := test_template_parse(
$template15$<div>text before<div>
{{ include inner_template }}
{{ name }}
{{ for item in array }}
some text: {{ name2 }}
{{ item.name }}
{{ end }}
<div>code insertion:</div>
{{ exec
context + '{"name3": "zalupa"}';
IF context->condition THEN
RAISE INFO 'some log';
RETURN 'some text';
END
RETURN 'some other text';
}}
<div id="footer">...</div>$template15$,
$expected15$Template parsed successfully. Structure:
TEXT: "<div>text before<div>
"
INCLUDE: "inner_template"
TEXT: "
"
INTERPOLATE: "name"
TEXT: "
"
SECTION: iterator="item", collection="array"
TEXT: "
some text: "
INTERPOLATE: "name2"
TEXT: "
"
INTERPOLATE: "item.name"
TEXT: "
"
TEXT: "
<div>code insertion:</div>
"
EXECUTE: "context + '{"name3": "zalupa"}';
IF context->condition THEN
RAISE INFO 'some log';
RETURN 'some text';
END
RETURN 'some other text';"
TEXT: "
<div id="footer">...</div>"$expected15$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Complex example from documentation - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Complex example from documentation - FAILED', total_tests;
END IF;
-- Test 16: Multiple nested sections
total_tests := total_tests + 1;
result := test_template_parse(
'{{ for a in items }}
{{ for b in a.items }}
{{ for c in b.items }}
{{ c.name }}
{{ end }}
{{ end }}
{{ end }}',
'SECTION: iterator="a", collection="items"
TEXT: "
"
SECTION: iterator="b", collection="a.items"
TEXT: "
"
SECTION: iterator="c", collection="b.items"
TEXT: "
"
INTERPOLATE: "c.name"
TEXT: "
"'
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Multiple nested sections - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Multiple nested sections - FAILED', total_tests;
END IF;
-- Test 17: Interpolation with special characters
total_tests := total_tests + 1;
result := test_template_parse(
'{{ special@field }}',
'INTERPOLATE: "special@field"'
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Interpolation with special characters - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Interpolation with special characters - FAILED', total_tests;
END IF;
-- Test 18: Section with complex iterator and collection names
total_tests := total_tests + 1;
result := test_template_parse(
'{{ for complex_item.with.dots in complex_collection[0].items }}{{ end }}',
'SECTION: iterator="complex_item.with.dots", collection="complex_collection[0].items"'
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Section with complex iterator and collection names - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Section with complex iterator and collection names - FAILED', total_tests;
END IF;
-- Test 19: Include with complex path
total_tests := total_tests + 1;
result := test_template_parse(
'{{ include templates[0].nested.path }}',
'INCLUDE: "templates[0].nested.path"'
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Include with complex path - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Include with complex path - FAILED', total_tests;
END IF;
-- Test 20: Execute with complex SQL and quotes
total_tests := total_tests + 1;
result := test_template_parse(
$template20$
{{ exec SELECT 'text with "double" quotes' AS result; }}
$template20$,
$expected20$EXECUTE: "SELECT 'text with "double" quotes' AS result;"$expected20$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Execute with complex SQL and quotes - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Execute with complex SQL and quotes - FAILED', total_tests;
END IF;
-- Test 21: Execute tag with braces inside SQL code
total_tests := total_tests + 1;
result := test_template_parse(
$template21${{ exec
-- SQL with curly braces in string literals and comments
/* Comment with {{ braces }} inside */
SELECT
'{{ This is inside a string literal }}' AS braced_text,
$str$String with {{ and }} inside$str$ AS dollar_quoted,
regexp_replace('test', 'e(.)t', 'a$1z') AS regex_with_curly;
}}$template21$,
$expected21$EXECUTE: "-- SQL with curly braces in string literals and comments
/* Comment with {{ braces }} inside */
SELECT
'{{ This is inside a string literal }}' AS braced_text,
$str$String with {{ and }} inside$str$ AS dollar_quoted,
regexp_replace('test', 'e(.)t', 'a$1z') AS regex_with_curly;"$expected21$
);
IF result THEN
passed_tests := passed_tests + 1;
RAISE NOTICE 'Test %: Execute tag with braces inside SQL code - PASSED', total_tests;
ELSE
RAISE WARNING 'Test %: Execute tag with braces inside SQL code - FAILED', total_tests;
END IF;
-- Print summary
IF passed_tests = total_tests THEN
RAISE NOTICE '------------------------------------';
RAISE NOTICE 'SUMMARY: % of % tests passed (100%%)',
passed_tests, total_tests;
RAISE NOTICE '------------------------------------';
ELSE
RAISE WARNING '------------------------------------';
RAISE WARNING 'SUMMARY: % of % tests passed (%)',
passed_tests,
total_tests,
round((passed_tests::numeric / total_tests::numeric) * 100, 2) || '%';
RAISE WARNING '------------------------------------';
END IF;
IF passed_tests != total_tests THEN
RAISE EXCEPTION 'Tests failed: % of % tests did not pass', (total_tests - passed_tests), total_tests;
END IF;
END $$;