1 module testutils;
2 
3 version (unittest) {
4 	import memgraph;
5 
6 	// Port where the memgraph container is listening.
7 	enum MEMGRAPH_PORT = 7688;
8 
9 	bool canConnect() {
10 		import std.string, std.conv, std.stdio;
11 		auto params = mg_session_params_make();
12 		assert(params != null);
13 
14 		mg_session_params_set_host(params, toStringz("localhost"));
15 		mg_session_params_set_port(params, to!ushort(MEMGRAPH_PORT));
16 		mg_session_params_set_sslmode(params, mg_sslmode.MG_SSLMODE_DISABLE);
17 
18 		mg_session *session = null;
19 		immutable status = mg_connect(params, &session);
20 		mg_session_params_destroy(params);
21 
22 		mg_session_destroy(session);
23 
24 		return status == 0;
25 	}	// canConnect()
26 
27 	// Start a memgraph container for unit testing if it is not already running.
28 	// Store the container id in $TMP/memgraph-d.container so it can be used in
29 	// other tests without having to start a new container each time.
30 	void startContainer() {
31 		import std.process, std.stdio, std.file, std.string;
32 
33 		import std.conv;
34 
35 		auto containerIdFileName = environment.get("TMP", "/tmp") ~ "/memgraph-d.container";
36 
37 		auto startContainer = true;
38 
39 		if (exists(containerIdFileName)) {
40 			// Read container id from temp storage.
41 			auto containerIdFile = File(containerIdFileName, "r");
42 			auto containerId = containerIdFile.readln();
43 			containerIdFile.close();
44 
45 			// Check if the container is still up and running.
46 			auto ps = execute(["docker", "ps", "-q", "--no-trunc"]);
47 			assert(ps.status == 0);
48 			startContainer = false;
49 
50 			if (ps.output.indexOf(containerId) < 0)
51 				startContainer = true;
52 		}
53 
54 		if (startContainer) {
55 			import std.conv;
56 
57 			// Pull the latest memgraph docker image.
58 			immutable pull = execute(["docker", "pull", "memgraph/memgraph"]);
59 			assert(pull.status == 0);
60 
61 			// Start a new memgraph docker container.
62 			auto containerIdFile = File(containerIdFileName, "w");
63 			immutable run = execute(["docker", "run", "-d", "-p",
64 								to!string(MEMGRAPH_PORT) ~ ":7687", "-d", "memgraph/memgraph"]);
65 			assert(run.status == 0);
66 
67 			// Store container id.
68 			auto containerId = run.output;
69 			containerIdFile.write(containerId);
70 			containerIdFile.close();
71 
72 			// Need to wait a while until the container is spun up, otherwise connecting will fail.
73 			while (!canConnect()) {
74 				import core.thread.osthread, core.time;
75 				Thread.sleep(dur!("msecs")(250));
76 			}
77 		}
78 	}	// startContainer()
79 
80 	// Create a client connection to the running unit test container.
81 	auto connectContainer() {
82 		startContainer(); // Make sure container is up.
83 		Params params;
84 		params.port = MEMGRAPH_PORT;
85 		return Client.connect(params);
86 	}	// connectContainer()
87 
88 	// Create an index on the test data.
89 	void createTestIndex(ref Client client) {
90 		assert(client.run("CREATE INDEX ON :Person(id);"), client.error);
91 	}	// createTestIndex()
92 
93 	// Delete the test data.
94 	void deleteTestData(ref Client client) {
95 		assert(client.run("MATCH (n) DETACH DELETE n;"), client.error);
96 	}	// deleteTestData()
97 
98 	// Create some test data.
99 	void createTestData(ref Client client) {
100 		// Create a few nodes.
101 		assert(client.run(
102 			"CREATE (:Person:Entrepreneur {id: 0, age: 40, name: 'John', " ~
103 				"isStudent: false, score: 5.0});"), client.error);
104 		assert(client.run(
105 			"CREATE (:Person:Entrepreneur {id: 1, age: 20, name: 'Valery', " ~
106 				"isStudent: true, score: 5.0});"), client.error);
107 		assert(client.run(
108 			"CREATE (:Person:Entrepreneur {id: 2, age: 50, name: 'Peter', " ~
109 				"isStudent: false, score: 4.0});"), client.error);
110 		assert(client.run(
111 			"CREATE (:Person:Entrepreneur {id: 3, age: 30, name: 'Ray', " ~
112 				"isStudent: false, score: 9.0});"), client.error);
113 		assert(client.run(
114 			"CREATE (:Person:Entrepreneur {id: 4, age: 25, name: 'Olaf', " ~
115 				"isStudent: true, score: 10.0});"), client.error);
116 
117 		// Create some relationships.
118 		assert(client.run(
119 			"MATCH (a:Person), (b:Person) WHERE a.name = 'John' AND b.name = 'Peter' " ~
120 				"CREATE (a)-[r:IS_MANAGER]->(b);"), client.error);
121 		assert(client.run(
122 			"MATCH (a:Person), (b:Person) WHERE a.name = 'Peter' AND b.name = 'Valery' " ~
123 				"CREATE (a)-[r:IS_MANAGER]->(b);"), client.error);
124 		assert(client.run(
125 			"MATCH (a:Person), (b:Person) WHERE a.name = 'Valery' AND b.name = 'Olaf' " ~
126 				"CREATE (a)-[r:IS_MANAGER]->(b);"), client.error);
127 	}	// createTestData()
128 
129 }