diff --git a/package/c/hemar/hemar.c b/package/c/hemar/hemar.c index 5a93276..2510424 100755 --- a/package/c/hemar/hemar.c +++ b/package/c/hemar/hemar.c @@ -1393,8 +1393,8 @@ render_template(TemplateNode *node, Jsonb *define, StringInfo result, MemoryCont while ((token = JsonbIteratorNext(&it, &v, true)) != WJB_END_ARRAY) { /* Create a new context with the current element */ - JsonbValue *new_context = palloc(sizeof(JsonbValue)); - new_context->type = jbvObject; + JsonbParseState *parse_state = NULL; + JsonbValue *res = pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); /* Copy the original context */ JsonbIterator *ctx_it = JsonbIteratorInit(&define->root); @@ -1406,16 +1406,12 @@ render_template(TemplateNode *node, Jsonb *define, StringInfo result, MemoryCont if (ctx_token == WJB_KEY) { /* Add the key */ - JsonbValue key_val; - key_val.type = jbvString; - key_val.val.string.val = pstrdup(ctx_v.val.string.val); - key_val.val.string.len = ctx_v.val.string.len; - JsonbValueToJsonb(&key_val); + pushJsonbValue(&parse_state, WJB_KEY, &ctx_v); } else if (ctx_token == WJB_VALUE) { /* Add the value */ - JsonbValueToJsonb(&ctx_v); + pushJsonbValue(&parse_state, WJB_VALUE, &ctx_v); } } @@ -1424,14 +1420,20 @@ render_template(TemplateNode *node, Jsonb *define, StringInfo result, MemoryCont key_val.type = jbvString; key_val.val.string.val = pstrdup(current->value->section.iterator); key_val.val.string.len = strlen(current->value->section.iterator); - JsonbValueToJsonb(&key_val); - JsonbValueToJsonb(&v); + pushJsonbValue(&parse_state, WJB_KEY, &key_val); + pushJsonbValue(&parse_state, WJB_VALUE, &v); + + /* Finish the object */ + res = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); + + /* Convert to Jsonb */ + Jsonb *context_jsonb = JsonbValueToJsonb(res); /* Render the section body with the new context */ - render_template(current->value->section.body, JsonbValueToJsonb(new_context), result, context); + render_template(current->value->section.body, context_jsonb, result, context); /* Free the temporary values */ - pfree(new_context); + pfree(key_val.val.string.val); index++; } } @@ -1443,8 +1445,8 @@ render_template(TemplateNode *node, Jsonb *define, StringInfo result, MemoryCont if (token == WJB_KEY) { /* Create a new context with the current key-value pair */ - JsonbValue *new_context = palloc(sizeof(JsonbValue)); - new_context->type = jbvObject; + JsonbParseState *parse_state = NULL; + JsonbValue *res = pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); /* Copy the original context */ JsonbIterator *ctx_it = JsonbIteratorInit(&define->root); @@ -1456,38 +1458,58 @@ render_template(TemplateNode *node, Jsonb *define, StringInfo result, MemoryCont if (ctx_token == WJB_KEY) { /* Add the key */ - JsonbValue key_val; - key_val.type = jbvString; - key_val.val.string.val = pstrdup(ctx_v.val.string.val); - key_val.val.string.len = ctx_v.val.string.len; - JsonbValueToJsonb(&key_val); + pushJsonbValue(&parse_state, WJB_KEY, &ctx_v); } else if (ctx_token == WJB_VALUE) { /* Add the value */ - JsonbValueToJsonb(&ctx_v); + pushJsonbValue(&parse_state, WJB_VALUE, &ctx_v); } } - /* Add the iterator value (key) */ + /* Add the iterator object with key and value */ JsonbValue key_val; key_val.type = jbvString; key_val.val.string.val = pstrdup(current->value->section.iterator); key_val.val.string.len = strlen(current->value->section.iterator); - JsonbValueToJsonb(&key_val); - JsonbValueToJsonb(&v); + pushJsonbValue(&parse_state, WJB_KEY, &key_val); + + /* Create an object for the iterator */ + JsonbParseState *item_parse_state = NULL; + JsonbValue *item_res = pushJsonbValue(&item_parse_state, WJB_BEGIN_OBJECT, NULL); + + /* Add the key */ + key_val.val.string.val = pstrdup("key"); + key_val.val.string.len = strlen("key"); + pushJsonbValue(&item_parse_state, WJB_KEY, &key_val); + pushJsonbValue(&item_parse_state, WJB_VALUE, &v); /* Get the value */ token = JsonbIteratorNext(&it, &v, true); /* Add the value */ - JsonbValueToJsonb(&v); + key_val.val.string.val = pstrdup("value"); + key_val.val.string.len = strlen("value"); + pushJsonbValue(&item_parse_state, WJB_KEY, &key_val); + pushJsonbValue(&item_parse_state, WJB_VALUE, &v); + + /* Finish the iterator object */ + item_res = pushJsonbValue(&item_parse_state, WJB_END_OBJECT, NULL); + + /* Add the iterator object to the context */ + pushJsonbValue(&parse_state, WJB_VALUE, item_res); + + /* Finish the context object */ + res = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); + + /* Convert to Jsonb */ + Jsonb *context_jsonb = JsonbValueToJsonb(res); /* Render the section body with the new context */ - render_template(current->value->section.body, JsonbValueToJsonb(new_context), result, context); + render_template(current->value->section.body, context_jsonb, result, context); /* Free the temporary values */ - pfree(new_context); + pfree(key_val.val.string.val); } } } diff --git a/package/c/hemar/test/test_render_interpolate.sql b/package/c/hemar/test/test_render_interpolate.sql index 129e07e..869b5d8 100755 --- a/package/c/hemar/test/test_render_interpolate.sql +++ b/package/c/hemar/test/test_render_interpolate.sql @@ -1,190 +1,190 @@ --- Test the render function with interpolation tags -CREATE EXTENSION IF NOT EXISTS hemar; - -DO $$ -DECLARE - total_tests INT := 0; - passed_tests INT := 0; - test_result TEXT; - expected TEXT; - passed BOOLEAN; -BEGIN - -- Test 1: Simple string interpolation - total_tests := total_tests + 1; - test_result := hemar.render( - '{"name": "John"}'::jsonb, - 'Hello {{ name }}!' - ); - expected := 'Hello John!'; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Simple string interpolation: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Simple string interpolation: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 2: Numeric interpolation - total_tests := total_tests + 1; - test_result := hemar.render( - '{"age": 30, "price": 19.99}'::jsonb, - 'Age: {{ age }}, Price: {{ price }}' - ); - expected := 'Age: 30, Price: 19.99'; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Numeric interpolation: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Numeric interpolation: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 3: Boolean interpolation - total_tests := total_tests + 1; - test_result := hemar.render( - '{"is_active": true, "is_deleted": false}'::jsonb, - 'Status: {{ is_active }}, Deleted: {{ is_deleted }}' - ); - expected := 'Status: true, Deleted: false'; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Boolean interpolation: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Boolean interpolation: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 4: Null value interpolation - total_tests := total_tests + 1; - test_result := hemar.render( - '{"name": null}'::jsonb, - 'Name: {{ name }}' - ); - expected := 'Name: '; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Null value interpolation: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Null value interpolation: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 5: Nested object interpolation - total_tests := total_tests + 1; - test_result := hemar.render( - '{"user": {"profile": {"name": "John", "age": 30}}}'::jsonb, - 'User: {{ user.profile.name }}, Age: {{ user.profile.age }}' - ); - expected := 'User: John, Age: 30'; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Nested object interpolation: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Nested object interpolation: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 6: Array interpolation - total_tests := total_tests + 1; - test_result := hemar.render( - '{"numbers": [1, 2, 3], "names": ["John", "Jane"]}'::jsonb, - 'Numbers: {{ numbers }}, Names: {{ names }}' - ); - expected := 'Numbers: [1, 2, 3], Names: ["John", "Jane"]'; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Array interpolation: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Array interpolation: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 7: Array index interpolation - total_tests := total_tests + 1; - test_result := hemar.render( - '{"items": [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]}'::jsonb, - 'First item: {{ items[0].name }}, Second item: {{ items[1].name }}' - ); - expected := 'First item: Item 1, Second item: Item 2'; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Array index interpolation: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Array index interpolation: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 8: Complex nested structure interpolation - total_tests := total_tests + 1; - test_result := hemar.render( - '{"company": {"name": "Tech Corp", "employees": [{"name": "John", "role": "Developer"}, {"name": "Jane", "role": "Manager"}]}}'::jsonb, - 'Company: {{ company.name }}, First employee: {{ company.employees[0].name }} ({{ company.employees[0].role }})' - ); - expected := 'Company: Tech Corp, First employee: John (Developer)'; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Complex nested structure interpolation: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Complex nested structure interpolation: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 9: Multiple interpolations in text - total_tests := total_tests + 1; - test_result := hemar.render( - '{"greeting": "Hello", "name": "John", "punctuation": "!"}'::jsonb, - '{{ greeting }} {{ name }}{{ punctuation }} How are you {{ name }}?' - ); - expected := 'Hello John! How are you John?'; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Multiple interpolations in text: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Multiple interpolations in text: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Test 10: Invalid path handling - total_tests := total_tests + 1; - test_result := hemar.render( - '{"name": "John"}'::jsonb, - 'Name: {{ name }}, Age: {{ age }}, Address: {{ address.street }}' - ); - expected := 'Name: John, Age: , Address: '; - passed := test_result = expected; - passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); - IF passed THEN - RAISE NOTICE 'Test %: Invalid path handling: PASSED', total_tests; - ELSE - RAISE WARNING 'Test %: Invalid path handling: FAILED. Expected "%", got "%"', - total_tests, expected, test_result; - END IF; - - -- Print summary - IF passed_tests = total_tests THEN - RAISE NOTICE '------------------------------------'; - RAISE NOTICE 'SUMMARY: % of % interpolation render tests passed (100%%)', - passed_tests, total_tests; - RAISE NOTICE '------------------------------------'; - ELSE - RAISE WARNING '------------------------------------'; - RAISE WARNING 'SUMMARY: % of % interpolation render 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 % interpolation render tests did not pass', (total_tests - passed_tests), total_tests; - END IF; +-- Test the render function with interpolation tags +CREATE EXTENSION IF NOT EXISTS hemar; + +DO $$ +DECLARE + total_tests INT := 0; + passed_tests INT := 0; + test_result TEXT; + expected TEXT; + passed BOOLEAN; +BEGIN + -- Test 1: Simple string interpolation + total_tests := total_tests + 1; + test_result := hemar.render( + '{"name": "John"}'::jsonb, + 'Hello {{ name }}!' + ); + expected := 'Hello John!'; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Simple string interpolation: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Simple string interpolation: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 2: Numeric interpolation + total_tests := total_tests + 1; + test_result := hemar.render( + '{"age": 30, "price": 19.99}'::jsonb, + 'Age: {{ age }}, Price: {{ price }}' + ); + expected := 'Age: 30, Price: 19.99'; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Numeric interpolation: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Numeric interpolation: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 3: Boolean interpolation + total_tests := total_tests + 1; + test_result := hemar.render( + '{"is_active": true, "is_deleted": false}'::jsonb, + 'Status: {{ is_active }}, Deleted: {{ is_deleted }}' + ); + expected := 'Status: true, Deleted: false'; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Boolean interpolation: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Boolean interpolation: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 4: Null value interpolation + total_tests := total_tests + 1; + test_result := hemar.render( + '{"name": null}'::jsonb, + 'Name: {{ name }}' + ); + expected := 'Name: '; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Null value interpolation: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Null value interpolation: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 5: Nested object interpolation + total_tests := total_tests + 1; + test_result := hemar.render( + '{"user": {"profile": {"name": "John", "age": 30}}}'::jsonb, + 'User: {{ user.profile.name }}, Age: {{ user.profile.age }}' + ); + expected := 'User: John, Age: 30'; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Nested object interpolation: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Nested object interpolation: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 6: Array interpolation + total_tests := total_tests + 1; + test_result := hemar.render( + '{"numbers": [1, 2, 3], "names": ["John", "Jane"]}'::jsonb, + 'Numbers: {{ numbers }}, Names: {{ names }}' + ); + expected := 'Numbers: [1, 2, 3], Names: ["John", "Jane"]'; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Array interpolation: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Array interpolation: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 7: Array index interpolation + total_tests := total_tests + 1; + test_result := hemar.render( + '{"items": [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]}'::jsonb, + 'First item: {{ items[0].name }}, Second item: {{ items[1].name }}' + ); + expected := 'First item: Item 1, Second item: Item 2'; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Array index interpolation: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Array index interpolation: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 8: Complex nested structure interpolation + total_tests := total_tests + 1; + test_result := hemar.render( + '{"company": {"name": "Tech Corp", "employees": [{"name": "John", "role": "Developer"}, {"name": "Jane", "role": "Manager"}]}}'::jsonb, + 'Company: {{ company.name }}, First employee: {{ company.employees[0].name }} ({{ company.employees[0].role }})' + ); + expected := 'Company: Tech Corp, First employee: John (Developer)'; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Complex nested structure interpolation: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Complex nested structure interpolation: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 9: Multiple interpolations in text + total_tests := total_tests + 1; + test_result := hemar.render( + '{"greeting": "Hello", "name": "John", "punctuation": "!"}'::jsonb, + '{{ greeting }} {{ name }}{{ punctuation }} How are you {{ name }}?' + ); + expected := 'Hello John! How are you John?'; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Multiple interpolations in text: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Multiple interpolations in text: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Test 10: Invalid path handling + total_tests := total_tests + 1; + test_result := hemar.render( + '{"name": "John"}'::jsonb, + 'Name: {{ name }}, Age: {{ age }}, Address: {{ address.street }}' + ); + expected := 'Name: John, Age: , Address: '; + passed := test_result = expected; + passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END); + IF passed THEN + RAISE NOTICE 'Test %: Invalid path handling: PASSED', total_tests; + ELSE + RAISE WARNING 'Test %: Invalid path handling: FAILED. Expected "%", got "%"', + total_tests, expected, test_result; + END IF; + + -- Print summary + IF passed_tests = total_tests THEN + RAISE NOTICE '------------------------------------'; + RAISE NOTICE 'SUMMARY: % of % interpolation render tests passed (100%%)', + passed_tests, total_tests; + RAISE NOTICE '------------------------------------'; + ELSE + RAISE WARNING '------------------------------------'; + RAISE WARNING 'SUMMARY: % of % interpolation render 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 % interpolation render tests did not pass', (total_tests - passed_tests), total_tests; + END IF; END $$; \ No newline at end of file diff --git a/package/c/hemar/test/test_render_section.sql b/package/c/hemar/test/test_render_section.sql index 42fe3d1..49a4dd2 100755 --- a/package/c/hemar/test/test_render_section.sql +++ b/package/c/hemar/test/test_render_section.sql @@ -1,98 +1,209 @@ --- Test section rendering -DO $$ -DECLARE - test_result text; - expected text; -BEGIN - -- Test 1: String iteration - test_result := hemar.render( - '{"text": "Hello"}'::jsonb, - '{{for char in text}}{{char}}{{end}}' - ); - expected := 'Hello'; - ASSERT test_result = expected, format('Test 1: String iteration: FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 1: String iteration: PASSED'; - - -- Test 2: Array iteration - test_result := hemar.render( - '{"numbers": [1, 2, 3]}'::jsonb, - '{{for num in numbers}}{{num}}{{end}}' - ); - expected := '123'; - ASSERT test_result = expected, format('Test 2: Array iteration: FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 2: Array iteration: PASSED'; - - -- Test 3: Object iteration - test_result := hemar.render( - '{"user": {"name": "John", "age": 30}}'::jsonb, - '{{for item in user}}{{item.key}}: {{item.value}}{{end}}' - ); - expected := 'name: Johnage: 30'; - ASSERT test_result = expected, format('Test 3: Object iteration: FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 3: Object iteration: PASSED'; - - -- Test 4: Boolean condition (true) - test_result := hemar.render( - '{"show": true}'::jsonb, - '{{for show in show}}Content{{end}}' - ); - expected := 'Content'; - ASSERT test_result = expected, format('Test 4: Boolean condition (true): FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 4: Boolean condition (true): PASSED'; - - -- Test 5: Boolean condition (false) - test_result := hemar.render( - '{"show": false}'::jsonb, - '{{for show in show}}Content{{end}}' - ); - expected := ''; - ASSERT test_result = expected, format('Test 5: Boolean condition (false): FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 5: Boolean condition (false): PASSED'; - - -- Test 6: Nested sections - test_result := hemar.render( - '{"items": [{"name": "Item 1", "tags": ["tag1", "tag2"]}, {"name": "Item 2", "tags": ["tag3"]}]}'::jsonb, - '{{for item in items}}{{item.name}}: {{for tag in item.tags}}{{tag}} {{end}}{{end}}' - ); - expected := 'Item 1: tag1 tag2 Item 2: tag3 '; - ASSERT test_result = expected, format('Test 6: Nested sections: FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 6: Nested sections: PASSED'; - - -- Test 7: Section with context - test_result := hemar.render( - '{"items": ["a", "b"], "prefix": "Item: "}'::jsonb, - '{{for item in items}}{{prefix}}{{item}}{{end}}' - ); - expected := 'Item: aItem: b'; - ASSERT test_result = expected, format('Test 7: Section with context: FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 7: Section with context: PASSED'; - - -- Test 8: Empty array - test_result := hemar.render( - '{"items": []}'::jsonb, - '{{for item in items}}{{item}}{{end}}' - ); - expected := ''; - ASSERT test_result = expected, format('Test 8: Empty array: FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 8: Empty array: PASSED'; - - -- Test 9: Empty object - test_result := hemar.render( - '{"user": {}}'::jsonb, - '{{for key in user}}{{key}}{{end}}' - ); - expected := ''; - ASSERT test_result = expected, format('Test 9: Empty object: FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 9: Empty object: PASSED'; - - -- Test 10: Invalid collection type (number) - test_result := hemar.render( - '{"number": 42}'::jsonb, - '{{for item in number}}{{item}}{{end}}' - ); - expected := ''; - ASSERT test_result = expected, format('Test 10: Invalid collection type: FAILED. Expected "%s", got "%s"', expected, test_result); - RAISE NOTICE 'Test 10: Invalid collection type: PASSED (error raised as expected)'; - - RAISE NOTICE 'All section rendering tests completed successfully!'; -END $$; \ No newline at end of file +-- Test section rendering +DO $$ +DECLARE + test_result text; + expected text; + total_tests integer := 0; + passed_tests integer := 0; +BEGIN + -- Test 1: String iteration + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"text": "Hello"}'::jsonb, + '{{for char in text}}{{char}}{{end}}' + ); + expected := 'Hello'; + IF test_result = expected THEN + RAISE NOTICE 'Test %: String iteration: PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: String iteration: FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: String iteration: FAILED with error: %', total_tests, SQLERRM; + END; + + -- Test 2: Array iteration + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"numbers": [1, 2, 3]}'::jsonb, + '{{for num in numbers}}{{num}}{{end}}' + ); + expected := '123'; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Array iteration: PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Array iteration: FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Array iteration: FAILED with error: %', total_tests, SQLERRM; + END; + + -- Test 3: Object iteration + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"user": {"name": "John", "age": 30}}'::jsonb, + '{{for item in user}}{{item.key}}: {{item.value}}{{end}}' + ); + expected := 'age: 30name: John'; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Object iteration: PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Object iteration: FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Object iteration: FAILED with error: %', total_tests, SQLERRM; + END; + + -- Test 4: Boolean condition (true) + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"show": true}'::jsonb, + '{{for show in show}}Content{{end}}' + ); + expected := 'Content'; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Boolean condition (true): PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Boolean condition (true): FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Boolean condition (true): FAILED with error: %', total_tests, SQLERRM; + END; + + -- Test 5: Boolean condition (false) + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"show": false}'::jsonb, + '{{for show in show}}Content{{end}}' + ); + expected := ''; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Boolean condition (false): PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Boolean condition (false): FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Boolean condition (false): FAILED with error: %', total_tests, SQLERRM; + END; + + + -- Test 6: Nested sections + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"items": [{"name": "Item 1", "tags": ["tag1", "tag2"]}, {"name": "Item 2", "tags": ["tag3"]}]}'::jsonb, + '{{for item in items}}{{item.name}}: {{for tag in item.tags}}{{tag}} {{end}}{{end}}' + ); + expected := 'Item 1: tag1 tag2 Item 2: tag3 '; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Nested sections: PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Nested sections: FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Nested sections: FAILED with error: %', total_tests, SQLERRM; + END; + + -- Test 7: Section with context + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"items": ["a", "b"], "prefix": "Item: "}'::jsonb, + '{{for item in items}}{{prefix}}{{item}}{{end}}' + ); + expected := 'Item: aItem: b'; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Section with context: PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Section with context: FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Section with context: FAILED with error: %', total_tests, SQLERRM; + END; + + -- Test 8: Empty array + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"items": []}'::jsonb, + '{{for item in items}}{{item}}{{end}}' + ); + expected := ''; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Empty array: PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Empty array: FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Empty array: FAILED with error: %', total_tests, SQLERRM; + END; + + -- Test 9: Empty object + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"user": {}}'::jsonb, + '{{for key in user}}{{key}}{{end}}' + ); + expected := ''; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Empty object: PASSED', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Empty object: FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Empty object: FAILED with error: %', total_tests, SQLERRM; + END; + + -- Test 10: Invalid collection type (number) + total_tests := total_tests + 1; + BEGIN + test_result := hemar.render( + '{"number": 42}'::jsonb, + '{{for item in number}}{{item}}{{end}}' + ); + expected := ''; + IF test_result = expected THEN + RAISE NOTICE 'Test %: Invalid collection type: PASSED (error raised as expected)', total_tests; + passed_tests := passed_tests + 1; + ELSE + RAISE WARNING 'Test %: Invalid collection type: FAILED. Expected "%", got "%"', total_tests, expected, test_result; + END IF; + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'Test %: Invalid collection type: FAILED with error: %', total_tests, SQLERRM; + END; + + + -- Print summary + IF passed_tests = total_tests THEN + RAISE NOTICE '------------------------------------'; + RAISE NOTICE 'SUMMARY: % of % template section tests passed (100%%)', + passed_tests, total_tests; + RAISE NOTICE '------------------------------------'; + ELSE + RAISE WARNING '------------------------------------'; + RAISE WARNING 'SUMMARY: % of % template section 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 % template section tests did not pass', (total_tests - passed_tests), total_tests; + END IF; +END $$; \ No newline at end of file