refactor: hemar reboot
This commit is contained in:
8
package/c/hemar-legacy/.gitignore
vendored
Executable file
8
package/c/hemar-legacy/.gitignore
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
<<<<<<< HEAD:package/c/hemar/.gitignore
|
||||
hemar.o
|
||||
hemar.so
|
||||
=======
|
||||
package/c/postgreact/postgreact.control
|
||||
package/c/postgreact/postgreact.o
|
||||
package/c/postgreact/postgreact.so
|
||||
>>>>>>> 016db3d06ae814e0f0cc8f39cd4e5af729bb39ac:package/c/postgreact/.gitignore
|
||||
16
package/c/hemar-legacy/Makefile
Normal file
16
package/c/hemar-legacy/Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
MODULE_big = hemar
|
||||
OBJS = hemar.o
|
||||
EXTENSION = hemar
|
||||
|
||||
DATA = $(wildcard *.sql)
|
||||
|
||||
HECTIC_CONFIG = hectic-config
|
||||
PG_CONFIG = pg_config
|
||||
|
||||
PG_CFLAGS += $(shell $(HECTIC_CONFIG) --cflags)
|
||||
PG_LDFLAGS += -Wl,-rpath,$(shell $(HECTIC_CONFIG) --libdir)
|
||||
SHLIB_LINK += $(shell $(HECTIC_CONFIG) --libs)
|
||||
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
|
||||
include $(PGXS)
|
||||
17
package/c/hemar-legacy/default.nix
Executable file
17
package/c/hemar-legacy/default.nix
Executable file
@@ -0,0 +1,17 @@
|
||||
{ postgresql, pkg-config, patchelf }:
|
||||
buildPostgresqlExtension { inherit postgresql; } {
|
||||
pname = "hemar";
|
||||
version = "0.1";
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = [pkg-config c-hectic];
|
||||
|
||||
dontShrinkRPath = true;
|
||||
|
||||
postFixup = ''
|
||||
echo ">>> postFixup running..."
|
||||
${patchelf}/bin/patchelf --set-rpath ${c-hectic}/lib $out/lib/hemar.so
|
||||
'';
|
||||
|
||||
preInstall = ''mkdir $out'';
|
||||
};
|
||||
21
package/c/hemar-legacy/hemar--0.1.sql
Executable file
21
package/c/hemar-legacy/hemar--0.1.sql
Executable file
@@ -0,0 +1,21 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION hemar" to load this file. \quit
|
||||
|
||||
CREATE SCHEMA hemar;
|
||||
-- Parse function returns the structure of a template for debugging
|
||||
CREATE FUNCTION "hemar"."parse"("template" text)
|
||||
RETURNS text
|
||||
LANGUAGE C STRICT
|
||||
AS 'hemar', 'pg_template_parse';
|
||||
|
||||
-- JSONB path access function
|
||||
CREATE FUNCTION "hemar"."jsonb_get_by_path"("json" jsonb, "path" text)
|
||||
RETURNS jsonb
|
||||
LANGUAGE C STRICT
|
||||
AS 'hemar', 'pg_jsonb_get_by_path';
|
||||
|
||||
-- Template rendering function
|
||||
CREATE FUNCTION "hemar"."render"("define" jsonb, "template" text)
|
||||
RETURNS text
|
||||
LANGUAGE C STRICT
|
||||
AS 'hemar', 'pg_template_render';
|
||||
2612
package/c/hemar-legacy/hemar.c
Executable file
2612
package/c/hemar-legacy/hemar.c
Executable file
File diff suppressed because it is too large
Load Diff
3
package/c/hemar-legacy/hemar.control
Executable file
3
package/c/hemar-legacy/hemar.control
Executable file
@@ -0,0 +1,3 @@
|
||||
comment = 'My first extension'
|
||||
default_version = '0.1'
|
||||
module_pathname = '$libdir/hemar'
|
||||
116
package/c/hemar-legacy/hemar.h
Executable file
116
package/c/hemar-legacy/hemar.h
Executable file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* hemar.h
|
||||
* Template parser for Hemar
|
||||
*/
|
||||
#ifndef HEMAR_TEMPLATE_H
|
||||
#define HEMAR_TEMPLATE_H
|
||||
|
||||
#include "postgres.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
/* Maximum length for template syntax elements */
|
||||
#define TEMPLATE_MAX_PREFIX_LEN 32
|
||||
|
||||
/* Template error codes */
|
||||
typedef enum {
|
||||
TEMPLATE_ERROR_NONE = 0,
|
||||
TEMPLATE_ERROR_UNKNOWN_TAG,
|
||||
TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_CONTROLE,
|
||||
TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_SOURCE,
|
||||
TEMPLATE_ERROR_UNEXPECTED_INTERPOLATION_END,
|
||||
TEMPLATE_ERROR_NO_SOURSE_IN_SECTION,
|
||||
TEMPLATE_ERROR_NESTED_INTERPOLATION,
|
||||
TEMPLATE_ERROR_UNEXPECTED_SECTION_END,
|
||||
TEMPLATE_ERROR_NO_BEGIN_IN_SECTION,
|
||||
TEMPLATE_ERROR_NESTED_INCLUDE,
|
||||
TEMPLATE_ERROR_NESTED_EXECUTE,
|
||||
TEMPLATE_ERROR_INVALID_CONFIG,
|
||||
TEMPLATE_ERROR_OUT_OF_MEMORY,
|
||||
TEMPLATE_ERROR_UNEXPECTED_INCLUDE_END,
|
||||
TEMPLATE_ERROR_UNEXPECTED_EXECUTE_END
|
||||
} TemplateErrorCode;
|
||||
|
||||
/* Template node types */
|
||||
typedef enum {
|
||||
TEMPLATE_NODE_TEXT,
|
||||
TEMPLATE_NODE_INTERPOLATE,
|
||||
TEMPLATE_NODE_SECTION,
|
||||
TEMPLATE_NODE_EXECUTE,
|
||||
TEMPLATE_NODE_INCLUDE
|
||||
} TemplateNodeType;
|
||||
|
||||
/* Template configuration structure */
|
||||
typedef struct {
|
||||
struct {
|
||||
struct {
|
||||
const char *open; /* Default: "{%" */
|
||||
const char *close; /* Default: "%}" */
|
||||
} Braces;
|
||||
struct {
|
||||
const char *control; /* default: "for " */
|
||||
const char *source; /* default: "in " */
|
||||
const char *begin; /* default: "do " */
|
||||
const char *end; /* default: "end" */
|
||||
} Section;
|
||||
struct {
|
||||
const char *invoke; /* default: "" */
|
||||
} Interpolate;
|
||||
struct {
|
||||
const char *invoke; /* default: "include " */
|
||||
} Include;
|
||||
struct {
|
||||
const char *invoke; /* default: "exec " */
|
||||
} Execute;
|
||||
const char *nesting; /* default: "->" */
|
||||
} Syntax;
|
||||
} TemplateConfig;
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct TemplateNode TemplateNode;
|
||||
|
||||
/* Template value structures */
|
||||
typedef struct {
|
||||
char *iterator;
|
||||
char *collection;
|
||||
TemplateNode *body;
|
||||
} TemplateSectionValue;
|
||||
|
||||
typedef struct {
|
||||
char *key;
|
||||
} TemplateInterpolateValue;
|
||||
|
||||
typedef struct {
|
||||
char *code;
|
||||
} TemplateExecuteValue;
|
||||
|
||||
typedef struct {
|
||||
char *key;
|
||||
} TemplateIncludeValue;
|
||||
|
||||
typedef struct {
|
||||
char *content;
|
||||
} TemplateTextValue;
|
||||
|
||||
typedef union {
|
||||
TemplateSectionValue section;
|
||||
TemplateInterpolateValue interpolate;
|
||||
TemplateExecuteValue execute;
|
||||
TemplateIncludeValue include;
|
||||
TemplateTextValue text;
|
||||
} TemplateValue;
|
||||
|
||||
/* Template node structure */
|
||||
struct TemplateNode {
|
||||
TemplateNodeType type;
|
||||
TemplateValue *value;
|
||||
TemplateNode *next;
|
||||
};
|
||||
|
||||
/* Function declarations */
|
||||
TemplateConfig template_default_config(MemoryContext context);
|
||||
bool template_validate_config(const TemplateConfig *config, TemplateErrorCode *error_code);
|
||||
TemplateNode *template_parse(MemoryContext context, const char **s, const TemplateConfig *config, bool inner_parse, TemplateErrorCode *error_code);
|
||||
void template_free_node(TemplateNode *node);
|
||||
const char *template_error_to_string(TemplateErrorCode code, TemplateConfig *config);
|
||||
|
||||
#endif /* HEMAR_TEMPLATE_H */
|
||||
110
package/c/hemar-legacy/make.sh
Executable file
110
package/c/hemar-legacy/make.sh
Executable file
@@ -0,0 +1,110 @@
|
||||
#!/bin/sh
|
||||
# Usage: make.sh [build|watch] [--debug] [--color]
|
||||
# Options:
|
||||
# build Build the postgres extension (default if no mode is provided).
|
||||
# watch Build the extension and watch for changes.
|
||||
# --debug Build with -O0 (debug mode).
|
||||
# --color Pass -fdiagnostics-color=always to compiler.
|
||||
# help, --help Show this help message.
|
||||
|
||||
set -u
|
||||
|
||||
check_dependencies() {
|
||||
for dep in gcc pg_config; do
|
||||
if ! command -v "$dep" >/dev/null 2>&1; then
|
||||
echo "Error: Required dependency '$dep' not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for either fswatch or inotifywait for watch mode
|
||||
if [ "$MODE" = "watch" ] && ! command -v fswatch >/dev/null 2>&1 && ! command -v inotifywait >/dev/null 2>&1; then
|
||||
echo "Error: Neither fswatch nor inotifywait found. Please install one of them." >&2
|
||||
echo " On macOS: brew install fswatch" >&2
|
||||
echo " On Linux: sudo apt install inotify-tools" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
print_help() {
|
||||
cat <<EOF
|
||||
Usage: $0 [build|watch] [--debug] [--color]
|
||||
build Build the postgres extension (default).
|
||||
watch Build the extension and watch for changes.
|
||||
--debug Build with debug flags (-O0).
|
||||
--color Force colored compiler diagnostics.
|
||||
help, --help Display this help message.
|
||||
EOF
|
||||
}
|
||||
|
||||
# Show help if requested
|
||||
case "$1" in
|
||||
help|--help)
|
||||
print_help
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Default flags
|
||||
OPTFLAGS="-O2"
|
||||
CFLAGS="-Wall -Wextra -pedantic -fPIC"
|
||||
COLOR_FLAG=""
|
||||
DEBUG=0
|
||||
|
||||
# Process options
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--debug)
|
||||
OPTFLAGS="-O0 -gdwarf-2 -g3 -Wno-error"
|
||||
DEBUG=1
|
||||
;;
|
||||
--color)
|
||||
COLOR_FLAG="-fdiagnostics-color=always"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
MODE="${1:-build}"
|
||||
shift 2> /dev/null
|
||||
|
||||
if [ -n "$COLOR_FLAG" ]; then
|
||||
CFLAGS="$CFLAGS $COLOR_FLAG"
|
||||
fi
|
||||
|
||||
check_dependencies
|
||||
|
||||
# Get PostgreSQL include directory
|
||||
PG_INCLUDE=$(pg_config --includedir-server)
|
||||
PG_LIBDIR=$(pg_config --libdir)
|
||||
|
||||
case "$MODE" in
|
||||
watch)
|
||||
find . -type d | nix run .#watch -- 'sh ./make.sh build' -p '*.c' -p '*.h' 2>&1
|
||||
;;
|
||||
build)
|
||||
mkdir -p target
|
||||
echo "# Building PostgreSQL extension"
|
||||
|
||||
# Get hectic library paths from nix
|
||||
HECTIC_PATH=$(nix build --print-out-paths -f ../../../. c-hectic)
|
||||
HECTIC_INCLUDE="$HECTIC_PATH/include"
|
||||
HECTIC_LIB="$HECTIC_PATH/lib"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
gcc $CFLAGS $OPTFLAGS -I$PG_INCLUDE -I$HECTIC_INCLUDE -shared -o target/hemar.so hemar.c -L$HECTIC_LIB -lhectic
|
||||
|
||||
# Copy extension files to target directory
|
||||
cp hemar.control target/
|
||||
cp hemar--0.1.sql target/
|
||||
|
||||
echo "Build complete. Files available in target/ directory."
|
||||
;;
|
||||
*)
|
||||
print_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
12
package/c/hemar-legacy/postgreact--1.0.sql
Normal file
12
package/c/hemar-legacy/postgreact--1.0.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION postgreact" to load this file. \quit
|
||||
|
||||
-- Define the hello function that uses our C implementation
|
||||
CREATE FUNCTION hello()
|
||||
RETURNS
|
||||
TEXT
|
||||
STRICT VOLATILE
|
||||
LANGUAGE C
|
||||
AS
|
||||
'MODULE_PATHNAME', 'hello'
|
||||
;
|
||||
4
package/c/hemar-legacy/postgreact.control.in
Normal file
4
package/c/hemar-legacy/postgreact.control.in
Normal file
@@ -0,0 +1,4 @@
|
||||
comment = '@EXTENSION_COMMENT@'
|
||||
default_version = '@EXTENSION_VERSION@'
|
||||
module_pathname = '$libdir/@EXTENSION@'
|
||||
relocatable = false
|
||||
16
package/c/hemar-legacy/postgreact.h
Normal file
16
package/c/hemar-legacy/postgreact.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef POSTGREACT_H
|
||||
#define POSTGREACT_H
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#ifdef PG_MODULE_MAGIC
|
||||
PG_MODULE_MAGIC;
|
||||
#endif
|
||||
|
||||
void _PG_init(void);
|
||||
void _PG_fini(void);
|
||||
|
||||
Datum hello(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1(hello);
|
||||
|
||||
#endif // POSTGREACT_H
|
||||
38
package/c/hemar-legacy/test/mod.sql
Executable file
38
package/c/hemar-legacy/test/mod.sql
Executable file
@@ -0,0 +1,38 @@
|
||||
BEGIN;
|
||||
CREATE OR REPLACE FUNCTION pg_temp.diff(string1 text, string2 text) RETURNS TABLE("index" int, char1 text, char2 text) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY WITH
|
||||
s1 AS (SELECT string1 AS str),
|
||||
s2 AS (SELECT string2 AS str)
|
||||
SELECT i,
|
||||
substring(s1.str FROM i FOR 1) AS char1,
|
||||
substring(s2.str FROM i FOR 1) AS char2
|
||||
FROM s1, s2,
|
||||
generate_series(1, GREATEST(length(s1.str), length(s2.str))) AS i
|
||||
WHERE substring(s1.str FROM i FOR 1) IS DISTINCT FROM substring(s2.str FROM i FOR 1);
|
||||
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION pg_temp.test_regexp_replace(string text) RETURNS text AS $$
|
||||
BEGIN
|
||||
RETURN regexp_replace(
|
||||
regexp_replace(
|
||||
regexp_replace(
|
||||
regexp_replace(
|
||||
regexp_replace(string, E'\t', '\\t', 'g'),
|
||||
E'\n', '\\n', 'g'),
|
||||
E'\r', '\\r', 'g'),
|
||||
' ', '[S]', 'g'),
|
||||
'\s', '\\s', 'g');
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
\ir test_jsonb_path.sql
|
||||
--\ir test_template_parser.sql
|
||||
\ir test_render_exec.sql
|
||||
\ir test_render_interpolate.sql
|
||||
\ir test_render_section.sql
|
||||
\ir test_render_include.sql
|
||||
--\ir test_render_all.sql
|
||||
ROLLBACK;
|
||||
516
package/c/hemar-legacy/test/test_jsonb_path.sql
Executable file
516
package/c/hemar-legacy/test/test_jsonb_path.sql
Executable file
@@ -0,0 +1,516 @@
|
||||
-- Test file for hemar.jsonb_get_by_path function
|
||||
-- Run with: psql -f test_jsonb_path.sql
|
||||
|
||||
-- Load extension if not already loaded
|
||||
-- CREATE EXTENSION IF NOT EXISTS hemar;
|
||||
|
||||
-- Create sample test data
|
||||
DO $$
|
||||
DECLARE
|
||||
test_json jsonb;
|
||||
result jsonb;
|
||||
passed boolean;
|
||||
total_tests integer := 0;
|
||||
passed_tests integer := 0;
|
||||
current_path text;
|
||||
BEGIN
|
||||
test_json := jsonb_build_object(
|
||||
'name', 'John Doe',
|
||||
'age', 30,
|
||||
'is_active', true,
|
||||
'tags', jsonb_build_array('developer', 'postgresql', 'jsonb'),
|
||||
'address', jsonb_build_object(
|
||||
'street', '123 Main St',
|
||||
'city', 'New York',
|
||||
'zip', '10001'
|
||||
),
|
||||
'contacts', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'type', 'email',
|
||||
'value', 'john@example.com'
|
||||
),
|
||||
jsonb_build_object(
|
||||
'type', 'phone',
|
||||
'value', '555-1234',
|
||||
'verified', true
|
||||
)
|
||||
),
|
||||
'skills', jsonb_build_array(
|
||||
jsonb_build_array('PostgreSQL', 5),
|
||||
jsonb_build_array('Python', 4),
|
||||
jsonb_build_array('JavaScript', 3)
|
||||
)
|
||||
);
|
||||
|
||||
-- Test basic field access
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'name';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"John Doe"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Simple field access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Simple field access (path="%"): % | PASSED: % (expected: "John Doe")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'age';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '30'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Numeric field access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Numeric field access (path="%"): % | PASSED: % (expected: 30)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'is_active';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = 'true'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Boolean field access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Boolean field access (path="%"): % | PASSED: % (expected: true)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test nested field access
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'address.city';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"New York"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Nested object field access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Nested object field access (path="%"): % | PASSED: % (expected: "New York")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test array access
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'tags[1]';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"postgresql"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Simple array access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Simple array access (path="%"): % | PASSED: % (expected: "postgresql")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'contacts[0].type';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"email"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Object in array access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Object in array access (path="%"): % | PASSED: % (expected: "email")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'skills[1][0]';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"Python"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Nested array access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Nested array access (path="%"): % | PASSED: % (expected: "Python")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'contacts[1].value';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"555-1234"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Complex path with multiple array indices (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Complex path with multiple array indices (path="%"): % | PASSED: % (expected: "555-1234")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test object and array returns
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'address';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := jsonb_typeof(result) = 'object';
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Path to object (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Path to object (path="%"): % | PASSED: % (expected type: object, got: %)',
|
||||
total_tests, current_path, result, passed, jsonb_typeof(result);
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'contacts';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := jsonb_typeof(result) = 'array';
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Path to array (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Path to array (path="%"): % | PASSED: % (expected type: array, got: %)',
|
||||
total_tests, current_path, result, passed, jsonb_typeof(result);
|
||||
END IF;
|
||||
|
||||
-- Test error cases
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'unknown_field';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result IS NULL;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Non-existent field (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Non-existent field (path="%"): % | PASSED: % (expected: NULL)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'address.country';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result IS NULL;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Non-existent nested field (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Non-existent nested field (path="%"): % | PASSED: % (expected: NULL)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'tags[10]';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result IS NULL;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Array index out of bounds (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Array index out of bounds (path="%"): % | PASSED: % (expected: NULL)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test edge cases
|
||||
total_tests := total_tests + 1;
|
||||
current_path := '';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result IS NULL;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Empty path (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Empty path (path="%"): % | PASSED: % (expected: NULL)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
total_tests := total_tests + 1;
|
||||
current_path := 'skills[0][1]';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '5'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Multiple array indices (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Multiple array indices (path="%"): % | PASSED: % (expected: 5)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Additional complex test cases
|
||||
|
||||
-- Test 16: Deep nested object access
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_object(
|
||||
'level1', jsonb_build_object(
|
||||
'level2', jsonb_build_object(
|
||||
'level3', jsonb_build_object(
|
||||
'level4', jsonb_build_object(
|
||||
'value', 'deep nested value'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
current_path := 'level1.level2.level3.level4.value';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"deep nested value"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Deep nested object access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Deep nested object access (path="%"): % | PASSED: % (expected: "deep nested value")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 17: Deep nested array access
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_array(
|
||||
jsonb_build_array(
|
||||
jsonb_build_array(
|
||||
jsonb_build_array(
|
||||
'nested array value'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
current_path := '[0][0][0][0]';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"nested array value"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Deep nested array access (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Deep nested array access (path="%"): % | PASSED: % (expected: "nested array value")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 18: Complex mixed nesting (object -> array -> object -> array)
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_object(
|
||||
'users', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'name', 'Alice',
|
||||
'permissions', jsonb_build_array('read', 'write', 'admin')
|
||||
),
|
||||
jsonb_build_object(
|
||||
'name', 'Bob',
|
||||
'permissions', jsonb_build_array('read', 'write')
|
||||
)
|
||||
)
|
||||
);
|
||||
current_path := 'users[1].permissions[0]';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"read"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Complex mixed nesting (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Complex mixed nesting (path="%"): % | PASSED: % (expected: "read")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 19: Array with mixed types
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_array(
|
||||
'string',
|
||||
42,
|
||||
true,
|
||||
jsonb_build_object('key', 'value'),
|
||||
jsonb_build_array(1, 2, 3)
|
||||
);
|
||||
current_path := '[3].key';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"value"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Array with mixed types (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Array with mixed types (path="%"): % | PASSED: % (expected: "value")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 20: Path with array at the end
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_object(
|
||||
'data', jsonb_build_object(
|
||||
'items', jsonb_build_array(10, 20, 30, 40)
|
||||
)
|
||||
);
|
||||
current_path := 'data.items[2]';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '30'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Path with array at the end (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Path with array at the end (path="%"): % | PASSED: % (expected: 30)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 21: Numeric field names
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_object(
|
||||
'123', 'numeric key',
|
||||
'456', jsonb_build_object(
|
||||
'789', 'nested numeric key'
|
||||
)
|
||||
);
|
||||
current_path := '456.789';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"nested numeric key"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Numeric field names (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Numeric field names (path="%"): % | PASSED: % (expected: "nested numeric key")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 22: Special characters in field names
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_object(
|
||||
'special@field', 'special value',
|
||||
'nested', jsonb_build_object(
|
||||
'field-with-hyphens', 'hyphenated value'
|
||||
)
|
||||
);
|
||||
current_path := 'nested.field-with-hyphens';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"hyphenated value"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Special characters in field names (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Special characters in field names (path="%"): % | PASSED: % (expected: "hyphenated value")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 23: Array of arrays of arrays
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_array(
|
||||
jsonb_build_array(
|
||||
jsonb_build_array(1, 2),
|
||||
jsonb_build_array(3, 4)
|
||||
),
|
||||
jsonb_build_array(
|
||||
jsonb_build_array(5, 6),
|
||||
jsonb_build_array(7, 8)
|
||||
)
|
||||
);
|
||||
current_path := '[1][0][1]';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '6'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Array of arrays of arrays (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Array of arrays of arrays (path="%"): % | PASSED: % (expected: 6)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 24: Complex path with multiple array indices and object fields
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_object(
|
||||
'companies', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'name', 'Company A',
|
||||
'departments', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'name', 'Engineering',
|
||||
'teams', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'name', 'Backend',
|
||||
'members', jsonb_build_array(
|
||||
jsonb_build_object('name', 'John', 'role', 'Developer'),
|
||||
jsonb_build_object('name', 'Jane', 'role', 'Lead')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
current_path := 'companies[0].departments[0].teams[0].members[1].role';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := result = '"Lead"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Very complex path (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Very complex path (path="%"): % | PASSED: % (expected: "Lead")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test 25: Empty array and object edge cases
|
||||
total_tests := total_tests + 1;
|
||||
test_json := jsonb_build_object(
|
||||
'emptyArray', jsonb_build_array(),
|
||||
'emptyObject', jsonb_build_object(),
|
||||
'arrayWithEmptyObject', jsonb_build_array(jsonb_build_object()),
|
||||
'objectWithEmptyArray', jsonb_build_object('empty', jsonb_build_array())
|
||||
);
|
||||
current_path := 'objectWithEmptyArray.empty';
|
||||
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||
passed := jsonb_typeof(result) = 'array' AND jsonb_array_length(result) = 0;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Empty array/object edge cases (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Empty array/object edge cases (path="%"): % | PASSED: % (expected: empty array)',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Test nested object path parsing
|
||||
total_tests := total_tests + 1;
|
||||
result := hemar.jsonb_get_by_path(
|
||||
'{"user": {"profile": {"name": "John", "age": 30}}}'::jsonb,
|
||||
'user.profile.name'
|
||||
);
|
||||
passed := result = '"John"'::jsonb;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Nested object path parsing (path="%"): % | PASSED: %',
|
||||
total_tests, current_path, result, passed;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Nested object path parsing (path="%"): % | PASSED: % (expected: "John")',
|
||||
total_tests, current_path, result, passed;
|
||||
END IF;
|
||||
|
||||
-- Print summary
|
||||
IF passed_tests = total_tests THEN
|
||||
RAISE NOTICE '------------------------------------';
|
||||
RAISE NOTICE 'SUMMARY: % of % jsonb_get_by_path tests passed (100%%)',
|
||||
passed_tests, total_tests;
|
||||
RAISE NOTICE '------------------------------------';
|
||||
ELSE
|
||||
RAISE WARNING '------------------------------------';
|
||||
RAISE WARNING 'SUMMARY: % of % jsonb_get_by_path 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 % jsonb_get_by_path tests did not pass', (total_tests - passed_tests), total_tests;
|
||||
END IF;
|
||||
END $$;
|
||||
261
package/c/hemar-legacy/test/test_render_all.sql
Executable file
261
package/c/hemar-legacy/test/test_render_all.sql
Executable file
@@ -0,0 +1,261 @@
|
||||
-- Test all template tags together
|
||||
CREATE OR REPLACE FUNCTION pg_temp.diff(string1 text, string2 text) RETURNS TABLE("index" int, char1 text, char2 text) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY WITH
|
||||
s1 AS (SELECT string1 AS str),
|
||||
s2 AS (SELECT string2 AS str)
|
||||
SELECT i,
|
||||
substring(s1.str FROM i FOR 1) AS char1,
|
||||
substring(s2.str FROM i FOR 1) AS char2
|
||||
FROM s1, s2,
|
||||
generate_series(1, GREATEST(length(s1.str), length(s2.str))) AS i
|
||||
WHERE substring(s1.str FROM i FOR 1) IS DISTINCT FROM substring(s2.str FROM i FOR 1);
|
||||
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION pg_temp.test_regexp_replace(string text) RETURNS text AS $$
|
||||
BEGIN
|
||||
RETURN regexp_replace(
|
||||
regexp_replace(
|
||||
regexp_replace(
|
||||
regexp_replace(
|
||||
regexp_replace(string, E'\t', '[TAB]', 'g'),
|
||||
E'\n', '[LF]', 'g'),
|
||||
E'\r', '[CR]', 'g'),
|
||||
' ', '[SPACE]', 'g'),
|
||||
'\s', '[WHITESPACE]', 'g');
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
total_tests INT := 0;
|
||||
passed_tests INT := 0;
|
||||
test_result TEXT;
|
||||
expected TEXT;
|
||||
passed BOOLEAN;
|
||||
item INT;
|
||||
c1 TEXT;
|
||||
c2 TEXT;
|
||||
BEGIN
|
||||
-- Test 1: Template with execute tag using context from section
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{
|
||||
"items": [
|
||||
{"id": 1, "value": 100},
|
||||
{"id": 2, "value": 200},
|
||||
{"id": 3, "value": 300}
|
||||
]
|
||||
}'::jsonb,
|
||||
$template$Items:
|
||||
{{ for item in items }}
|
||||
Item {{ item.id }}: {{ exec RETURN (context->'item'->>'value')::int * 2; }}
|
||||
{{ end }}$template$
|
||||
);
|
||||
|
||||
expected:='Items:
|
||||
Item 1: 200
|
||||
Item 2: 400
|
||||
Item 3: 600
|
||||
';
|
||||
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Template with execute tag using context from section: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Template with execute tag using context from section: FAILED. Expected "%", got "%"',
|
||||
total_tests, pg_temp.test_regexp_replace(expected), pg_temp.test_regexp_replace(test_result);
|
||||
FOR item, c1, c2 IN
|
||||
SELECT * FROM pg_temp.diff(expected, test_result)
|
||||
LOOP
|
||||
RAISE NOTICE ' % | % | %', item, c1, c2;
|
||||
END LOOP;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test % failed: Error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 2: Complex template with all tag types
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{
|
||||
"page": {
|
||||
"title": "My Page",
|
||||
"sections": [
|
||||
{
|
||||
"id": "section1",
|
||||
"title": "Section 1",
|
||||
"items": [
|
||||
{
|
||||
"id": "item1",
|
||||
"status": "active",
|
||||
"content": "Item 1 Content",
|
||||
"template": "item_template"
|
||||
},
|
||||
{
|
||||
"id": "item2",
|
||||
"status": "inactive",
|
||||
"content": "Item 2 Content",
|
||||
"template": "item_template"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": {
|
||||
"meta_tags": {
|
||||
"content": "<meta name=\"description\" content=\"Test Page\">"
|
||||
},
|
||||
"header": {
|
||||
"template": "Welcome to {{ page.title }}!",
|
||||
"context": {
|
||||
"page": {
|
||||
"title": "My Page"
|
||||
}
|
||||
}
|
||||
},
|
||||
"item_template": {
|
||||
"template": "Status: {{ status }}, Content: {{ content }}"
|
||||
},
|
||||
"footer": {
|
||||
"content": "<footer>Copyright 2024</footer>"
|
||||
}
|
||||
}
|
||||
}'::jsonb,
|
||||
$template$<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ page.title }}</title>
|
||||
{{ include meta_tags }}
|
||||
</head>
|
||||
<body>
|
||||
<header>{{ include header }}</header>
|
||||
<main>
|
||||
{{ for section in page.sections }}
|
||||
<section id="{{ section.id }}">
|
||||
<h2>{{ section.title }}</h2>
|
||||
{{ for item in section.items }}
|
||||
<div class="item {{ item.status }}">
|
||||
{{ include item.template }}
|
||||
{{ exec
|
||||
DECLARE
|
||||
v_status TEXT;
|
||||
BEGIN
|
||||
v_status := context->'item'->>'status';
|
||||
RETURN CASE
|
||||
WHEN v_status = 'active' THEN ' (Active Item)'
|
||||
ELSE ' (Inactive Item)'
|
||||
END;
|
||||
END;
|
||||
}}
|
||||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
</main>
|
||||
<footer>{{ include footer }}</footer>
|
||||
</body>
|
||||
</html>$template$
|
||||
);
|
||||
|
||||
expected := '<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>My Page</title>
|
||||
<meta name="description" content="Test Page">
|
||||
</head>
|
||||
<body>
|
||||
<header>Welcome to My Page!</header>
|
||||
<main>
|
||||
<section id="section1">
|
||||
<h2>Section 1</h2>
|
||||
<div class="item active">
|
||||
Status: active, Content: Item 1 Content (Active Item)
|
||||
</div>
|
||||
<div class="item inactive">
|
||||
Status: inactive, Content: Item 2 Content (Inactive Item)
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer><footer>Copyright 2024</footer></footer>
|
||||
</body>
|
||||
</html>';
|
||||
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Complex template with all tag types: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Complex template with all tag types: FAILED. Expected "%", got "%"',
|
||||
total_tests, pg_temp.test_regexp_replace(expected), pg_temp.test_regexp_replace(test_result);
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test % failed: Error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 3: Template with nested includes and shared context
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{
|
||||
"user": {
|
||||
"name": "John",
|
||||
"role": "admin"
|
||||
},
|
||||
"include": {
|
||||
"user_info": {
|
||||
"template": "User: {{ user.name }} ({{ user.role }})"
|
||||
},
|
||||
"permissions": {
|
||||
"template": "{{ include user_info }} - Permissions: {{ for perm in user.permissions }}{{ perm }} {{ end }}",
|
||||
"context": {
|
||||
"user": {
|
||||
"name": "John",
|
||||
"role": "admin",
|
||||
"permissions": ["read", "write", "delete"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}'::jsonb,
|
||||
$template${{ include permissions }}$template$
|
||||
);
|
||||
|
||||
expected := 'User: John (admin) - Permissions: read write delete ';
|
||||
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Template with nested includes and shared context: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Template with nested includes and shared context: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test % failed: Error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Print summary
|
||||
IF passed_tests = total_tests THEN
|
||||
RAISE NOTICE '------------------------------------';
|
||||
RAISE NOTICE 'SUMMARY: % of % combined template tests passed (100%%)',
|
||||
passed_tests, total_tests;
|
||||
RAISE NOTICE '------------------------------------';
|
||||
ELSE
|
||||
RAISE WARNING '------------------------------------';
|
||||
RAISE WARNING 'SUMMARY: % of % combined template 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 % combined template tests did not pass', (total_tests - passed_tests), total_tests;
|
||||
END IF;
|
||||
END $$;
|
||||
343
package/c/hemar-legacy/test/test_render_exec.sql
Executable file
343
package/c/hemar-legacy/test/test_render_exec.sql
Executable file
@@ -0,0 +1,343 @@
|
||||
-- Test the render function with execute 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 execute tag that sets a variable
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"name": "John", "age": 30}'::jsonb,
|
||||
'Hello {{ exec PERFORM 1; }}'
|
||||
);
|
||||
expected := 'Hello ';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Simple execute tag: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Simple execute tag: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 2: Execute tag with context access
|
||||
total_tests := total_tests + 1;
|
||||
DROP TABLE IF EXISTS test_output;
|
||||
CREATE TEMP TABLE test_output (value TEXT);
|
||||
|
||||
test_result := hemar.render(
|
||||
'{"name": "John", "age": 30}'::jsonb,
|
||||
$expected$Hello {{ exec INSERT INTO test_output VALUES (context->'name'); }}$expected$
|
||||
);
|
||||
|
||||
SELECT value INTO expected FROM test_output;
|
||||
passed := expected = '"John"';
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with context access: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with context access: FAILED. Expected "John", got "%"',
|
||||
total_tests, expected;
|
||||
END IF;
|
||||
|
||||
-- Test 3: Execute tag with quotes and complex SQL
|
||||
total_tests := total_tests + 1;
|
||||
DROP TABLE IF EXISTS test_output;
|
||||
CREATE TEMP TABLE test_output (value TEXT);
|
||||
|
||||
test_result := hemar.render(
|
||||
'{"items": [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]}'::jsonb,
|
||||
$expected$Items: {{ exec
|
||||
INSERT INTO test_output
|
||||
SELECT jsonb_array_elements(context->'items')->>'name';
|
||||
}}$expected$
|
||||
);
|
||||
|
||||
SELECT string_agg(value, ', ' ORDER BY value) INTO expected FROM test_output;
|
||||
passed := expected = 'Item 1, Item 2';
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with complex SQL: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with complex SQL: FAILED. Expected "Item 1, Item 2", got "%"',
|
||||
total_tests, expected;
|
||||
END IF;
|
||||
|
||||
-- Test 4: Execute tag with output capture
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"name": "John", "age": 30}'::jsonb,
|
||||
$expected$Hello {{ exec RETURN context->>'name'; }}$expected$
|
||||
);
|
||||
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 %: Execute tag with output capture: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with output capture: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 5: Execute tag with complex output
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"items": [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]}'::jsonb,
|
||||
$expected$Items: {{ exec
|
||||
RETURN (SELECT string_agg(value, ', ')
|
||||
FROM (
|
||||
SELECT jsonb_array_elements(context->'items')->>'name' as value
|
||||
) t);
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Items: Item 1, Item 2';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with complex output: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with complex output: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 6: Execute tag with multiple statements
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"name": "John", "age": 30}'::jsonb,
|
||||
$expected$Hello {{ exec
|
||||
DECLARE
|
||||
v_name TEXT;
|
||||
BEGIN
|
||||
v_name := context->>'name';
|
||||
RETURN v_name;
|
||||
END;
|
||||
}}$expected$
|
||||
);
|
||||
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 %: Execute tag with multiple statements: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with multiple statements: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 7: Execute tag with array operations
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"numbers": [1, 2, 3, 4, 5]}'::jsonb,
|
||||
$expected$Sum: {{ exec
|
||||
RETURN (SELECT sum(value::int)
|
||||
FROM jsonb_array_elements_text(context->'numbers') as value);
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Sum: 15';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with array operations: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with array operations: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 8: Execute tag with nested JSON operations
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"user": {"profile": {"settings": {"theme": "dark", "notifications": true}}}}'::jsonb,
|
||||
$expected$Settings: {{ exec
|
||||
RETURN context->'user'->'profile'->'settings'->>'theme';
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Settings: dark';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with nested JSON operations: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with nested JSON operations: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 9: Execute tag with conditional logic
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"age": 25, "country": "US"}'::jsonb,
|
||||
$expected$Status: {{ exec
|
||||
DECLARE
|
||||
v_status TEXT;
|
||||
BEGIN
|
||||
IF (context->>'age')::int >= 21 AND context->>'country' = 'US' THEN
|
||||
v_status := 'Adult in US';
|
||||
ELSE
|
||||
v_status := 'Other';
|
||||
END IF;
|
||||
RETURN v_status;
|
||||
END;
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Status: Adult in US';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with conditional logic: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with conditional logic: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 10: Execute tag with string manipulation
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"text": "hello world"}'::jsonb,
|
||||
$expected$Text: {{ exec
|
||||
RETURN upper(context->>'text');
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Text: HELLO WORLD';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with string manipulation: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with string manipulation: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 11: Execute tag with date operations
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"date": "2024-03-15"}'::jsonb,
|
||||
$expected$Date: {{ exec
|
||||
RETURN to_char((context->>'date')::date, 'Month DD, YYYY');
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Date: March 15, 2024';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with date operations: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with date operations: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 12: Execute tag with aggregation
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"scores": [85, 92, 78, 95, 88]}'::jsonb,
|
||||
$expected$Stats: {{ exec
|
||||
RETURN (SELECT format('Avg: %s, Max: %s',
|
||||
round(avg(value::float)::numeric, 1),
|
||||
max(value::int))
|
||||
FROM jsonb_array_elements_text(context->'scores') as value);
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Stats: Avg: 87.6, Max: 95';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with aggregation: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with aggregation: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 13: Execute tag with error handling
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"value": "not_a_number"}'::jsonb,
|
||||
$expected$Result: {{ exec
|
||||
BEGIN
|
||||
RETURN (context->>'value')::int::text;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RETURN 'Error: Invalid number';
|
||||
END;
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Result: Error: Invalid number';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with error handling: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with error handling: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 14: Execute tag with complex JSON transformation
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"users": [{"name": "Alice", "roles": ["admin", "user"]}, {"name": "Bob", "roles": ["user"]}]}'::jsonb,
|
||||
$expected$Users: {{ exec
|
||||
RETURN (SELECT string_agg(
|
||||
format('%s (%s)',
|
||||
user_data->>'name',
|
||||
(SELECT string_agg(role, ', ')
|
||||
FROM jsonb_array_elements_text(user_data->'roles') as role)
|
||||
),
|
||||
'; '
|
||||
)
|
||||
FROM jsonb_array_elements(context->'users') as user_data);
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Users: Alice (admin, user); Bob (user)';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with complex JSON transformation: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with complex JSON transformation: FAILED. Expected "%", got "%"',
|
||||
total_tests, expected, test_result;
|
||||
END IF;
|
||||
|
||||
-- Test 15: Execute tag with empty/null handling
|
||||
total_tests := total_tests + 1;
|
||||
test_result := hemar.render(
|
||||
'{"name": null, "items": []}'::jsonb,
|
||||
$expected$Result: {{ exec
|
||||
DECLARE
|
||||
v_name TEXT;
|
||||
v_count INT;
|
||||
BEGIN
|
||||
v_name := COALESCE(context->>'name', 'Unknown');
|
||||
v_count := jsonb_array_length(context->'items');
|
||||
RETURN format('Name: %s, Items: %s', v_name, v_count);
|
||||
END;
|
||||
}}$expected$
|
||||
);
|
||||
expected := 'Result: Name: Unknown, Items: 0';
|
||||
passed := test_result = expected;
|
||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||
IF passed THEN
|
||||
RAISE NOTICE 'Test %: Execute tag with empty/null handling: PASSED', total_tests;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Execute tag with empty/null 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 % render exec tests passed (100%%)',
|
||||
passed_tests, total_tests;
|
||||
RAISE NOTICE '------------------------------------';
|
||||
ELSE
|
||||
RAISE WARNING '------------------------------------';
|
||||
RAISE WARNING 'SUMMARY: % of % render exec 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 % render exec tests did not pass', (total_tests - passed_tests), total_tests;
|
||||
END IF;
|
||||
END $$;
|
||||
235
package/c/hemar-legacy/test/test_render_include.sql
Executable file
235
package/c/hemar-legacy/test/test_render_include.sql
Executable file
@@ -0,0 +1,235 @@
|
||||
-- Test include tag functionality
|
||||
DO $$
|
||||
DECLARE
|
||||
result text;
|
||||
total_tests integer := 0;
|
||||
passed_tests integer := 0;
|
||||
BEGIN
|
||||
-- Test 1: Plain text inclusion
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
result := hemar.render(
|
||||
'{
|
||||
"include": {
|
||||
"inner_template": {
|
||||
"content": "<p>Hello World</p>"
|
||||
}
|
||||
}
|
||||
}'::jsonb,
|
||||
$hemar${{ include inner_template }}$hemar$
|
||||
);
|
||||
|
||||
IF result = '<p>Hello World</p>' THEN
|
||||
RAISE NOTICE 'Test %: Plain text inclusion works correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: failed, Expected "<p>Hello World</p>", got "%"', total_tests, result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Plain text inclusion: FAILED with error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 2: Template with separate context
|
||||
total_tests := total_tests + 1;
|
||||
result := hemar.render(
|
||||
'{
|
||||
"include": {
|
||||
"inner_template": {
|
||||
"template": "Hello {{ name }}!",
|
||||
"context": {
|
||||
"name": "John"
|
||||
}
|
||||
}
|
||||
}
|
||||
}'::jsonb,
|
||||
$hemar${{ include inner_template }}$hemar$
|
||||
);
|
||||
|
||||
IF result = 'Hello John!' THEN
|
||||
RAISE NOTICE 'Test %: Template with separate context works correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: failed, Expected "Hello John!", got "%"', total_tests, result;
|
||||
END IF;
|
||||
|
||||
-- Test 3: Template with shared context
|
||||
total_tests := total_tests + 1;
|
||||
result := hemar.render(
|
||||
'{
|
||||
"name": "John",
|
||||
"include": {
|
||||
"inner_template": {
|
||||
"template": "Hello {{ name }}!"
|
||||
}
|
||||
}
|
||||
}'::jsonb,
|
||||
$hemar${{ include inner_template }}$hemar$
|
||||
);
|
||||
|
||||
IF result = 'Hello John!' THEN
|
||||
RAISE NOTICE 'Test % passed: Template with shared context works correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test % failed: Expected "Hello John!", got "%"', total_tests, result;
|
||||
END IF;
|
||||
|
||||
-- Test 4: Nested includes
|
||||
total_tests := total_tests + 1;
|
||||
result := hemar.render(
|
||||
'{
|
||||
"include": {
|
||||
"outer_template": {
|
||||
"template": "Outer: {{ include inner_template }}",
|
||||
"context": {
|
||||
"include": {
|
||||
"inner_template": {
|
||||
"template": "Inner: {{ name }}",
|
||||
"context": {
|
||||
"name": "John"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}'::jsonb,
|
||||
$hemar${{ include outer_template }}$hemar$
|
||||
);
|
||||
|
||||
IF result = 'Outer: Inner: John' THEN
|
||||
RAISE NOTICE 'Test % passed: Nested includes work correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test % failed: Expected "Outer: Inner: John", got "%"', total_tests, result;
|
||||
END IF;
|
||||
|
||||
-- Test 5: Complex template with multiple includes
|
||||
total_tests := total_tests + 1;
|
||||
result := hemar.render(
|
||||
'{
|
||||
"include": {
|
||||
"header": {
|
||||
"content": "<header>Welcome</header>"
|
||||
},
|
||||
"content": {
|
||||
"template": "Hello {{ user.name }}!",
|
||||
"context": {
|
||||
"user": {
|
||||
"name": "John"
|
||||
}
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"template": "Copyright {{ year }}",
|
||||
"context": {
|
||||
"year": "2024"
|
||||
}
|
||||
}
|
||||
}
|
||||
}'::jsonb,
|
||||
$hemar$Header: {{ include header }}
|
||||
Content: {{ include content }}
|
||||
Footer: {{ include footer }}$hemar$
|
||||
);
|
||||
|
||||
IF result = 'Header: <header>Welcome</header>
|
||||
Content: Hello John!
|
||||
Footer: Copyright 2024' THEN
|
||||
RAISE NOTICE 'Test % passed: Complex template with multiple includes works correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test % failed: Expected , got "%"', total_tests, result;
|
||||
END IF;
|
||||
|
||||
-- Test 6: Error handling - missing include data
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
result := hemar.render(
|
||||
'{{ include missing_template }}',
|
||||
'{}'::jsonb
|
||||
);
|
||||
RAISE WARNING 'Test % failed: Should have raised an error for missing include data', total_tests;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE NOTICE 'Test % passed: Error handling for missing include data works correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
END;
|
||||
|
||||
-- Test 7: Error handling - invalid include data
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
result := hemar.render(
|
||||
'{
|
||||
"include": {
|
||||
"invalid_template": "not an object"
|
||||
}
|
||||
}'::jsonb,
|
||||
'{{ include invalid_template }}'
|
||||
);
|
||||
|
||||
IF result = '' THEN
|
||||
RAISE NOTICE 'Test % passed: Error handling for invalid include data works correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test % failed: Expected "", got "%"', total_tests, result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test % failed: Should have raised an error for invalid include data', total_tests;
|
||||
END;
|
||||
|
||||
-- Test 8: Error handling - unexisting include object
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
result := hemar.render(
|
||||
'{}'::jsonb,
|
||||
'{{ include invalid_template }}'
|
||||
);
|
||||
|
||||
IF result = '' THEN
|
||||
RAISE NOTICE 'Test % passed: Error handling for unexisting include object works correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test % failed: Expected "", got "%"', total_tests, result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test % failed: Should have raised an error for unexisting include object', total_tests;
|
||||
END;
|
||||
|
||||
-- Test 9: Error handling - unexisting include data
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
result := hemar.render(
|
||||
'{
|
||||
"include": { }
|
||||
}'::jsonb,
|
||||
'{{ include invalid_template }}'
|
||||
);
|
||||
|
||||
IF result = '' THEN
|
||||
RAISE NOTICE 'Test % passed: Error handling for unexisting include data works correctly', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test % failed: Expected "", got "%"', total_tests, result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test % failed: Should have raised an error for unexisting include data', total_tests;
|
||||
END;
|
||||
|
||||
IF passed_tests = total_tests THEN
|
||||
RAISE NOTICE '------------------------------------';
|
||||
RAISE NOTICE 'SUMMARY: % of % template include tests passed (100%%)',
|
||||
passed_tests, total_tests;
|
||||
RAISE NOTICE '------------------------------------';
|
||||
ELSE
|
||||
RAISE WARNING '------------------------------------';
|
||||
RAISE WARNING 'SUMMARY: % of % template include 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 include tests did not pass', (total_tests - passed_tests), total_tests;
|
||||
END IF;
|
||||
END $$;
|
||||
190
package/c/hemar-legacy/test/test_render_interpolate.sql
Executable file
190
package/c/hemar-legacy/test/test_render_interpolate.sql
Executable file
@@ -0,0 +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;
|
||||
END $$;
|
||||
401
package/c/hemar-legacy/test/test_render_section.sql
Executable file
401
package/c/hemar-legacy/test/test_render_section.sql
Executable file
@@ -0,0 +1,401 @@
|
||||
-- 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;
|
||||
|
||||
|
||||
-- Test 11: Section whitespaces
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{"array": [1, 2, 3]}'::jsonb,
|
||||
'{{for item in array}}item{{end}}'
|
||||
);
|
||||
expected := 'itemitemitem';
|
||||
IF test_result = expected THEN
|
||||
RAISE NOTICE 'Test %: Section whitespaces: PASSED', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Section whitespaces: FAILED. Expected "%", got "%"', total_tests, expected, test_result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Section whitespaces: FAILED with error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 12: Section whitespaces 2
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{"array": [1, 2, 3]}'::jsonb,
|
||||
'{{for item in array}}
|
||||
item
|
||||
{{end}}'
|
||||
);
|
||||
expected := ' item
|
||||
item
|
||||
item
|
||||
';
|
||||
IF test_result = expected THEN
|
||||
RAISE NOTICE 'Test %: Section whitespaces 2: PASSED', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Section whitespaces 2: FAILED. Expected "%", got "%"', total_tests, expected, test_result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Section whitespaces 2: FAILED with error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 13: Section whitespaces 3
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{"array": [1, 2, 3]}'::jsonb,
|
||||
'{{for item in array}} item
|
||||
{{end}}'
|
||||
);
|
||||
expected := ' item
|
||||
item
|
||||
item
|
||||
';
|
||||
IF test_result = expected THEN
|
||||
RAISE NOTICE 'Test %: Section whitespaces 3: PASSED', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Section whitespaces 3: FAILED. Expected "%", got "%"', total_tests, expected, test_result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Section whitespaces 3: FAILED with error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 14: Section whitespaces 4
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{"array": [1, 2, 3]}'::jsonb,
|
||||
'{{for item in array}}
|
||||
item {{end}}'
|
||||
);
|
||||
expected := ' item item item ';
|
||||
IF test_result = expected THEN
|
||||
RAISE NOTICE 'Test %: Section whitespaces 4: PASSED', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Section whitespaces 4: FAILED. Expected "%", got "%"', total_tests, expected, test_result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Section whitespaces 4: FAILED with error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 15: Section whitespaces 5
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{"array": [1, 2, 3]}'::jsonb,
|
||||
'{{for item in array}}
|
||||
item
|
||||
{{end}}
|
||||
'
|
||||
);
|
||||
expected := ' item
|
||||
item
|
||||
item
|
||||
';
|
||||
IF test_result = expected THEN
|
||||
RAISE NOTICE 'Test %: Section whitespaces 5: PASSED', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Section whitespaces 5: FAILED. Expected "%", got "%"', total_tests, pg_temp.test_regexp_replace(expected), pg_temp.test_regexp_replace(test_result);
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Section whitespaces 5: FAILED with error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 16: Tabs
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{"array": [1, 2, 3]}'::jsonb,
|
||||
'
|
||||
identation1
|
||||
{{for item in array}}
|
||||
identation2
|
||||
{{end}}
|
||||
identation1
|
||||
'
|
||||
);
|
||||
expected := '
|
||||
identation1
|
||||
identation2
|
||||
identation2
|
||||
identation2
|
||||
identation1
|
||||
';
|
||||
|
||||
IF test_result = expected THEN
|
||||
RAISE NOTICE 'Test %: Tabs: PASSED', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Tabs: FAILED. Expected "%", got "%"', total_tests, pg_temp.test_regexp_replace(expected), pg_temp.test_regexp_replace(test_result);
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Tabs: FAILED with error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 17: Tabs 2
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{"array": [1, 2, 3]}'::jsonb,
|
||||
'
|
||||
identation1
|
||||
{{for item in array}}
|
||||
identation2
|
||||
{{end}}
|
||||
identation1
|
||||
'
|
||||
);
|
||||
expected := '
|
||||
identation1
|
||||
identation2
|
||||
identation2
|
||||
identation2
|
||||
identation1
|
||||
';
|
||||
|
||||
IF test_result = expected THEN
|
||||
RAISE NOTICE 'Test %: Tabs: PASSED', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Tabs: FAILED. Expected "%", got "%"', total_tests, pg_temp.test_regexp_replace(expected), pg_temp.test_regexp_replace(test_result);
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Tabs: FAILED with error: %', total_tests, SQLERRM;
|
||||
END;
|
||||
|
||||
-- Test 18: Context
|
||||
total_tests := total_tests + 1;
|
||||
BEGIN
|
||||
test_result := hemar.render(
|
||||
'{"value": 12, "array": [1, 2, 3]}'::jsonb,
|
||||
'
|
||||
{{for item in array}}
|
||||
{{exec RETURN context::TEXT}}
|
||||
{{end}}
|
||||
'
|
||||
);
|
||||
expected := '
|
||||
';
|
||||
|
||||
IF test_result = expected THEN
|
||||
RAISE NOTICE 'Test %: Context: PASSED', total_tests;
|
||||
passed_tests := passed_tests + 1;
|
||||
ELSE
|
||||
RAISE WARNING 'Test %: Context: FAILED. Expected "%", got "%"', total_tests, expected, test_result;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Test %: Context: 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 $$;
|
||||
1134
package/c/hemar-legacy/test/test_template_parser.sql
Executable file
1134
package/c/hemar-legacy/test/test_template_parser.sql
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user