<commit_before>/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package fr.hhdev.test;

import fr.hhdev.test.dataservices.EJBDataService;
import fr.hhdev.test.dataservices.CDIDataService;
import fr.hhdev.test.dataservices.SingletonCDIDataService;
import fr.hhdev.test.dataservices.PojoDataService;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.hhdev.ocelot.Constants;
import fr.hhdev.ocelot.annotations.JsCacheStore;
import fr.hhdev.ocelot.i18n.Locale;
import fr.hhdev.ocelot.messaging.Fault;
import fr.hhdev.ocelot.messaging.Command;
import fr.hhdev.ocelot.messaging.MessageFromClient;
import fr.hhdev.ocelot.messaging.MessageToClient;
import fr.hhdev.ocelot.messaging.MessageEvent;
import fr.hhdev.ocelot.resolvers.CdiResolver;
import fr.hhdev.ocelot.spi.DataServiceException;
import fr.hhdev.ocelot.spi.IDataServiceResolver;
import fr.hhdev.ocelot.resolvers.DataServiceResolverIdLitteral;
import fr.hhdev.ocelot.resolvers.EJBResolver;
import fr.hhdev.ocelot.resolvers.PojoResolver;
import fr.hhdev.test.dataservices.GetValue;
import fr.hhdev.test.dataservices.SessionCDIDataService;
import fr.hhdev.test.dataservices.SessionEJBDataService;
import fr.hhdev.test.dataservices.SingletonEJBDataService;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.asset.FileAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.jboss.weld.exceptions.UnsatisfiedResolutionException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author hhfrancois
 */
@RunWith(Arquillian.class)
public class OcelotTest {

	final static Logger logger = LoggerFactory.getLogger(OcelotTest.class);

	private final static long TIMEOUT = 1000;
	private final static String PORT = "8282";

	private final static String ctxpath = "ocelot-test";

	@Inject
	@MessageEvent
	Event<MessageToClient> wsEvent;

	@Inject
	@Any
	private Instance<IDataServiceResolver> resolvers;

	private IDataServiceResolver getResolver(String type) {
		return resolvers.select(new DataServiceResolverIdLitteral(type)).get();
	}

	private final PojoDataService destination = new PojoDataService();

	/**
	 * Pour tester l'api dans le contener JEE on crée un war
	 *
	 * @return
	 */
	@Deployment(name = "glassfish")
	public static WebArchive createWarGlassfishArchive() {
		return createWarArchive();
	}

	/**
	 * Pour tester l'api dans le contener JEE on crée un war
	 *
	 * @return
	 */
//	@Deployment(name = "tomcat")
//	public static WebArchive createWarTomcatArchive() {
//		File web = new File("src/test/resources/tomcat/web.xml");
//		File context = new File("src/test/resources/tomcat/context.xml");
//		return createWarArchive()
//				  .addAsManifestResource(new FileAsset(context), "context.xml")
//				  .addAsWebInfResource(new FileAsset(web), "web.xml");
//	}
	public static WebArchive createWarArchive() {
		File[] libs = Maven.resolver().loadPomFromFile("pom.xml").importRuntimeDependencies().resolve().withTransitivity().asFile();
		File logback = new File("src/test/resources/logback.xml");
		File localeFr = new File("src/test/resources/test_fr_FR.properties");
		File localeUs = new File("src/test/resources/test_en_US.properties");
		return ShrinkWrap.create(WebArchive.class, ctxpath + ".war")
				  .addAsLibraries(libs)
				  .addAsLibraries(createLibArchive())
				  .addPackages(true, OcelotTest.class.getPackage())
				  .addAsWebInfResource(new FileAsset(logback), "logback.xml")
				  .addAsResource(new FileAsset(localeUs), "test_en_US.properties")
				  .addAsResource(new FileAsset(localeFr), "test_fr_FR.properties")
				  .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
	}

	public static JavaArchive createLibArchive() {
		File bean = new File("src/main/resources/META-INF/beans.xml");
		File core = new File("src/main/resources/ocelot-core.js");
		return ShrinkWrap.create(JavaArchive.class, "ocelot-web.jar")
				  .addPackages(true, "services")
				  .addPackages(true, "fr.hhdev.ocelot.encoders")
				  .addPackages(true, "fr.hhdev.ocelot.exceptions")
				  .addPackages(true, "fr.hhdev.ocelot.resolvers")
				  .addPackages(true, "fr.hhdev.ocelot.web")
				  .addPackages(true, "fr.hhdev.ocelot.core")
				  .addPackages(true, "fr.hhdev.ocelot.configuration")
				  .addAsManifestResource(new FileAsset(bean), "beans.xml")
				  .addAsResource(new FileAsset(core), "ocelot-core.js");
	}

	@BeforeClass
	public static void setUpClass() {
		System.out.println("===============================================================================================================");
	}

	@AfterClass
	public static void tearDownClass() {
		System.out.println("===============================================================================================================");
	}

	@Before
	public void setUp() {
		System.out.println("---------------------------------------------------------------------------------------------------------------");
	}

	@After
	public void tearDown() {
		System.out.println("---------------------------------------------------------------------------------------------------------------");
	}

	/**
	 * Créer une session localement au test
	 *
	 * @return
	 */
	public static Session createAndGetSession() {
		WebSocketContainer container = ContainerProvider.getWebSocketContainer();
		try {
			System.out.println("TRY TO CONNECT");
			StringBuilder sb = new StringBuilder("ws://localhost:");
			sb.append(PORT).append(Constants.SLASH).append(ctxpath).append(Constants.SLASH).append("ocelot-endpoint");
			URI uri = new URI(sb.toString());
			return container.connectToServer(OcelotClientEnpoint.class, uri);
		} catch (URISyntaxException | DeploymentException | IOException ex) {
			fail("CONNEXION FAILED " + ex.getMessage());
		}
		return null;
	}

	/**
	 * Crée un message formé avec les argments en parametres
	 *
	 * @param clazz
	 * @param operation
	 * @param params
	 * @return
	 */
	private MessageFromClient getMessageFromClient(String classname, String operation, String... params) {
		MessageFromClient messageFromClient = new MessageFromClient();
		messageFromClient.setId(UUID.randomUUID().toString());
		messageFromClient.setDataService(classname);
		messageFromClient.setOperation(operation);
		if (params != null) {
			messageFromClient.getParameters().addAll(Arrays.asList(params));
		}
		return messageFromClient;
	}

	private MessageFromClient getMessageFromClient(Class cls, String operation, String paramNames, String... params) {
		MessageFromClient messageFromClient = getMessageFromClient(cls.getName(), operation, params);
		messageFromClient.setParameterNames(Arrays.asList(paramNames.split(",")));
		return messageFromClient;
	}

	/**
	 * Transforme un objet en json, attention aux string
	 *
	 * @param obj
	 * @return
	 */
	private String getJson(Object obj) {
		try {
			if (String.class.isInstance(obj)) {
				return "\"" + obj + "\"";
			}
			ObjectMapper mapper = new ObjectMapper();
			return mapper.writeValueAsString(obj);
		} catch (IOException ex) {
			return null;
		}
	}

	/**
	 * Handler de message de type result Si le handler compte un id, il decomptera le lock uniquement s'il arrive à  récuperer un message avec le bon id Sinon la récupération d'un message décompte le
	 * lock
	 */
	private class CountDownMessageHandler implements MessageHandler.Whole<String> {

		private final CountDownLatch lock;
		private MessageToClient messageToClient = null;
		private String id = null;

		CountDownMessageHandler(String id, CountDownLatch lock) {
			this.lock = lock;
			this.id = id;
		}

		CountDownMessageHandler(CountDownLatch lock) {
			this.lock = lock;
		}

		@Override
		public void onMessage(String message) {
			logger.debug("RECEIVE RESPONSE FROM SERVER = {}", message);
			MessageToClient messageToClientIn = MessageToClient.createFromJson(message);
			if (id == null || id.equals(messageToClientIn.getId())) {
//			if ((id != null && id.equals(messageToClientIn.getId())) || (id == null && messageToClientIn.getId() != null)) {
				messageToClient = messageToClientIn;
				synchronized (lock) {
					lock.countDown();
				}
			}
		}

		public MessageToClient getMessageToClient() {
			return messageToClient;
		}
	}

	/**
	 * Cette methode appel via la session passé en argument sur la classe l'operation et retourne le resultat
	 *
	 * @param wsSession
	 * @param clazz
	 * @param operation
	 * @return
	 */
	private Object getResultAfterSendInSession(Session wsSession, Class clazz, String operation, String... params) {
		return getMessageToClientAfterSendInSession(wsSession, clazz.getName(), operation, params).getResult();
	}

	/**
	 * Cette methode appel via la session passé en argument sur la classe l'operation et decompte le lock
	 *
	 * @param session
	 * @param className
	 * @param operation
	 * @return
	 */
	private void checkMessageAfterSendInSession(Session session, String className, String operation, String... params) {
		// contruction de l'objet command
		Command cmd = new Command();
		cmd.setCommand(Constants.Command.Value.CALL);
		// construction de la commande
		MessageFromClient messageFromClient = getMessageFromClient(className, operation, params);
		cmd.setMessage(messageFromClient.toJson());
		// on crée un handler client de reception de la réponse
		try {
			// send
			session.getBasicRemote().sendText(cmd.toJson());
		} catch (IOException ex) {
			fail("Bean not reached");
		}
	}

	private MessageToClient getMessageToClientAfterSendInSession(Session session, String classname, String operation, String... params) {
		MessageToClient result = null;
		try {
			// contruction de l'objet command
			Command cmd = new Command();
			cmd.setCommand(Constants.Command.Value.CALL);
			// construction de lac commande
			MessageFromClient messageFromClient = getMessageFromClient(classname, operation, params);
			cmd.setMessage(messageFromClient.toJson());
			// on pose un locker
			CountDownLatch lock = new CountDownLatch(1);
			// on crée un handler client de reception de la réponse
			CountDownMessageHandler messageHandler = new CountDownMessageHandler(messageFromClient.getId(), lock);
			session.addMessageHandler(messageHandler);
			// send
			session.getBasicRemote().sendText(cmd.toJson());
			// wait le delock ou timeout
			lock.await(TIMEOUT, TimeUnit.MILLISECONDS);
			// lockCount doit être à  zero sinon, on a pas eu le resultat
			assertEquals("Timeout", 0, lock.getCount());
			// lecture du resultat dans le handler
			result = messageHandler.getMessageToClient();
			assertNotNull(result);
			session.removeMessageHandler(messageHandler);
		} catch (InterruptedException | IOException ex) {
			fail("Bean not reached");
		}
		return result;
	}

	/**
	 * Teste de la récupération de 2 instances du mm bean, il doivent être differents on met l'un dans un thread, on lui set value à  500 en dehors du thread on recup un autre bean, et on compare
	 * value, elles doivent etre different
	 */
	private void testDifferentInstancesInDifferentThreads(final Class<? extends GetValue> clazz, String resolverId) {
		final IDataServiceResolver resolver = getResolver(resolverId);
		try {
			// hors session, deux beans session scope doivent être differents
			ExecutorService executorService = Executors.newSingleThreadExecutor();
			executorService.execute(new Runnable() {

				@Override
				public void run() {
					try {
						GetValue bean1 = resolver.resolveDataService(clazz);
						bean1.setValue(500);
						Thread.sleep(1000);
						assertNotNull(bean1);
						assertTrue(clazz.isInstance(bean1));
					} catch (DataServiceException | InterruptedException ex) {
					}
				}
			});
			executorService.shutdown();
			GetValue bean2 = resolver.resolveDataService(clazz);
			assertNotNull(bean2);
			Assert.assertNotEquals("two instances of session bean should be differents", bean2.getValue(), 500);
		} catch (DataServiceException ex) {
			fail(resolverId + " bean not reached");
		}
	}

	/**
	 * Teste de la récupération de 2 beans request, il doivent être differents
	 */
	private void testInstanceRequestScope(Class clazz, String resolverId) {
		IDataServiceResolver resolver = getResolver(resolverId);
		try {
			// deux beans doivent être differents
			Object bean1 = resolver.resolveDataService(clazz);
			assertNotNull(bean1);
			assertTrue(clazz.isInstance(bean1));
			Object bean2 = resolver.resolveDataService(clazz);
			assertNotNull(bean2);
			assertFalse("two instances of request bean should be differents", bean1.equals(bean2));
		} catch (DataServiceException ex) {
			fail(resolverId + " bean not reached");
		}
	}

	/**
	 * Teste de la récupération d'un bean avec scope request
	 *
	 * @param clazz
	 */
	public void testResultRequestScope(Class clazz) {
		// premiere requete 
		Object firstResult = null;
		try (Session wssession = createAndGetSession()) {
			firstResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// deuxieme requetesur une autre session
		Object secondResult = null;
		try (Session wssession = createAndGetSession()) {
			secondResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// controle
		Assert.assertNotEquals("two instances of request bean should be differents", firstResult, secondResult); // doit etre different
	}

	/**
	 * Teste de la récupération de 2 beans singleton, il doivent être identiques
	 */
	private void testInstanceSingletonScope(Class clazz, String resolverId) {
		IDataServiceResolver resolver = getResolver(resolverId);
		try {
			// deux singletons doivent être identiques
			Object singleton1 = resolver.resolveDataService(clazz);
			assertNotNull(singleton1);
			Object singleton2 = resolver.resolveDataService(clazz);
			assertNotNull(singleton2);
			assertEquals(singleton1, singleton2);
		} catch (DataServiceException ex) {
			fail(resolverId + " bean not reached");
		}
	}

	/**
	 * Teste de la récupération d'un Singleton On excecute une methode via 2 session distincte sur le même bean. le resultat stocké  l'interieur du bean doit etre identique
	 *
	 * @param clazz
	 */
	public void testResultSingletonScope(Class clazz) {
		// premiere requete 
		Object firstResult = null;
		try (Session wssession = createAndGetSession()) {
			firstResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// deuxieme requete sur autre session
		Object secondResult = null;
		try (Session wssession = createAndGetSession()) {
			secondResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// controle, doit etre identique
		assertEquals(firstResult, secondResult);
	}

	/**
	 * Teste de la récupération de 2 beans session hors sessions, il doivent être differents
	 */
	private void testInstanceSessionScope(Class clazz, String resolverId) {
		IDataServiceResolver resolver = getResolver(resolverId);
		try {
			// hors session, deux beans session scope doivent être differents
			Object bean1 = resolver.resolveDataService(clazz);
			assertNotNull(bean1);
			assertTrue(clazz.isInstance(bean1));
			Object bean2 = resolver.resolveDataService(clazz);
			assertNotNull(bean2);
			assertFalse("two instances of session bean should be differents", bean1.equals(bean2));
		} catch (DataServiceException ex) {
			fail(resolverId + " bean not reached");
		}
	}

	/**
	 * Teste de la récupération d'un bean session, on le récupere deux fois et on check que le resultat soit identique pour une meme session, puis on crée une new session cela doit donner un resultat
	 * different
	 */
	private void testResultSessionScope(Class clazz) {
		// premiere requete 
		Object firstResult = null;
		Object secondResult = null;
		try (Session wssession = createAndGetSession()) {
			firstResult = getResultAfterSendInSession(wssession, clazz, "getValue");
			// deuxieme requete 
			secondResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// controle : sur la meme session cela doit se comporter comme un singleton, donc meme resultat
		assertEquals(secondResult, firstResult);
		// troisiement appel sur une session differente
		Object thirdResult = null;
		try (Session wssession = createAndGetSession()) {
			thirdResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// controle : sur != session cela doit etre different
		Assert.assertNotEquals(secondResult, thirdResult);
	}

	/**
	 * Récupere la resource via un HttpConnection
	 *
	 * @param resource
	 * @param min
	 * @return
	 * @throws MalformedURLException
	 * @throws IOException
	 */
	public HttpURLConnection getConnectionForResource(String resource, boolean min) throws MalformedURLException, IOException {
		StringBuilder sb = new StringBuilder("http://localhost:");
		sb.append(PORT).append(Constants.SLASH).append(ctxpath).append(Constants.SLASH).append(resource);
		if (!min) {
			sb.append("?").append(Constants.MINIFY_PARAMETER).append("=false");
		}
		URL url = new URL(sb.toString());
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestMethod("GET");
		connection.connect();
		assertEquals(200, connection.getResponseCode());
		return connection;
	}

	/**
	 * Vérification de la minification des javascripts
	 *
	 * @param resource : nom de la ressource sans slash
	 */
	public void javascriptMinification(final String resource) {
		HttpURLConnection connection1 = null;
		HttpURLConnection connection2 = null;
		try {
			connection1 = getConnectionForResource(resource, true);
			int minlength = connection1.getContentLength();
//			traceFile(connection1.getInputStream(), resource, minlength, true);
			connection2 = getConnectionForResource(resource, false);
			int length = connection2.getContentLength();
//			traceFile(connection2.getInputStream(), resource, length, false);
			assertTrue("Minification doesn't work, same size of file magnifier : " + length + " / minifer : " + minlength, minlength < length);
		} catch (Exception e) {
			fail(e.getMessage());
		} finally {
			if (connection1 != null) {
				connection1.disconnect();
			}
			if (connection2 != null) {
				connection2.disconnect();
			}
		}
	}

	private void traceFile(InputStream input, String filename, int size, boolean min) {
		try (BufferedReader in = new BufferedReader(new InputStreamReader(input))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				System.out.write(inputLine.getBytes());
				System.out.write(Constants.BACKSLASH_N.getBytes());
			}
		} catch(IOException e) {
		}
	}

	/**
	 * Vérification de la generation des services
	 */
	@Test
	public void testJavascriptServicesGeneration() {
		System.out.println("testJavascriptServicesGeneration");
		HttpURLConnection connection = null;
		try {
			connection = getConnectionForResource(Constants.OCELOT_SERVICES + Constants.JS, false);
		} catch (Exception e) {
			fail(e.getMessage());
		} finally {
			if (connection != null) {
				connection.disconnect();
			}
		}
	}

	/**
	 * Vérification de la minification des services
	 */
	@Test
	public void testJavascriptServicesMinification() {
		System.out.println("testJavascriptServicesMinification");
		javascriptMinification(Constants.OCELOT_SERVICES + Constants.JS);
	}

	/**
	 * Vérification de la generation du core
	 */
	@Test
	public void testJavascriptCoreGeneration() {
		System.out.println("testJavascriptCoreGeneration");
		try {
			HttpURLConnection connection = getConnectionForResource(Constants.OCELOT_CORE + Constants.JS, false);
			boolean replaced;
			try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
				String inputLine;
				replaced = false;
				while ((inputLine = in.readLine()) != null) {
					assertFalse("Dynamic replacement of " + Constants.CTXPATH + " doen't work", inputLine.contains(Constants.CTXPATH));
					replaced |= inputLine.contains(ctxpath);
				}
			}
			assertTrue("Dynamic replacement of context doen't work", replaced);
		} catch (Exception e) {
			fail(e.getMessage());
		}
	}

	/**
	 * Vérification de la minification des services
	 */
	@Test
	public void testJavascriptCoreMinification() {
		System.out.println("testJavascriptCoreMinification");
		javascriptMinification(Constants.OCELOT_CORE + Constants.JS);
	}

	/**
	 * Vérification qu'un resolver inconnu remonte bien une exception
	 */
	@Test(expected = UnsatisfiedResolutionException.class)
	public void testDataServiceExceptionOnUnknownResolver() {
		System.out.println("failResolveDataService");
		getResolver("foo");
	}

	/**
	 * Teste de récupération du resolver d'EJB
	 */
	@Test
	public void testGetResolverEjb() {
		System.out.println("getResolverEjb");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.EJB);
		assertNotNull(resolver);
		assertTrue(EJBResolver.class.isInstance(resolver));
	}

	/**
	 * Teste de la récupération d'EJBs par default les EJB on un scope REQUEST
	 */
	@Test
	public void testGetEjbs() {
		System.out.println("getEjbs");
		String resolverId = Constants.Resolver.EJB;
		testDifferentInstancesInDifferentThreads(EJBDataService.class, resolverId);
	}

	/**
	 * Teste de la récupération d'EJBs statefull les EJBs statefull on un scope REQUEST hors session il doivent etre donc distinct
	 */
	@Test
	public void testGetEJBStatefull() {
		System.out.println("getEJBSession");
		String resolverId = Constants.Resolver.EJB;
//		testDifferentInstancesInDifferentThreads(SessionEJBDataService.class, resolverId);
		testInstanceSessionScope(SessionEJBDataService.class, Constants.Resolver.EJB);
	}

	/**
	 * Teste de la récupération d'un ejb session (stateful), on le récupere deux fois et on check que le resultat soit identique pour une meme session, puis on crée une new session cela doit donner un
	 * resultat different les EJBs stateful on un scope SESSION
	 */
	@Test
	public void testGetResultEJBSession() {
		System.out.println("getResultEJBSession");
		testResultSessionScope(SessionEJBDataService.class);
	}

	/**
	 * Teste de la récupération d'EJBs singleton les EJBs singleton on un scope APPLICATION
	 */
	@Test
	public void testGetEJBSingleton() {
		System.out.println("getEJBSingleton");
		testInstanceSingletonScope(SingletonEJBDataService.class, Constants.Resolver.EJB);
	}

	/**
	 * Teste de la récupération d'un EJB Singleton les EJBs Singleton on un scope APPLICATION
	 */
	@Test
	public void testGetResultEjbSingleton() {
		System.out.println("getResultEjbSingleton");
		testResultSingletonScope(SingletonEJBDataService.class);
	}

	/**
	 * Teste de récupération du resolver de POJO
	 */
	@Test
	public void testGetResolverPojo() {
		System.out.println("getResolverPojo");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.POJO);
		assertNotNull(resolver);
		assertTrue(PojoResolver.class.isInstance(resolver));
	}

	/**
	 * Teste de la récupération d'un Pojo
	 */
	@Test
	public void testGetPojo() {
		System.out.println("getPojo");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.POJO);
		try {
			PojoDataService resolveDataService = resolver.resolveDataService(PojoDataService.class);
			assertNotNull(resolveDataService);
			assertEquals(PojoDataService.class, resolveDataService.getClass());
		} catch (DataServiceException ex) {
			fail("Pojo not reached");
		}
	}

	/**
	 * Teste de récupération du resolver de CDI
	 */
	@Test
	public void testGetResolverCdi() {
		System.out.println("getResolverCdi");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.CDI);
		assertNotNull(resolver);
		assertTrue(CdiResolver.class.isInstance(resolver));
	}

	/**
	 * Teste de la récupération de cdi beans par default les EJB on un scope REQUEST
	 */
	@Test
	public void testGetCdiBeans() {
		System.out.println("getCdiBeans");
		testInstanceRequestScope(CDIDataService.class, Constants.Resolver.CDI);
	}

	/**
	 * Teste de la récupération de cdi beans et test les resultats par default les EJB on un scope REQUEST
	 */
	@Test
	public void testGetResultCdiBeans() {
		System.out.println("getResultCdiBeans");
		testResultRequestScope(CDIDataService.class);
	}

	/**
	 * Teste de la récupération d'un cdi bean et verify que la classe est bien managé en controllant la presence d'un injection à  l'interieur
	 */
	@Test
	public void testGetCdiBeanIsManaged() {
		System.out.println("getCdiBeanIsManaged");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.CDI);
		try {
			CDIDataService cdids = resolver.resolveDataService(CDIDataService.class);
			assertNotNull(cdids);
			assertEquals(CDIDataService.class, cdids.getClass());
			assertNotNull(cdids.getBeanManager());
		} catch (DataServiceException ex) {
			fail("Cdi bean not reached");
		}
	}

	/**
	 * Teste de la récupération de cdi beans annoté Dependent effectivement il depend du scope de l'objet le gérant donc hors session c'est comme un scope REQUEST
	 */
	@Test
	public void testGetCdiBeanSession() {
		System.out.println("getCdiBeanSession");
		testInstanceSessionScope(SessionCDIDataService.class, Constants.Resolver.CDI);
	}

	/**
	 * Teste de la récupération d'un cdi bean session, on le récupere deux fois et on check que le resultat soit identique pour une meme session, puis on crée une new session cela doit donner un
	 * resultat different
	 */
	@Test
	public void testGetResultCdiBeanSession() {
		System.out.println("getResultCdiBeanSession");
		testResultSessionScope(SessionCDIDataService.class);
	}

	/**
	 * Teste de la récupération d'un bean CDI singleton les singleton on un scope APPLICATION
	 */
	@Test
	public void testGetCdiBeanSingleton() {
		System.out.println("getCdiBeanSingleton");
		testInstanceSingletonScope(SingletonCDIDataService.class, Constants.Resolver.CDI);
	}

	/**
	 * Teste de la récupération d'un cdi bean singleton, on le récupere deux fois et on check que c'est la meme classe
	 */
	@Test
	public void testGetResultCdiBeanSingleton() {
		System.out.println("getResultCdiBeanSingleton");
		testResultSingletonScope(SingletonCDIDataService.class);
	}

	/**
	 * Vérifie qu'un service n'existant pas remonte bien une exception
	 *
	 * @throws fr.hhdev.ocelot.spi.DataServiceException
	 */
	@Test(expected = DataServiceException.class)
	public void testDataServiceExceptionOnResolveDataService() throws DataServiceException {
		System.out.println("failResolveDataService");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.POJO);
		resolver.resolveDataService(String.class);
	}

	/**
	 * Vérifie que le pojo-resolver remonte le bien PojoDataService
	 */
	@Test
	public void testResolvePojoDataService() {
		System.out.println("resolveDataService");
		try {
			IDataServiceResolver resolver = getResolver(Constants.Resolver.POJO);
			Object dest = resolver.resolveDataService(PojoDataService.class);
			assertNotNull(dest);
			assertEquals(PojoDataService.class, dest.getClass());
		} catch (DataServiceException ex) {
			fail(ex.getMessage());
		}
	}

	/**
	 * Vérifie la désérialisation de MessageToClient result = 1
	 */
	@Test
	public void testMessageIntResultToClientCreator() {
		System.out.println("MessageToClient.createFromJson");
		String uuid = UUID.randomUUID().toString();
		Object expectedResult = 1;
		String json = String.format("{\"%s\":\"%s\",\"%s\":%s,\"%s\":\"%s\",\"%s\":%s}",
				  Constants.Message.ID, uuid, Constants.Message.DEADLINE, 0, Constants.Message.STORE, JsCacheStore.NONE, Constants.Message.RESULT, expectedResult);
		MessageToClient result = MessageToClient.createFromJson(json);
		assertEquals(uuid, result.getId());
		assertEquals("" + expectedResult, result.getResult());
	}

	/**
	 * Vérifie la désérialisation de MessageToClient result = "foo"
	 */
	@Test
	public void testMessageStringResultToClientCreator() {
		System.out.println("MessageToClient.createFromJson");
		String uuid = UUID.randomUUID().toString();
		String expectedResultJS = "\"foo\"";
		String json = String.format("{\"%s\":\"%s\",\"%s\":%s,\"%s\":\"%s\",\"%s\":%s}",
				  Constants.Message.ID, uuid, Constants.Message.DEADLINE, 0, Constants.Message.STORE, JsCacheStore.NONE, Constants.Message.RESULT, expectedResultJS);
		MessageToClient result = MessageToClient.createFromJson(json);
		assertEquals(uuid, result.getId());
		assertEquals(expectedResultJS, result.getResult());
	}

	/**
	 * Vérifie la désérialisation de MessageToClient result = {"integer": 5, "foo": "foo"}
	 */
	@Test
	public void testMessageObjectResultToClientCreator() {
		System.out.println("MessageToClient.createFromJson");
		String uuid = UUID.randomUUID().toString();
		Object expectedResult = "{\"integer\":5,\"foo\":\"foo\"}";
		String json = String.format("{\"%s\":\"%s\",\"%s\":%s,\"%s\":\"%s\",\"%s\":%s}",
				  Constants.Message.ID, uuid, Constants.Message.DEADLINE, 0, Constants.Message.STORE, JsCacheStore.NONE, Constants.Message.RESULT, expectedResult);
		MessageToClient result = MessageToClient.createFromJson(json);
		assertEquals(uuid, result.getId());
		assertEquals(expectedResult, result.getResult());
	}

	/**
	 * Vérifie la désérialisation de MessageToClient fault = "java.lang.NullPointerException"
	 */
	@Test
	public void testMessageFaultToClientCreator() {
		System.out.println("MessageToClient.createFromJson");
		String uuid = UUID.randomUUID().toString();
		Fault f = new Fault(new NullPointerException("Message d'erreur"), 0);
		String json = String.format("{\"%s\":\"%s\",\"%s\":%s,\"%s\":\"%s\",\"%s\":%s}",
				  Constants.Message.ID, uuid, Constants.Message.DEADLINE, 0, Constants.Message.STORE, JsCacheStore.NONE, Constants.Message.FAULT, f.toJson());
		MessageToClient result = MessageToClient.createFromJson(json);<commit_msg>add js on test package remove store choice cache, use only localeStore<commit_after>/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package fr.hhdev.test;

import fr.hhdev.test.dataservices.EJBDataService;
import fr.hhdev.test.dataservices.CDIDataService;
import fr.hhdev.test.dataservices.SingletonCDIDataService;
import fr.hhdev.test.dataservices.PojoDataService;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.hhdev.ocelot.Constants;
import fr.hhdev.ocelot.i18n.Locale;
import fr.hhdev.ocelot.messaging.Fault;
import fr.hhdev.ocelot.messaging.Command;
import fr.hhdev.ocelot.messaging.MessageFromClient;
import fr.hhdev.ocelot.messaging.MessageToClient;
import fr.hhdev.ocelot.messaging.MessageEvent;
import fr.hhdev.ocelot.resolvers.CdiResolver;
import fr.hhdev.ocelot.spi.DataServiceException;
import fr.hhdev.ocelot.spi.IDataServiceResolver;
import fr.hhdev.ocelot.resolvers.DataServiceResolverIdLitteral;
import fr.hhdev.ocelot.resolvers.EJBResolver;
import fr.hhdev.ocelot.resolvers.PojoResolver;
import fr.hhdev.test.dataservices.GetValue;
import fr.hhdev.test.dataservices.SessionCDIDataService;
import fr.hhdev.test.dataservices.SessionEJBDataService;
import fr.hhdev.test.dataservices.SingletonEJBDataService;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.asset.FileAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.jboss.weld.exceptions.UnsatisfiedResolutionException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author hhfrancois
 */
@RunWith(Arquillian.class)
public class OcelotTest {

	final static Logger logger = LoggerFactory.getLogger(OcelotTest.class);

	private final static long TIMEOUT = 1000;
	private final static String PORT = "8282";

	private final static String ctxpath = "ocelot-test";

	@Inject
	@MessageEvent
	Event<MessageToClient> wsEvent;

	@Inject
	@Any
	private Instance<IDataServiceResolver> resolvers;

	private IDataServiceResolver getResolver(String type) {
		return resolvers.select(new DataServiceResolverIdLitteral(type)).get();
	}

	private final PojoDataService destination = new PojoDataService();

	/**
	 * Pour tester l'api dans le contener JEE on crée un war
	 *
	 * @return
	 */
	@Deployment(name = "glassfish")
	public static WebArchive createWarGlassfishArchive() {
		return createWarArchive();
	}

	/**
	 * Pour tester l'api dans le contener JEE on crée un war
	 *
	 * @return
	 */
//	@Deployment(name = "tomcat")
//	public static WebArchive createWarTomcatArchive() {
//		File web = new File("src/test/resources/tomcat/web.xml");
//		File context = new File("src/test/resources/tomcat/context.xml");
//		return createWarArchive()
//				  .addAsManifestResource(new FileAsset(context), "context.xml")
//				  .addAsWebInfResource(new FileAsset(web), "web.xml");
//	}
	public static WebArchive createWarArchive() {
		File[] libs = Maven.resolver().loadPomFromFile("pom.xml").importRuntimeDependencies().resolve().withTransitivity().asFile();
		File logback = new File("src/test/resources/logback.xml");
		File localeFr = new File("src/test/resources/test_fr_FR.properties");
		File localeUs = new File("src/test/resources/test_en_US.properties");
		File classes = new File("target/test-classes");
		File[] jsFiles = classes.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return file.isFile() && file.getName().endsWith(".js");
			}
		});
		WebArchive webArchive = ShrinkWrap.create(WebArchive.class, ctxpath + ".war")
				  .addAsLibraries(libs)
				  .addAsLibraries(createLibArchive())
				  .addPackages(true, "services")
				  .addPackages(true, OcelotTest.class.getPackage())
				  .addAsWebInfResource(new FileAsset(logback), "logback.xml")
				  .addAsResource(new FileAsset(localeUs), "test_en_US.properties")
				  .addAsResource(new FileAsset(localeFr), "test_fr_FR.properties")
				  .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
		for (File file : jsFiles) {
			webArchive.addAsResource(new FileAsset(file), file.getName());
		}
		return webArchive;
	}

	public static JavaArchive createLibArchive() {
		File bean = new File("src/main/resources/META-INF/beans.xml");
		File core = new File("src/main/resources/ocelot-core.js");
		return ShrinkWrap.create(JavaArchive.class, "ocelot-web.jar")
				  .addPackages(true, "fr.hhdev.ocelot.encoders")
				  .addPackages(true, "fr.hhdev.ocelot.exceptions")
				  .addPackages(true, "fr.hhdev.ocelot.resolvers")
				  .addPackages(true, "fr.hhdev.ocelot.web")
				  .addPackages(true, "fr.hhdev.ocelot.core")
				  .addPackages(true, "fr.hhdev.ocelot.configuration")
				  .addAsManifestResource(new FileAsset(bean), "beans.xml")
				  .addAsResource(new FileAsset(core), "ocelot-core.js");
	}

	@BeforeClass
	public static void setUpClass() {
		System.out.println("===============================================================================================================");
	}

	@AfterClass
	public static void tearDownClass() {
		System.out.println("===============================================================================================================");
	}

	@Before
	public void setUp() {
		System.out.println("---------------------------------------------------------------------------------------------------------------");
	}

	@After
	public void tearDown() {
		System.out.println("---------------------------------------------------------------------------------------------------------------");
	}

	/**
	 * Créer une session localement au test
	 *
	 * @return
	 */
	public static Session createAndGetSession() {
		WebSocketContainer container = ContainerProvider.getWebSocketContainer();
		try {
			System.out.println("TRY TO CONNECT");
			StringBuilder sb = new StringBuilder("ws://localhost:");
			sb.append(PORT).append(Constants.SLASH).append(ctxpath).append(Constants.SLASH).append("ocelot-endpoint");
			URI uri = new URI(sb.toString());
			return container.connectToServer(OcelotClientEnpoint.class, uri);
		} catch (URISyntaxException | DeploymentException | IOException ex) {
			fail("CONNEXION FAILED " + ex.getMessage());
		}
		return null;
	}

	/**
	 * Crée un message formé avec les argments en parametres
	 *
	 * @param clazz
	 * @param operation
	 * @param params
	 * @return
	 */
	private MessageFromClient getMessageFromClient(String classname, String operation, String... params) {
		MessageFromClient messageFromClient = new MessageFromClient();
		messageFromClient.setId(UUID.randomUUID().toString());
		messageFromClient.setDataService(classname);
		messageFromClient.setOperation(operation);
		if (params != null) {
			messageFromClient.getParameters().addAll(Arrays.asList(params));
		}
		return messageFromClient;
	}

	private MessageFromClient getMessageFromClient(Class cls, String operation, String paramNames, String... params) {
		MessageFromClient messageFromClient = getMessageFromClient(cls.getName(), operation, params);
		messageFromClient.setParameterNames(Arrays.asList(paramNames.split(",")));
		return messageFromClient;
	}

	/**
	 * Transforme un objet en json, attention aux string
	 *
	 * @param obj
	 * @return
	 */
	private String getJson(Object obj) {
		try {
			if (String.class.isInstance(obj)) {
				return "\"" + obj + "\"";
			}
			ObjectMapper mapper = new ObjectMapper();
			return mapper.writeValueAsString(obj);
		} catch (IOException ex) {
			return null;
		}
	}

	/**
	 * Handler de message de type result Si le handler compte un id, il decomptera le lock uniquement s'il arrive à  récuperer un message avec le bon id Sinon la récupération d'un message décompte le
	 * lock
	 */
	private class CountDownMessageHandler implements MessageHandler.Whole<String> {

		private final CountDownLatch lock;
		private MessageToClient messageToClient = null;
		private String id = null;

		CountDownMessageHandler(String id, CountDownLatch lock) {
			this.lock = lock;
			this.id = id;
		}

		CountDownMessageHandler(CountDownLatch lock) {
			this.lock = lock;
		}

		@Override
		public void onMessage(String message) {
			logger.debug("RECEIVE RESPONSE FROM SERVER = {}", message);
			MessageToClient messageToClientIn = MessageToClient.createFromJson(message);
			if (id == null || id.equals(messageToClientIn.getId())) {
//			if ((id != null && id.equals(messageToClientIn.getId())) || (id == null && messageToClientIn.getId() != null)) {
				messageToClient = messageToClientIn;
				synchronized (lock) {
					lock.countDown();
				}
			}
		}

		public MessageToClient getMessageToClient() {
			return messageToClient;
		}
	}

	/**
	 * Cette methode appel via la session passé en argument sur la classe l'operation et retourne le resultat
	 *
	 * @param wsSession
	 * @param clazz
	 * @param operation
	 * @return
	 */
	private Object getResultAfterSendInSession(Session wsSession, Class clazz, String operation, String... params) {
		return getMessageToClientAfterSendInSession(wsSession, clazz.getName(), operation, params).getResult();
	}

	/**
	 * Cette methode appel via la session passé en argument sur la classe l'operation et decompte le lock
	 *
	 * @param session
	 * @param className
	 * @param operation
	 * @return
	 */
	private void checkMessageAfterSendInSession(Session session, String className, String operation, String... params) {
		// contruction de l'objet command
		Command cmd = new Command();
		cmd.setCommand(Constants.Command.Value.CALL);
		// construction de la commande
		MessageFromClient messageFromClient = getMessageFromClient(className, operation, params);
		cmd.setMessage(messageFromClient.toJson());
		// on crée un handler client de reception de la réponse
		try {
			// send
			session.getBasicRemote().sendText(cmd.toJson());
		} catch (IOException ex) {
			fail("Bean not reached");
		}
	}

	private MessageToClient getMessageToClientAfterSendInSession(Session session, String classname, String operation, String... params) {
		MessageToClient result = null;
		try {
			// contruction de l'objet command
			Command cmd = new Command();
			cmd.setCommand(Constants.Command.Value.CALL);
			// construction de lac commande
			MessageFromClient messageFromClient = getMessageFromClient(classname, operation, params);
			cmd.setMessage(messageFromClient.toJson());
			// on pose un locker
			CountDownLatch lock = new CountDownLatch(1);
			// on crée un handler client de reception de la réponse
			CountDownMessageHandler messageHandler = new CountDownMessageHandler(messageFromClient.getId(), lock);
			session.addMessageHandler(messageHandler);
			// send
			session.getBasicRemote().sendText(cmd.toJson());
			// wait le delock ou timeout
			lock.await(TIMEOUT, TimeUnit.MILLISECONDS);
			// lockCount doit être à  zero sinon, on a pas eu le resultat
			assertEquals("Timeout", 0, lock.getCount());
			// lecture du resultat dans le handler
			result = messageHandler.getMessageToClient();
			assertNotNull(result);
			session.removeMessageHandler(messageHandler);
		} catch (InterruptedException | IOException ex) {
			fail("Bean not reached");
		}
		return result;
	}

	/**
	 * Teste de la récupération de 2 instances du mm bean, il doivent être differents on met l'un dans un thread, on lui set value à  500 en dehors du thread on recup un autre bean, et on compare
	 * value, elles doivent etre different
	 */
	private void testDifferentInstancesInDifferentThreads(final Class<? extends GetValue> clazz, String resolverId) {
		final IDataServiceResolver resolver = getResolver(resolverId);
		try {
			// hors session, deux beans session scope doivent être differents
			ExecutorService executorService = Executors.newSingleThreadExecutor();
			executorService.execute(new Runnable() {

				@Override
				public void run() {
					try {
						GetValue bean1 = resolver.resolveDataService(clazz);
						bean1.setValue(500);
						Thread.sleep(1000);
						assertNotNull(bean1);
						assertTrue(clazz.isInstance(bean1));
					} catch (DataServiceException | InterruptedException ex) {
					}
				}
			});
			executorService.shutdown();
			GetValue bean2 = resolver.resolveDataService(clazz);
			assertNotNull(bean2);
			Assert.assertNotEquals("two instances of session bean should be differents", bean2.getValue(), 500);
		} catch (DataServiceException ex) {
			fail(resolverId + " bean not reached");
		}
	}

	/**
	 * Teste de la récupération de 2 beans request, il doivent être differents
	 */
	private void testInstanceRequestScope(Class clazz, String resolverId) {
		IDataServiceResolver resolver = getResolver(resolverId);
		try {
			// deux beans doivent être differents
			Object bean1 = resolver.resolveDataService(clazz);
			assertNotNull(bean1);
			assertTrue(clazz.isInstance(bean1));
			Object bean2 = resolver.resolveDataService(clazz);
			assertNotNull(bean2);
			assertFalse("two instances of request bean should be differents", bean1.equals(bean2));
		} catch (DataServiceException ex) {
			fail(resolverId + " bean not reached");
		}
	}

	/**
	 * Teste de la récupération d'un bean avec scope request
	 *
	 * @param clazz
	 */
	public void testResultRequestScope(Class clazz) {
		// premiere requete 
		Object firstResult = null;
		try (Session wssession = createAndGetSession()) {
			firstResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// deuxieme requetesur une autre session
		Object secondResult = null;
		try (Session wssession = createAndGetSession()) {
			secondResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// controle
		Assert.assertNotEquals("two instances of request bean should be differents", firstResult, secondResult); // doit etre different
	}

	/**
	 * Teste de la récupération de 2 beans singleton, il doivent être identiques
	 */
	private void testInstanceSingletonScope(Class clazz, String resolverId) {
		IDataServiceResolver resolver = getResolver(resolverId);
		try {
			// deux singletons doivent être identiques
			Object singleton1 = resolver.resolveDataService(clazz);
			assertNotNull(singleton1);
			Object singleton2 = resolver.resolveDataService(clazz);
			assertNotNull(singleton2);
			assertEquals(singleton1, singleton2);
		} catch (DataServiceException ex) {
			fail(resolverId + " bean not reached");
		}
	}

	/**
	 * Teste de la récupération d'un Singleton On excecute une methode via 2 session distincte sur le même bean. le resultat stocké  l'interieur du bean doit etre identique
	 *
	 * @param clazz
	 */
	public void testResultSingletonScope(Class clazz) {
		// premiere requete 
		Object firstResult = null;
		try (Session wssession = createAndGetSession()) {
			firstResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// deuxieme requete sur autre session
		Object secondResult = null;
		try (Session wssession = createAndGetSession()) {
			secondResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// controle, doit etre identique
		assertEquals(firstResult, secondResult);
	}

	/**
	 * Teste de la récupération de 2 beans session hors sessions, il doivent être differents
	 */
	private void testInstanceSessionScope(Class clazz, String resolverId) {
		IDataServiceResolver resolver = getResolver(resolverId);
		try {
			// hors session, deux beans session scope doivent être differents
			Object bean1 = resolver.resolveDataService(clazz);
			assertNotNull(bean1);
			assertTrue(clazz.isInstance(bean1));
			Object bean2 = resolver.resolveDataService(clazz);
			assertNotNull(bean2);
			assertFalse("two instances of session bean should be differents", bean1.equals(bean2));
		} catch (DataServiceException ex) {
			fail(resolverId + " bean not reached");
		}
	}

	/**
	 * Teste de la récupération d'un bean session, on le récupere deux fois et on check que le resultat soit identique pour une meme session, puis on crée une new session cela doit donner un resultat
	 * different
	 */
	private void testResultSessionScope(Class clazz) {
		// premiere requete 
		Object firstResult = null;
		Object secondResult = null;
		try (Session wssession = createAndGetSession()) {
			firstResult = getResultAfterSendInSession(wssession, clazz, "getValue");
			// deuxieme requete 
			secondResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// controle : sur la meme session cela doit se comporter comme un singleton, donc meme resultat
		assertEquals(secondResult, firstResult);
		// troisiement appel sur une session differente
		Object thirdResult = null;
		try (Session wssession = createAndGetSession()) {
			thirdResult = getResultAfterSendInSession(wssession, clazz, "getValue");
		} catch (IOException exception) {
		}
		// controle : sur != session cela doit etre different
		Assert.assertNotEquals(secondResult, thirdResult);
	}

	/**
	 * Récupere la resource via un HttpConnection
	 *
	 * @param resource
	 * @param min
	 * @return
	 * @throws MalformedURLException
	 * @throws IOException
	 */
	public HttpURLConnection getConnectionForResource(String resource, boolean min) throws MalformedURLException, IOException {
		StringBuilder sb = new StringBuilder("http://localhost:");
		sb.append(PORT).append(Constants.SLASH).append(ctxpath).append(Constants.SLASH).append(resource);
		if (!min) {
			sb.append("?").append(Constants.MINIFY_PARAMETER).append("=false");
		}
		URL url = new URL(sb.toString());
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestMethod("GET");
		connection.connect();
		assertEquals(200, connection.getResponseCode());
		return connection;
	}

	/**
	 * Vérification de la minification des javascripts
	 *
	 * @param resource : nom de la ressource sans slash
	 */
	public void javascriptMinification(final String resource) {
		HttpURLConnection connection1 = null;
		HttpURLConnection connection2 = null;
		try {
			connection1 = getConnectionForResource(resource, true);
			int minlength = connection1.getContentLength();
			traceFile(connection1.getInputStream(), resource, minlength, true);
			connection2 = getConnectionForResource(resource, false);
			int length = connection2.getContentLength();
			traceFile(connection2.getInputStream(), resource, length, false);
			assertTrue("Minification doesn't work, same size of file magnifier : " + length + " / minifer : " + minlength, minlength < length);
		} catch (Exception e) {
			fail(e.getMessage());
		} finally {
			if (connection1 != null) {
				connection1.disconnect();
			}
			if (connection2 != null) {
				connection2.disconnect();
			}
		}
	}

	private void traceFile(InputStream input, String filename, int size, boolean min) {
		try (BufferedReader in = new BufferedReader(new InputStreamReader(input))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				System.out.write(inputLine.getBytes());
				System.out.write(Constants.BACKSLASH_N.getBytes());
			}
		} catch(IOException e) {
		}
	}

	/**
	 * Vérification de la generation des services
	 */
	@Test
	public void testJavascriptServicesGeneration() {
		System.out.println("testJavascriptServicesGeneration");
		HttpURLConnection connection = null;
		try {
			connection = getConnectionForResource(Constants.OCELOT_SERVICES + Constants.JS, false);
		} catch (Exception e) {
			fail(e.getMessage());
		} finally {
			if (connection != null) {
				connection.disconnect();
			}
		}
	}

	/**
	 * Vérification de la minification des services
	 */
	@Test
	public void testJavascriptServicesMinification() {
		System.out.println("testJavascriptServicesMinification");
		javascriptMinification(Constants.OCELOT_SERVICES + Constants.JS);
	}

	/**
	 * Vérification de la generation du core
	 */
	@Test
	public void testJavascriptCoreGeneration() {
		System.out.println("testJavascriptCoreGeneration");
		try {
			HttpURLConnection connection = getConnectionForResource(Constants.OCELOT_CORE + Constants.JS, false);
			boolean replaced;
			try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
				String inputLine;
				replaced = false;
				while ((inputLine = in.readLine()) != null) {
					assertFalse("Dynamic replacement of " + Constants.CTXPATH + " doen't work", inputLine.contains(Constants.CTXPATH));
					replaced |= inputLine.contains(ctxpath);
				}
			}
			assertTrue("Dynamic replacement of context doen't work", replaced);
		} catch (Exception e) {
			fail(e.getMessage());
		}
	}

	/**
	 * Vérification de la minification des services
	 */
	@Test
	public void testJavascriptCoreMinification() {
		System.out.println("testJavascriptCoreMinification");
		javascriptMinification(Constants.OCELOT_CORE + Constants.JS);
	}

	/**
	 * Vérification qu'un resolver inconnu remonte bien une exception
	 */
	@Test(expected = UnsatisfiedResolutionException.class)
	public void testDataServiceExceptionOnUnknownResolver() {
		System.out.println("failResolveDataService");
		getResolver("foo");
	}

	/**
	 * Teste de récupération du resolver d'EJB
	 */
	@Test
	public void testGetResolverEjb() {
		System.out.println("getResolverEjb");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.EJB);
		assertNotNull(resolver);
		assertTrue(EJBResolver.class.isInstance(resolver));
	}

	/**
	 * Teste de la récupération d'EJBs par default les EJB on un scope REQUEST
	 */
	@Test
	public void testGetEjbs() {
		System.out.println("getEjbs");
		String resolverId = Constants.Resolver.EJB;
		testDifferentInstancesInDifferentThreads(EJBDataService.class, resolverId);
	}

	/**
	 * Teste de la récupération d'EJBs statefull les EJBs statefull on un scope REQUEST hors session il doivent etre donc distinct
	 */
	@Test
	public void testGetEJBStatefull() {
		System.out.println("getEJBSession");
		String resolverId = Constants.Resolver.EJB;
//		testDifferentInstancesInDifferentThreads(SessionEJBDataService.class, resolverId);
		testInstanceSessionScope(SessionEJBDataService.class, Constants.Resolver.EJB);
	}

	/**
	 * Teste de la récupération d'un ejb session (stateful), on le récupere deux fois et on check que le resultat soit identique pour une meme session, puis on crée une new session cela doit donner un
	 * resultat different les EJBs stateful on un scope SESSION
	 */
	@Test
	public void testGetResultEJBSession() {
		System.out.println("getResultEJBSession");
		testResultSessionScope(SessionEJBDataService.class);
	}

	/**
	 * Teste de la récupération d'EJBs singleton les EJBs singleton on un scope APPLICATION
	 */
	@Test
	public void testGetEJBSingleton() {
		System.out.println("getEJBSingleton");
		testInstanceSingletonScope(SingletonEJBDataService.class, Constants.Resolver.EJB);
	}

	/**
	 * Teste de la récupération d'un EJB Singleton les EJBs Singleton on un scope APPLICATION
	 */
	@Test
	public void testGetResultEjbSingleton() {
		System.out.println("getResultEjbSingleton");
		testResultSingletonScope(SingletonEJBDataService.class);
	}

	/**
	 * Teste de récupération du resolver de POJO
	 */
	@Test
	public void testGetResolverPojo() {
		System.out.println("getResolverPojo");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.POJO);
		assertNotNull(resolver);
		assertTrue(PojoResolver.class.isInstance(resolver));
	}

	/**
	 * Teste de la récupération d'un Pojo
	 */
	@Test
	public void testGetPojo() {
		System.out.println("getPojo");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.POJO);
		try {
			PojoDataService resolveDataService = resolver.resolveDataService(PojoDataService.class);
			assertNotNull(resolveDataService);
			assertEquals(PojoDataService.class, resolveDataService.getClass());
		} catch (DataServiceException ex) {
			fail("Pojo not reached");
		}
	}

	/**
	 * Teste de récupération du resolver de CDI
	 */
	@Test
	public void testGetResolverCdi() {
		System.out.println("getResolverCdi");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.CDI);
		assertNotNull(resolver);
		assertTrue(CdiResolver.class.isInstance(resolver));
	}

	/**
	 * Teste de la récupération de cdi beans par default les EJB on un scope REQUEST
	 */
	@Test
	public void testGetCdiBeans() {
		System.out.println("getCdiBeans");
		testInstanceRequestScope(CDIDataService.class, Constants.Resolver.CDI);
	}

	/**
	 * Teste de la récupération de cdi beans et test les resultats par default les EJB on un scope REQUEST
	 */
	@Test
	public void testGetResultCdiBeans() {
		System.out.println("getResultCdiBeans");
		testResultRequestScope(CDIDataService.class);
	}

	/**
	 * Teste de la récupération d'un cdi bean et verify que la classe est bien managé en controllant la presence d'un injection à  l'interieur
	 */
	@Test
	public void testGetCdiBeanIsManaged() {
		System.out.println("getCdiBeanIsManaged");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.CDI);
		try {
			CDIDataService cdids = resolver.resolveDataService(CDIDataService.class);
			assertNotNull(cdids);
			assertEquals(CDIDataService.class, cdids.getClass());
			assertNotNull(cdids.getBeanManager());
		} catch (DataServiceException ex) {
			fail("Cdi bean not reached");
		}
	}

	/**
	 * Teste de la récupération de cdi beans annoté Dependent effectivement il depend du scope de l'objet le gérant donc hors session c'est comme un scope REQUEST
	 */
	@Test
	public void testGetCdiBeanSession() {
		System.out.println("getCdiBeanSession");
		testInstanceSessionScope(SessionCDIDataService.class, Constants.Resolver.CDI);
	}

	/**
	 * Teste de la récupération d'un cdi bean session, on le récupere deux fois et on check que le resultat soit identique pour une meme session, puis on crée une new session cela doit donner un
	 * resultat different
	 */
	@Test
	public void testGetResultCdiBeanSession() {
		System.out.println("getResultCdiBeanSession");
		testResultSessionScope(SessionCDIDataService.class);
	}

	/**
	 * Teste de la récupération d'un bean CDI singleton les singleton on un scope APPLICATION
	 */
	@Test
	public void testGetCdiBeanSingleton() {
		System.out.println("getCdiBeanSingleton");
		testInstanceSingletonScope(SingletonCDIDataService.class, Constants.Resolver.CDI);
	}

	/**
	 * Teste de la récupération d'un cdi bean singleton, on le récupere deux fois et on check que c'est la meme classe
	 */
	@Test
	public void testGetResultCdiBeanSingleton() {
		System.out.println("getResultCdiBeanSingleton");
		testResultSingletonScope(SingletonCDIDataService.class);
	}

	/**
	 * Vérifie qu'un service n'existant pas remonte bien une exception
	 *
	 * @throws fr.hhdev.ocelot.spi.DataServiceException
	 */
	@Test(expected = DataServiceException.class)
	public void testDataServiceExceptionOnResolveDataService() throws DataServiceException {
		System.out.println("failResolveDataService");
		IDataServiceResolver resolver = getResolver(Constants.Resolver.POJO);
		resolver.resolveDataService(String.class);
	}

	/**
	 * Vérifie que le pojo-resolver remonte le bien PojoDataService
	 */
	@Test
	public void testResolvePojoDataService() {
		System.out.println("resolveDataService");
		try {
			IDataServiceResolver resolver = getResolver(Constants.Resolver.POJO);
			Object dest = resolver.resolveDataService(PojoDataService.class);
			assertNotNull(dest);
			assertEquals(PojoDataService.class, dest.getClass());
		} catch (DataServiceException ex) {
			fail(ex.getMessage());
		}
	}

	/**
	 * Vérifie la désérialisation de MessageToClient result = 1
	 */
	@Test
	public void testMessageIntResultToClientCreator() {
		System.out.println("MessageToClient.createFromJson");
		String uuid = UUID.randomUUID().toString();
		Object expectedResult = 1;
		String json = String.format("{\"%s\":\"%s\",\"%s\":%s,\"%s\":%s}",
				  Constants.Message.ID, uuid, Constants.Message.DEADLINE, 0, Constants.Message.RESULT, expectedResult);
		MessageToClient result = MessageToClient.createFromJson(json);
		assertEquals(uuid, result.getId());
		assertEquals("" + expectedResult, result.getResult());
	}

	/**
	 * Vérifie la désérialisation de MessageToClient result = "foo"
	 */
	@Test
	public void testMessageStringResultToClientCreator() {
		System.out.println("MessageToClient.createFromJson");
		String uuid = UUID.randomUUID().toString();
		String expectedResultJS = "\"foo\"";
		String json = String.format("{\"%s\":\"%s\",\"%s\":%s,\"%s\":%s}",
				  Constants.Message.ID, uuid, Constants.Message.DEADLINE, 0, Constants.Message.RESULT, expectedResultJS);
		MessageToClient result = MessageToClient.createFromJson(json);
		assertEquals(uuid, result.getId());
		assertEquals(expectedResultJS, result.getResult());
	}

	/**
	 * Vérifie la désérialisation de MessageToClient result = {"integer": 5, "foo": "foo"}
	 */
	@Test
	public void testMessageObjectResultToClientCreator() {
		System.out.println("MessageToClient.createFromJson");
		String uuid = UUID.randomUUID().toString();
		Object expectedResult = "{\"integer\":5,\"foo\":\"foo\"}";
		String json = String.format("{\"%s\":\"%s\",\"%s\":%s,\"%s\":%s}",
				  Constants.Message.ID, uuid, Constants.Message.DEADLINE, 0, Constants.Message.RESULT, expectedResult);
		MessageToClient result = MessageToClient.createFromJson(json);
		assertEquals(uuid, result.getId());
		assertEquals(expectedResult, result.getResult());
	}

	/**
	 * Vérifie la désérialisation de MessageToClient fault = "java.lang.NullPointerException"
	 */
	@Test
	public void testMessageFaultToClientCreator() {
		System.out.println("MessageToClient.createFromJson");
		String uuid = UUID.randomUUID().toString();
		Fault f = new Fault(new NullPointerException("Message d'erreur"), 0);
		String json = String.format("{\"%s\":\"%s\",\"%s\":%s,\"%s\":%s}",
				  Constants.Message.ID, uuid, Constants.Message.DEADLINE, 0, Constants.Message.FAULT, f.toJson());
		MessageToClient result = MessageToClient.createFromJson(json);
{'fraction_non_alphanumeric': 0.12385546757224242, 'fraction_numerical': 0.0026342098163987496, 'mean_word_length': 4.554204124801692, 'pattern_counts': {'":': 36, '<': 13, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 22}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1688437', 'n_tokens_mistral': 20799, 'n_tokens_neox': 18797, 'n_words': 4502}