Testing nit with various services is a PITA.
The previous solution was to test them on localhost and requires that the services are available and configured on each test node (it is not always as easy as it seems).
Another problem with `localhost` is that testing within docker is complex as running multiple services in a single container is discouraged.
Here, we propose to simply move the service from localhost to specific hostnames.
This is to be the current "good" practice and is supported out-of-the-box bu various CI infrastructure including gitlab-ci.
Pull-Request: #2737
image: nitlang/nit-ci
+services:
+ - mongo
+ - neo4j:2.3
+ - postgres
+
+variables:
+ NEO4J_AUTH: none
+
cache:
paths:
- .ccache
redef var key = name is lazy
# Mongo server url where this game data are stored.
- var mongo_url = "mongodb://localhost:27017" is writable
+ var mongo_url = "mongodb://mongo:27017" is writable
# Mongo db client.
var client = new MongoClient(mongo_url) is lazy
end
# Mongo API client
- var mongo = new MongoClient("mongodb://localhost:27017/")
+ var mongo = new MongoClient("mongodb://mongo:27017/")
# Load a new test database by with a name
private fun load_db(name: String): MongoDb do return mongo.database(name)
fun load_games: Array[Game] do
var res = new Array[Game]
# TODO should be option
- var mongo = new MongoClient("mongodb://localhost:27017")
+ var mongo = new MongoClient("mongodb://mongo:27017")
var db = mongo.database("nitrpg")
for obj in db.collection("games").find_all(new JsonObject) do
var repo = api.load_repo(obj["name"].to_s)
redef var default_config_file = "loader.ini"
# Default database host string for MongoDb
- var default_db_host = "mongodb://localhost:27017/"
+ var default_db_host = "mongodb://mongo:27017/"
# Default database hostname
var default_db_name = "github_loader"
#
# ~~~
# # Opens the connexion with the Mongo server.
-# var client = new MongoClient("mongodb://localhost:27017/")
+# var client = new MongoClient("mongodb://mongo:27017/")
#
# # Select the database.
# var db_suffix = "NIT_TESTING_ID".environ
# Usage:
#
# ~~~
-# var uri = "mongodb://localhost:27017/"
+# var uri = "mongodb://mongo:27017/"
# var client = new MongoClient(uri)
# assert client.server_uri == uri
# ~~~
# Returns `null` if an error occured. See `last_error`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# assert client.server_status["process"] == "mongod"
# ~~~
fun server_status: nullable JsonObject do
# Lists available database names.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# There is no need to create a database manually.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Returns `null` if an error occured. See `Sys::last_mongoc_error`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Loads or creates a collection by its `name`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Checks if a collection named `name` exists.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Returns `false` if an error occured. See `Sys::last_mongoc_error`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Returns `false` if an error occured. See `Sys::last_mongoc_error`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Returns `false` if an error occured. See `Sys::last_mongoc_error`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# No upsert is done, see `save` instead.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Returns `-1` if an error occured. See `Sys::last_mongoc_error`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Returns `null` if an error occured. See `Sys::last_mongoc_error`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# * `limit` number of documents to return
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Applies an aggregation `pipeline` over the collection.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Returns `null` if an error occured. See `Sys::last_mongoc_error`.
#
# ~~~
- # var client = new MongoClient("mongodb://localhost:27017/")
+ # var client = new MongoClient("mongodb://mongo:27017/")
# var db_suffix = "NIT_TESTING_ID".environ
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# Neo4j connector through its JSON REST API using curl.
#
-# For ease of use and testing this module provide a wrapper to the `neo4j` command:
-#
-# # Start the Neo4j server
-# var srv = new Neo4jServer
-# assert srv.start_quiet
-#
# In order to connect to Neo4j you need a connector:
#
# # Create new Neo4j client
-# var client = new Neo4jClient("http://localhost:7474")
+# var client = new Neo4jClient("http://neo4j:7474")
# assert client.is_ok
#
# The fundamental units that form a graph are nodes and relationships.
import curl_json
import error
-# Handles Neo4j server start and stop command
-#
-# `neo4j` binary must be in `PATH` in order to work
-class Neo4jServer
-
- # Start the local Neo4j server instance
- fun start: Bool do
- sys.system("neo4j start console")
- return true
- end
-
- # Like `start` but redirect the console output to `/dev/null`
- fun start_quiet: Bool do
- sys.system("neo4j start console > /dev/null")
- return true
- end
-
- # Stop the local Neo4j server instance
- fun stop: Bool do
- sys.system("neo4j stop")
- return true
- end
-
- # Like `stop` but redirect the console output to `/dev/null`
- fun stop_quiet: Bool do
- sys.system("neo4j stop > /dev/null")
- return true
- end
-end
-
# `Neo4jClient` is needed to communicate through the REST API
#
-# var client = new Neo4jClient("http://localhost:7474")
+# var client = new Neo4jClient("http://neo4j:7474")
# assert client.is_ok
class Neo4jClient
# Save the node in base
#
- # var client = new Neo4jClient("http://localhost:7474")
+ # var client = new Neo4jClient("http://neo4j:7474")
#
# # Create a node
# var andres = new NeoNode
# Save the edge in base
# From and to nodes will be created.
#
- # var client = new Neo4jClient("http://localhost:7474")
+ # var client = new Neo4jClient("http://neo4j:7474")
#
# var andres = new NeoNode
# var kate = new NeoNode
# Retrieve all nodes with specified `lbl`
#
- # var client = new Neo4jClient("http://localhost:7474")
+ # var client = new Neo4jClient("http://neo4j:7474")
#
# var andres = new NeoNode
# andres.labels.add_all(["Human", "Male"])
# Retrieve nodes belonging to all the specified `labels`.
#
- # var client = new Neo4jClient("http://localhost:7474")
+ # var client = new Neo4jClient("http://neo4j:7474")
#
# var andres = new NeoNode
# andres.labels.add_all(["Human", "Male"])
#
# Example:
#
-# var client = new Neo4jClient("http://localhost:7474")
+# var client = new Neo4jClient("http://neo4j:7474")
# var query = new CypherQuery
# query.nmatch("(n)-[r:LOVES]->(m)")
# query.nwhere("n.name=\"Andres\"")
# Then we can link the entity to the base:
#
# # Init client
-# var client = new Neo4jClient("http://localhost:7474")
+# var client = new Neo4jClient("http://neo4j:7474")
# client.save_node(andres)
# # The node is now linked
# assert andres.is_linked
#
# Creating new nodes:
#
-# var client = new Neo4jClient("http://localhost:7474")
+# var client = new Neo4jClient("http://neo4j:7474")
#
# var andres = new NeoNode
# andres.labels.add "Person"
#
# Create a relationship:
#
-# var client = new Neo4jClient("http://localhost:7474")
+# var client = new Neo4jClient("http://neo4j:7474")
# # Create nodes
# var andres = new NeoNode
# andres["name"] = "Andres"
#
# Example:
#
-# var client = new Neo4jClient("http://localhost:7474")
+# var client = new Neo4jClient("http://neo4j:7474")
#
# var node1 = new NeoNode
# var node2 = new NeoNode
# This is a representation of a neo job in JSON Format
#
# Each job description should contain a `to` attribute, with a value relative to the data API root
-# (so http://localhost:7474/db/data/node becomes just /node), and a `method` attribute containing
+# (so http://neo4j:7474/db/data/node becomes just /node), and a `method` attribute containing
# HTTP verb to use.
#
# Optionally you may provide a `body` attribute, and an `id` attribute to help you keep track
end
end
-var mongo = new MongoClient("mongodb://localhost:27017/")
+var mongo = new MongoClient("mongodb://mongo:27017/")
var db = mongo.database("mongo_example")
var app = new App
end
end
-var mongo = new MongoClient("mongodb://localhost:27017/")
+var mongo = new MongoClient("mongodb://mongo:27017/")
var db = mongo.database("mongo_example")
var app = new App
# # Let's wrap it all together in a Popcorn app:
#
# # Init database
-# var mongo = new MongoClient("mongodb://localhost:27017/")
+# var mongo = new MongoClient("mongodb://mongo:27017/")
# var db = mongo.database("tests_app_{100000.rand}")
# var coll = db.collection("books")
#
redef class AppConfig
# Default database host string for MongoDb
- var default_db_host = "mongodb://localhost:27017/"
+ var default_db_host = "mongodb://mongo:27017/"
# Default database hostname
var default_db_name = "popcorn"
# # The repository can then be used with User instances:
#
# # Init database
-# var mongo = new MongoClient("mongodb://localhost:27017/")
+# var mongo = new MongoClient("mongodb://mongo:27017/")
# var db = mongo.database("tests")
# var coll = db.collection("test_pop_repo_{100000.rand}")
#
# animals.add(turtle)
#
# var db_suffix = "NIT_TESTING_ID".environ
-# var db = new Postgres.open("dbname=postgres")
+# var db = new Postgres.open("host=postgres user=postgres dbname=postgres")
#
# assert db_is_open: not db.is_closed
# assert create_table: db.create_table("IF NOT EXISTS animals_{db_suffix} (aname TEXT PRIMARY KEY, kind TEXT NOT NULL, age INT NOT NULL)") else print db.error
--- /dev/null
+# Local setup of services for tests
+
+Some tests and libs require specific external services like postgres.
+To simplify the automation and the setup, we just assume these services are accessible at specific hostnames.
+
+When running and testing nit within a docker, it is easy to compose/link nit with other dockers/services.
+See also .gitlab-ci.yml
+
+~~~
+$ docker run --link postgres [...]
+~~~
+
+When running and testing on a local host, one can setup those services locally then add aliases for localhost in `/etc/hosts`.
+
+~~~
+$ cat /etc/hosts
+[...]
+127.0.0.1 postgres
+~~~
+
+An other way is to run/test on a local host and have the services running in dockers.
+
+Because accessing a specific container by its name is cumbersome, a proposed way is to:
+
+* run these services with specific and fixed IPs
+* add these IPs to `/etc/hosts`
+
+~~~
+$ cd misc/docker/ci-local
+$ docker-compose up -d
+$ cat /etc/hosts
+[...]
+172.16.238.4 postgres
+~~~
--- /dev/null
+version: '2'
+services:
+ mongo:
+ image: mongo
+ restart: always
+ networks:
+ nitci:
+ ipv4_address: 172.16.238.2
+ neo4j:
+ image: neo4j:2.3
+ restart: always
+ environment:
+ NEO4J_AUTH: none
+ networks:
+ nitci:
+ ipv4_address: 172.16.238.3
+ postgres:
+ image: postgres
+ restart: always
+ networks:
+ nitci:
+ ipv4_address: 172.16.238.4
+networks:
+ nitci:
+ driver: bridge
+ ipam:
+ driver: default
+ config:
+ - subnet: 172.16.238.0/24
libsqlite3-dev \
libx11-dev \
libxdg-basedir-dev \
- postgresql \
+ netcat \
+ psmisc \
# Packages needed for contrib, platforms and FFI
ant \
clang \
emscripten
java
glsl
-mongo
mpi
-neo
objc
-postgres
-nitrpg
wiringPi
-localhost 7474 test_prog/test_prog.nit
+neo4j 7474 test_prog/test_prog.nit
# key used to loosely assume unicity and prevent conflicting db accesses
var key = "NIT_TESTING_ID".environ.to_i
-var srv = new Neo4jServer
-srv.start_quiet
-
print "# Test local\n"
-var client = new Neo4jClient("http://localhost:7474")
+var client = new Neo4jClient("http://neo4j:7474")
assert client.is_ok
# Clear the previous objects, if any
print "\n# Test lazy\n"
-client = new Neo4jClient("http://localhost:7474/")
+client = new Neo4jClient("http://neo4j:7474/")
assert client.is_ok
# Read Andres
import neo4j
-var srv = new Neo4jServer
-srv.start_quiet
-
# key used to loosely assume unicity and prevent conflicting db accesses
var key = "NIT_TESTING_ID".environ.to_i
var loves = new NeoEdge(andres, "LOVES", kate)
loves["since"] = 1999
-var client = new Neo4jClient("http://localhost:7474")
+var client = new Neo4jClient("http://neo4j:7474")
assert client.is_ok
# Clear the previous objects, if any
var kate_url = kate.url.to_s
var loves_url = loves.url.to_s
-client = new Neo4jClient("http://localhost:7474")
+client = new Neo4jClient("http://neo4j:7474")
assert client.is_ok
# Read Andres
import postgresql::native_postgres
var db_suffix = "NIT_TESTING_ID".environ
-var db = new NativePostgres.connectdb("dbname=postgres")
+var db = new NativePostgres.connectdb("host=postgres user=postgres dbname=postgres")
assert postgres_open: db.status.is_ok else print_error db.error
var result = db.exec("CREATE TABLE IF NOT EXISTS animals_{db_suffix} (aname TEXT PRIMARY KEY, class TEXT NOT NULL, sex INTEGER)")
import postgresql::postgres
var db_suffix = "NIT_TESTING_ID".environ
-var db = new Postgres.open("dbname=postgres")
+var db = new Postgres.open("host=postgres user=postgres dbname=postgres")
assert open_db: not db.is_closed else print db.error
assert create_table: db.create_table("IF NOT EXISTS users_{db_suffix} (uname TEXT PRIMARY KEY, pass TEXT NOT NULL, activated INTEGER, perc FLOAT)") else