/* * Copyright 2014, The Sporting Exchange Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.betfair.cougar.core.impl.ev; import java.util.Date; import com.betfair.cougar.core.api.ev.*; import com.betfair.cougar.core.impl.DefaultTimeConstraints; import org.slf4j.LoggerFactory; import com.betfair.cougar.util.UUIDGeneratorImpl; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentMatcher; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import com.betfair.cougar.api.ExecutionContext; import com.betfair.cougar.api.LogExtension; import com.betfair.cougar.api.LoggableEvent; import com.betfair.cougar.api.RequestContext; import com.betfair.cougar.api.RequestUUID; import com.betfair.cougar.core.api.ServiceVersion; import com.betfair.cougar.core.api.exception.CougarFrameworkException; import com.betfair.cougar.core.api.logging.EventLogger; import com.betfair.cougar.core.impl.logging.RequestLogEvent; import com.betfair.cougar.util.RequestUUIDImpl; import static org.mockito.Mockito.*; import static org.junit.Assert.*; public class ServiceExecutableResolverTest { private final OperationKey op1Key = new OperationKey(new ServiceVersion("v1.0"), "Service1", "Operation1"); private ExecutableResolver simpleResolver; private EventLogger eventLogger; private ExecutionContext context; private ServiceExecutableResolver serviceResolver; private Object[] fieldsToLog = new Object[] {"logme"}; private String logName = "logName"; private RequestUUID requestuuid = new RequestUUIDImpl(); private ServiceRegisterableExecutionVenue ev; @BeforeClass public static void setupStatic() { RequestUUIDImpl.setGenerator(new UUIDGeneratorImpl()); } @Before public void init() { simpleResolver = mock(ExecutableResolver.class); ServiceLogManager manager = mock(ServiceLogManager.class); when(manager.getLogExtensionClass()).thenAnswer(new Answer<Class<? extends LogExtension>>() { @Override public Class<? extends LogExtension> answer( InvocationOnMock invocation) throws Throwable { return TestLogExtension.class; }}); when(manager.getNumLogExtensionFields()).thenReturn(fieldsToLog.length); when(manager.getLoggerName()).thenReturn(logName); ev = mock(ServiceRegisterableExecutionVenue.class); when(ev.getServiceLogManager(anyString(), anyString(), any(ServiceVersion.class))).thenReturn(manager); eventLogger = mock(EventLogger.class); serviceResolver = new ServiceExecutableResolver(); serviceResolver.registerExecutableResolver(simpleResolver); serviceResolver.setEventLogger(eventLogger); context = mock(ExecutionContext.class); when(context.getRequestUUID()).thenReturn(requestuuid); } @Test public void testResolveExecutable() { Executable executable = mock(Executable.class); when(simpleResolver.resolveExecutable(op1Key, ev)).thenReturn(executable); //Resolve the executable Executable resultExecutable = serviceResolver.resolveExecutable(op1Key, ev); //Execute the returned executable resultExecutable.execute(context, op1Key, new Object[] {}, mock(ExecutionObserver.class), mock(ExecutionVenue.class), DefaultTimeConstraints.NO_CONSTRAINTS); //Verify that the registered executable was invoked with a RequestContext verify(executable).execute(any(RequestContext.class), eq(op1Key), any(Object[].class), any(ExecutionObserver.class), any(ExecutionVenue.class), eq(DefaultTimeConstraints.NO_CONSTRAINTS)); } @Test public void testLoggingResult() { ObserverFunction function = new ObserverFunction() { @Override public void perform(ExecutionObserver observer) { observer.onResult(new ExecutionResult("hello")); } }; testLogging(function, new RequestLogEventMatcher("")); } @Test public void testLoggingException() { ObserverFunction function = new ObserverFunction() { @Override public void perform(ExecutionObserver observer) { observer.onResult(new ExecutionResult(new CougarFrameworkException("test error"))); } }; testLogging(function, new RequestLogEventMatcher("DSC-0002")); } private void testLogging(ObserverFunction function, ArgumentMatcher<RequestLogEvent> matchesRequestLogEvent) { TestExecutable executable = new TestExecutable(); when(simpleResolver.resolveExecutable(op1Key, ev)).thenReturn(executable); Executable resultExecutable = serviceResolver.resolveExecutable(op1Key, ev); resultExecutable.execute(context, op1Key, new Object[] {}, mock(ExecutionObserver.class), mock(ExecutionVenue.class),DefaultTimeConstraints.NO_CONSTRAINTS); LoggableEvent event = mock(LoggableEvent.class); RequestContext context = executable.getContext(); context.addEventLogRecord(event); context.setRequestLogExtension(new TestLogExtension()); ExecutionObserver observer = executable.getObserver(); function.perform(observer); verify(eventLogger).logEvent(argThat(matchesRequestLogEvent), eq(fieldsToLog)); verify(eventLogger).logEvent(event, null); function.perform(observer); verifyNoMoreInteractions(eventLogger); } private interface ObserverFunction { public void perform(ExecutionObserver observer); } private class RequestLogEventMatcher extends ArgumentMatcher<RequestLogEvent> { private String errorCode; public RequestLogEventMatcher(String errorCode) { super(); this.errorCode = errorCode; } @Override public boolean matches(Object argument) { if (argument instanceof RequestLogEvent) { RequestLogEvent event = (RequestLogEvent)argument; assertEquals(logName, event.getLogName()); Object[] eventFields = event.getFieldsToLog(); assertNotNull(eventFields); assertEquals(6, eventFields.length); //Field 0 is request start time assertTrue(eventFields[0] instanceof Date); //Field 1 is Request UUID assertEquals(requestuuid.toCougarLogString(), eventFields[1]); //Field 2 is version without the "v" character assertEquals("1.0", eventFields[2]); //Field 3 is the operation name assertEquals("Operation1", eventFields[3]); //Field 4 is the error code assertEquals(errorCode, eventFields[4]); //Field 5 is the length of time nanos for the operation assertTrue(eventFields[5] instanceof Long); //If we haven't failed an assert yet then this must match return true; } return false; } } private class TestExecutable implements Executable { private ExecutionObserver observer; private RequestContext context; @Override public void execute(ExecutionContext ctx, OperationKey key, Object[] args, ExecutionObserver observer, ExecutionVenue executionVenue, TimeConstraints timeConstraints) { this.observer = observer; this.context = (RequestContext)ctx; } public ExecutionObserver getObserver() { return observer; } public RequestContext getContext() { return context; } } public class TestLogExtension implements LogExtension { @Override public Object[] getFieldsToLog() { return fieldsToLog; } } }