Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 8 additions & 7 deletions fluidize/adapters/local/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
wrapping the core GraphProcessor with adapter-specific functionality.
"""

from typing import Optional
from typing import TYPE_CHECKING, Optional

from fluidize.core.modules.graph.processor import GraphProcessor
from fluidize.core.types.graph import GraphData, GraphEdge, GraphNode
Expand All @@ -14,6 +14,9 @@
from fluidize.core.types.project import ProjectSummary
from fluidize.core.utils.pathfinder.path_finder import PathFinder

if TYPE_CHECKING:
from fluidize.managers.graph import InsertNodeRequest


class GraphHandler:
"""
Expand All @@ -40,20 +43,18 @@ def get_graph(self, project: ProjectSummary) -> GraphData:
processor = GraphProcessor(project)
return processor.get_graph()

def insert_node(self, project: ProjectSummary, node: GraphNode, sim_global: bool = True) -> GraphNode:
def insert_node(self, request: "InsertNodeRequest") -> GraphNode:
"""
Insert a new node into the project graph.

Args:
project: The project to add the node to
node: The node to insert
sim_global: Whether to use global simulations (placeholder for future)
request: InsertNodeRequest containing node, project, and sim_global

Returns:
The inserted node
"""
processor = GraphProcessor(project)
return processor.insert_node(node, sim_global)
processor = GraphProcessor(request.project)
return processor.insert_node(request.node, request.sim_global)

def insert_node_from_scratch(
self,
Expand Down
15 changes: 14 additions & 1 deletion fluidize/managers/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from typing import TYPE_CHECKING, Any, Optional

from pydantic import BaseModel

from fluidize.core.types.graph import GraphData, GraphEdge, GraphNode

if TYPE_CHECKING:
Expand All @@ -13,6 +15,15 @@
from fluidize.core.types.project import ProjectSummary


# TODO: the ty
class InsertNodeRequest(BaseModel):
"""Uniform request structure for inserting nodes in both local and API modes."""

node: GraphNode
project: ProjectSummary
sim_global: bool = True


class GraphManager:
"""
Graph manager for a specific project.
Expand Down Expand Up @@ -68,7 +79,9 @@ def add_node(self, node: GraphNode, sim_global: bool = True) -> "NodeManager":
Returns:
The added node
"""
inserted_node = self.adapter.graph.insert_node(self.project, node, sim_global)
# Create uniform request structure for both local and API modes
request = InsertNodeRequest(node=node, project=self.project, sim_global=sim_global)
inserted_node = self.adapter.graph.insert_node(request)
return self.get_node(inserted_node.id)

def add_node_from_scratch(
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ classifiers = [
dependencies = [
"asciitree>=0.3.3",
"docker>=7.1.0",
"fluidize-sdk>=0.6.0",
"fluidize-sdk>=0.7.0",
"jinja2>=3.1.6",
"mlflow>=3.1.4",
"networkx>=3.2.1",
Expand Down
23 changes: 17 additions & 6 deletions tests/unit/backends/local/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from fluidize.adapters.local.graph import GraphHandler
from fluidize.core.types.graph import GraphData
from fluidize.core.types.parameters import Parameter
from fluidize.managers.graph import InsertNodeRequest
from tests.fixtures.sample_graphs import SampleGraphs
from tests.fixtures.sample_projects import SampleProjects

Expand Down Expand Up @@ -77,7 +78,8 @@ def test_insert_node_success(self, graph_handler, mock_processor, sample_project
node = SampleGraphs.sample_nodes()[0]
mock_processor.insert_node.return_value = node

result = graph_handler.insert_node(sample_project, node, True)
request = InsertNodeRequest(node=node, project=sample_project, sim_global=True)
result = graph_handler.insert_node(request)

assert result == node
mock_processor.insert_node.assert_called_once_with(node, True)
Expand All @@ -87,7 +89,8 @@ def test_insert_node_with_sim_global_false(self, graph_handler, mock_processor,
node = SampleGraphs.sample_nodes()[1]
mock_processor.insert_node.return_value = node

result = graph_handler.insert_node(sample_project, node, False)
request = InsertNodeRequest(node=node, project=sample_project, sim_global=False)
result = graph_handler.insert_node(request)

assert result == node
mock_processor.insert_node.assert_called_once_with(node, False)
Expand All @@ -97,7 +100,8 @@ def test_insert_node_default_sim_global(self, graph_handler, mock_processor, sam
node = SampleGraphs.sample_nodes()[0]
mock_processor.insert_node.return_value = node

result = graph_handler.insert_node(sample_project, node)
request = InsertNodeRequest(node=node, project=sample_project) # sim_global defaults to True
result = graph_handler.insert_node(request)

assert result == node
mock_processor.insert_node.assert_called_once_with(node, True) # Default is True
Expand Down Expand Up @@ -160,7 +164,8 @@ def test_processor_error_propagation_insert_node(self, graph_handler, mock_proce
mock_processor.insert_node.side_effect = ValueError("Invalid node data")

with pytest.raises(ValueError, match="Invalid node data"):
graph_handler.insert_node(sample_project, node)
request = InsertNodeRequest(node=node, project=sample_project)
graph_handler.insert_node(request)

def test_processor_error_propagation_delete_node(self, graph_handler, mock_processor, sample_project):
"""Test that processor errors are propagated for delete_node."""
Expand Down Expand Up @@ -196,7 +201,8 @@ def test_processor_creation_per_operation(self, sample_project):

# Perform multiple operations
handler.get_graph(sample_project)
handler.insert_node(sample_project, SampleGraphs.sample_nodes()[0])
request = InsertNodeRequest(node=SampleGraphs.sample_nodes()[0], project=sample_project)
handler.insert_node(request)
handler.delete_node(sample_project, "test-id")

# Verify processor was created for each operation
Expand Down Expand Up @@ -245,7 +251,8 @@ def test_all_crud_operations_flow(self, sample_project):

# Perform full CRUD cycle
graph_data = handler.get_graph(sample_project)
inserted_node = handler.insert_node(sample_project, node)
request = InsertNodeRequest(node=node, project=sample_project)
inserted_node = handler.insert_node(request)
updated_node = handler.update_node_position(sample_project, node)
handler.delete_node(sample_project, "test-node-id")
upserted_edge = handler.upsert_edge(sample_project, edge)
Expand Down Expand Up @@ -297,6 +304,10 @@ def test_individual_operations(self, sample_project, operation, method_name, arg
handler_method = getattr(handler, operation)
if operation == "ensure_graph_initialized":
handler_method(sample_project)
elif operation == "insert_node":
# insert_node now uses InsertNodeRequest
request = InsertNodeRequest(node=args[0], project=sample_project, sim_global=args[1])
handler_method(request)
else:
handler_method(sample_project, *args)

Expand Down
45 changes: 34 additions & 11 deletions tests/unit/managers/test_project_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from fluidize.core.types.node import author, nodeMetadata_simulation, nodeProperties_simulation, tag
from fluidize.core.types.parameters import Parameter
from fluidize.core.types.runs import RunStatus
from fluidize.managers.graph import GraphManager
from fluidize.managers.graph import GraphManager, InsertNodeRequest
from fluidize.managers.node import NodeManager
from tests.fixtures.sample_graphs import SampleGraphs
from tests.fixtures.sample_projects import SampleProjects
Expand Down Expand Up @@ -97,11 +97,14 @@ def test_add_node_success(self, project_graph, mock_adapter):

assert isinstance(result, NodeManager)
assert result.node_id == node.id
mock_adapter.graph.insert_node.assert_called_once_with(
project_graph.project,
node,
True, # Default sim_global=True
)
# Verify that insert_node was called with an InsertNodeRequest
mock_adapter.graph.insert_node.assert_called_once()
call_args = mock_adapter.graph.insert_node.call_args[0]
request = call_args[0]
assert isinstance(request, InsertNodeRequest)
assert request.node == node
assert request.project == project_graph.project
assert request.sim_global is True

def test_add_node_with_sim_global_false(self, project_graph, mock_adapter):
"""Test node addition with sim_global=False."""
Expand All @@ -112,7 +115,14 @@ def test_add_node_with_sim_global_false(self, project_graph, mock_adapter):

assert isinstance(result, NodeManager)
assert result.node_id == node.id
mock_adapter.graph.insert_node.assert_called_once_with(project_graph.project, node, False)
# Verify that insert_node was called with an InsertNodeRequest with sim_global=False
mock_adapter.graph.insert_node.assert_called_once()
call_args = mock_adapter.graph.insert_node.call_args[0]
request = call_args[0]
assert isinstance(request, InsertNodeRequest)
assert request.node == node
assert request.project == project_graph.project
assert request.sim_global is False

def test_add_node_from_scratch_success(self, project_graph, mock_adapter):
"""Test successful node creation from scratch."""
Expand Down Expand Up @@ -296,8 +306,13 @@ def test_project_scoping(self, mock_adapter):
# Verify each call was made with correct project context
calls = mock_adapter.graph.insert_node.call_args_list
assert len(calls) == 2
assert calls[0][0][0] == project1 # First call with project1
assert calls[1][0][0] == project2 # Second call with project2
# Check that both calls received InsertNodeRequest with correct projects
request1 = calls[0][0][0]
request2 = calls[1][0][0]
assert isinstance(request1, InsertNodeRequest)
assert isinstance(request2, InsertNodeRequest)
assert request1.project == project1 # First call with project1
assert request2.project == project2 # Second call with project2

def test_all_methods_delegate_to_adapter(self, project_graph, mock_adapter):
"""Test that all GraphManager methods properly delegate to adapter."""
Expand Down Expand Up @@ -386,9 +401,17 @@ def test_project_context_consistency(self, project_graph, mock_adapter):
]

# All calls should include the same project as first argument
for call_list in all_calls:
# (except insert_node which now uses InsertNodeRequest)
for i, call_list in enumerate(all_calls):
if call_list: # If method was called
assert call_list[0][0][0] == project
if i == 1: # insert_node call index
# For insert_node, check the project in the InsertNodeRequest
request = call_list[0][0][0]
assert isinstance(request, InsertNodeRequest)
assert request.project == project
else:
# For other calls, project is still the first argument
assert call_list[0][0][0] == project

def test_get_parameters_success(self, project_graph, mock_adapter):
"""Test successful parameter retrieval through ProjectGraph."""
Expand Down
8 changes: 4 additions & 4 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.