mirror of
https://github.com/reconurge/flowsint.git
synced 2026-07-12 17:12:31 -05:00
Compare commits
9 Commits
fix/prebui
...
v1.2.10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12bf2937c1 | ||
|
|
612d39eadf | ||
|
|
dc36ef633e | ||
|
|
22c6908a0e | ||
|
|
71e3a68830 | ||
|
|
a24551ed55 | ||
|
|
17e36c9162 | ||
|
|
07584ebc4a | ||
|
|
db9f50d914 |
37
.github/workflows/tests.yml
vendored
Normal file
37
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Python tests
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
enable-cache: true
|
||||
|
||||
# Same entrypoint as local development — keeps CI and `make test` in sync.
|
||||
- name: Run tests
|
||||
run: make test
|
||||
env:
|
||||
# flowsint_core hard-requires these at import time (provided by .env
|
||||
# locally). Dummy values — no service is reached during tests:
|
||||
# connections (redis.from_url, create_engine) are lazy.
|
||||
AUTH_SECRET: ci-only-dummy-secret
|
||||
REDIS_URL: redis://127.0.0.1:6379/0
|
||||
1
Makefile
1
Makefile
@@ -140,6 +140,7 @@ test:
|
||||
cd flowsint-types && uv run pytest
|
||||
cd flowsint-core && uv run pytest
|
||||
cd flowsint-enrichers && uv run pytest
|
||||
cd flowsint-api && uv run pytest
|
||||
|
||||
install:
|
||||
$(MAKE) infra-dev
|
||||
|
||||
@@ -14,6 +14,7 @@ dependencies = [
|
||||
"redis>=5.0,<6.0",
|
||||
"celery>=5.3,<6.0",
|
||||
"python-dotenv>=1.0,<2.0",
|
||||
"pyyaml>=6.0,<7.0",
|
||||
"requests>=2.31,<3.0",
|
||||
"httpx>=0.28,<0.29",
|
||||
"networkx>=2.6.3,<3.0.0",
|
||||
|
||||
@@ -17,6 +17,12 @@ def setup_test_environment(monkeypatch):
|
||||
# Set a test master key for vault tests
|
||||
test_key = "base64:qnHTmwYb+uoygIw9MsRMY22vS5YPchY+QOi/E79GAvM="
|
||||
monkeypatch.setenv("MASTER_VAULT_KEY_V1", test_key)
|
||||
# Dummy Neo4j credentials: the Neo4jConnection singleton requires them
|
||||
# at construction, but driver creation is lazy — nothing connects.
|
||||
# Without these, tests silently depend on the developer's local .env.
|
||||
monkeypatch.setenv("NEO4J_URI_BOLT", "bolt://127.0.0.1:7687")
|
||||
monkeypatch.setenv("NEO4J_USERNAME", "neo4j")
|
||||
monkeypatch.setenv("NEO4J_PASSWORD", "test-password")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -7,6 +7,17 @@ from datetime import datetime, timezone
|
||||
from flowsint_core.core.graph import Neo4jGraphRepository
|
||||
|
||||
|
||||
def repo_without_connection() -> Neo4jGraphRepository:
|
||||
"""Repository with no underlying connection.
|
||||
|
||||
Constructed with a mock to avoid the constructor's singleton fallback,
|
||||
which requires NEO4J_* credentials from the environment.
|
||||
"""
|
||||
repo = Neo4jGraphRepository(neo4j_connection=MagicMock())
|
||||
repo._connection = None
|
||||
return repo
|
||||
|
||||
|
||||
class TestNeo4jGraphRepositoryInit:
|
||||
def test_init_with_connection(self):
|
||||
mock_connection = MagicMock()
|
||||
@@ -43,8 +54,7 @@ class TestCreateNode:
|
||||
mock_connection.query.assert_called_once()
|
||||
|
||||
def test_create_node_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.create_node({"nodeLabel": "test", "nodeType": "domain"}, "sketch-1")
|
||||
|
||||
@@ -80,8 +90,7 @@ class TestCreateRelationship:
|
||||
mock_connection.execute_write.assert_called_once()
|
||||
|
||||
def test_create_relationship_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
rel_obj = {
|
||||
"from_type": "domain",
|
||||
@@ -231,8 +240,7 @@ class TestBatchOperations:
|
||||
mock_connection.execute_batch.assert_not_called()
|
||||
|
||||
def test_flush_batch_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
repo._batch_operations = [("query", {})]
|
||||
|
||||
repo.flush_batch()
|
||||
@@ -285,8 +293,7 @@ class TestBatchCreateNodes:
|
||||
assert result["errors"] == []
|
||||
|
||||
def test_batch_create_nodes_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.batch_create_nodes(
|
||||
[{"nodeLabel": "test", "nodeType": "domain"}], sketch_id="sketch-1"
|
||||
@@ -338,8 +345,7 @@ class TestBatchCreateEdges:
|
||||
assert result["errors"] == []
|
||||
|
||||
def test_batch_create_edges_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.batch_create_edges([{}], sketch_id="sketch-1")
|
||||
|
||||
@@ -385,8 +391,7 @@ class TestBatchCreateEdgesByElementId:
|
||||
assert any("Missing required fields" in e for e in result["errors"])
|
||||
|
||||
def test_batch_create_edges_by_element_id_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.batch_create_edges_by_element_id([{}], sketch_id="sketch-1")
|
||||
|
||||
@@ -409,8 +414,7 @@ class TestUpdateNode:
|
||||
assert result == "elem-1"
|
||||
|
||||
def test_update_node_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.update_node("elem-1", {"nodeLabel": "x"}, "sketch-1")
|
||||
|
||||
@@ -428,8 +432,7 @@ class TestDeleteNodes:
|
||||
assert result == 3
|
||||
|
||||
def test_delete_nodes_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.delete_nodes(["id-1"], sketch_id="sketch-1")
|
||||
|
||||
@@ -456,8 +459,7 @@ class TestDeleteRelationships:
|
||||
assert result == 2
|
||||
|
||||
def test_delete_relationships_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.delete_relationships(["rel-1"], sketch_id="sketch-1")
|
||||
|
||||
@@ -483,8 +485,7 @@ class TestDeleteAllSketchNodes:
|
||||
assert result == 10
|
||||
|
||||
def test_delete_all_sketch_nodes_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.delete_all_sketch_nodes(sketch_id="sketch-1")
|
||||
|
||||
@@ -519,8 +520,7 @@ class TestGetSketchGraph:
|
||||
assert len(result["edges"]) == 1
|
||||
|
||||
def test_get_sketch_graph_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.get_sketch_graph(sketch_id="sketch-1")
|
||||
|
||||
@@ -553,8 +553,7 @@ class TestUpdateRelationship:
|
||||
assert result["id"] == "rel-1"
|
||||
|
||||
def test_update_relationship_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.update_relationship("rel-1", {}, "sketch-1")
|
||||
|
||||
@@ -577,8 +576,7 @@ class TestCreateRelationshipByElementId:
|
||||
assert result["sketch_id"] == "sketch-1"
|
||||
|
||||
def test_create_relationship_by_element_id_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.create_relationship_by_element_id(
|
||||
"elem-1", "elem-2", "CONNECTS", "sketch-1"
|
||||
@@ -598,8 +596,7 @@ class TestQuery:
|
||||
assert result == [{"count": 5}]
|
||||
|
||||
def test_query_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.query("MATCH (n) RETURN n", {})
|
||||
|
||||
@@ -622,8 +619,7 @@ class TestUpdateNodesPositions:
|
||||
assert result == 2
|
||||
|
||||
def test_update_nodes_positions_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.update_nodes_positions([{"nodeId": "x", "x": 0, "y": 0}], "s")
|
||||
|
||||
@@ -652,8 +648,7 @@ class TestGetNodesByIds:
|
||||
assert len(result) == 2
|
||||
|
||||
def test_get_nodes_by_ids_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.get_nodes_by_ids(["id-1"], sketch_id="sketch-1")
|
||||
|
||||
@@ -706,8 +701,7 @@ class TestMergeNodes:
|
||||
assert result == "old-1"
|
||||
|
||||
def test_merge_nodes_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.merge_nodes(["old-1"], {}, None, "sketch-1")
|
||||
|
||||
@@ -764,8 +758,7 @@ class TestGetNeighbors:
|
||||
assert len(result["edges"]) == 0
|
||||
|
||||
def test_get_neighbors_no_connection(self):
|
||||
repo = Neo4jGraphRepository(neo4j_connection=None)
|
||||
repo._connection = None
|
||||
repo = repo_without_connection()
|
||||
|
||||
result = repo.get_neighbors("node-1", "sketch-1")
|
||||
|
||||
|
||||
@@ -2,6 +2,19 @@ import pytest
|
||||
from tests.logger import TestLogger
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_test_environment(monkeypatch):
|
||||
"""Set up test environment variables.
|
||||
|
||||
Dummy Neo4j credentials: the Neo4jConnection singleton requires them
|
||||
at construction, but driver creation is lazy — nothing connects.
|
||||
Without these, tests silently depend on the developer's local .env.
|
||||
"""
|
||||
monkeypatch.setenv("NEO4J_URI_BOLT", "bolt://127.0.0.1:7687")
|
||||
monkeypatch.setenv("NEO4J_USERNAME", "neo4j")
|
||||
monkeypatch.setenv("NEO4J_PASSWORD", "test-password")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_logger(monkeypatch):
|
||||
"""Automatically replace the production Logger with TestLogger for all tests."""
|
||||
|
||||
2
uv.lock
generated
2
uv.lock
generated
@@ -1205,6 +1205,7 @@ dependencies = [
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "python-jose", extra = ["cryptography"] },
|
||||
{ name = "python-multipart" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "redis" },
|
||||
{ name = "requests" },
|
||||
{ name = "sqlalchemy" },
|
||||
@@ -1243,6 +1244,7 @@ requires-dist = [
|
||||
{ name = "python-dotenv", specifier = ">=1.0,<2.0" },
|
||||
{ name = "python-jose", extras = ["cryptography"], specifier = ">=3.3,<4.0" },
|
||||
{ name = "python-multipart", specifier = ">=0.0.27,<0.1.0" },
|
||||
{ name = "pyyaml", specifier = ">=6.0,<7.0" },
|
||||
{ name = "redis", specifier = ">=5.0,<6.0" },
|
||||
{ name = "requests", specifier = ">=2.31,<3.0" },
|
||||
{ name = "sqlalchemy", specifier = ">=2.0,<3.0" },
|
||||
|
||||
Reference in New Issue
Block a user