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