/FileExtraction/Java_unseen/Orange-OpenSource_fiware-cepheus/cepheus-broker/src/test/java/com/orange/cepheus/broker/controller/NgsiControllerTest.java
/*
 * Copyright (C) 2015 Orange
 *
 * This software is distributed under the terms and conditions of the 'GNU GENERAL PUBLIC LICENSE
 * Version 2' license which can be found in the file 'LICENSE.txt' in this package distribution or
 * at 'http://www.gnu.org/licenses/gpl-2.0-standalone.html'.
 */

package com.orange.cepheus.broker.controller;

import com.orange.cepheus.broker.Application;
import com.orange.cepheus.broker.Configuration;
import com.orange.cepheus.broker.LocalRegistrations;
import com.orange.cepheus.broker.Subscriptions;
import com.orange.cepheus.broker.exception.RegistrationException;
import com.orange.cepheus.broker.exception.RegistrationPersistenceException;
import com.orange.cepheus.broker.exception.SubscriptionException;
import com.orange.cepheus.broker.exception.SubscriptionPersistenceException;
import com.orange.cepheus.broker.model.Subscription;
import com.orange.ngsi.client.NgsiClient;
import com.orange.ngsi.model.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.context.WebApplicationContext;

import java.net.URI;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import static com.orange.cepheus.broker.Util.*;
import static com.orange.cepheus.broker.Util.createSubscribeContextTemperature;
import static com.orange.cepheus.broker.Util.createUpdateContextResponseTempSensorAndPressure;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.atLeastOnce;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

/**
 * Tests for the NGSI controller
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class NgsiControllerTest {

    private MockMvc mockMvc;

    @Mock
    LocalRegistrations localRegistrations;

    @Mock
    Subscriptions subscriptions;

    @Mock
    NgsiClient ngsiClient;

    @Mock
    Configuration configuration;

    @Mock
    Iterator<URI> providingApplication;

    @Mock
    Iterator<Subscription> matchedSubscriptions;

    @Mock
    ListenableFuture<UpdateContextResponse> updateContextResponseListenableFuture;

    @Mock
    ListenableFuture<QueryContextResponse> queryContextResponseListenableFuture;

    @Mock
    ListenableFuture<NotifyContextResponse> notifyContextResponseListenableFuture;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Autowired
    private MappingJackson2HttpMessageConverter mapper;

    @Captor
    private ArgumentCaptor<Set<String>> attributeArgumentCaptor;

    @Captor
    private ArgumentCaptor<EntityId> entityIdArgumentCaptor;

    @InjectMocks
    @Autowired
    private NgsiController ngsiController;

    private HttpHeaders httpHeaders = new HttpHeaders();

    @Before
    public void setup() throws Exception {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = webAppContextSetup(webApplicationContext).build();
        when(configuration.getLocalUrl()).thenReturn("http://localhost:8081");
        when(configuration.getRemoteUrl()).thenReturn("http://orionhost:9999");
        when(configuration.getRemoteServiceName()).thenReturn(null);
        when(configuration.getRemoteServicePath()).thenReturn(null);
        when(configuration.getRemoteAuthToken()).thenReturn(null);
        when(configuration.isRemoteForwardUpdateContext()).thenReturn(true);
        when(updateContextResponseListenableFuture.get()).thenReturn(createUpdateContextResponseTempSensorAndPressure());
        doNothing().when(updateContextResponseListenableFuture).addCallback(any(), any());
        when(queryContextResponseListenableFuture.get()).thenReturn(createQueryContextResponseTemperature());
        doNothing().when(notifyContextResponseListenableFuture).addCallback(any(), any());
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        when(ngsiClient.getRequestHeaders(any())).thenReturn(httpHeaders);
    }

    @After
    public void resetMocks() {
        reset(localRegistrations);
        reset(subscriptions);
        reset(ngsiClient);
        reset(configuration);
        reset(providingApplication);
        reset(matchedSubscriptions);
        reset(updateContextResponseListenableFuture);
        reset(notifyContextResponseListenableFuture);
    }

    @Test
    public void postRegisterContextWithEmptyContextRegistrations() throws Exception {

        mockMvc.perform(post("/v1/registerContext")
                .content(json(mapper, new RegisterContext()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value(CodeEnum.CODE_471.getLabel()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value(CodeEnum.CODE_471.getShortPhrase()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details")
                        .value("The parameter contextRegistrations of type List<ContextRegistration> is missing in the request"));
    }

    @Test
    public void postRegisterContextWithBadContextRegistrations() throws Exception {
        // RegisterContext with a bad pattern
        RegisterContext registerContext = createRegistrationContext();
        registerContext.getContextRegistrationList().get(0).getEntityIdList().get(0).setId("]|,\\((");
        registerContext.getContextRegistrationList().get(0).getEntityIdList().get(0).setIsPattern(true);

        when(localRegistrations.updateRegistrationContext(any())).thenThrow(new RegistrationException("bad pattern", new RuntimeException()));

        mockMvc.perform(post("/v1/registerContext").content(json(mapper, registerContext)).contentType(MediaType.APPLICATION_JSON))
                .andDo(mvcResult -> System.out.println(mvcResult.getResponse().getContentAsString()))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("400"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("registration error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("bad pattern"));
    }

    @Test
    public void postRegisterContextWithPersistenceException() throws Exception {

        when(localRegistrations.updateRegistrationContext(any())).thenThrow(new RegistrationPersistenceException("Failed to save", new RuntimeException()));

        mockMvc.perform(post("/v1/registerContext").content(json(mapper, createRegisterContextTemperature())).contentType(MediaType.APPLICATION_JSON))
                .andDo(mvcResult -> System.out.println(mvcResult.getResponse().getContentAsString()))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("error in registration persistence"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("Failed to save"));
    }

    @Test
    public void postNewRegisterContext() throws Exception {

        when(localRegistrations.updateRegistrationContext(any())).thenReturn("12345678");
        ListenableFuture<RegisterContextResponse> responseFuture = Mockito.mock(ListenableFuture.class);
        doNothing().when(responseFuture).addCallback(any(), any());
        when(ngsiClient.registerContext(any(), eq(null), any())).thenReturn(responseFuture);

        mockMvc.perform(post("/v1/registerContext")
                .content(json(mapper, createRegisterContextTemperature()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.registrationId").value("12345678"));
    }

    @Test
    public void postUpdateContextWithNullUpdateAction() throws Exception {

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, new UpdateContext()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value(CodeEnum.CODE_471.getLabel()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value(CodeEnum.CODE_471.getShortPhrase()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("The parameter updateAction of type string is missing in the request"));
    }

    @Test
    public void postUpdateContextWithoutProvidingApplication() throws Exception {

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);
        //subscriptions mock return always without matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(false);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].name").value("pressure"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].type").value("int"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].value").value("1015"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        verify(configuration, atLeastOnce()).isRemoteForwardUpdateContext();

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S1", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertFalse(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(2, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));
        assertTrue(attributeArgumentCaptor.getValue().contains("pressure"));

        // Capture updateContext when postUpdateContextRequest is called on updateContextRequest,
        ArgumentCaptor<UpdateContext> updateContextArg = ArgumentCaptor.forClass(UpdateContext.class);
        String urlProvider = "http://orionhost:9999";

        //check ListenableFuture is called at least Once and with addCallback method
        verify(updateContextResponseListenableFuture, atLeastOnce()).addCallback(any(), any());

        //check ngsiClient.getRequestHeaders() is called at least Once
        verify(ngsiClient, atLeastOnce()).getRequestHeaders(eq("http://orionhost:9999"));

        //check configuration.getHeadersForBroker() is called at least Once
        verify(configuration, atLeastOnce()).addRemoteHeaders(any());

        //verify urlProvider
        verify(ngsiClient, atLeastOnce()).updateContext(eq(urlProvider), any(), updateContextArg.capture());

        // Check id correspond to the required
        ContextElement contextElement = updateContextArg.getValue().getContextElements().get(0);
        assertEquals("S1", contextElement.getEntityId().getId());

        //check ngsiClient.notify is not called
        verify(ngsiClient, never()).notifyContext(any(), any(), any());
    }

    @Test
    public void checkRemoteBrokerNotCalledWhenForwardIsDisabled() throws Exception {
        // Disable updateContext forwarding to remote broker
        when(configuration.isRemoteForwardUpdateContext()).thenReturn(false);

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);
        //subscriptions mock return always without matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(false);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        verify(configuration, atLeastOnce()).isRemoteForwardUpdateContext();
        verify(ngsiClient, never()).updateContext(eq("http://orionhost:9999"), any(), any());
    }

    @Test
    public void postUpdateContextWithoutProvidingApplicationAndWithoutRemoteBrokerButWithNotify() throws Exception {

        when(configuration.getRemoteUrl()).thenReturn(null);

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always with matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(true, false);
        SubscribeContext subscribeContext = createSubscribeContextTemperature();
        Subscription subscription = new Subscription("999999", Instant.now().plus(1, ChronoUnit.DAYS), subscribeContext);
        when(matchedSubscriptions.next()).thenReturn(subscription);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        //ngsiClient mock return always CODE_200
        when(ngsiClient.notifyContextCustomURL(any(), any(), any())).thenReturn(notifyContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].name").value("pressure"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].type").value("int"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].value").value("1015"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S1", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertFalse(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(2, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));
        assertTrue(attributeArgumentCaptor.getValue().contains("pressure"));

        //check ListenableFuture is called never and with addCallback method
        verify(updateContextResponseListenableFuture, never()).addCallback(any(), any());

        //verify ngsiClient.updateContext is never called
        verify(ngsiClient, never()).updateContext(any(), any(), any());

        //check ngsiClient.notify is called at least once
        // Capture notifyContext when postNotifyContextRequest is called on updateContextRequest,
        ArgumentCaptor<NotifyContext> notifyContextArg = ArgumentCaptor.forClass(NotifyContext.class);
        verify(notifyContextResponseListenableFuture, atLeastOnce()).addCallback(any(), any());
        String urlReference = subscribeContext.getReference().toString();
        verify(ngsiClient, atLeastOnce()).notifyContextCustomURL(eq(urlReference), any(), notifyContextArg.capture());
        // Check id and status correspond to the required
        assertEquals(1, notifyContextArg.getValue().getContextElementResponseList().size());
        ContextElementResponse contextElementResponse = notifyContextArg.getValue().getContextElementResponseList().get(0);
        assertEquals("S1", contextElementResponse.getContextElement().getEntityId().getId());
        assertEquals("200", contextElementResponse.getStatusCode().getCode());
    }

    @Test
    public void postUpdateContextWithoutProvidingApplicationAndWithoutRemoteBrokerUrlButWithNotify() throws Exception {

        when(configuration.getRemoteUrl()).thenReturn(null);

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always with matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(true, false);
        SubscribeContext subscribeContext = createSubscribeContextTemperature();
        Subscription subscription = new Subscription("999999", Instant.now().plus(1, ChronoUnit.DAYS), subscribeContext);
        when(matchedSubscriptions.next()).thenReturn(subscription);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        //ngsiClient mock return always CODE_200
        when(ngsiClient.notifyContextCustomURL(any(), any(), any())).thenReturn(notifyContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].name").value("pressure"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].type").value("int"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].value").value("1015"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S1", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertFalse(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(2, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));
        assertTrue(attributeArgumentCaptor.getValue().contains("pressure"));

        //check ListenableFuture is called never and with addCallback method
        verify(updateContextResponseListenableFuture, never()).addCallback(any(), any());

        //verify ngsiClient.updateContext is never called
        verify(ngsiClient, never()).updateContext(any(), any(), any());

        //check ngsiClient.notify is called at least once
        // Capture notifyContext when postNotifyContextRequest is called on updateContextRequest,
        ArgumentCaptor<NotifyContext> notifyContextArg = ArgumentCaptor.forClass(NotifyContext.class);
        verify(notifyContextResponseListenableFuture, atLeastOnce()).addCallback(any(), any());
        String urlReference = subscribeContext.getReference().toString();
        verify(ngsiClient, atLeastOnce()).notifyContextCustomURL(eq(urlReference), any(), notifyContextArg.capture());
        // Check id and status correspond to the required
        assertEquals(1, notifyContextArg.getValue().getContextElementResponseList().size());
        ContextElementResponse contextElementResponse = notifyContextArg.getValue().getContextElementResponseList().get(0);
        assertEquals("S1", contextElementResponse.getContextElement().getEntityId().getId());
        assertEquals("200", contextElementResponse.getStatusCode().getCode());
    }

    @Test
    public void postUpdateContextWithoutProvidingApplicationAndWithEmptyRemoteBrokerButWithNotify() throws Exception {

        when(configuration.getRemoteUrl()).thenReturn("");

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always with matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(true, false);
        SubscribeContext subscribeContext = createSubscribeContextTemperature();
        Subscription subscription = new Subscription("999999", Instant.now().plus(1, ChronoUnit.DAYS), subscribeContext);
        when(matchedSubscriptions.next()).thenReturn(subscription);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        //ngsiClient mock return always CODE_200
        when(ngsiClient.notifyContextCustomURL(any(), any(), any())).thenReturn(notifyContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].name").value("pressure"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].type").value("int"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].value").value("1015"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S1", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertFalse(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(2, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));
        assertTrue(attributeArgumentCaptor.getValue().contains("pressure"));

        //check ListenableFuture is called never and with addCallback method
        verify(updateContextResponseListenableFuture, never()).addCallback(any(), any());

        //verify ngsiClient.updateContext is never called
        verify(ngsiClient, never()).updateContext(any(), any(), any());

        //check ngsiClient.notify is called at least once
        // Capture notifyContext when postNotifyContextRequest is called on updateContextRequest,
        ArgumentCaptor<NotifyContext> notifyContextArg = ArgumentCaptor.forClass(NotifyContext.class);
        verify(notifyContextResponseListenableFuture, atLeastOnce()).addCallback(any(), any());
        String urlReference = subscribeContext.getReference().toString();
        verify(ngsiClient, atLeastOnce()).notifyContextCustomURL(eq(urlReference), any(), notifyContextArg.capture());
        // Check id and status correspond to the required
        assertEquals(1, notifyContextArg.getValue().getContextElementResponseList().size());
        ContextElementResponse contextElementResponse = notifyContextArg.getValue().getContextElementResponseList().get(0);
        assertEquals("S1", contextElementResponse.getContextElement().getEntityId().getId());
        assertEquals("200", contextElementResponse.getStatusCode().getCode());
    }

    @Test
    public void postUpdateContextWithoutProvidingApplicationAndWithoutRemoteBrokerButWithNullOriginator() throws Exception {

        when(configuration.getRemoteUrl()).thenReturn(null);
        when(configuration.getLocalUrl()).thenReturn(null);

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always with matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(true, false);
        SubscribeContext subscribeContext = createSubscribeContextTemperature();
        Subscription subscription = new Subscription("999999", Instant.now().plus(1, ChronoUnit.DAYS), subscribeContext);
        when(matchedSubscriptions.next()).thenReturn(subscription);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        //ngsiClient mock return always CODE_200
        when(ngsiClient.notifyContextCustomURL(any(), any(), any())).thenReturn(notifyContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].name").value("pressure"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].type").value("int"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].value").value("1015"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S1", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertFalse(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(2, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));
        assertTrue(attributeArgumentCaptor.getValue().contains("pressure"));

        //check ListenableFuture is called never and with addCallback method
        verify(updateContextResponseListenableFuture, never()).addCallback(any(), any());

        //verify ngsiClient.updateContext is never called
        verify(ngsiClient, never()).updateContext(any(), any(), any());

        //check ngsiClient.notify is never called
        verify(notifyContextResponseListenableFuture, never()).addCallback(any(), any());
        verify(ngsiClient, never()).notifyContext(any(), any(), any());
    }

    @Test
    public void postUpdateContextWithoutProvidingApplicationAndWithoutRemoteBrokerButWithEmptyOriginator() throws Exception {

        when(configuration.getRemoteUrl()).thenReturn(null);
        when(configuration.getLocalUrl()).thenReturn("");

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always with matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(true, false);
        SubscribeContext subscribeContext = createSubscribeContextTemperature();
        Subscription subscription = new Subscription("999999", Instant.now().plus(1, ChronoUnit.DAYS), subscribeContext);
        when(matchedSubscriptions.next()).thenReturn(subscription);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        //ngsiClient mock return always CODE_200
        when(ngsiClient.notifyContext(any(), any(), any())).thenReturn(notifyContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].name").value("pressure"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].type").value("int"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].value").value("1015"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S1", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertFalse(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(2, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));
        assertTrue(attributeArgumentCaptor.getValue().contains("pressure"));

        //check ListenableFuture is called never and with addCallback method
        verify(updateContextResponseListenableFuture, never()).addCallback(any(), any());

        //verify ngsiClient.updateContext is never called
        verify(ngsiClient, never()).updateContext(any(), any(), any());

        //check ngsiClient.notify is never called
        verify(notifyContextResponseListenableFuture, never()).addCallback(any(), any());
        verify(ngsiClient, never()).notifyContext(any(), any(), any());
    }

    @Test
    public void postUpdateContextWithProvidingApplication() throws Exception {

        //localRegistrations mock return always a providingApplication
        when(providingApplication.hasNext()).thenReturn(true);
        when(providingApplication.next()).thenReturn(new URI("http//iotagent:1234"));
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always without matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(false);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].name").value("pressure"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].type").value("int"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].value").value("1015"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S1", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertFalse(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(2, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));
        assertTrue(attributeArgumentCaptor.getValue().contains("pressure"));

        // Capture updateContext when updateContextRequest is called on updateContextRequest,
        ArgumentCaptor<UpdateContext> updateContextArg = ArgumentCaptor.forClass(UpdateContext.class);
        String urlProvider = "http//iotagent:1234";

        //check ListenableFuture is called at least Once and with get method
        verify(updateContextResponseListenableFuture, atLeastOnce()).get();

        //verify urlProvider
        verify(ngsiClient, atLeastOnce()).updateContext(eq(urlProvider), any(), updateContextArg.capture());

        // Check id correspond to the required
        ContextElement contextElement = updateContextArg.getValue().getContextElements().get(0);
        assertEquals("S1", contextElement.getEntityId().getId());

        //check ngsiClient.notify is not called
        verify(ngsiClient, never()).notifyContext(any(), any(), any());
    }

    @Test
    public void postUpdateContextWithProvidingApplicationAndMatchedSusbcriptions() throws Exception {

        //localRegistrations mock return always a providingApplication
        when(providingApplication.hasNext()).thenReturn(true);
        when(providingApplication.next()).thenReturn(new URI("http//iotagent:1234"));
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always without matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(true);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].name").value("pressure"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].type").value("int"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[1].value").value("1015"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S1", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertFalse(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(2, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));
        assertTrue(attributeArgumentCaptor.getValue().contains("pressure"));

        // Capture updateContext when updateContextRequest is called on updateContextRequest,
        ArgumentCaptor<UpdateContext> updateContextArg = ArgumentCaptor.forClass(UpdateContext.class);
        String urlProvider = "http//iotagent:1234";

        //check ListenableFuture is called at least Once and with get method
        verify(updateContextResponseListenableFuture, atLeastOnce()).get();

        //verify urlProvider
        verify(ngsiClient, atLeastOnce()).updateContext(eq(urlProvider), any(), updateContextArg.capture());

        // Check id correspond to the required
        ContextElement contextElement = updateContextArg.getValue().getContextElements().get(0);
        assertEquals("S1", contextElement.getEntityId().getId());

        //check ngsiClient.notify is not called
        verify(ngsiClient, never()).notifyContext(any(), any(), any());
    }

    @Test
    public void postUpdateContextWithThrowExecutionException() throws Exception {

        //localRegistrations mock return always a providingApplication
        when(providingApplication.hasNext()).thenReturn(true);
        when(providingApplication.next()).thenReturn(new URI("http//iotagent:1234"));
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always without matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(false);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        when(updateContextResponseListenableFuture.get()).thenThrow(new ExecutionException("execution exception", new Throwable()));

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        //check ngsiClient.notify is not called
        verify(ngsiClient, never()).notifyContext(any(), any(), any());

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("Receiver internal error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("An unknown error at the receiver has occured"));
    }

    @Test
    public void postUpdateContextWithThrowInterruptedException() throws Exception {

        //localRegistrations mock return always a providingApplication
        when(providingApplication.hasNext()).thenReturn(true);
        when(providingApplication.next()).thenReturn(new URI("http//iotagent:1234"));
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //subscriptions mock return always without matched subscriptions
        when(matchedSubscriptions.hasNext()).thenReturn(false);
        when(subscriptions.findSubscriptions(any(), any())).thenReturn(matchedSubscriptions);

        when(updateContextResponseListenableFuture.get()).thenThrow(new InterruptedException());

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.updateContext(any(), any(), any())).thenReturn(updateContextResponseListenableFuture);

        //check ngsiClient.notify is not called
        verify(ngsiClient, never()).notifyContext(any(), any(), any());

        mockMvc.perform(post("/v1/updateContext")
                .content(json(mapper, createUpdateContextTempSensorAndPressure()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("Receiver internal error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("An unknown error at the receiver has occured"));
    }

    @Test
    public void postQueryContextWithProvidingApplication() throws Exception {

        //localRegistrations mock return always a providingApplication
        when(providingApplication.hasNext()).thenReturn(true);
        when(providingApplication.next()).thenReturn(new URI("http//iotagent:1234"));
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //ngsiclient mock return always createQueryContextResponseTemperature when call queryContext
        when(ngsiClient.queryContext(any(), any(), any())).thenReturn(queryContextResponseListenableFuture);

        mockMvc.perform(post("/v1/queryContext")
                .content(json(mapper, createQueryContextTemperature()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S*", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertTrue(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(1, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));

        // Capture queryContext when queryContextRequest is called on updateContextRequest,
        ArgumentCaptor<QueryContext> queryContextArg = ArgumentCaptor.forClass(QueryContext.class);
        String urlProvider = "http//iotagent:1234";

        //check ListenableFuture is called at least Once and with get method
        verify(queryContextResponseListenableFuture, atLeastOnce()).get();

        //verify urlProvider
        verify(ngsiClient, atLeastOnce()).queryContext(eq(urlProvider), any(), queryContextArg.capture());

        // Check id correspond to the required
        assertEquals(1, queryContextArg.getValue().getEntityIdList().size());
        assertEquals("S*", queryContextArg.getValue().getEntityIdList().get(0).getId());
    }

    @Test
    public void postQueryContextWithProvidingApplicationWithNullAttributes() throws Exception {

        //localRegistrations mock return always a providingApplication
        when(providingApplication.hasNext()).thenReturn(true);
        when(providingApplication.next()).thenReturn(new URI("http//iotagent:1234"));
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //ngsiclient mock return always createQueryContextResponseTemperature when call queryContext
        when(ngsiClient.queryContext(any(), any(), any())).thenReturn(queryContextResponseListenableFuture);

        QueryContext queryContext = createQueryContextTemperature();
        queryContext.setAttributeList(null);

        mockMvc.perform(post("/v1/queryContext")
                .content(json(mapper, queryContext))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S*", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertTrue(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(0, attributeArgumentCaptor.getValue().size());

        // Capture queryContext when queryContextRequest is called on queryContextRequest
        ArgumentCaptor<QueryContext> queryContextArg = ArgumentCaptor.forClass(QueryContext.class);
        String urlProvider = "http//iotagent:1234";

        //check ListenableFuture is called at least Once and with get method
        verify(queryContextResponseListenableFuture, atLeastOnce()).get();

        //verify urlProvider
        verify(ngsiClient, atLeastOnce()).queryContext(eq(urlProvider), any(), queryContextArg.capture());

        // Check id correspond to the required
        assertEquals(1, queryContextArg.getValue().getEntityIdList().size());
        assertEquals("S*", queryContextArg.getValue().getEntityIdList().get(0).getId());

    }

    @Test
    public void postQueryContextWithoutProvidingApplication() throws Exception {

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //ngsiclient mock return always createQueryContextResponseTemperature when call queryContext
        when(ngsiClient.queryContext(any(), any(), any())).thenReturn(queryContextResponseListenableFuture);

        mockMvc.perform(post("/v1/queryContext")
                .content(json(mapper, createQueryContextTemperature()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.id").value("S1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.type").value("TempSensor"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].name").value("temp"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].type").value("float"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].contextElement.attributes[0].value").value("15.5"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.contextResponses[0].statusCode.code").value("200"));

        //Capture attributes (Set<String> searchAttributes) when findProvidingApplication is called on localRegistrations Set<String> searchAttributes
        verify(localRegistrations).findProvidingApplication(entityIdArgumentCaptor.capture(), attributeArgumentCaptor.capture());

        //check entityId
        assertEquals("S*", entityIdArgumentCaptor.getValue().getId());
        assertEquals("TempSensor", entityIdArgumentCaptor.getValue().getType());
        assertTrue(entityIdArgumentCaptor.getValue().getIsPattern());

        //check attributes
        assertEquals(1, attributeArgumentCaptor.getValue().size());
        assertTrue(attributeArgumentCaptor.getValue().contains("temp"));

        // Capture queryContext when queryContextRequest is called on updateContextRequest,
        ArgumentCaptor<QueryContext> queryContextArg = ArgumentCaptor.forClass(QueryContext.class);
        String urlProvider = "http://orionhost:9999";

        // Capture HttpHeaders queryContextRequest is called on queryContextRequest
        ArgumentCaptor<HttpHeaders> httpHeadersArg = ArgumentCaptor.forClass(HttpHeaders.class);

        //check ListenableFuture is called at least Once and with get method
        verify(queryContextResponseListenableFuture, atLeastOnce()).get();

        //check ngsiClient.getRequestHeaders() is called at least Once
        verify(ngsiClient, atLeastOnce()).getRequestHeaders(eq("http://orionhost:9999"));

        //check configuration.getHeadersForBroker() is called at least Once
        verify(configuration, atLeastOnce()).addRemoteHeaders(any());

        //verify urlProvider
        verify(ngsiClient, atLeastOnce()).queryContext(eq(urlProvider), httpHeadersArg.capture(), queryContextArg.capture());

        // Check id correspond to the required
        assertEquals(1, queryContextArg.getValue().getEntityIdList().size());
        assertEquals("S*", queryContextArg.getValue().getEntityIdList().get(0).getId());
    }

    @Test
    public void postQueryContextWithNullRemoteBroker() throws Exception {

        //configuration mock return null as remoteBroker
        when(configuration.getRemoteUrl()).thenReturn(null);

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.queryContext(any(), any(), any())).thenReturn(queryContextResponseListenableFuture);

        mockMvc.perform(post("/v1/queryContext")
                .content(json(mapper, createQueryContextTemperature()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("missing remote broker error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("No remote.url parameter defined to forward queryContext"));
    }

    @Test
    public void postQueryContextWithNullRemoteBrokerUrl() throws Exception {

        //configuration mock return null as remoteBroker
        when(configuration.getRemoteUrl()).thenReturn(null);

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.queryContext(any(), any(), any())).thenReturn(queryContextResponseListenableFuture);

        mockMvc.perform(post("/v1/queryContext")
                .content(json(mapper, createQueryContextTemperature()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("missing remote broker error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("No remote.url parameter defined to forward queryContext"));
    }

    @Test
    public void postQueryContextWithEmptyRemoteBrokerUrl() throws Exception {

        //configuration mock return "" as remoteBrokerUrl
        when(configuration.getRemoteUrl()).thenReturn("");

        //localRegistrations mock return always without providingApplication
        when(providingApplication.hasNext()).thenReturn(false);
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.queryContext(any(), any(), any())).thenReturn(queryContextResponseListenableFuture);

        mockMvc.perform(post("/v1/queryContext")
                .content(json(mapper, createQueryContextTemperature()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("missing remote broker error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("No remote.url parameter defined to forward queryContext"));
    }

    @Test
    public void postQueryContextWithExecutionException() throws Exception {

        //localRegistrations mock return always a providingApplication
        when(providingApplication.hasNext()).thenReturn(true);
        when(providingApplication.next()).thenReturn(new URI("http//iotagent:1234"));
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        when(queryContextResponseListenableFuture.get()).thenThrow(new ExecutionException("execution exception", new Throwable()));

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.queryContext(any(), any(), any())).thenReturn(queryContextResponseListenableFuture);

        mockMvc.perform(post("/v1/queryContext")
                .content(json(mapper, createQueryContextTemperature()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("Receiver internal error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("An unknown error at the receiver has occured"));
    }

    @Test
    public void postQueryContextWithInterruptedException() throws Exception {

        //localRegistrations mock return always a providingApplication
        when(providingApplication.hasNext()).thenReturn(true);
        when(providingApplication.next()).thenReturn(new URI("http//iotagent:1234"));
        when(localRegistrations.findProvidingApplication(any(), any())).thenReturn(providingApplication);

        when(queryContextResponseListenableFuture.get()).thenThrow(new InterruptedException());

        //ngsiclient mock return always createUpdateContextREsponseTemperature when call updateContext
        when(ngsiClient.queryContext(any(), any(), any())).thenReturn(queryContextResponseListenableFuture);

        mockMvc.perform(post("/v1/queryContext")
                .content(json(mapper, createQueryContextTemperature()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.reasonPhrase").value("Receiver internal error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorCode.details").value("An unknown error at the receiver has occured"));
    }

    @Test
    public void postSubscribeContextWithEmptyEntities() throws Exception {

        mockMvc.perform(post("/v1/subscribeContext")
                .content(json(mapper, new SubscribeContext()))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.code").value(CodeEnum.CODE_471.getLabel()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.reasonPhrase").value(CodeEnum.CODE_471.getShortPhrase()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.details")
                        .value("The parameter entities of type List<EntityId> is missing in the request"));
    }

    @Test
    public void postSubscribeContextWithBadDuration() throws Exception {
        // Bad duration
        SubscribeContext subscribeContext = createSubscribeContextTemperature();
        subscribeContext.setDuration("PIPO");

        when(subscriptions.addSubscription(any())).thenThrow(new SubscriptionException("bad duration", new RuntimeException()));

        mockMvc.perform(post("/v1/subscribeContext")
                .content(json(mapper, subscribeContext))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.code").value("400"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.reasonPhrase").value("subscription error"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.details").value("bad duration"));
    }

    @Test
    public void postNewSubscribeContext() throws Exception {

        SubscribeContext subscribeContext = createSubscribeContextTemperature();
        Subscription subscription = new Subscription("12345678", Instant.now().plus(1, ChronoUnit.DAYS), subscribeContext);
        when(subscriptions.addSubscription(any())).thenReturn("12345678");
        when(subscriptions.getSubscription("12345678")).thenReturn(subscription);

        mockMvc.perform(post("/v1/subscribeContext")
                .content(json(mapper, subscribeContext))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeResponse.subscriptionId").value("12345678"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeResponse.duration").value("P1M"));

        verify(subscriptions, atLeastOnce()).addSubscription(any());
        verify(subscriptions, atLeastOnce()).getSubscription("12345678");
    }

    @Test
    public void postNewSubscribeContextWithNoDuration() throws Exception {

        SubscribeContext subscribeContext = createSubscribeContextTemperature();
        subscribeContext.setDuration(null);

        SubscribeContext subscribeContextInSubscription = createSubscribeContextTemperature();
        Subscription subscription = new Subscription("12345678", Instant.now().plus(1, ChronoUnit.DAYS), subscribeContextInSubscription);
        when(subscriptions.addSubscription(any())).thenReturn("12345678");
        when(subscriptions.getSubscription("12345678")).thenReturn(subscription);

        mockMvc.perform(post("/v1/subscribeContext")
                .content(json(mapper, subscribeContext))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeResponse.subscriptionId").value("12345678"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeResponse.duration").value("P1M"));

        verify(subscriptions, atLeastOnce()).addSubscription(any());
        verify(subscriptions, atLeastOnce()).getSubscription("12345678");
    }

    @Test
    public void postSubscribeContextWithPersistenceFailure() throws Exception {

        SubscribeContext subscribeContext = createSubscribeContextTemperature();

        when(subscriptions.addSubscription(any())).thenThrow(new SubscriptionPersistenceException("table not exist", new RuntimeException()));

        mockMvc.perform(post("/v1/subscribeContext")
                .content(json(mapper, subscribeContext))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.code").value("500"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.reasonPhrase").value("error in subscription persistence"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscribeError.errorCode.details").value("table not exist"));
    }

    @Test
    public void postUnSubscribeContextWhichExist() throws Exception {

        UnsubscribeContext unsubscribeContext = new UnsubscribeContext("12345678");
        when(subscriptions.deleteSubscription(any())).thenReturn(true);

        mockMvc.perform(post("/v1/unsubscribeContext")
                .content(json(mapper, unsubscribeContext))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscriptionId").value("12345678"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.statusCode.code").value(CodeEnum.CODE_200.getLabel()));

        verify(subscriptions, atLeastOnce()).deleteSubscription(any());
    }

    @Test
    public void postUnSubscribeContextWhichNotExist() throws Exception {

        UnsubscribeContext unsubscribeContext = new UnsubscribeContext("12345678");
        when(subscriptions.deleteSubscription(any())).thenReturn(false);

        mockMvc.perform(post("/v1/unsubscribeContext")
                .content(json(mapper, unsubscribeContext))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.subscriptionId").value("12345678"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.statusCode.code").value(CodeEnum.CODE_470.getLabel()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.statusCode.reasonPhrase").value(CodeEnum.CODE_470.getShortPhrase()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.statusCode.details").value("The subscription ID specified 12345678 does not correspond to an active subscription"));

        verify(subscriptions, atLeastOnce()).deleteSubscription(any());
    }

    @Test
    public void postUnSubscribeContextWithPersistenceFailure() throws Exception {

        UnsubscribeContext unsubscribeContext = new UnsubscribeContext("12345678");
        when(subscriptions.deleteSubscription(any())).thenThrow(new SubscriptionPersistenceException("table not exist", new RuntimeException()));

        mockMvc.perform(post("/v1/unsubscribeContext")
                .content(json(mapper, unsubscribeContext))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.statusCode.code").value(CodeEnum.CODE_500.getLabel()))
                .andExpect(MockMvcResultMatchers.jsonPath("$.statusCode.reasonPhrase").value("error in subscription persistence"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.statusCode.details").value("table not exist"));

        verify(subscriptions, atLeastOnce()).deleteSubscription(any());
    }
}
