Commit 9fabff99 authored by sim's avatar sim

Remove old tests

parent 55b78035
UNIT TESTS
==========
Prerequisite
------------
Running unit tests will involve creating a **temporary test DB** !
+ it implies **CREATEDB permssions** for settings.DATABASES.user
(this has security consequences)
+ for instance in gargantext you would need to run this in psql as postgres:
`# ALTER USER gargantua CREATEDB;`
A "principe de précaution" could be to allow gargantua the CREATEDB rights on the **dev** machines (to be able to run tests) and not give it on the **prod** machines (no testing but more protection just in case).
Usage
------
```
./manage.py test unittests/ -v 2 # in django root container directory
# or for a single module
./manage.py test unittests.tests_010_basic -v 2
```
( `-v 2` is the verbosity level )
Tests
------
1. **tests_010_basic**
2. ** tests ??? **
3. ** tests ??? **
4. ** tests ??? **
5. ** tests ??? **
6. ** tests ??? **
7. **tests_070_routes**
Checks the response types from the app url routes:
- "/"
- "/api/nodes"
- "/api/nodes/<ID>"
8. ** tests users ??? **
9. **tests_090_toolchain**
Checks each data source parserbot (CSV, Pubmed, Zotero, Istex, etc.)
- correct parsing for a small sample
GargTestRunner
---------------
Most of the tests will interact with a DB but we don't want to touch the real one so we provide a customized test_runner class in `unittests/framework.py` that creates a test database.
It must be referenced in django's `settings.py` like this:
```
TEST_RUNNER = 'unittests.framework.GargTestRunner'
```
(This way the `./manage.py test` command will be using GargTestRunner.)
Using a DB session
------------------
The GargTestRunner overrides default settings so that the test database is used in the way we usually do it in gargantext :
**Example**
```
from gargantext.util.db import session
session.query(Nodes).all() # gives all the nodes of the testdb
```
Accessing the URLS
------------------
Django tests provide a client to browse the urls
**Example**
```
from django.test import Client
class MyTestRecipes(TestCase):
def setUp(self):
self.client = Client()
def test_001_get_front_page(self):
''' get the about page localhost/about '''
# --------------------------------------
the_response = self.client.get('/about')
# --------------------------------------
self.assertEqual(the_response.status_code, 200)
```
Logging in
-----------
Most of our functionalities are only available on login so we provide a fake user at the initialization of the test DB.
His login in 'pcorser' and password is 'peter'
**Example**
```
from django.test import Client
class MyTestRecipes(TestCase):
def setUp(self):
self.client = Client()
# login ---------------------------------------------------
response = self.client.post(
'/auth/login/',
{'username': 'pcorser', 'password': 'peter'}
)
# ---------------------------------------------------------
def test_002_get_to_a_restricted_page(self):
''' get the projects page /projects '''
the_response = self.client.get('/projects')
self.assertEqual(the_response.status_code, 200)
```
"""
A test runner derived from default (DiscoverRunner) but adapted to our custom DB
cf. docs.djangoproject.com/en/1.9/topics/testing/advanced/#using-different-testing-frameworks
cf. gargantext/settings.py => TEST_RUNNER
cf. dbmigrate.py
cf ./session_and_db_remarks.md
"""
# basic elements
from django.test.runner import DiscoverRunner, get_unique_databases_and_mirrors
from sqlalchemy import create_engine
from gargantext.settings import DATABASES
# things needed to create a user
from django.contrib.auth.models import User
# here we setup a minimal django so as to load SQLAlchemy models ---------------
# and then be able to import models and Base.metadata.tables
from os import environ
from django import setup
environ.setdefault("DJANGO_SETTINGS_MODULE", "gargantext.settings")
# hack to make our minimal django use the same test DB settings as DiscoverRunner
# (more details: cf ./session_and_db_remarks.md)
DATABASES['default']['NAME'] = DATABASES['default']['TEST']['NAME']
setup() # models can now be imported
from gargantext.models import Base # contains metadata.tables
# ------------------------------------------------------------------------------
# thanks to our hack, util.db.engine and util.db.session already use the test DB
from gargantext.util.db import engine, session
class GargTestRunner(DiscoverRunner):
"""
We use the default test runner but we just add
our own dbmigrate elements at db creation
1) we let django.test.runner do the test db creation + auto migrations
2) we create tables for our models like in dbmigrate with the test engine
POSSIBLE: definitions of tables to be created should be fully hard coded like the list in self.models
=> then remove django.setup() used to import models and DATABASES renaming to prevent its secondary effects
"""
def __init__(self, *args, **kwargs):
# our custom tablenames to be created (in correct order)
self.models = ['ngrams', 'nodes', 'contacts', 'nodes_nodes', 'nodes_ngrams', 'nodes_nodes_ngrams', 'nodes_ngrams_ngrams', 'nodes_hyperdata']
# POSSIBLE: hard-code here our custom table declarations
# self.tables = [Table('ngrams', MetaData(bind=None)....)]
# and execute default django unittests init
old_config = super(GargTestRunner, self).__init__(*args, **kwargs)
def setup_databases(self, *args, **kwargs):
"""
Complement the database creation
by our own "models to tables" migration
"""
# default django setup performs base creation + auto migrations
old_config = super(GargTestRunner, self).setup_databases(*args, **kwargs)
# we retrieve real tables declarations from our loaded Base
sqla_models = (Base.metadata.tables[model_name] for model_name in self.models)
# example: Base.metadata.tables['ngrams']
# ---------------------------------------
# Table('ngrams', Column('id', Integer(), table=<ngrams>, primary_key=True),
# Column('terms', String(length=255), table=<ngrams>),
# Column('n', Integer(), table=<ngrams>),
# schema=None)
# and now creation of each table in our test db (like dbmigrate)
for model in sqla_models:
try:
model.create(engine)
print('TESTDB INIT: created model: `%s`' % model)
except Exception as e:
print('TESTDB INIT ERROR: could not create model: `%s`, %s' % (model, e))
# and let's create a user too otherwise we'll never be able to login
user = User.objects.create_user(username='pcorser', password='peter')
# old_config will be used by DiscoverRunner
# (to remove everything at the end)
return old_config
def teardown_databases(self, old_config, *args, **kwargs):
"""
After all tests
"""
# close the session
session.close()
# free the connection
engine.dispose()
# default django teardown performs destruction of the test base
super(GargTestRunner, self).teardown_databases(old_config, *args, **kwargs)
# snippets if we choose direct model building instead of setup() and Base.metadata.tables[model_name]
# from sqlalchemy.types import Integer, String, DateTime, Text, Boolean, Float
# from gargantext.models.nodes import NodeType
# from gargantext.models.hyperdata import HyperdataKey
# from sqlalchemy.schema import Table, Column, ForeignKey, UniqueConstraint, MetaData
# from sqlalchemy.dialects.postgresql import JSONB, DOUBLE_PRECISION
# from sqlalchemy.ext.mutable import MutableDict, MutableList
# Double = DOUBLE_PRECISION
# sqla_models = [i for i in sqla_models]
# print (sqla_models)
# sqla_models = [Table('ngrams', MetaData(bind=None), Column('id', Integer(), primary_key=True, nullable=False), Column('terms', String(length=255)), Column('n', Integer()), schema=None), Table('nodes', MetaData(bind=None), Column('id', Integer(), primary_key=True, nullable=False), Column('typename', NodeType()), Column('user_id', Integer(), ForeignKey('auth_user.id')), Column('parent_id', Integer(), ForeignKey('nodes.id')), Column('name', String(length=255)), Column('date', DateTime()), Column('hyperdata', JSONB(astext_type=Text())), schema=None), Table('contacts', MetaData(bind=None), Column('id', Integer(), primary_key=True, nullable=False), Column('user1_id', Integer(), primary_key=True, nullable=False), Column('user2_id', Integer(), primary_key=True, nullable=False), Column('is_blocked', Boolean()), Column('date_creation', DateTime()), schema=None), Table('nodes_nodes', MetaData(bind=None), Column('node1_id', Integer(), ForeignKey('nodes.id'), primary_key=True, nullable=False), Column('node2_id', Integer(), ForeignKey('nodes.id'), primary_key=True, nullable=False), Column('score', Float(precision=24)), schema=None), Table('nodes_ngrams', MetaData(bind=None), Column('node_id', Integer(), ForeignKey('nodes.id'), primary_key=True, nullable=False), Column('ngram_id', Integer(), ForeignKey('ngrams.id'), primary_key=True, nullable=False), Column('weight', Float()), schema=None), Table('nodes_nodes_ngrams', MetaData(bind=None), Column('node1_id', Integer(), ForeignKey('nodes.id'), primary_key=True, nullable=False), Column('node2_id', Integer(), ForeignKey('nodes.id'), primary_key=True, nullable=False), Column('ngram_id', Integer(), ForeignKey('ngrams.id'), primary_key=True, nullable=False), Column('score', Float(precision=24)), schema=None), Table('nodes_ngrams_ngrams', MetaData(bind=None), Column('node_id', Integer(), ForeignKey('nodes.id'), primary_key=True, nullable=False), Column('ngram1_id', Integer(), ForeignKey('ngrams.id'), primary_key=True, nullable=False), Column('ngram2_id', Integer(), ForeignKey('ngrams.id'), primary_key=True, nullable=False), Column('weight', Float(precision=24)), schema=None), Table('nodes_hyperdata', MetaData(bind=None), Column('id', Integer(), primary_key=True, nullable=False), Column('node_id', Integer(), ForeignKey('nodes.id')), Column('key', HyperdataKey()), Column('value_int', Integer()), Column('value_flt', DOUBLE_PRECISION()), Column('value_utc', DateTime(timezone=True)), Column('value_str', String(length=255)), Column('value_txt', Text()), schema=None)]
# About the DB settings during the tests
rloth 2016-08-22
#### Correct ordering strategies
Our specific database model causes a problem for the correct order of doing things
- the good practice in creating the test framework is:
1. create a child class of DiscoverRunner
2. define a 'TEST' key in `settings.DATABASES.default`
3. let the DiscoverRunner create the tables in his `__init__()` by calling `super.__init__()` from the child class
(cf. https://docs.djangoproject.com/en/1.10/topics/testing/advanced/)
- but we have tables not in the migrations... so creating our full database model (with the `nodes_*` tables) implies either to hard-code their definitions or to:
1. do a `django.setup()` first so we can load the SQLAlchemy models (`import gargantext.models`)
2. from there use `util.db.Base` as the table definitions
- Table('nodes', Column('id', Integer()...)
- Table('ngrams' ...)
- etc.
3. Use those definitions to create the tables: `table_definition.create(engine)`
*(cf. db_migrate.py)*
#### But we see these two ordering strategies are contradictory!
**Explanation**: Doing the `django.setup()` to get the models will load the app modules before using the test database created by `DiscoverRunner.__init__()`
**Consequence**: `util.db.session` will use the native settings for the "real DB" instead of the "test DB".
#### So we need to "cheat" a little bit...
**Solution 1** *(=> will be better in the long run when the tables stop changing)*
We could hard-code the list of tables and columns to create in the test DB. Then there would be no need to load the models to do the migration, so therefore no need to do a `django.setup()` before the `DiscoverRunner.__init__()`
**Solution 2** *(=> used now)*
We do the `django.setup()` but we modify its `gargantext.settings.DATABASES` on-the-fly with this line:
```
DATABASES['default']['NAME'] = DATABASES['default']['TEST']['NAME']
```
This is a dirty hack because changing settings at runtime makes final values difficult to track, but this way, the setup part and the DiscoverRunner part will share the same DB name (`test_gargandb`)
### To inspect the testdb
Run tests with:
```
./manage.py test unittests/ --keepdb
```
And after the tests, connect to it as gargantua with `psql test_gargandb`
"""
BASIC UNIT TESTS FOR GARGANTEXT IN DJANGO
=========================================
"""
from django.test import TestCase
class NodeTestCase(TestCase):
def setUp(self):
from gargantext.models import nodes
self.node_1000 = nodes.Node(id=1000)
self.new_node = nodes.Node()
def test_010_node_has_id(self):
'''node_1000.id'''
self.assertEqual(self.node_1000.id, 1000)
def test_011_node_write(self):
'''write new_node to DB and commit'''
from gargantext.util.db import session
self.assertFalse(self.new_node._sa_instance_state._attached)
session.add(self.new_node)
session.commit()
self.assertTrue(self.new_node._sa_instance_state._attached)
"""
ROUTE UNIT TESTS
================
"""
from django.test import TestCase
from django.test import Client
# to be able to create Nodes
from gargantext.models import Node
# to be able to compare in test_073_get_api_one_node()
from gargantext.constants import NODETYPES
from gargantext.util.db import session
class RoutesChecker(TestCase):
def setUp(self):
"""
Will be run before *each* test
"""
self.client = Client()
# login with our fake user
response = self.client.post(
'/auth/login/',
{'username': 'pcorser', 'password': 'peter'}
)
# print(response.status_code) # expected: 302 FOUND
new_project = Node(
typename = 'PROJECT',
name = "hello i'm a project",
user_id = 1 # todo make sure it's the same user as login
)
session.add(new_project)
session.commit()
self.a_node_id = new_project.id
print("created a project with id: %i" % new_project.id)
def test_071a_get_front_page(self):
''' get the front page / '''
front_response = self.client.get('/')
self.assertEqual(front_response.status_code, 200)
self.assertIn('text/html', front_response.get('Content-Type'))
# on suppose que la page contiendra toujours ce titre
self.assertIn(b'<h1>Gargantext</h1>', front_response.content)
def test_071b_get_inexisting_page(self):
''' get the inexisting page /foo '''
front_response = self.client.get('/foo')
self.assertEqual(front_response.status_code, 404)
def test_072_get_api_nodes(self):
''' get "/api/nodes" '''
api_response = self.client.get('/api/nodes')
self.assertEqual(api_response.status_code, 200)
# 1) check the type is json
self.assertTrue(api_response.has_header('Content-Type'))
self.assertIn('application/json', api_response.get('Content-Type'))
# 2) let's try to get things in the json
json_content = api_response.json()
print(json_content)
json_count = json_content['count']
json_nodes = json_content['records']
self.assertEqual(type(json_count), int)
self.assertEqual(type(json_nodes), list)
def test_073_get_api_one_node(self):
''' get "api/nodes/<node_id>" '''
one_node_route = '/api/nodes/%i' % self.a_node_id
# print("\ntesting node route: %s" % one_node_route)
api_response = self.client.get(one_node_route)
self.assertTrue(api_response.has_header('Content-Type'))
self.assertIn('application/json', api_response.get('Content-Type'))
json_content = api_response.json()
nodetype = json_content['typename']
nodename = json_content['name']
print("\ntesting nodename:", nodename)
print("\ntesting nodetype:", nodetype)
self.assertIn(nodetype, NODETYPES)
self.assertEqual(nodename, "hello i'm a project")
# TODO http://localhost:8000/api/nodes?types[]=CORPUS
# £TODO test request.*
# print ("request")
# print ("user.id", request.user.id)
# print ("user.name", request.user.username)
# print ("path", request.path)
# print ("path_info", request.path_info)
#!/usr/bin/python3 env
"""
STORY TEST SUITE
testing toolchain
"""
import os, sys, logging
from django.test import TestCase, Client, RequestFactory
from gargantext.models import Node, User
from gargantext.util.db import session
from gargantext.constants import RESOURCETYPES, NODETYPES, get_resource
from gargantext.util.toolchain.main import *
DATA_SAMPLE_DIR = "/srv/gargantext/unittests/mini_test_samples/"
# todo make it read NDOCS from a json overview to add in DATA_SAMPLE_DIR
DATA_SAMPLE_NDOCS = [
None, # RESOURCETYPES
[7], # 1-europresse
[], # 2-jstor
[10], # 3-pubmed
[], # 4-scopus
[], # 5-web_of_science
[12], # 6-zotero
[], #  7-csv
[32], #  8-istex
[], # 9-scoap
[], # 10-repec
]
class ToolChainRecipes(TestCase):
def setUp(self):
#self.session = GargTestRunner.testdb_session
self.session = session
self.log= logging.getLogger( "unitests.test_090_toolchain" )
self.client = Client()
self.user = User()
self.project = self._create_project()
self.source_list = [(resource["type"], resource["name"]) for resource in RESOURCETYPES]
self.source_list.insert(0, (0,"Select a database below"))
self.sample_files = self._collect_samples_files()
def _create_project(self):
project = Node(
user_id = self.user.id,
typename = 'PROJECT',
name = "test1000",
)
self.session.add(project)
self.session.commit()
return project
def __count_node_children__(self, CurrNode, typename=None):
'''count ALL the children of a given Node [optional filter TYPENAME] '''
if typename is None:
children = CurrNode.children('').count()
else:
children = CurrNode.children(typename).count()
return children
def __find_node_parent__(self, CurrNode):
'''find the parent Node given a CurrNode '''
self.parent = self.session.query(Node).filter(Node.id == CurrNode.parent_id).first()
def _collect_samples_files(self):
from collections import defaultdict
from os.path import isfile, join
self.sample_files = {}
sources = [ r["name"].split("[")[0].lower().strip() for r in RESOURCETYPES]
sources = [r.replace(" ", "_") for r in sources]
#self.log.debug(sources)
for format_source in os.listdir(DATA_SAMPLE_DIR):
#self.log.debug(format_source)
full_path = join(DATA_SAMPLE_DIR, format_source)
if not isfile(full_path):
if format_source in sources:
self.sample_files[format_source] = [join(full_path, samplef) for samplef in os.listdir(full_path)]
return self.sample_files
def _create_corpus(self,name, source_type, sample_file):
corpus = self.project.add_child(
name = name,
typename = 'CORPUS',
)
corpus.add_resource(
type = int(source_type),
path = sample_file,
)
self.session.add(corpus)
self.session.commit()
return corpus
def _get_corpus(self, name):
corpus = self.session.query(Node).filter(Node.typename == "CORPUS", Node.name == name).first()
return corpus
def _run_recipe(self, source_type, expected_results):
"""
Each of the resources input test can follow this common recipe base
@param source_type: int (cf. constants.py RESOURCETYPES)
@param expected_results: int[] (number of docs for each sample corpora of this source)
"""
source = get_resource(source_type)
source_name = source["name"].split("[")[0].lower().strip().replace(" ", "_")
self.test_name = ">> "+ sys._getframe().f_code.co_name +"_"+str(source_name)+":"
self.log.debug("\n" + self.test_name)
for i,sample_file in enumerate(self.sample_files[source_name]):
print("... sample_file:", sample_file)
expected_ndocs = expected_results[i]
name = "test_"+source_name+str(i)
self.log.debug("\t- Checking creation of corpus %s" %name)
self.corpus = self._create_corpus(name, source_type, sample_file)
db_corpus = self._get_corpus(name)
#corpus check
self.assertEqual(self.corpus.name, db_corpus.name)
self.log.debug("\t- Checking creation of resource type '%s' " %get_resource(source_type)["name"])
self.assertEqual(self.corpus.resources()[0]["type"], db_corpus.resources()[0]["type"])
self.log.debug("\t- Parsing and indexing corpus")
parse(self.corpus)
real_ndocs = self.__count_node_children__(self.corpus, "DOCUMENT")
# print('==>\t'+str(source_type)+'\t'+str(i)+'\t'+sample_file+'\t'+str(real_ndocs))
self.assertEqual(real_ndocs, expected_ndocs)
status = self.corpus.status()
self.log.debug("\t- Extracting ngrams")
extract_ngrams(self.corpus)
# ngrams = self.__count_node_children__(self.corpus, "NGRAMS")
status = self.corpus.status()
self.assertTrue(status["complete"])
def test_000_get_project(self):
self.client.get("/projects/%i" %self.project.id)
def tests_001_europresse(self):
'''testing Europresse parsing'''
self._run_recipe(1, DATA_SAMPLE_NDOCS[1])
# def tests_002_jstor(self):
# self._run_recipe(2, DATA_SAMPLE_NDOCS[2])
def tests_003_pubmed(self):
self._run_recipe(3, DATA_SAMPLE_NDOCS[3])
# def tests_004_scopus(self):
# self._run_recipe(4, DATA_SAMPLE_NDOCS[4])
#
# def tests_005_web_of_science(self):
# self._run_recipe(5, DATA_SAMPLE_NDOCS[5])
def tests_006_zotero(self):
self._run_recipe(6, DATA_SAMPLE_NDOCS[6])
# def tests_007_csv(self):
# self._run_recipe(7, DATA_SAMPLE_NDOCS[7])
def tests_008_istex(self):
self._run_recipe(8, DATA_SAMPLE_NDOCS[8])
# def tests_009_scoap(self):
# self._run_recipe(9, DATA_SAMPLE_NDOCS[9])
#
# def tests_010_repec(self):
# self._run_recipe(10, DATA_SAMPLE_NDOCS[10])
"""
API UNIT TESTS
================
"""
from django.test import TestCase, Client
from gargantext.models import Node
from gargantext.util.db import session
from rest_framework.test import APIClient
from rest_framework.test import APIRequestFactory
# Using the standard RequestFactory API to create a form POST request
#factory = APIRequestFactory()
class APIRecipe(TestCase):
def setUp(self):
"""
Will be run before each test
"""
self.client = Client()
# login with our fake user
response = self.client.post(
'/auth/login/',
{'username': 'pcorser', 'password': 'peter'}
)
self.create_project()
self.create_corpus()
self.factory = APIRequestFactory()
def create_project(self):
new_project = Node(
typename = 'PROJECT',
name = "My project",
)
session.add(new_project)
session.commit()
self.project = new_project
def create_corpus(self):
#create a default corpus
self.corpus = self.project.add_child(
name = "My Corpus",
typename = 'CORPUS',
)
session.add(self.corpus)
session.commit()
def test_001_post_project(self):
'''POST /projects'''
request = self.factory.post('/api/projects/', {'name': 'PROJECT TEST'}, format='json')
def test_002_get_projects(self):
'''GET /projects'''
request = self.factory.get('/api/projects/', format='json')
def test_003_put_projects(self):
'''PUT /projects'''
request = self.factory.put('/api/projects/', {"name": "My TEST PROJECT"}, format='json')
def test_004_delete_projects(self):
'''DELETE /projects'''
request = self.factory.delete('/api/projects/', format='json')
def test_005_delete_project(self):
'''DELETE /project'''
request = self.factory.delete('/api/project/%s' %self.project.id, format='json')
def test_006_get_project(self):
'''GET /PROJECT'''
request = self.factory.get('/api/project/%s' %self.project.id, format='json')
def test_007_put_project(self):
''' PUT /PROJECT '''
request = self.factory.put('/api/project/%s' %self.project.id, {"name": "My New Project"}, format='json')
# def test_008_post_corpus(self):
# '''POST /project'''
# request = self.factory.post('/project/', {'name': 'PROJECT TEST'})
#!/usr/bin/python3 env
from gargantext.util.db import session
from django import TestCase
class UserRecipes(TestCase):
def setUp(self):
#before any test
self.session = session
self.client = Client()
def tearDown(self):
#after any test
pass
def test_000_create_user(self):
pass
def test_001_login(self):
pass
def test_002_authenticate(self):
pass
def test_003_unlogin(self):
pass
#!/usr/bin/python3 env
from django.test import TestCase
class ProjectsRecipes(TestCase):
def setUp(self):
#before anytest
self.session = session
self.client = Client()
def tearDown(self):
#after any test
pass
def _create_projet(self):
#resp = self.client.post('/projects/', data={"name":"test"})
self.project = Node(
user_id = user.id,
typename = 'PROJECT',
name = "test1",
)
session.add(self.project)
session.commit()
return self.project
def test_001_get_projects(self):
'''get every projects'''
resp = self.client.get('/projects/')
self.assertEqual(resp.status_code, 200)
def test_002_delete_projects(self):
'''delete every projects'''
resp = self.client.delete('/projects/')
self.assertEqual(resp.status_code, 204)
def test_003_put_projects(self):
'''modify every projects'''
resp = self.client.put('/projects?name="test"')
self.assertEqual(resp.status_code, 202)
def test_004_post_project(self):
'''create a project'''
resp = self.client.post('/projects/', data={"name":"test"})
self.assertEqual(resp.status_code, 201)
def test_005_get_project(self):
'''get one project'''
project = self._create_projet()
resp = self.client.delete('/project/'+project.id)
self.assertEqual(resp.status_code, 200)
def test_006_delete_project(self):
'''delete one project'''
project = self._create_projet()
#delete it
resp = self.client.delete('/project/'+project.id)
self.assertEqual(resp.status_code, 204)
def test_007_put_project(self):
project = self._create_projet()
resp = self.client.put('/project/'+project.id+"?name=newname")
self.assertEqual(resp.status_code, 204)
pass
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment