/* * Copyright (c) 2015 Spotify AB. * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.spotify.heroic.test; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.spotify.heroic.HeroicConfig; import com.spotify.heroic.HeroicCore; import com.spotify.heroic.HeroicCoreInstance; import com.spotify.heroic.common.DateRange; import com.spotify.heroic.common.GroupMember; import com.spotify.heroic.common.OptionalLimit; import com.spotify.heroic.common.Series; import com.spotify.heroic.dagger.LoadingComponent; import com.spotify.heroic.filter.FalseFilter; import com.spotify.heroic.filter.Filter; import com.spotify.heroic.filter.MatchKeyFilter; import com.spotify.heroic.filter.TrueFilter; import com.spotify.heroic.metadata.CountSeries; import com.spotify.heroic.metadata.DeleteSeries; import com.spotify.heroic.metadata.FindKeys; import com.spotify.heroic.metadata.FindSeries; import com.spotify.heroic.metadata.FindSeriesIds; import com.spotify.heroic.metadata.FindTags; import com.spotify.heroic.metadata.MetadataBackend; import com.spotify.heroic.metadata.MetadataManagerModule; import com.spotify.heroic.metadata.MetadataModule; import com.spotify.heroic.metadata.WriteMetadata; import eu.toolchain.async.AsyncFramework; import eu.toolchain.async.AsyncFuture; import eu.toolchain.async.RetryPolicy; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; import java.util.Set; import static com.spotify.heroic.filter.Filter.and; import static com.spotify.heroic.filter.Filter.hasTag; import static com.spotify.heroic.filter.Filter.matchKey; import static com.spotify.heroic.filter.Filter.matchTag; import static com.spotify.heroic.filter.Filter.not; import static com.spotify.heroic.filter.Filter.or; import static com.spotify.heroic.filter.Filter.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; @RunWith(MockitoJUnitRunner.class) public abstract class AbstractMetadataBackendIT { private AsyncFramework async; private final Series s1 = Series.of("s1", ImmutableMap.of("role", "foo")); private final Series s2 = Series.of("s2", ImmutableMap.of("role", "bar")); private final Series s3 = Series.of("s3", ImmutableMap.of("role", "baz")); private final DateRange range = new DateRange(0L, 0L); private HeroicCoreInstance core; private MetadataBackend backend; protected boolean findTagsSupport = true; protected boolean orFilterSupport = true; protected abstract MetadataModule setupModule() throws Exception; protected void setupConditions() { } @Before public final void abstractSetup() throws Exception { final HeroicConfig.Builder fragment = HeroicConfig .builder() .metadata(MetadataManagerModule.builder().backends(ImmutableList.of(setupModule()))); core = HeroicCore .builder() .setupService(false) .setupShellServer(false) .configFragment(fragment) .build() .newInstance(); core.start().get(); async = core.inject(LoadingComponent::async); backend = core .inject(c -> c .metadataManager() .groupSet() .inspectAll() .stream() .map(GroupMember::getMember) .findFirst()) .orElseThrow(() -> new IllegalStateException("Failed to find backend")); final List<AsyncFuture<Void>> writes = new ArrayList<>(); writes.add(writeSeries(backend, s1, range)); writes.add(writeSeries(backend, s2, range)); writes.add(writeSeries(backend, s3, range)); async.collectAndDiscard(writes).get(); setupConditions(); } @After public final void abstractTeardown() throws Exception { core.shutdown().get(); } @Test public void findSeriesComplexTest() throws Exception { final FindSeries.Request f = new FindSeries.Request(and(matchKey("s2"), startsWith("role", "ba")), range, OptionalLimit.empty()); assertEquals(ImmutableSet.of(s2), backend.findSeries(f).get().getSeries()); } @Test public void findSeriesTest() throws Exception { final FindSeries.Request f = new FindSeries.Request(TrueFilter.get(), range, OptionalLimit.empty()); final FindSeries result = backend.findSeries(f).get(); assertEquals(ImmutableSet.of(s1, s2, s3), result.getSeries()); } @Test public void findSeriesLimitedTest() throws Exception { final FindSeries r1 = backend .findSeries(new FindSeries.Request(TrueFilter.get(), range, OptionalLimit.of(1L))) .get(); assertTrue("Result should be limited", r1.isLimited()); assertEquals("Result size should be same as limit", 1, r1.getSeries().size()); final FindSeries r2 = backend .findSeries(new FindSeries.Request(TrueFilter.get(), range, OptionalLimit.of(3L))) .get(); assertFalse("Result should not be limited", r2.isLimited()); assertEquals("Result size should be all entries", 3, r2.getSeries().size()); } @Test public void findTags() throws Exception { assumeTrue(findTagsSupport); final FindTags.Request request = new FindTags.Request(TrueFilter.get(), range, OptionalLimit.empty()); final FindTags result = backend.findTags(request).get(); assertEquals(ImmutableMap.of("role", ImmutableSet.of("bar", "foo", "baz")), result.getTags()); } @Test public void findKeys() throws Exception { final FindKeys.Request request = new FindKeys.Request(TrueFilter.get(), range, OptionalLimit.empty()); final FindKeys result = backend.findKeys(request).get(); assertEquals(ImmutableSet.of(s1.getKey(), s2.getKey(), s3.getKey()), result.getKeys()); } @Test public void countSeriesTest() throws Exception { final CountSeries.Request f = new CountSeries.Request(not(matchKey(s2.getKey())), range, OptionalLimit.empty()); final CountSeries result = backend.countSeries(f).get(); assertEquals(2L, result.getCount()); } @Test public void findSeriesIdsTest() throws Exception { final FindSeriesIds.Request f = new FindSeriesIds.Request(not(matchKey(s2.getKey())), range, OptionalLimit.empty()); final FindSeriesIds result = backend.findSeriesIds(f).get(); assertEquals(ImmutableSet.of(s1.hash(), s3.hash()), result.getIds()); } @Test public void deleteSeriesTest() throws Exception { { final DeleteSeries.Request request = new DeleteSeries.Request(not(matchKey(s2.getKey())), range, OptionalLimit.empty()); backend.deleteSeries(request).get(); } { final FindSeries.Request f = new FindSeries.Request(TrueFilter.get(), range, OptionalLimit.empty()); final FindSeries result = backend.findSeries(f).get(); assertEquals(ImmutableSet.of(s2), result.getSeries()); } } @Test public void filterTest() throws Exception { assertEquals(ImmutableSet.of(s1.hash(), s2.hash(), s3.hash()), findIds(TrueFilter.get())); assertEquals(ImmutableSet.of(), findIds(FalseFilter.get())); assertEquals(ImmutableSet.of(s2.hash()), findIds(matchKey(s2.getKey()))); assertEquals(ImmutableSet.of(s1.hash(), s3.hash()), findIds(not(matchKey(s2.getKey())))); assertEquals(ImmutableSet.of(s1.hash()), findIds(matchTag("role", "foo"))); if (orFilterSupport) { assertEquals(ImmutableSet.of(s1.hash(), s2.hash()), findIds(or(matchKey(s1.getKey()), matchKey(s2.getKey())))); } assertEquals(ImmutableSet.of(s1.hash()), findIds(and(matchKey(s1.getKey()), matchTag("role", "foo")))); assertEquals(ImmutableSet.of(s1.hash(), s2.hash(), s3.hash()), findIds(hasTag("role"))); } private Set<String> findIds(final Filter filter) throws Exception { return backend .findSeriesIds(new FindSeriesIds.Request(filter, range, OptionalLimit.empty())) .get() .getIds(); } private AsyncFuture<Void> writeSeries( final MetadataBackend metadata, final Series s, final DateRange range ) throws Exception { final FindSeries.Request f = new FindSeries.Request(new MatchKeyFilter(s.getKey()), range, OptionalLimit.empty()); return metadata .write(new WriteMetadata.Request(s, range)) .lazyTransform(v -> async .retryUntilResolved(() -> metadata.findSeries(f).directTransform(result -> { if (!result.getSeries().contains(s)) { throw new RuntimeException("Expected to find the written series"); } return null; }), RetryPolicy.timed(10000, RetryPolicy.exponential(100, 200))) .directTransform(r -> null)); } }