90 lines
2.9 KiB
Python
90 lines
2.9 KiB
Python
|
|
# Copyright (c) "Neo4j"
|
||
|
|
# Neo4j Sweden AB [https://neo4j.com]
|
||
|
|
# #
|
||
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
# you may not use this file except in compliance with the License.
|
||
|
|
# You may obtain a copy of the License at
|
||
|
|
# #
|
||
|
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
# #
|
||
|
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
# See the License for the specific language governing permissions and
|
||
|
|
# limitations under the License.
|
||
|
|
from unittest.mock import AsyncMock
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from neo4j_graphrag.experimental.pipeline import Component
|
||
|
|
from neo4j_graphrag.experimental.pipeline.types.context import RunContext
|
||
|
|
from .components import ComponentMultiply, ComponentMultiplyWithContext, IntResultModel
|
||
|
|
|
||
|
|
|
||
|
|
def test_component_inputs() -> None:
|
||
|
|
inputs = ComponentMultiply.component_inputs
|
||
|
|
assert "number1" in inputs
|
||
|
|
assert inputs["number1"]["has_default"] is False
|
||
|
|
assert "number2" in inputs
|
||
|
|
assert inputs["number2"]["has_default"] is True
|
||
|
|
|
||
|
|
|
||
|
|
def test_component_outputs() -> None:
|
||
|
|
outputs = ComponentMultiply.component_outputs
|
||
|
|
assert "result" in outputs
|
||
|
|
assert outputs["result"]["has_default"] is True
|
||
|
|
assert outputs["result"]["annotation"] == int
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_component_run() -> None:
|
||
|
|
c = ComponentMultiply()
|
||
|
|
result = await c.run(number1=1, number2=2)
|
||
|
|
assert isinstance(result, IntResultModel)
|
||
|
|
assert isinstance(
|
||
|
|
result.result,
|
||
|
|
# we know this is a type and not a bool or str:
|
||
|
|
ComponentMultiply.component_outputs["result"]["annotation"], # type: ignore
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_component_run_with_context_default_implementation() -> None:
|
||
|
|
c = ComponentMultiply()
|
||
|
|
result = await c.run_with_context(
|
||
|
|
# context can not be null in the function signature,
|
||
|
|
# but it's ignored in this case
|
||
|
|
None, # type: ignore
|
||
|
|
number1=1,
|
||
|
|
number2=2,
|
||
|
|
)
|
||
|
|
# the type checker doesn't know about the type
|
||
|
|
# because the method is not re-declared
|
||
|
|
assert result.result == 2 # type: ignore
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_component_run_with_context() -> None:
|
||
|
|
c = ComponentMultiplyWithContext()
|
||
|
|
notifier_mock = AsyncMock()
|
||
|
|
result = await c.run_with_context(
|
||
|
|
RunContext(run_id="run_id", task_name="task_name", notifier=notifier_mock),
|
||
|
|
number1=1,
|
||
|
|
number2=2,
|
||
|
|
)
|
||
|
|
assert result.result == 2
|
||
|
|
notifier_mock.assert_awaited_once()
|
||
|
|
|
||
|
|
|
||
|
|
def test_component_missing_method() -> None:
|
||
|
|
with pytest.raises(RuntimeError) as e:
|
||
|
|
|
||
|
|
class WrongComponent(Component):
|
||
|
|
# we must have either run or run_with_context
|
||
|
|
pass
|
||
|
|
|
||
|
|
assert (
|
||
|
|
"You must implement either `run` or `run_with_context` in Component 'WrongComponent'"
|
||
|
|
in str(e)
|
||
|
|
)
|