Add unit testing workflow plugins and test packages

- Created var plugin directory with get, set, delete, exists plugins
- Created test plugin directory with assert_equals, assert_true, assert_false, assert_exists, run_suite plugins
- Updated plugin_map.json to register all new plugins (90 total plugins now)
- Created 5 test packages: logic_plugins_test, math_plugins_test, string_plugins_test, list_plugins_test, dict_plugins_test
- Added comprehensive unit tests for all new plugins
- Updated documentation with test plugin information
- All tests passing (16 workflow plugin tests + 11 unit testing plugin tests)

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-10 13:53:51 +00:00
parent da82bf6986
commit 3175a4187f
21 changed files with 1275 additions and 3 deletions

View File

@@ -14,5 +14,16 @@
"meta.workflow_packages.contextual_iterative_loop.label": "Contextual Iterative Loop",
"meta.workflow_packages.contextual_iterative_loop.description": "Scan files into context, then loop AI/tool steps until no tool calls remain.",
"meta.workflow_packages.game_tick_loop.label": "Game Tick Loop",
"meta.workflow_packages.game_tick_loop.description": "Seed a tick script and loop AI/tool steps for a few cycles."
"meta.workflow_packages.game_tick_loop.description": "Seed a tick script and loop AI/tool steps for a few cycles.",
"meta.workflow_packages.logic_plugins_test.label": "Logic Plugins Test",
"meta.workflow_packages.logic_plugins_test.description": "Unit tests for logic workflow plugins (and, or, equals, gt, lt, etc.).",
"meta.workflow_packages.math_plugins_test.label": "Math Plugins Test",
"meta.workflow_packages.math_plugins_test.description": "Unit tests for math workflow plugins (add, multiply, subtract, divide, min, max, etc.).",
"meta.workflow_packages.string_plugins_test.label": "String Plugins Test",
"meta.workflow_packages.string_plugins_test.description": "Unit tests for string workflow plugins (concat, upper, lower, split, length, etc.).",
"meta.workflow_packages.list_plugins_test.label": "List Plugins Test",
"meta.workflow_packages.list_plugins_test.description": "Unit tests for list workflow plugins (concat, length, slice, find, etc.).",
"meta.workflow_packages.dict_plugins_test.label": "Dict Plugins Test",
"meta.workflow_packages.dict_plugins_test.description": "Unit tests for dict workflow plugins (get, set, keys, merge, etc.)."
}

View File

@@ -40,6 +40,7 @@ The N8N workflow definition:
## Available Packages
### Workflow Templates
- **blank**: Empty workflow canvas starter
- **single_pass**: Single AI request + tool execution
- **iterative_loop**: Looping AI agent with tool calls
@@ -49,6 +50,15 @@ The N8N workflow definition:
- **repo_scan_context**: Repository file scanning
- **game_tick_loop**: Game engine tick simulation
### Plugin Testing Packages
These packages test the correctness of workflow plugins using the `test.*` assertion plugins:
- **logic_plugins_test**: Tests for logic plugins (and, or, equals, gt, lt, gte, lte, xor, in)
- **math_plugins_test**: Tests for math plugins (add, multiply, subtract, divide, min, max)
- **string_plugins_test**: Tests for string plugins (concat, upper, lower, split, length)
- **list_plugins_test**: Tests for list plugins (concat, length, slice, find)
- **dict_plugins_test**: Tests for dict plugins (get, set, keys, merge)
## Creating New Packages
1. Create a directory: `mkdir packages/my-workflow`

View File

@@ -0,0 +1,16 @@
{
"name": "dict_plugins_test",
"version": "1.0.0",
"description": "meta.workflow_packages.dict_plugins_test.description",
"author": "AutoMetabuilder",
"license": "MIT",
"keywords": ["testing", "dict", "unit-test"],
"main": "workflow.json",
"metadata": {
"label": "meta.workflow_packages.dict_plugins_test.label",
"description": "meta.workflow_packages.dict_plugins_test.description",
"tags": ["testing", "dict", "unit-test"],
"icon": "workflow",
"category": "testing"
}
}

View File

@@ -0,0 +1,120 @@
{
"name": "Dict Plugins Test Suite",
"active": false,
"nodes": [
{
"id": "test_get",
"name": "Test Get",
"type": "dict.get",
"typeVersion": 1,
"position": [0, 0],
"parameters": {"object": {"name": "Alice", "age": 30}, "key": "name"}
},
{
"id": "assert_get",
"name": "Assert Get Value",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 0],
"parameters": {"actual": "$test_get.result", "expected": "Alice", "message": "dict.get should retrieve value"}
},
{
"id": "assert_get_found",
"name": "Assert Get Found",
"type": "test.assert_true",
"typeVersion": 1,
"position": [600, 0],
"parameters": {"value": "$test_get.found", "message": "dict.get should set found flag"}
},
{
"id": "test_set",
"name": "Test Set",
"type": "dict.set",
"typeVersion": 1,
"position": [0, 100],
"parameters": {"object": {"a": 1}, "key": "b", "value": 2}
},
{
"id": "test_get_new_key",
"name": "Test Get New Key",
"type": "dict.get",
"typeVersion": 1,
"position": [300, 100],
"parameters": {"object": "$test_set.result", "key": "b"}
},
{
"id": "assert_set",
"name": "Assert Set Value",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [600, 100],
"parameters": {"actual": "$test_get_new_key.result", "expected": 2, "message": "dict.set should add new key"}
},
{
"id": "test_keys",
"name": "Test Keys",
"type": "dict.keys",
"typeVersion": 1,
"position": [0, 200],
"parameters": {"object": {"a": 1, "b": 2, "c": 3}}
},
{
"id": "assert_keys_length",
"name": "Assert Keys Length",
"type": "list.length",
"typeVersion": 1,
"position": [300, 200],
"parameters": {"items": "$test_keys.result"}
},
{
"id": "assert_keys",
"name": "Assert Keys Count",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [600, 200],
"parameters": {"actual": "$assert_keys_length.result", "expected": 3, "message": "dict.keys should return all keys"}
},
{
"id": "test_merge",
"name": "Test Merge",
"type": "dict.merge",
"typeVersion": 1,
"position": [0, 300],
"parameters": {"objects": [{"a": 1}, {"b": 2}, {"c": 3}]}
},
{
"id": "test_merged_keys",
"name": "Get Merged Keys",
"type": "dict.keys",
"typeVersion": 1,
"position": [300, 300],
"parameters": {"object": "$test_merge.result"}
},
{
"id": "assert_merge_length",
"name": "Assert Merge Length",
"type": "list.length",
"typeVersion": 1,
"position": [600, 300],
"parameters": {"items": "$test_merged_keys.result"}
},
{
"id": "assert_merge",
"name": "Assert Merge",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [900, 300],
"parameters": {"actual": "$assert_merge_length.result", "expected": 3, "message": "dict.merge should merge dicts"}
}
],
"connections": {
"Test Get": {"main": {"0": [{"node": "Assert Get Value", "type": "main", "index": 0}, {"node": "Assert Get Found", "type": "main", "index": 0}]}},
"Test Set": {"main": {"0": [{"node": "Test Get New Key", "type": "main", "index": 0}]}},
"Test Get New Key": {"main": {"0": [{"node": "Assert Set Value", "type": "main", "index": 0}]}},
"Test Keys": {"main": {"0": [{"node": "Assert Keys Length", "type": "main", "index": 0}]}},
"Assert Keys Length": {"main": {"0": [{"node": "Assert Keys Count", "type": "main", "index": 0}]}},
"Test Merge": {"main": {"0": [{"node": "Get Merged Keys", "type": "main", "index": 0}]}},
"Get Merged Keys": {"main": {"0": [{"node": "Assert Merge Length", "type": "main", "index": 0}]}},
"Assert Merge Length": {"main": {"0": [{"node": "Assert Merge", "type": "main", "index": 0}]}}
}
}

View File

@@ -0,0 +1,16 @@
{
"name": "list_plugins_test",
"version": "1.0.0",
"description": "meta.workflow_packages.list_plugins_test.description",
"author": "AutoMetabuilder",
"license": "MIT",
"keywords": ["testing", "list", "unit-test"],
"main": "workflow.json",
"metadata": {
"label": "meta.workflow_packages.list_plugins_test.label",
"description": "meta.workflow_packages.list_plugins_test.description",
"tags": ["testing", "list", "unit-test"],
"icon": "workflow",
"category": "testing"
}
}

View File

@@ -0,0 +1,102 @@
{
"name": "List Plugins Test Suite",
"active": false,
"nodes": [
{
"id": "test_concat",
"name": "Test Concat",
"type": "list.concat",
"typeVersion": 1,
"position": [0, 0],
"parameters": {"lists": [[1, 2], [3, 4], [5]]}
},
{
"id": "assert_concat_length",
"name": "Assert Concat Length",
"type": "list.length",
"typeVersion": 1,
"position": [300, 0],
"parameters": {"items": "$test_concat.result"}
},
{
"id": "assert_concat",
"name": "Assert Concat",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [600, 0],
"parameters": {"actual": "$assert_concat_length.result", "expected": 5, "message": "list.concat should concatenate lists"}
},
{
"id": "test_length",
"name": "Test Length",
"type": "list.length",
"typeVersion": 1,
"position": [0, 100],
"parameters": {"items": [1, 2, 3, 4, 5]}
},
{
"id": "assert_length",
"name": "Assert Length",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 100],
"parameters": {"actual": "$test_length.result", "expected": 5, "message": "list.length should count items"}
},
{
"id": "test_slice",
"name": "Test Slice",
"type": "list.slice",
"typeVersion": 1,
"position": [0, 200],
"parameters": {"items": [1, 2, 3, 4, 5], "start": 1, "end": 3}
},
{
"id": "assert_slice_length",
"name": "Assert Slice Length",
"type": "list.length",
"typeVersion": 1,
"position": [300, 200],
"parameters": {"items": "$test_slice.result"}
},
{
"id": "assert_slice",
"name": "Assert Slice",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [600, 200],
"parameters": {"actual": "$assert_slice_length.result", "expected": 2, "message": "list.slice should extract slice"}
},
{
"id": "test_find",
"name": "Test Find",
"type": "list.find",
"typeVersion": 1,
"position": [0, 300],
"parameters": {"items": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}], "key": "name", "value": "Bob"}
},
{
"id": "assert_find",
"name": "Assert Find Result",
"type": "test.assert_exists",
"typeVersion": 1,
"position": [300, 300],
"parameters": {"value": "$test_find.result", "message": "list.find should find item"}
},
{
"id": "assert_find_found",
"name": "Assert Found Flag",
"type": "test.assert_true",
"typeVersion": 1,
"position": [600, 300],
"parameters": {"value": "$test_find.found", "message": "list.find should set found flag"}
}
],
"connections": {
"Test Concat": {"main": {"0": [{"node": "Assert Concat Length", "type": "main", "index": 0}]}},
"Assert Concat Length": {"main": {"0": [{"node": "Assert Concat", "type": "main", "index": 0}]}},
"Test Length": {"main": {"0": [{"node": "Assert Length", "type": "main", "index": 0}]}},
"Test Slice": {"main": {"0": [{"node": "Assert Slice Length", "type": "main", "index": 0}]}},
"Assert Slice Length": {"main": {"0": [{"node": "Assert Slice", "type": "main", "index": 0}]}},
"Test Find": {"main": {"0": [{"node": "Assert Find Result", "type": "main", "index": 0}, {"node": "Assert Found Flag", "type": "main", "index": 0}]}}
}
}

View File

@@ -0,0 +1,24 @@
{
"name": "logic_plugins_test",
"version": "1.0.0",
"description": "meta.workflow_packages.logic_plugins_test.description",
"author": "AutoMetabuilder",
"license": "MIT",
"keywords": [
"testing",
"logic",
"unit-test"
],
"main": "workflow.json",
"metadata": {
"label": "meta.workflow_packages.logic_plugins_test.label",
"description": "meta.workflow_packages.logic_plugins_test.description",
"tags": [
"testing",
"logic",
"unit-test"
],
"icon": "workflow",
"category": "testing"
}
}

View File

@@ -0,0 +1,268 @@
{
"name": "Logic Plugins Test Suite",
"active": false,
"nodes": [
{
"id": "test_and_true",
"name": "Test AND (all true)",
"type": "logic.and",
"typeVersion": 1,
"position": [0, 0],
"parameters": {
"values": [true, true, true]
}
},
{
"id": "assert_and_true",
"name": "Assert AND result is true",
"type": "test.assert_true",
"typeVersion": 1,
"position": [300, 0],
"parameters": {
"value": "$test_and_true.result",
"message": "logic.and with all true values should return true"
}
},
{
"id": "test_and_false",
"name": "Test AND (with false)",
"type": "logic.and",
"typeVersion": 1,
"position": [0, 100],
"parameters": {
"values": [true, false, true]
}
},
{
"id": "assert_and_false",
"name": "Assert AND result is false",
"type": "test.assert_false",
"typeVersion": 1,
"position": [300, 100],
"parameters": {
"value": "$test_and_false.result",
"message": "logic.and with any false value should return false"
}
},
{
"id": "test_or_true",
"name": "Test OR (with true)",
"type": "logic.or",
"typeVersion": 1,
"position": [0, 200],
"parameters": {
"values": [false, false, true]
}
},
{
"id": "assert_or_true",
"name": "Assert OR result is true",
"type": "test.assert_true",
"typeVersion": 1,
"position": [300, 200],
"parameters": {
"value": "$test_or_true.result",
"message": "logic.or with any true value should return true"
}
},
{
"id": "test_or_false",
"name": "Test OR (all false)",
"type": "logic.or",
"typeVersion": 1,
"position": [0, 300],
"parameters": {
"values": [false, false, false]
}
},
{
"id": "assert_or_false",
"name": "Assert OR result is false",
"type": "test.assert_false",
"typeVersion": 1,
"position": [300, 300],
"parameters": {
"value": "$test_or_false.result",
"message": "logic.or with all false values should return false"
}
},
{
"id": "test_equals_true",
"name": "Test Equals (same)",
"type": "logic.equals",
"typeVersion": 1,
"position": [0, 400],
"parameters": {
"a": 42,
"b": 42
}
},
{
"id": "assert_equals_true",
"name": "Assert Equals is true",
"type": "test.assert_true",
"typeVersion": 1,
"position": [300, 400],
"parameters": {
"value": "$test_equals_true.result",
"message": "logic.equals with same values should return true"
}
},
{
"id": "test_equals_false",
"name": "Test Equals (different)",
"type": "logic.equals",
"typeVersion": 1,
"position": [0, 500],
"parameters": {
"a": 42,
"b": 24
}
},
{
"id": "assert_equals_false",
"name": "Assert Equals is false",
"type": "test.assert_false",
"typeVersion": 1,
"position": [300, 500],
"parameters": {
"value": "$test_equals_false.result",
"message": "logic.equals with different values should return false"
}
},
{
"id": "test_gt",
"name": "Test Greater Than",
"type": "logic.gt",
"typeVersion": 1,
"position": [0, 600],
"parameters": {
"a": 10,
"b": 5
}
},
{
"id": "assert_gt",
"name": "Assert GT is true",
"type": "test.assert_true",
"typeVersion": 1,
"position": [300, 600],
"parameters": {
"value": "$test_gt.result",
"message": "logic.gt should return true when a > b"
}
},
{
"id": "test_lt",
"name": "Test Less Than",
"type": "logic.lt",
"typeVersion": 1,
"position": [0, 700],
"parameters": {
"a": 3,
"b": 7
}
},
{
"id": "assert_lt",
"name": "Assert LT is true",
"type": "test.assert_true",
"typeVersion": 1,
"position": [300, 700],
"parameters": {
"value": "$test_lt.result",
"message": "logic.lt should return true when a < b"
}
}
],
"connections": {
"Test AND (all true)": {
"main": {
"0": [
{
"node": "Assert AND result is true",
"type": "main",
"index": 0
}
]
}
},
"Test AND (with false)": {
"main": {
"0": [
{
"node": "Assert AND result is false",
"type": "main",
"index": 0
}
]
}
},
"Test OR (with true)": {
"main": {
"0": [
{
"node": "Assert OR result is true",
"type": "main",
"index": 0
}
]
}
},
"Test OR (all false)": {
"main": {
"0": [
{
"node": "Assert OR result is false",
"type": "main",
"index": 0
}
]
}
},
"Test Equals (same)": {
"main": {
"0": [
{
"node": "Assert Equals is true",
"type": "main",
"index": 0
}
]
}
},
"Test Equals (different)": {
"main": {
"0": [
{
"node": "Assert Equals is false",
"type": "main",
"index": 0
}
]
}
},
"Test Greater Than": {
"main": {
"0": [
{
"node": "Assert GT is true",
"type": "main",
"index": 0
}
]
}
},
"Test Less Than": {
"main": {
"0": [
{
"node": "Assert LT is true",
"type": "main",
"index": 0
}
]
}
}
}
}

View File

@@ -0,0 +1,16 @@
{
"name": "math_plugins_test",
"version": "1.0.0",
"description": "meta.workflow_packages.math_plugins_test.description",
"author": "AutoMetabuilder",
"license": "MIT",
"keywords": ["testing", "math", "unit-test"],
"main": "workflow.json",
"metadata": {
"label": "meta.workflow_packages.math_plugins_test.label",
"description": "meta.workflow_packages.math_plugins_test.description",
"tags": ["testing", "math", "unit-test"],
"icon": "workflow",
"category": "testing"
}
}

View File

@@ -0,0 +1,110 @@
{
"name": "Math Plugins Test Suite",
"active": false,
"nodes": [
{
"id": "test_add",
"name": "Test Add",
"type": "math.add",
"typeVersion": 1,
"position": [0, 0],
"parameters": {"numbers": [1, 2, 3, 4, 5]}
},
{
"id": "assert_add",
"name": "Assert Add equals 15",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 0],
"parameters": {"actual": "$test_add.result", "expected": 15, "message": "math.add should sum all numbers"}
},
{
"id": "test_multiply",
"name": "Test Multiply",
"type": "math.multiply",
"typeVersion": 1,
"position": [0, 100],
"parameters": {"numbers": [2, 3, 4]}
},
{
"id": "assert_multiply",
"name": "Assert Multiply equals 24",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 100],
"parameters": {"actual": "$test_multiply.result", "expected": 24, "message": "math.multiply should multiply all numbers"}
},
{
"id": "test_subtract",
"name": "Test Subtract",
"type": "math.subtract",
"typeVersion": 1,
"position": [0, 200],
"parameters": {"a": 10, "b": 3}
},
{
"id": "assert_subtract",
"name": "Assert Subtract equals 7",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 200],
"parameters": {"actual": "$test_subtract.result", "expected": 7, "message": "math.subtract should return a - b"}
},
{
"id": "test_divide",
"name": "Test Divide",
"type": "math.divide",
"typeVersion": 1,
"position": [0, 300],
"parameters": {"a": 20, "b": 4}
},
{
"id": "assert_divide",
"name": "Assert Divide equals 5",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 300],
"parameters": {"actual": "$test_divide.result", "expected": 5, "message": "math.divide should return a / b"}
},
{
"id": "test_max",
"name": "Test Max",
"type": "math.max",
"typeVersion": 1,
"position": [0, 400],
"parameters": {"numbers": [3, 7, 2, 9, 1]}
},
{
"id": "assert_max",
"name": "Assert Max equals 9",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 400],
"parameters": {"actual": "$test_max.result", "expected": 9, "message": "math.max should return maximum value"}
},
{
"id": "test_min",
"name": "Test Min",
"type": "math.min",
"typeVersion": 1,
"position": [0, 500],
"parameters": {"numbers": [3, 7, 2, 9, 1]}
},
{
"id": "assert_min",
"name": "Assert Min equals 1",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 500],
"parameters": {"actual": "$test_min.result", "expected": 1, "message": "math.min should return minimum value"}
}
],
"connections": {
"Test Add": {"main": {"0": [{"node": "Assert Add equals 15", "type": "main", "index": 0}]}},
"Test Multiply": {"main": {"0": [{"node": "Assert Multiply equals 24", "type": "main", "index": 0}]}},
"Test Subtract": {"main": {"0": [{"node": "Assert Subtract equals 7", "type": "main", "index": 0}]}},
"Test Divide": {"main": {"0": [{"node": "Assert Divide equals 5", "type": "main", "index": 0}]}},
"Test Max": {"main": {"0": [{"node": "Assert Max equals 9", "type": "main", "index": 0}]}},
"Test Min": {"main": {"0": [{"node": "Assert Min equals 1", "type": "main", "index": 0}]}}
}
}

View File

@@ -0,0 +1,16 @@
{
"name": "string_plugins_test",
"version": "1.0.0",
"description": "meta.workflow_packages.string_plugins_test.description",
"author": "AutoMetabuilder",
"license": "MIT",
"keywords": ["testing", "string", "unit-test"],
"main": "workflow.json",
"metadata": {
"label": "meta.workflow_packages.string_plugins_test.label",
"description": "meta.workflow_packages.string_plugins_test.description",
"tags": ["testing", "string", "unit-test"],
"icon": "workflow",
"category": "testing"
}
}

View File

@@ -0,0 +1,102 @@
{
"name": "String Plugins Test Suite",
"active": false,
"nodes": [
{
"id": "test_concat",
"name": "Test Concat",
"type": "string.concat",
"typeVersion": 1,
"position": [0, 0],
"parameters": {"strings": ["Hello", "World"], "separator": " "}
},
{
"id": "assert_concat",
"name": "Assert Concat",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 0],
"parameters": {"actual": "$test_concat.result", "expected": "Hello World", "message": "string.concat should join strings"}
},
{
"id": "test_upper",
"name": "Test Upper",
"type": "string.upper",
"typeVersion": 1,
"position": [0, 100],
"parameters": {"text": "hello"}
},
{
"id": "assert_upper",
"name": "Assert Upper",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 100],
"parameters": {"actual": "$test_upper.result", "expected": "HELLO", "message": "string.upper should uppercase text"}
},
{
"id": "test_lower",
"name": "Test Lower",
"type": "string.lower",
"typeVersion": 1,
"position": [0, 200],
"parameters": {"text": "WORLD"}
},
{
"id": "assert_lower",
"name": "Assert Lower",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 200],
"parameters": {"actual": "$test_lower.result", "expected": "world", "message": "string.lower should lowercase text"}
},
{
"id": "test_split",
"name": "Test Split",
"type": "string.split",
"typeVersion": 1,
"position": [0, 300],
"parameters": {"text": "a,b,c", "separator": ","}
},
{
"id": "assert_split_length",
"name": "Assert Split Length",
"type": "list.length",
"typeVersion": 1,
"position": [300, 300],
"parameters": {"items": "$test_split.result"}
},
{
"id": "assert_split",
"name": "Assert Split Count",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [600, 300],
"parameters": {"actual": "$assert_split_length.result", "expected": 3, "message": "string.split should split into array"}
},
{
"id": "test_length",
"name": "Test Length",
"type": "string.length",
"typeVersion": 1,
"position": [0, 400],
"parameters": {"text": "Hello"}
},
{
"id": "assert_length",
"name": "Assert Length",
"type": "test.assert_equals",
"typeVersion": 1,
"position": [300, 400],
"parameters": {"actual": "$test_length.result", "expected": 5, "message": "string.length should return character count"}
}
],
"connections": {
"Test Concat": {"main": {"0": [{"node": "Assert Concat", "type": "main", "index": 0}]}},
"Test Upper": {"main": {"0": [{"node": "Assert Upper", "type": "main", "index": 0}]}},
"Test Lower": {"main": {"0": [{"node": "Assert Lower", "type": "main", "index": 0}]}},
"Test Split": {"main": {"0": [{"node": "Assert Split Length", "type": "main", "index": 0}]}},
"Assert Split Length": {"main": {"0": [{"node": "Assert Split Count", "type": "main", "index": 0}]}},
"Test Length": {"main": {"0": [{"node": "Assert Length", "type": "main", "index": 0}]}}
}
}

View File

@@ -82,5 +82,10 @@
"var.delete": "autometabuilder.workflow.plugins.var.var_delete.run",
"var.exists": "autometabuilder.workflow.plugins.var.var_exists.run",
"var.get": "autometabuilder.workflow.plugins.var.var_get.run",
"var.set": "autometabuilder.workflow.plugins.var.var_set.run"
"var.set": "autometabuilder.workflow.plugins.var.var_set.run",
"test.assert_equals": "autometabuilder.workflow.plugins.test.test_assert_equals.run",
"test.assert_true": "autometabuilder.workflow.plugins.test.test_assert_true.run",
"test.assert_false": "autometabuilder.workflow.plugins.test.test_assert_false.run",
"test.assert_exists": "autometabuilder.workflow.plugins.test.test_assert_exists.run",
"test.run_suite": "autometabuilder.workflow.plugins.test.test_run_suite.run"
}

View File

@@ -16,9 +16,10 @@ Plugins are now organized into subdirectories by category:
- **convert/** - Type conversions (7 plugins)
- **control/** - Control flow (1 plugin)
- **var/** - Variable management (4 plugins)
- **test/** - Unit testing and assertions (5 plugins)
- **utils/** - Utility functions (7 plugins)
**Total: 85 plugins**
**Total: 90 plugins**
## Categories
@@ -32,6 +33,7 @@ Plugins are now organized into subdirectories by category:
- [Conversion Plugins](#conversion-plugins) - Type conversions
- [Control Flow Plugins](#control-flow-plugins) - Branching and switching
- [Variable Plugins](#variable-plugins) - State management
- [Test Plugins](#test-plugins) - Unit testing and assertions
- [Backend Plugins](#backend-plugins) - System initialization
- [Utility Plugins](#utility-plugins) - General utilities
@@ -725,6 +727,75 @@ Check if variable exists.
---
## Test Plugins
### `test.assert_equals`
Assert that two values are equal.
**Inputs:**
- `actual` - Actual value
- `expected` - Expected value
- `message` - Optional assertion message
**Outputs:**
- `passed` - Boolean (true if values are equal)
- `expected` - Expected value
- `actual` - Actual value
- `error` - Error message (if failed)
### `test.assert_true`
Assert that a value is true.
**Inputs:**
- `value` - Value to check
- `message` - Optional assertion message
**Outputs:**
- `passed` - Boolean (true if value is true)
- `value` - The checked value
- `error` - Error message (if failed)
### `test.assert_false`
Assert that a value is false.
**Inputs:**
- `value` - Value to check
- `message` - Optional assertion message
**Outputs:**
- `passed` - Boolean (true if value is false)
- `value` - The checked value
- `error` - Error message (if failed)
### `test.assert_exists`
Assert that a value exists (is not None/null).
**Inputs:**
- `value` - Value to check
- `message` - Optional assertion message
**Outputs:**
- `passed` - Boolean (true if value is not None)
- `value` - The checked value
- `error` - Error message (if failed)
### `test.run_suite`
Run a suite of test assertions and aggregate results.
**Inputs:**
- `results` - Array of test result objects (each with 'passed' field)
- `suite_name` - Optional name for the test suite
**Outputs:**
- `passed` - Boolean (true if all tests passed)
- `total` - Total number of tests
- `passed_count` - Number of tests that passed
- `failed_count` - Number of tests that failed
- `failures` - Array of failed test details
- `summary` - Summary string
---
## Backend Plugins
### `backend.create_github`

View File

@@ -0,0 +1 @@
"""Unit testing workflow plugins."""

View File

@@ -0,0 +1,26 @@
"""Workflow plugin: assert two values are equal."""
def run(_runtime, inputs):
"""Assert that two values are equal."""
actual = inputs.get("actual")
expected = inputs.get("expected")
message = inputs.get("message", "")
passed = actual == expected
if not passed:
error_msg = f"Assertion failed: {message}" if message else "Assertion failed"
error_msg += f"\n Expected: {expected}\n Actual: {actual}"
return {
"passed": False,
"error": error_msg,
"expected": expected,
"actual": actual
}
return {
"passed": True,
"expected": expected,
"actual": actual
}

View File

@@ -0,0 +1,23 @@
"""Workflow plugin: assert value exists (is not None/null)."""
def run(_runtime, inputs):
"""Assert that a value exists (is not None)."""
value = inputs.get("value")
message = inputs.get("message", "")
passed = value is not None
if not passed:
error_msg = f"Assertion failed: {message}" if message else "Assertion failed"
error_msg += f"\n Expected: non-null value\n Actual: None"
return {
"passed": False,
"error": error_msg,
"value": value
}
return {
"passed": True,
"value": value
}

View File

@@ -0,0 +1,23 @@
"""Workflow plugin: assert value is false."""
def run(_runtime, inputs):
"""Assert that a value is false."""
value = inputs.get("value")
message = inputs.get("message", "")
passed = value is False
if not passed:
error_msg = f"Assertion failed: {message}" if message else "Assertion failed"
error_msg += f"\n Expected: False\n Actual: {value}"
return {
"passed": False,
"error": error_msg,
"value": value
}
return {
"passed": True,
"value": value
}

View File

@@ -0,0 +1,23 @@
"""Workflow plugin: assert value is true."""
def run(_runtime, inputs):
"""Assert that a value is true."""
value = inputs.get("value")
message = inputs.get("message", "")
passed = value is True
if not passed:
error_msg = f"Assertion failed: {message}" if message else "Assertion failed"
error_msg += f"\n Expected: True\n Actual: {value}"
return {
"passed": False,
"error": error_msg,
"value": value
}
return {
"passed": True,
"value": value
}

View File

@@ -0,0 +1,64 @@
"""Workflow plugin: run a suite of test assertions and report results."""
def run(_runtime, inputs):
"""
Run a suite of test assertions and aggregate results.
Inputs:
- results: Array of test result objects (each with 'passed' field)
- suite_name: Optional name for the test suite
Outputs:
- passed: Boolean indicating if all tests passed
- total: Total number of tests
- passed_count: Number of tests that passed
- failed_count: Number of tests that failed
- failures: Array of failed test details
"""
results = inputs.get("results", [])
suite_name = inputs.get("suite_name", "Test Suite")
if not isinstance(results, list):
return {
"passed": False,
"error": "results must be an array",
"total": 0,
"passed_count": 0,
"failed_count": 0,
"failures": []
}
total = len(results)
passed_count = 0
failed_count = 0
failures = []
for i, result in enumerate(results):
if isinstance(result, dict) and result.get("passed") is True:
passed_count += 1
else:
failed_count += 1
failure_info = {
"test_index": i,
"error": result.get("error", "Unknown error") if isinstance(result, dict) else str(result)
}
if isinstance(result, dict):
failure_info.update({
"expected": result.get("expected"),
"actual": result.get("actual")
})
failures.append(failure_info)
all_passed = failed_count == 0 and total > 0
summary = f"{suite_name}: {passed_count}/{total} tests passed"
return {
"passed": all_passed,
"total": total,
"passed_count": passed_count,
"failed_count": failed_count,
"failures": failures,
"summary": summary
}

View File

@@ -0,0 +1,225 @@
"""Test the new unit testing workflow plugins."""
from autometabuilder.workflow.plugin_registry import PluginRegistry, load_plugin_map
from autometabuilder.workflow.runtime import WorkflowRuntime
class MockLogger:
"""Mock logger for testing."""
def info(self, *args, **kwargs):
pass
def debug(self, *args, **kwargs):
pass
def error(self, *args, **kwargs):
pass
def create_test_runtime():
"""Create a test runtime with empty context."""
logger = MockLogger()
return WorkflowRuntime(context={}, store={}, tool_runner=None, logger=logger)
def test_assert_equals_pass():
"""Test test.assert_equals plugin when values are equal."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.assert_equals")
assert plugin is not None
result = plugin(runtime, {"actual": 42, "expected": 42})
assert result["passed"] is True
assert result["actual"] == 42
assert result["expected"] == 42
def test_assert_equals_fail():
"""Test test.assert_equals plugin when values are not equal."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.assert_equals")
assert plugin is not None
result = plugin(runtime, {"actual": 42, "expected": 24, "message": "Test message"})
assert result["passed"] is False
assert "error" in result
assert "Test message" in result["error"]
def test_assert_true_pass():
"""Test test.assert_true plugin when value is true."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.assert_true")
assert plugin is not None
result = plugin(runtime, {"value": True})
assert result["passed"] is True
def test_assert_true_fail():
"""Test test.assert_true plugin when value is not true."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.assert_true")
assert plugin is not None
result = plugin(runtime, {"value": False})
assert result["passed"] is False
assert "error" in result
def test_assert_false_pass():
"""Test test.assert_false plugin when value is false."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.assert_false")
assert plugin is not None
result = plugin(runtime, {"value": False})
assert result["passed"] is True
def test_assert_false_fail():
"""Test test.assert_false plugin when value is not false."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.assert_false")
assert plugin is not None
result = plugin(runtime, {"value": True})
assert result["passed"] is False
assert "error" in result
def test_assert_exists_pass():
"""Test test.assert_exists plugin when value exists."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.assert_exists")
assert plugin is not None
result = plugin(runtime, {"value": "some value"})
assert result["passed"] is True
def test_assert_exists_fail():
"""Test test.assert_exists plugin when value is None."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.assert_exists")
assert plugin is not None
result = plugin(runtime, {"value": None})
assert result["passed"] is False
assert "error" in result
def test_run_suite_all_pass():
"""Test test.run_suite plugin when all tests pass."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.run_suite")
assert plugin is not None
results = [
{"passed": True},
{"passed": True},
{"passed": True}
]
result = plugin(runtime, {"results": results, "suite_name": "Test Suite"})
assert result["passed"] is True
assert result["total"] == 3
assert result["passed_count"] == 3
assert result["failed_count"] == 0
assert len(result["failures"]) == 0
def test_run_suite_with_failures():
"""Test test.run_suite plugin when some tests fail."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("test.run_suite")
assert plugin is not None
results = [
{"passed": True},
{"passed": False, "error": "Test failed", "expected": 5, "actual": 3},
{"passed": True}
]
result = plugin(runtime, {"results": results})
assert result["passed"] is False
assert result["total"] == 3
assert result["passed_count"] == 2
assert result["failed_count"] == 1
assert len(result["failures"]) == 1
assert result["failures"][0]["test_index"] == 1
assert "Test failed" in result["failures"][0]["error"]
def test_var_plugins():
"""Test var.get, var.set, var.exists, var.delete plugins."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
set_plugin = registry.get("var.set")
get_plugin = registry.get("var.get")
exists_plugin = registry.get("var.exists")
delete_plugin = registry.get("var.delete")
assert set_plugin is not None
assert get_plugin is not None
assert exists_plugin is not None
assert delete_plugin is not None
# Set a variable
set_result = set_plugin(runtime, {"key": "test_key", "value": "test_value"})
assert set_result["result"] == "test_value"
assert set_result["key"] == "test_key"
# Check if exists
exists_result = exists_plugin(runtime, {"key": "test_key"})
assert exists_result["result"] is True
# Get the variable
get_result = get_plugin(runtime, {"key": "test_key"})
assert get_result["result"] == "test_value"
assert get_result["exists"] is True
# Delete the variable
delete_result = delete_plugin(runtime, {"key": "test_key"})
assert delete_result["result"] is True
assert delete_result["deleted"] is True
# Check if still exists
exists_result2 = exists_plugin(runtime, {"key": "test_key"})
assert exists_result2["result"] is False
# Try to get deleted variable
get_result2 = get_plugin(runtime, {"key": "test_key", "default": "default_value"})
assert get_result2["result"] == "default_value"
assert get_result2["exists"] is False