|
@@ -1,6 +1,8 @@
|
|
|
from __future__ import annotations
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
import os
|
|
import os
|
|
|
|
|
+from concurrent.futures import ThreadPoolExecutor
|
|
|
|
|
+from threading import Barrier
|
|
|
|
|
|
|
|
import pytest
|
|
import pytest
|
|
|
from neo4j import GraphDatabase
|
|
from neo4j import GraphDatabase
|
|
@@ -10,6 +12,76 @@ from app.core.common.identifiers import new_governance_uid
|
|
|
from app.core.data_flow.dataflows import DataFlowService
|
|
from app.core.data_flow.dataflows import DataFlowService
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def test_real_neo4j_concurrent_different_uids_same_name_is_closed():
|
|
|
|
|
+ uri = os.environ.get("DATA_RULE_NEO4J_ACCEPTANCE_URI")
|
|
|
|
|
+ password = os.environ.get("DATA_RULE_NEO4J_ACCEPTANCE_PASSWORD")
|
|
|
|
|
+ if not uri or not password:
|
|
|
|
|
+ pytest.skip("real Neo4j acceptance connection is not configured")
|
|
|
|
|
+ user = os.environ.get("DATA_RULE_NEO4J_ACCEPTANCE_USER", "neo4j")
|
|
|
|
|
+ app = create_app()
|
|
|
|
|
+ app.config.update(
|
|
|
|
|
+ TESTING=True,
|
|
|
|
|
+ NEO4J_URI=uri,
|
|
|
|
|
+ NEO4J_USER=user,
|
|
|
|
|
+ NEO4J_PASSWORD=password,
|
|
|
|
|
+ NEO4J_ENCRYPTED=False,
|
|
|
|
|
+ )
|
|
|
|
|
+ name = f"同名并发生产线-{new_governance_uid()}"
|
|
|
|
|
+ uids = [new_governance_uid(), new_governance_uid()]
|
|
|
|
|
+ start = Barrier(2)
|
|
|
|
|
+ driver = GraphDatabase.driver(
|
|
|
|
|
+ uri, auth=(user, password), encrypted=False
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ def create(uid):
|
|
|
|
|
+ start.wait()
|
|
|
|
|
+ try:
|
|
|
|
|
+ with app.app_context():
|
|
|
|
|
+ DataFlowService._merge_governed_dataflow(
|
|
|
|
|
+ {
|
|
|
|
|
+ "uid": uid,
|
|
|
|
|
+ "name_zh": name,
|
|
|
|
|
+ "name_en": f"concurrent_{uid.replace('-', '')}",
|
|
|
|
|
+ "script_type": "governed",
|
|
|
|
|
+ "script_requirement": "{}",
|
|
|
|
|
+ "script_path": "",
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ return "created"
|
|
|
|
|
+ except ValueError as exc:
|
|
|
|
|
+ return str(exc)
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ with driver.session() as session:
|
|
|
|
|
+ session.run(
|
|
|
|
|
+ "MATCH (n:DataFlow {name_zh: $name}) DETACH DELETE n",
|
|
|
|
|
+ {"name": name},
|
|
|
|
|
+ ).consume()
|
|
|
|
|
+ with ThreadPoolExecutor(max_workers=2) as pool:
|
|
|
|
|
+ outcomes = list(pool.map(create, uids))
|
|
|
|
|
+ assert sorted(outcomes) == ["created", "dataflow_uid_conflict"]
|
|
|
|
|
+ with driver.session() as session:
|
|
|
|
|
+ count = session.run(
|
|
|
|
|
+ "MATCH (n:DataFlow {name_zh: $name}) "
|
|
|
|
|
+ "RETURN count(n) AS count",
|
|
|
|
|
+ {"name": name},
|
|
|
|
|
+ ).single()["count"]
|
|
|
|
|
+ constraints = session.run(
|
|
|
|
|
+ "SHOW CONSTRAINTS YIELD name "
|
|
|
|
|
+ "WHERE name IN ['data_flow_uid', 'data_flow_name_zh'] "
|
|
|
|
|
+ "RETURN collect(name) AS names"
|
|
|
|
|
+ ).single()["names"]
|
|
|
|
|
+ assert count == 1
|
|
|
|
|
+ assert set(constraints) == {"data_flow_uid", "data_flow_name_zh"}
|
|
|
|
|
+ finally:
|
|
|
|
|
+ with driver.session() as session:
|
|
|
|
|
+ session.run(
|
|
|
|
|
+ "MATCH (n:DataFlow {name_zh: $name}) DETACH DELETE n",
|
|
|
|
|
+ {"name": name},
|
|
|
|
|
+ ).consume()
|
|
|
|
|
+ driver.close()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def test_real_neo4j_uid_constraint_merge_replay_and_conflict():
|
|
def test_real_neo4j_uid_constraint_merge_replay_and_conflict():
|
|
|
uri = os.environ.get("DATA_RULE_NEO4J_ACCEPTANCE_URI")
|
|
uri = os.environ.get("DATA_RULE_NEO4J_ACCEPTANCE_URI")
|
|
|
password = os.environ.get("DATA_RULE_NEO4J_ACCEPTANCE_PASSWORD")
|
|
password = os.environ.get("DATA_RULE_NEO4J_ACCEPTANCE_PASSWORD")
|
|
@@ -52,12 +124,13 @@ def test_real_neo4j_uid_constraint_merge_replay_and_conflict():
|
|
|
constraints = [
|
|
constraints = [
|
|
|
record["name"]
|
|
record["name"]
|
|
|
for record in session.run(
|
|
for record in session.run(
|
|
|
- "SHOW CONSTRAINTS YIELD name WHERE name = 'data_flow_uid' "
|
|
|
|
|
|
|
+ "SHOW CONSTRAINTS YIELD name "
|
|
|
|
|
+ "WHERE name IN ['data_flow_uid', 'data_flow_name_zh'] "
|
|
|
"RETURN name"
|
|
"RETURN name"
|
|
|
)
|
|
)
|
|
|
]
|
|
]
|
|
|
assert count == 1
|
|
assert count == 1
|
|
|
- assert constraints == ["data_flow_uid"]
|
|
|
|
|
|
|
+ assert set(constraints) == {"data_flow_uid", "data_flow_name_zh"}
|
|
|
finally:
|
|
finally:
|
|
|
with driver.session() as session:
|
|
with driver.session() as session:
|
|
|
session.run("MATCH (n:DataFlow {uid: $uid}) DETACH DELETE n", {"uid": uid})
|
|
session.run("MATCH (n:DataFlow {uid: $uid}) DETACH DELETE n", {"uid": uid})
|