Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep history depth needs to be a positive integer and test refactor #33

Merged
merged 25 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
918b80b
chore: Refactor clear_history_but_keep_depth method to handle negativ…
efunneko Sep 6, 2024
7b6a162
chore: small change to how this is solved
efunneko Sep 6, 2024
47b2330
chore: one more try
efunneko Sep 6, 2024
2aa837a
refactor: move utils_for_test_files.py to solace_ai_connector module
efunneko Sep 7, 2024
12637d8
refactor: removed the orginal utils_for_test_files.py
efunneko Sep 7, 2024
4c581cc
refactor: update import statements in test files
efunneko Sep 7, 2024
d16e97c
refactor: add sys.path.append("src") to test files
efunneko Sep 7, 2024
9161989
refactor: standardize import order and sys.path.append in test files
efunneko Sep 7, 2024
657c376
refactor: a bit more test infrastructure changes
efunneko Sep 7, 2024
0b68f05
feat: allow component_module to accept module objects directly
efunneko Sep 7, 2024
0524b99
feat: add types module import to utils.py
efunneko Sep 7, 2024
d4278b1
test: add static import and object config test
efunneko Sep 7, 2024
a4faa82
refactor: update test_static_import_and_object_config to use create_t…
efunneko Sep 7, 2024
3b3e7ed
refactor: Improve test structure and remove duplicate test case
efunneko Sep 7, 2024
dad0874
fix: remove duplicate import of yaml module
efunneko Sep 7, 2024
d3bd5ac
refactor: Modify test config to use dict instead of YAML string
efunneko Sep 7, 2024
6594f1b
refactor: convert config_yaml from string to dictionary
efunneko Sep 7, 2024
61fa23a
refactor: update static import test to use pass_through component
efunneko Sep 7, 2024
29683e0
test: Add delay component message passing test
efunneko Sep 7, 2024
9ecab7a
feat: add test for delay component message processing
efunneko Sep 7, 2024
151f808
feat: Added a new test function (test_one_component) to make it very …
efunneko Sep 7, 2024
c46ba2f
feat: added input_transforms to the test_one_component so that input …
efunneko Sep 7, 2024
0acd854
chore: a bit of cleanup and new tests for test_one_component
efunneko Sep 8, 2024
04cb63a
chore: rename test_one_component because it was being picked up as a …
efunneko Sep 8, 2024
768241f
fix: fixed a typo
efunneko Sep 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions src/solace_ai_connector/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
import builtins
import subprocess
import types

from .log import log

Expand Down Expand Up @@ -94,8 +95,11 @@ def resolve_config_values(config, allow_source_expression=False):
return config


def import_module(name, base_path=None, component_package=None):
"""Import a module by name"""
def import_module(module, base_path=None, component_package=None):
"""Import a module by name or return the module object if it's already imported"""

if isinstance(module, types.ModuleType):
return module

if component_package:
install_package(component_package)
Expand All @@ -104,14 +108,13 @@ def import_module(name, base_path=None, component_package=None):
if base_path not in sys.path:
sys.path.append(base_path)
try:
module = importlib.import_module(name)
return module
return importlib.import_module(module)
except ModuleNotFoundError as exc:
# If the module does not have a path associated with it, try
# importing it from the known prefixes - annoying that this
# is necessary. It seems you can't dynamically import a module
# that is listed in an __init__.py file :(
if "." not in name:
if "." not in module:
for prefix_prefix in ["solace_ai_connector", "."]:
for prefix in [
".components",
Expand All @@ -123,22 +126,21 @@ def import_module(name, base_path=None, component_package=None):
".transforms",
".common",
]:
full_name = f"{prefix_prefix}{prefix}.{name}"
full_name = f"{prefix_prefix}{prefix}.{module}"
try:
if full_name.startswith("."):
module = importlib.import_module(
return importlib.import_module(
full_name, package=__package__
)
else:
module = importlib.import_module(full_name)
return module
return importlib.import_module(full_name)
except ModuleNotFoundError:
pass
except Exception as e:
raise ImportError(
f"Module load error for {full_name}: {e}"
) from e
raise ModuleNotFoundError(f"Module '{name}' not found") from exc
raise ModuleNotFoundError(f"Module '{module}' not found") from exc


def invoke_config(config, allow_source_expression=False):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ def __init__(self, **kwargs):
def invoke(self, message, data):
session_id = data.get("session_id")
clear_history_but_keep_depth = data.get("clear_history_but_keep_depth")
try:
if clear_history_but_keep_depth is not None:
clear_history_but_keep_depth = max(0, int(clear_history_but_keep_depth))
except (TypeError, ValueError):
clear_history_but_keep_depth = 0
messages = data.get("messages", [])

with self.get_lock(self.history_key):
Expand Down
3 changes: 0 additions & 3 deletions src/solace_ai_connector/flow/flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,7 @@ def create_component_group(self, component, index):
base_path = component.get("component_base_path", None)
component_package = component.get("component_package", None)
num_instances = component.get("num_instances", 1)
# component_config = component.get("component_config", {})
# component_name = component.get("component_name", "")

# imported_module = import_from_directories(component_module)
imported_module = import_module(component_module, base_path, component_package)

try:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
"""Collection of functions to be used in test files"""

import os
import queue
import sys
import os
import yaml

sys.path.insert(0, os.path.abspath("src"))

from solace_ai_connector.solace_ai_connector import SolaceAiConnector
from solace_ai_connector.common.log import log
from solace_ai_connector.common.event import Event, EventType
from solace_ai_connector.common.message import Message

# from solace_ai_connector.common.message import Message

Expand Down Expand Up @@ -61,12 +60,16 @@ def enqueue(self, message):
self.next_component_queue.put(event)


def create_connector(config_yaml, event_handlers=None, error_queue=None):
"""Create a connector from a config"""
def create_connector(config_or_yaml, event_handlers=None, error_queue=None):
"""Create a connector from a config that can be an object or a yaml string"""

config = config_or_yaml
if isinstance(config_or_yaml, str):
config = yaml.safe_load(config_or_yaml)

# Create the connector
connector = SolaceAiConnector(
yaml.safe_load(config_yaml),
config,
event_handlers=event_handlers,
error_queue=error_queue,
)
Expand All @@ -76,9 +79,77 @@ def create_connector(config_yaml, event_handlers=None, error_queue=None):
return connector


def create_test_flows(config_yaml, queue_timeout=None, error_queue=None, queue_size=0):
def run_component_test(
module_or_name,
validation_func,
component_config=None,
input_data=None,
input_messages=None,
input_selection=None,
input_transforms=None,
):
if not input_data and not input_messages:
raise ValueError("Either input_data or input_messages must be provided")

if input_data and input_messages:
raise ValueError("Only one of input_data or input_messages can be provided")

if input_data and not isinstance(input_data, list):
input_data = [input_data]

if input_messages and not isinstance(input_messages, list):
input_messages = [input_messages]

if not input_messages:
input_messages = []

if input_selection:
if isinstance(input_selection, str):
input_selection = {"source_expression": input_selection}

connector = None
try:
connector, flows = create_test_flows(
{
"flows": [
{
"name": "test_flow",
"components": [
{
"component_name": "test_component",
"component_module": module_or_name,
"component_config": component_config or {},
"input_selection": input_selection,
"input_transforms": input_transforms,
}
],
}
]
}
)

if input_data:
for data in input_data:
message = Message(payload=data)
message.set_previous(data)
input_messages.append(message)

# Send each message through, one at a time
for message in input_messages:
send_message_to_flow(flows[0], message)
output_message = get_message_from_flow(flows[0])
validation_func(output_message.get_previous(), output_message, message)

finally:
if connector:
dispose_connector(connector)


def create_test_flows(
config_or_yaml, queue_timeout=None, error_queue=None, queue_size=0
):
# Create the connector
connector = create_connector(config_yaml, error_queue=error_queue)
connector = create_connector(config_or_yaml, error_queue=error_queue)

flows = connector.get_flows()

Expand Down
4 changes: 2 additions & 2 deletions tests/test_acks.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""This file tests acks in a flow"""

import sys
import queue

sys.path.append("src")
import queue

from utils_for_test_files import ( # pylint: disable=wrong-import-position
from solace_ai_connector.test_utils.utils_for_test_files import ( # pylint: disable=wrong-import-position
# create_connector,
# create_and_run_component,
dispose_connector,
Expand Down
5 changes: 4 additions & 1 deletion tests/test_aggregate.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""Some tests to verify the aggregate component works as expected"""

import sys

sys.path.append("src")
import time

from utils_for_test_files import (
from solace_ai_connector.test_utils.utils_for_test_files import (
create_test_flows,
dispose_connector,
send_message_to_flow,
Expand Down
51 changes: 49 additions & 2 deletions tests/test_config_file.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
"""Test various things related to the configuration file"""

import sys
import yaml
import pytest
import yaml

sys.path.append("src")

from utils_for_test_files import ( # pylint: disable=wrong-import-position
from solace_ai_connector.test_utils.utils_for_test_files import ( # pylint: disable=wrong-import-position
create_connector,
create_test_flows,
dispose_connector,
send_message_to_flow,
get_message_from_flow,
)

from solace_ai_connector.solace_ai_connector import ( # pylint: disable=wrong-import-position
SolaceAiConnector,
)

from solace_ai_connector.common.message import Message
import solace_ai_connector.components.general.pass_through

# from solace_ai_connector.common.log import log


Expand Down Expand Up @@ -143,6 +150,46 @@ def test_no_component_module():
assert str(e) == "component_module not provided in flow 0, component 0"


def test_static_import_and_object_config():
"""Test that we can statically import a module and pass an object for the config"""

config = {
"log": {"log_file_level": "DEBUG", "log_file": "solace_ai_connector.log"},
"flows": [
{
"name": "test_flow",
"components": [
{
"component_name": "delay1",
"component_module": solace_ai_connector.components.general.pass_through,
"component_config": {"delay": 0.1},
"input_selection": {"source_expression": "input.payload"},
}
],
}
],
}
connector = None
try:
connector, flows = create_test_flows(config)

# Test pushing a simple message through the delay component
message = Message(payload={"text": "Hello, World!"})
send_message_to_flow(flows[0], message)

# Get the output message
output_message = get_message_from_flow(flows[0])

# Check that the output is correct
assert output_message.get_data("previous") == {"text": "Hello, World!"}

except Exception as e:
pytest.fail(f"Test failed with exception: {e}")
finally:
if "connector" in locals():
dispose_connector(connector)


def test_bad_module():
"""Test that the program exits if the component module is not found"""
try:
Expand Down
6 changes: 3 additions & 3 deletions tests/test_error_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import sys

# import queue

sys.path.append("src")

from utils_for_test_files import ( # pylint: disable=wrong-import-position
# import queue

from solace_ai_connector.test_utils.utils_for_test_files import ( # pylint: disable=wrong-import-position
create_test_flows,
# create_and_run_component,
dispose_connector,
Expand Down
6 changes: 5 additions & 1 deletion tests/test_filter.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""Some tests to verify the filter component works as expected"""

import sys

sys.path.append("src")

# import pytest

from utils_for_test_files import (
from solace_ai_connector.test_utils.utils_for_test_files import (
create_test_flows,
# create_connector,
dispose_connector,
Expand Down
5 changes: 4 additions & 1 deletion tests/test_flows.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""This test file tests all things to do with the flows and the components that make up the flows"""

import sys

sys.path.append("src")
import pytest
import time

from utils_for_test_files import (
from solace_ai_connector.test_utils.utils_for_test_files import (
create_test_flows,
create_connector,
dispose_connector,
Expand Down
18 changes: 8 additions & 10 deletions tests/test_invoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

sys.path.append("src")

from utils_for_test_files import ( # pylint: disable=wrong-import-position

from solace_ai_connector.test_utils.utils_for_test_files import (
create_and_run_component,
)
from solace_ai_connector.common.utils import ( # pylint: disable=wrong-import-position
from solace_ai_connector.common.utils import (
resolve_config_values,
)
from solace_ai_connector.common.message import ( # pylint: disable=wrong-import-position
from solace_ai_connector.common.message import (
Message,
)

Expand Down Expand Up @@ -1083,16 +1084,13 @@ def test_invoke_with_uuid_generator():
response = resolve_config_values(
{
"a": {
"invoke": {
"module": "invoke_functions",
"function": "uuid"
},
"invoke": {"module": "invoke_functions", "function": "uuid"},
},
}
)
)

# Check if the output is of type string
assert type(response["a"]) == str

# Check if the output is a valid UUID
assert len(response["a"]) == 36
assert len(response["a"]) == 36
Loading
Loading