blob: e6cb20e216ce95263d77a9b79853c30d804e70fd [file] [log] [blame]
// Copyright (C) 2019 The Android Open Source Project
//
// 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.googlesource.gerrit.plugins.websession.broker;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.gerritforge.gerrit.eventbroker.BrokerApi;
import com.gerritforge.gerrit.eventbroker.EventMessage;
import com.gerritforge.gerrit.eventbroker.EventMessage.Header;
import com.google.common.cache.Cache;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.httpd.WebSessionManager.Val;
import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.events.Event;
import com.googlesource.gerrit.plugins.websession.broker.BrokerBasedWebSessionCache.WebSessionEvent;
import com.googlesource.gerrit.plugins.websession.broker.BrokerBasedWebSessionCache.WebSessionEvent.Operation;
import com.googlesource.gerrit.plugins.websession.broker.log.WebSessionLogger;
import com.googlesource.gerrit.plugins.websession.broker.util.TimeMachine;
import java.time.Instant;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class BrokerBasedWebSessionCacheTest {
private static final int DEFAULT_ACCOUNT_ID = 1000000;
private static final String KEY = "aSceprtma6B0qZ0hKxXHvQ5iyfUhCcFXxG";
private static final String PLUGIN_NAME = "websession-broker";
private byte[] emptyPayload = new byte[] {-84, -19, 0, 5, 112};
byte[] defaultPayload =
new byte[] {
-84, -19, 0, 5, 115, 114, 0, 45, 99, 111, 109, 46, 103, 111, 111, 103, 108, 101, 46, 103,
101, 114, 114, 105, 116, 46, 104, 116, 116, 112, 100, 46, 87, 101, 98, 83, 101, 115, 115,
105, 111, 110, 77, 97, 110, 97, 103, 101, 114, 36, 86, 97, 108, 0, 0, 0, 0, 0, 0, 0, 2, 3,
0, 0, 120, 112, 119, 97, 1, -64, -124, 61, 2, 0, 0, 1, 111, 13, -8, 90, 7, 3, 0, 5, 34, 97,
83, 99, 101, 112, 114, 113, 86, 87, 54, 85, 79, 45, 88, 51, 107, 51, 116, 102, 85, 109, 86,
103, 82, 73, 90, 56, 53, 99, 99, 52, 71, 114, 87, 6, 0, 0, 1, 111, 16, 84, -103, -121, 7,
34, 97, 83, 99, 101, 112, 114, 114, 82, 103, 119, 49, 71, 110, 90, 56, 122, 54, 49, 49, 86,
52, 121, 110, 65, 100, 110, 113, 99, 68, 45, 105, 99, 75, 97, 0, 120
};
ExecutorService executorServce = MoreExecutors.newDirectExecutorService();
@Mock BrokerApi brokerApi;
@Mock Cache<String, Val> cache;
@Mock TimeMachine timeMachine;
@Mock PluginConfigFactory cfg;
@Mock PluginConfig pluginConfig;
@Mock WebSessionLogger webSessionLogger;
@Captor ArgumentCaptor<EventMessage> eventCaptor;
@Captor ArgumentCaptor<Val> valCaptor;
BrokerBasedWebSessionCache objectUnderTest;
@Before
public void setup() {
when(pluginConfig.getString("webSessionTopic", "gerrit_web_session"))
.thenReturn("gerrit_web_session");
when(cfg.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig);
when(timeMachine.now()).thenReturn(Instant.EPOCH);
DynamicItem<BrokerApi> item = DynamicItem.itemOf(BrokerApi.class, brokerApi);
objectUnderTest =
new BrokerBasedWebSessionCache(
cache, item, timeMachine, cfg, PLUGIN_NAME, webSessionLogger, executorServce);
}
@Test
public void shouldPublishMessageWhenLoginEvent() {
EventMessage eventMessage = createEventMessage();
Val value = createVal(eventMessage);
when(brokerApi.newMessage(any(UUID.class), any(Event.class))).thenReturn(eventMessage);
objectUnderTest.put(KEY, value);
verify(brokerApi, times(1)).send(anyString(), eventCaptor.capture());
assertThat(eventCaptor.getValue().getEvent()).isNotNull();
WebSessionEvent event = (WebSessionEvent) eventCaptor.getValue().getEvent();
assertThat(event.operation).isEqualTo(WebSessionEvent.Operation.ADD);
assertThat(event.key).isEqualTo(KEY);
assertThat(event.payload).isEqualTo(defaultPayload);
}
@Test
public void shouldPublishMessageWhenLogoutEvent() {
EventMessage eventMessage = createEventMessage(emptyPayload, Operation.REMOVE);
when(brokerApi.newMessage(any(UUID.class), any(Event.class))).thenReturn(eventMessage);
objectUnderTest.invalidate(KEY);
verify(brokerApi, times(1)).send(anyString(), eventCaptor.capture());
assertThat(eventCaptor.getValue().getEvent()).isNotNull();
WebSessionEvent event = (WebSessionEvent) eventCaptor.getValue().getEvent();
assertThat(event.operation).isEqualTo(WebSessionEvent.Operation.REMOVE);
assertThat(event.key).isEqualTo(KEY);
assertThat(event.payload).isEqualTo(emptyPayload);
}
@Test
public void shouldUpdateCacheWhenLoginMessageReceived() {
EventMessage eventMessage = createEventMessage();
objectUnderTest.processMessage(eventMessage);
verify(cache, times(1)).put(anyString(), valCaptor.capture());
assertThat(valCaptor.getValue()).isNotNull();
Val val = valCaptor.getValue();
assertThat(val.getAccountId().get()).isEqualTo(DEFAULT_ACCOUNT_ID);
}
@Test
public void shouldUpdateCacheWhenLogoutMessageReceived() {
EventMessage eventMessage = createEventMessage(emptyPayload, Operation.REMOVE);
objectUnderTest.processMessage(eventMessage);
verify(cache, times(1)).invalidate(KEY);
}
@Test
public void shouldSkipCacheUpdateWhenUnknownEventType() {
Header header =
new Header(
UUID.fromString("7cb80dbe-65c4-4f2c-84de-580d98199d4a"),
UUID.fromString("97711495-1013-414e-bfd2-44776787520d"));
Event event = new Event("sample-event") {};
EventMessage eventMessage = new EventMessage(header, event);
objectUnderTest.processMessage(eventMessage);
verifyZeroInteractions(cache);
}
@Test
public void shouldSkipCacheUpdateWhenInvalidPayload() {
EventMessage eventMessage = createEventMessage(new byte[] {1, 2, 3, 4}, Operation.ADD);
objectUnderTest.processMessage(eventMessage);
verifyZeroInteractions(cache);
}
@Test
public void shouldSkipCacheUpdateWhenSessionExpired() {
when(timeMachine.now()).thenReturn(Instant.MAX);
EventMessage eventMessage = createEventMessage();
objectUnderTest.processMessage(eventMessage);
verifyZeroInteractions(cache);
}
@Test
public void shouldCleanupExpiredSessions() {
when(timeMachine.now()).thenReturn(Instant.MIN, Instant.MAX);
EventMessage eventMessage = createEventMessage();
objectUnderTest.processMessage(eventMessage);
verify(cache, times(1)).put(anyString(), valCaptor.capture());
assertThat(valCaptor.getValue()).isNotNull();
ConcurrentMap<String, Val> cacheMap = Maps.newConcurrentMap();
cacheMap.put(KEY, valCaptor.getValue());
when(cache.asMap()).thenReturn(cacheMap);
objectUnderTest.cleanUp();
verify(cache, times(1)).invalidate(KEY);
}
@SuppressWarnings("unchecked")
private Val createVal(EventMessage message) {
ArgumentCaptor<Val> valArgumentCaptor = ArgumentCaptor.forClass(Val.class);
objectUnderTest.processMessage(message);
verify(cache).put(anyString(), valArgumentCaptor.capture());
reset(cache);
return valArgumentCaptor.getValue();
}
private EventMessage createEventMessage() {
return createEventMessage(defaultPayload, Operation.ADD);
}
private EventMessage createEventMessage(byte[] payload, Operation operation) {
Header header =
new Header(
UUID.fromString("7cb80dbe-65c4-4f2c-84de-580d98199d4a"),
UUID.fromString("97711495-1013-414e-bfd2-44776787520d"));
WebSessionEvent event = new WebSessionEvent(KEY, payload, operation);
return new EventMessage(header, event);
}
}