mirror of
				https://github.com/scm-manager/scm-manager.git
				synced 2025-10-31 10:35:56 +01:00 
			
		
		
		
	Add unit tests for micrometer / Wrap data stores with proxies
Squash commits of branch feature/store_metering: - Wrap jaxb stores with proxies to get micrometer insights - Handle exceptions from proxy - User meter for queryable stores, too - Log change - Change name for metrics - Fix imports - Rename invocation handler - Change change log - Replace RuntimeException - Merge branch 'develop' into feature/store_metering - Add store type to description of timer - Fix measuring methods for queryable store - License - Simplify usage of wrapper - Rename class - Remove unnecessary condition
This commit is contained in:
		
				
					committed by
					
						 Till-André Diegeler
						Till-André Diegeler
					
				
			
			
				
	
			
			
			
						parent
						
							57f15f3ac4
						
					
				
				
					commit
					ce01cfc602
				
			
							
								
								
									
										2
									
								
								gradle/changelog/meter_stores.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								gradle/changelog/meter_stores.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | - type: added | ||||||
|  |   description: Performance of stores can be measured with Micrometer metrics | ||||||
| @@ -0,0 +1,175 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 - present Cloudogu GmbH | ||||||
|  |  * | ||||||
|  |  * This program is free software: you can redistribute it and/or modify it under | ||||||
|  |  * the terms of the GNU Affero General Public License as published by the Free | ||||||
|  |  * Software Foundation, version 3. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||||
|  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||||||
|  |  * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | ||||||
|  |  * details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program. If not, see https://www.gnu.org/licenses/. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package sonia.scm.store; | ||||||
|  |  | ||||||
|  | import com.google.common.annotations.VisibleForTesting; | ||||||
|  | import io.micrometer.core.instrument.MeterRegistry; | ||||||
|  | import io.micrometer.core.instrument.Timer; | ||||||
|  | import sonia.scm.util.AssertUtil; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.InvocationHandler; | ||||||
|  | import java.lang.reflect.InvocationTargetException; | ||||||
|  | import java.lang.reflect.Method; | ||||||
|  | import java.lang.reflect.Proxy; | ||||||
|  | import java.lang.reflect.UndeclaredThrowableException; | ||||||
|  | import java.util.function.Consumer; | ||||||
|  | import java.util.function.Supplier; | ||||||
|  |  | ||||||
|  | @SuppressWarnings("unchecked") | ||||||
|  | public class StoreInvocationMicrometerWrapper implements InvocationHandler { | ||||||
|  |  | ||||||
|  |   private final Object store; | ||||||
|  |   private final MeterRegistry meterRegistry; | ||||||
|  |   private final Consumer<Timer.Builder> timerCustomizer; | ||||||
|  |  | ||||||
|  |   private final Supplier<Timer.Builder> timerBuilder; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Creates a wrapper for the given store which will record the time taken for each method call. | ||||||
|  |    * If the {@link MeterRegistry} is {@code null}, the store will not be wrapped and returned as is. | ||||||
|  |    */ | ||||||
|  |   public static <T> T create(String storeType, | ||||||
|  |                              TypedStoreParameters<?> storeParameters, | ||||||
|  |                              Class<T> storeClass, | ||||||
|  |                              T store, | ||||||
|  |                              MeterRegistry meterRegistry) { | ||||||
|  |     if (meterRegistry == null) { | ||||||
|  |       return store; | ||||||
|  |     } | ||||||
|  |     return (T) Proxy.newProxyInstance( | ||||||
|  |       storeParameters.getType().getClassLoader(), | ||||||
|  |       new Class[]{storeClass}, | ||||||
|  |       new StoreInvocationMicrometerWrapper( | ||||||
|  |         storeType, | ||||||
|  |         storeParameters, | ||||||
|  |         store, | ||||||
|  |         meterRegistry, | ||||||
|  |         () -> Timer.builder("scm.persistence") | ||||||
|  |       ) | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Creates a wrapper for the given store which will record the time taken for each method call. | ||||||
|  |    * If the {@link MeterRegistry} is {@code null}, the store will not be wrapped and returned as is. | ||||||
|  |    */ | ||||||
|  |   public static <T, Q> Q create(String storeType, | ||||||
|  |                                 Class<T> clazz, | ||||||
|  |                                 String[] parentIds, | ||||||
|  |                                 Class<Q> storeClass, | ||||||
|  |                                 Q store, | ||||||
|  |                                 MeterRegistry meterRegistry) { | ||||||
|  |     if (meterRegistry == null) { | ||||||
|  |       return store; | ||||||
|  |     } | ||||||
|  |     return (Q) Proxy.newProxyInstance( | ||||||
|  |       clazz.getClassLoader(), | ||||||
|  |       new Class[]{storeClass}, | ||||||
|  |       new StoreInvocationMicrometerWrapper( | ||||||
|  |         storeType, | ||||||
|  |         clazz, | ||||||
|  |         parentIds, | ||||||
|  |         store, | ||||||
|  |         meterRegistry, | ||||||
|  |         () -> Timer.builder("scm.persistence") | ||||||
|  |       ) | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @VisibleForTesting | ||||||
|  |   StoreInvocationMicrometerWrapper(String storeType, | ||||||
|  |                                    TypedStoreParameters<?> storeParameters, | ||||||
|  |                                    Object store, | ||||||
|  |                                    MeterRegistry meterRegistry, | ||||||
|  |                                    Supplier<Timer.Builder> timerBuilder) { | ||||||
|  |     this( | ||||||
|  |       meterRegistry, | ||||||
|  |       store, | ||||||
|  |       timer -> timer | ||||||
|  |         .description("Time taken for store operation in " + storeType) | ||||||
|  |         .tag("type", storeType) | ||||||
|  |         .tag("storeName", storeParameters.getName()) | ||||||
|  |         .tag("repositoryId", storeParameters.getRepositoryId() == null ? "none" : storeParameters.getRepositoryId()) | ||||||
|  |         .tag("namespace", storeParameters.getNamespace() == null ? "none" : storeParameters.getNamespace()), | ||||||
|  |       timerBuilder | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @VisibleForTesting | ||||||
|  |   <T, Q> StoreInvocationMicrometerWrapper(String storeType, | ||||||
|  |                                           Class<T> clazz, | ||||||
|  |                                           String[] parentIds, | ||||||
|  |                                           Q store, | ||||||
|  |                                           MeterRegistry meterRegistry, | ||||||
|  |                                           Supplier<Timer.Builder> timerBuilder) { | ||||||
|  |     this( | ||||||
|  |       meterRegistry, | ||||||
|  |       store, | ||||||
|  |       timer -> timer | ||||||
|  |         .description("Time taken for store operation in " + storeType) | ||||||
|  |         .tag("type", storeType) | ||||||
|  |         .tag("storeClass", clazz.getName()) | ||||||
|  |         .tag("parentIds", String.join(",", parentIds)), | ||||||
|  |       timerBuilder | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private StoreInvocationMicrometerWrapper(MeterRegistry meterRegistry, | ||||||
|  |                                            Object store, | ||||||
|  |                                            Consumer<Timer.Builder> timerCustomizer, | ||||||
|  |                                            Supplier<Timer.Builder> timerBuilder) { | ||||||
|  |     AssertUtil.assertIsNotNull(store); | ||||||
|  |     this.meterRegistry = meterRegistry; | ||||||
|  |     this.store = store; | ||||||
|  |     this.timerCustomizer = timerCustomizer; | ||||||
|  |     this.timerBuilder = timerBuilder; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | ||||||
|  |     if (meterRegistry == null) { | ||||||
|  |       return method.invoke(store, args); | ||||||
|  |     } | ||||||
|  |     if (QueryableStore.Query.class.isAssignableFrom(method.getReturnType())) { | ||||||
|  |       Object delegate = method.invoke(store, args); | ||||||
|  |       return Proxy.newProxyInstance( | ||||||
|  |         this.getClass().getClassLoader(), | ||||||
|  |         delegate.getClass().getInterfaces(), | ||||||
|  |         new StoreInvocationMicrometerWrapper(meterRegistry, delegate, timerCustomizer, timerBuilder) | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     try { | ||||||
|  |       if (method.getName().equals("close")) { | ||||||
|  |         return method.invoke(store, args); | ||||||
|  |       } | ||||||
|  |       Timer.Builder timer = timerBuilder.get() | ||||||
|  |         .tag("method", method.getName()); | ||||||
|  |       timerCustomizer.accept(timer); | ||||||
|  |       return timer | ||||||
|  |         .register(meterRegistry) | ||||||
|  |         .record(() -> { | ||||||
|  |           try { | ||||||
|  |             return method.invoke(store, args); | ||||||
|  |           } catch (Exception e) { | ||||||
|  |             throw new StoreException("Failed to invoke store method", e); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |     } catch (InvocationTargetException | UndeclaredThrowableException e) { | ||||||
|  |       throw e.getCause(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -16,17 +16,19 @@ | |||||||
|  |  | ||||||
| package sonia.scm.store.file; | package sonia.scm.store.file; | ||||||
|  |  | ||||||
|  | import com.google.common.annotations.VisibleForTesting; | ||||||
| import com.google.inject.Inject; | import com.google.inject.Inject; | ||||||
| import com.google.inject.Singleton; | import com.google.inject.Singleton; | ||||||
|  | import io.micrometer.core.instrument.MeterRegistry; | ||||||
| import sonia.scm.SCMContextProvider; | import sonia.scm.SCMContextProvider; | ||||||
| import sonia.scm.repository.RepositoryLocationResolver; | import sonia.scm.repository.RepositoryLocationResolver; | ||||||
| import sonia.scm.repository.RepositoryReadOnlyChecker; | import sonia.scm.repository.RepositoryReadOnlyChecker; | ||||||
| import sonia.scm.security.KeyGenerator; | import sonia.scm.security.KeyGenerator; | ||||||
| import sonia.scm.store.ConfigurationEntryStore; | import sonia.scm.store.ConfigurationEntryStore; | ||||||
| import sonia.scm.store.ConfigurationEntryStoreFactory; | import sonia.scm.store.ConfigurationEntryStoreFactory; | ||||||
|  | import sonia.scm.store.StoreInvocationMicrometerWrapper; | ||||||
| import sonia.scm.store.TypedStoreParameters; | import sonia.scm.store.TypedStoreParameters; | ||||||
|  |  | ||||||
|  |  | ||||||
| @Singleton | @Singleton | ||||||
| public class JAXBConfigurationEntryStoreFactory extends FileBasedStoreFactory | public class JAXBConfigurationEntryStoreFactory extends FileBasedStoreFactory | ||||||
|   implements ConfigurationEntryStoreFactory { |   implements ConfigurationEntryStoreFactory { | ||||||
| @@ -35,17 +37,39 @@ public class JAXBConfigurationEntryStoreFactory extends FileBasedStoreFactory | |||||||
|  |  | ||||||
|   private final StoreCache<ConfigurationEntryStore<?>> storeCache; |   private final StoreCache<ConfigurationEntryStore<?>> storeCache; | ||||||
|  |  | ||||||
|   @Inject |   private final MeterRegistry meterRegistry; | ||||||
|  |  | ||||||
|  |   @VisibleForTesting | ||||||
|   public JAXBConfigurationEntryStoreFactory( |   public JAXBConfigurationEntryStoreFactory( | ||||||
|     SCMContextProvider contextProvider, |     SCMContextProvider contextProvider, | ||||||
|     RepositoryLocationResolver repositoryLocationResolver, |     RepositoryLocationResolver repositoryLocationResolver, | ||||||
|     KeyGenerator keyGenerator, |     KeyGenerator keyGenerator, | ||||||
|     RepositoryReadOnlyChecker readOnlyChecker, |     RepositoryReadOnlyChecker readOnlyChecker, | ||||||
|     StoreCacheFactory storeCacheFactory |     StoreCacheFactory storeCacheFactory | ||||||
|  |   ) { | ||||||
|  |     this( | ||||||
|  |       contextProvider, | ||||||
|  |       repositoryLocationResolver, | ||||||
|  |       keyGenerator, | ||||||
|  |       readOnlyChecker, | ||||||
|  |       storeCacheFactory, | ||||||
|  |       null | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Inject | ||||||
|  |   public JAXBConfigurationEntryStoreFactory( | ||||||
|  |     SCMContextProvider contextProvider, | ||||||
|  |     RepositoryLocationResolver repositoryLocationResolver, | ||||||
|  |     KeyGenerator keyGenerator, | ||||||
|  |     RepositoryReadOnlyChecker readOnlyChecker, | ||||||
|  |     StoreCacheFactory storeCacheFactory, | ||||||
|  |     MeterRegistry meterRegistry | ||||||
|   ) { |   ) { | ||||||
|     super(contextProvider, repositoryLocationResolver, Store.CONFIG, readOnlyChecker); |     super(contextProvider, repositoryLocationResolver, Store.CONFIG, readOnlyChecker); | ||||||
|     this.keyGenerator = keyGenerator; |     this.keyGenerator = keyGenerator; | ||||||
|     this.storeCache = storeCacheFactory.createStoreCache(this::createStore); |     this.storeCache = storeCacheFactory.createStoreCache(this::createStore); | ||||||
|  |     this.meterRegistry = meterRegistry; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
| @@ -55,7 +79,7 @@ public class JAXBConfigurationEntryStoreFactory extends FileBasedStoreFactory | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private <T> ConfigurationEntryStore<T> createStore(TypedStoreParameters<T> storeParameters) { |   private <T> ConfigurationEntryStore<T> createStore(TypedStoreParameters<T> storeParameters) { | ||||||
|     return new JAXBConfigurationEntryStore<>( |     JAXBConfigurationEntryStore<T> store = new JAXBConfigurationEntryStore<>( | ||||||
|       getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), |       getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), | ||||||
|         storeParameters.getType(), |         storeParameters.getType(), | ||||||
|         storeParameters.getRepositoryId(), |         storeParameters.getRepositoryId(), | ||||||
| @@ -64,5 +88,13 @@ public class JAXBConfigurationEntryStoreFactory extends FileBasedStoreFactory | |||||||
|       storeParameters.getType(), |       storeParameters.getType(), | ||||||
|       TypedStoreContext.of(storeParameters) |       TypedStoreContext.of(storeParameters) | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     return StoreInvocationMicrometerWrapper.create( | ||||||
|  |       "configuration-entry-store", | ||||||
|  |       storeParameters, | ||||||
|  |       ConfigurationEntryStore.class, | ||||||
|  |       store, | ||||||
|  |       meterRegistry | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,14 +16,17 @@ | |||||||
|  |  | ||||||
| package sonia.scm.store.file; | package sonia.scm.store.file; | ||||||
|  |  | ||||||
|  | import com.google.common.annotations.VisibleForTesting; | ||||||
| import com.google.inject.Inject; | import com.google.inject.Inject; | ||||||
| import com.google.inject.Singleton; | import com.google.inject.Singleton; | ||||||
|  | import io.micrometer.core.instrument.MeterRegistry; | ||||||
| import sonia.scm.SCMContextProvider; | import sonia.scm.SCMContextProvider; | ||||||
| import sonia.scm.repository.RepositoryLocationResolver; | import sonia.scm.repository.RepositoryLocationResolver; | ||||||
| import sonia.scm.repository.RepositoryReadOnlyChecker; | import sonia.scm.repository.RepositoryReadOnlyChecker; | ||||||
| import sonia.scm.store.ConfigurationStore; | import sonia.scm.store.ConfigurationStore; | ||||||
| import sonia.scm.store.ConfigurationStoreDecoratorFactory; | import sonia.scm.store.ConfigurationStoreDecoratorFactory; | ||||||
| import sonia.scm.store.ConfigurationStoreFactory; | import sonia.scm.store.ConfigurationStoreFactory; | ||||||
|  | import sonia.scm.store.StoreInvocationMicrometerWrapper; | ||||||
| import sonia.scm.store.StoreDecoratorFactory; | import sonia.scm.store.StoreDecoratorFactory; | ||||||
| import sonia.scm.store.TypedStoreParameters; | import sonia.scm.store.TypedStoreParameters; | ||||||
|  |  | ||||||
| @@ -40,17 +43,39 @@ public class JAXBConfigurationStoreFactory extends FileBasedStoreFactory impleme | |||||||
|  |  | ||||||
|   private final StoreCache<ConfigurationStore<?>> storeCache; |   private final StoreCache<ConfigurationStore<?>> storeCache; | ||||||
|  |  | ||||||
|   @Inject |   private final MeterRegistry meterRegistry; | ||||||
|  |  | ||||||
|  |   @VisibleForTesting | ||||||
|   public JAXBConfigurationStoreFactory( |   public JAXBConfigurationStoreFactory( | ||||||
|     SCMContextProvider contextProvider, |     SCMContextProvider contextProvider, | ||||||
|     RepositoryLocationResolver repositoryLocationResolver, |     RepositoryLocationResolver repositoryLocationResolver, | ||||||
|     RepositoryReadOnlyChecker readOnlyChecker, |     RepositoryReadOnlyChecker readOnlyChecker, | ||||||
|     Set<ConfigurationStoreDecoratorFactory> decoratorFactories, |     Set<ConfigurationStoreDecoratorFactory> decoratorFactories, | ||||||
|     StoreCacheFactory storeCacheFactory |     StoreCacheFactory storeCacheFactory | ||||||
|  |   ) { | ||||||
|  |     this( | ||||||
|  |       contextProvider, | ||||||
|  |       repositoryLocationResolver, | ||||||
|  |       readOnlyChecker, | ||||||
|  |       decoratorFactories, | ||||||
|  |       storeCacheFactory, | ||||||
|  |       null | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Inject | ||||||
|  |   public JAXBConfigurationStoreFactory( | ||||||
|  |     SCMContextProvider contextProvider, | ||||||
|  |     RepositoryLocationResolver repositoryLocationResolver, | ||||||
|  |     RepositoryReadOnlyChecker readOnlyChecker, | ||||||
|  |     Set<ConfigurationStoreDecoratorFactory> decoratorFactories, | ||||||
|  |     StoreCacheFactory storeCacheFactory, | ||||||
|  |     MeterRegistry meterRegistry | ||||||
|   ) { |   ) { | ||||||
|     super(contextProvider, repositoryLocationResolver, Store.CONFIG, readOnlyChecker); |     super(contextProvider, repositoryLocationResolver, Store.CONFIG, readOnlyChecker); | ||||||
|     this.decoratorFactories = decoratorFactories; |     this.decoratorFactories = decoratorFactories; | ||||||
|     this.storeCache = storeCacheFactory.createStoreCache(this::createStore); |     this.storeCache = storeCacheFactory.createStoreCache(this::createStore); | ||||||
|  |     this.meterRegistry = meterRegistry; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
| @@ -76,6 +101,12 @@ public class JAXBConfigurationStoreFactory extends FileBasedStoreFactory impleme | |||||||
|       store = factory.createDecorator(store, new StoreDecoratorFactory.Context(storeParameters)); |       store = factory.createDecorator(store, new StoreDecoratorFactory.Context(storeParameters)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return store; |     return StoreInvocationMicrometerWrapper.create( | ||||||
|  |       "configuration-store", | ||||||
|  |       storeParameters, | ||||||
|  |       ConfigurationStore.class, | ||||||
|  |       store, | ||||||
|  |       meterRegistry | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,20 +16,22 @@ | |||||||
|  |  | ||||||
| package sonia.scm.store.file; | package sonia.scm.store.file; | ||||||
|  |  | ||||||
|  | import com.google.common.annotations.VisibleForTesting; | ||||||
| import com.google.inject.Inject; | import com.google.inject.Inject; | ||||||
| import com.google.inject.Singleton; | import com.google.inject.Singleton; | ||||||
|  | import io.micrometer.core.instrument.MeterRegistry; | ||||||
| import sonia.scm.SCMContextProvider; | import sonia.scm.SCMContextProvider; | ||||||
| import sonia.scm.repository.RepositoryLocationResolver; | import sonia.scm.repository.RepositoryLocationResolver; | ||||||
| import sonia.scm.repository.RepositoryReadOnlyChecker; | import sonia.scm.repository.RepositoryReadOnlyChecker; | ||||||
| import sonia.scm.security.KeyGenerator; | import sonia.scm.security.KeyGenerator; | ||||||
| import sonia.scm.store.DataStore; | import sonia.scm.store.DataStore; | ||||||
| import sonia.scm.store.DataStoreFactory; | import sonia.scm.store.DataStoreFactory; | ||||||
|  | import sonia.scm.store.StoreInvocationMicrometerWrapper; | ||||||
| import sonia.scm.store.TypedStoreParameters; | import sonia.scm.store.TypedStoreParameters; | ||||||
| import sonia.scm.util.IOUtil; | import sonia.scm.util.IOUtil; | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
|  |  | ||||||
|  |  | ||||||
| @Singleton | @Singleton | ||||||
| public class JAXBDataStoreFactory extends FileBasedStoreFactory | public class JAXBDataStoreFactory extends FileBasedStoreFactory | ||||||
|   implements DataStoreFactory { |   implements DataStoreFactory { | ||||||
| @@ -40,7 +42,9 @@ public class JAXBDataStoreFactory extends FileBasedStoreFactory | |||||||
|  |  | ||||||
|   private final DataFileCache dataFileCache; |   private final DataFileCache dataFileCache; | ||||||
|  |  | ||||||
|   @Inject |   private final MeterRegistry meterRegistry; | ||||||
|  |  | ||||||
|  |   @VisibleForTesting | ||||||
|   public JAXBDataStoreFactory( |   public JAXBDataStoreFactory( | ||||||
|     SCMContextProvider contextProvider, |     SCMContextProvider contextProvider, | ||||||
|     RepositoryLocationResolver repositoryLocationResolver, |     RepositoryLocationResolver repositoryLocationResolver, | ||||||
| @@ -48,11 +52,33 @@ public class JAXBDataStoreFactory extends FileBasedStoreFactory | |||||||
|     RepositoryReadOnlyChecker readOnlyChecker, |     RepositoryReadOnlyChecker readOnlyChecker, | ||||||
|     DataFileCache dataFileCache, |     DataFileCache dataFileCache, | ||||||
|     StoreCacheFactory storeCacheFactory |     StoreCacheFactory storeCacheFactory | ||||||
|  |   ) { | ||||||
|  |     this( | ||||||
|  |       contextProvider, | ||||||
|  |       repositoryLocationResolver, | ||||||
|  |       keyGenerator, | ||||||
|  |       readOnlyChecker, | ||||||
|  |       dataFileCache, | ||||||
|  |       storeCacheFactory, | ||||||
|  |       null | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Inject | ||||||
|  |   public JAXBDataStoreFactory( | ||||||
|  |     SCMContextProvider contextProvider, | ||||||
|  |     RepositoryLocationResolver repositoryLocationResolver, | ||||||
|  |     KeyGenerator keyGenerator, | ||||||
|  |     RepositoryReadOnlyChecker readOnlyChecker, | ||||||
|  |     DataFileCache dataFileCache, | ||||||
|  |     StoreCacheFactory storeCacheFactory, | ||||||
|  |     MeterRegistry meterRegistry | ||||||
|   ) { |   ) { | ||||||
|     super(contextProvider, repositoryLocationResolver, Store.DATA, readOnlyChecker); |     super(contextProvider, repositoryLocationResolver, Store.DATA, readOnlyChecker); | ||||||
|     this.keyGenerator = keyGenerator; |     this.keyGenerator = keyGenerator; | ||||||
|     this.dataFileCache = dataFileCache; |     this.dataFileCache = dataFileCache; | ||||||
|     this.storeCache = storeCacheFactory.createStoreCache(this::createStore); |     this.storeCache = storeCacheFactory.createStoreCache(this::createStore); | ||||||
|  |     this.meterRegistry = meterRegistry; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
| @@ -64,12 +90,22 @@ public class JAXBDataStoreFactory extends FileBasedStoreFactory | |||||||
|   private <T> DataStore<T> createStore(TypedStoreParameters<T> storeParameters) { |   private <T> DataStore<T> createStore(TypedStoreParameters<T> storeParameters) { | ||||||
|     File storeLocation = getStoreLocation(storeParameters); |     File storeLocation = getStoreLocation(storeParameters); | ||||||
|     IOUtil.mkdirs(storeLocation); |     IOUtil.mkdirs(storeLocation); | ||||||
|     return new JAXBDataStore<>( |  | ||||||
|  |     // Create a proxy for the created store and use Micrometer to instrument it | ||||||
|  |     JAXBDataStore<T> store = new JAXBDataStore<>( | ||||||
|       keyGenerator, |       keyGenerator, | ||||||
|       TypedStoreContext.of(storeParameters), |       TypedStoreContext.of(storeParameters), | ||||||
|       storeLocation, |       storeLocation, | ||||||
|       mustBeReadOnly(storeParameters), |       mustBeReadOnly(storeParameters), | ||||||
|       dataFileCache.instanceFor(storeParameters.getType()) |       dataFileCache.instanceFor(storeParameters.getType()) | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     return StoreInvocationMicrometerWrapper.create( | ||||||
|  |       "data-store", | ||||||
|  |       storeParameters, | ||||||
|  |       DataStore.class, | ||||||
|  |       store, | ||||||
|  |       meterRegistry | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; | |||||||
| import com.google.common.annotations.VisibleForTesting; | import com.google.common.annotations.VisibleForTesting; | ||||||
| import com.zaxxer.hikari.HikariConfig; | import com.zaxxer.hikari.HikariConfig; | ||||||
| import com.zaxxer.hikari.HikariDataSource; | import com.zaxxer.hikari.HikariDataSource; | ||||||
|  | import io.micrometer.core.instrument.MeterRegistry; | ||||||
| import jakarta.inject.Inject; | import jakarta.inject.Inject; | ||||||
| import jakarta.inject.Singleton; | import jakarta.inject.Singleton; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| @@ -31,7 +32,10 @@ import sonia.scm.plugin.QueryableTypeDescriptor; | |||||||
| import sonia.scm.repository.Repository; | import sonia.scm.repository.Repository; | ||||||
| import sonia.scm.repository.RepositoryReadOnlyChecker; | import sonia.scm.repository.RepositoryReadOnlyChecker; | ||||||
| import sonia.scm.security.KeyGenerator; | import sonia.scm.security.KeyGenerator; | ||||||
|  | import sonia.scm.store.StoreInvocationMicrometerWrapper; | ||||||
| import sonia.scm.store.QueryableMaintenanceStore; | import sonia.scm.store.QueryableMaintenanceStore; | ||||||
|  | import sonia.scm.store.QueryableMutableStore; | ||||||
|  | import sonia.scm.store.QueryableStore; | ||||||
| import sonia.scm.store.QueryableStoreFactory; | import sonia.scm.store.QueryableStoreFactory; | ||||||
| import sonia.scm.store.StoreException; | import sonia.scm.store.StoreException; | ||||||
|  |  | ||||||
| @@ -62,6 +66,7 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|   private final KeyGenerator keyGenerator; |   private final KeyGenerator keyGenerator; | ||||||
|   private final DataSource dataSource; |   private final DataSource dataSource; | ||||||
|   private final RepositoryReadOnlyChecker readOnlyChecker; |   private final RepositoryReadOnlyChecker readOnlyChecker; | ||||||
|  |   private final MeterRegistry meterRegistry; | ||||||
|  |  | ||||||
|   private final Map<String, QueryableTypeDescriptor> queryableTypes = new HashMap<>(); |   private final Map<String, QueryableTypeDescriptor> queryableTypes = new HashMap<>(); | ||||||
|  |  | ||||||
| @@ -73,6 +78,7 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|                                      ObjectMapper objectMapper, |                                      ObjectMapper objectMapper, | ||||||
|                                      KeyGenerator keyGenerator, |                                      KeyGenerator keyGenerator, | ||||||
|                                      RepositoryReadOnlyChecker readOnlyChecker, |                                      RepositoryReadOnlyChecker readOnlyChecker, | ||||||
|  |                                      MeterRegistry meterRegistry, | ||||||
|                                      @ConfigValue(key = "queryableStore.maxPoolSize", defaultValue = DEFAULT_MAX_POOL_SIZE) int maxPoolSize, |                                      @ConfigValue(key = "queryableStore.maxPoolSize", defaultValue = DEFAULT_MAX_POOL_SIZE) int maxPoolSize, | ||||||
|                                      @ConfigValue(key = "queryableStore.connectionTimeout", defaultValue = DEFAULT_CONNECTION_TIMEOUT_IN_SECONDS) int connectionTimeoutInSeconds, |                                      @ConfigValue(key = "queryableStore.connectionTimeout", defaultValue = DEFAULT_CONNECTION_TIMEOUT_IN_SECONDS) int connectionTimeoutInSeconds, | ||||||
|                                      @ConfigValue(key = "queryableStore.idleTimeout", defaultValue = DEFAULT_IDLE_TIMEOUT_IN_SECONDS) int idleTimeoutInSeconds, |                                      @ConfigValue(key = "queryableStore.idleTimeout", defaultValue = DEFAULT_IDLE_TIMEOUT_IN_SECONDS) int idleTimeoutInSeconds, | ||||||
| @@ -85,6 +91,7 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|       keyGenerator, |       keyGenerator, | ||||||
|       pluginLoader.getExtensionProcessor().getQueryableTypes(), |       pluginLoader.getExtensionProcessor().getQueryableTypes(), | ||||||
|       readOnlyChecker, |       readOnlyChecker, | ||||||
|  |       meterRegistry, | ||||||
|       maxPoolSize, |       maxPoolSize, | ||||||
|       connectionTimeoutInSeconds, |       connectionTimeoutInSeconds, | ||||||
|       idleTimeoutInSeconds, |       idleTimeoutInSeconds, | ||||||
| @@ -99,7 +106,19 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|                                      KeyGenerator keyGenerator, |                                      KeyGenerator keyGenerator, | ||||||
|                                      Iterable<QueryableTypeDescriptor> queryableTypeIterable, |                                      Iterable<QueryableTypeDescriptor> queryableTypeIterable, | ||||||
|                                      RepositoryReadOnlyChecker readOnlyChecker) { |                                      RepositoryReadOnlyChecker readOnlyChecker) { | ||||||
|     this(connectionString, objectMapper, keyGenerator, queryableTypeIterable, readOnlyChecker, 10, 30, 600, 1800, 30); |     this( | ||||||
|  |       connectionString, | ||||||
|  |       objectMapper, | ||||||
|  |       keyGenerator, | ||||||
|  |       queryableTypeIterable, | ||||||
|  |       readOnlyChecker, | ||||||
|  |       null, | ||||||
|  |       10, | ||||||
|  |       30, | ||||||
|  |       600, | ||||||
|  |       1800, | ||||||
|  |       30 | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private SQLiteQueryableStoreFactory(String connectionString, |   private SQLiteQueryableStoreFactory(String connectionString, | ||||||
| @@ -107,6 +126,7 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|                                       KeyGenerator keyGenerator, |                                       KeyGenerator keyGenerator, | ||||||
|                                       Iterable<QueryableTypeDescriptor> queryableTypeIterable, |                                       Iterable<QueryableTypeDescriptor> queryableTypeIterable, | ||||||
|                                       RepositoryReadOnlyChecker readOnlyChecker, |                                       RepositoryReadOnlyChecker readOnlyChecker, | ||||||
|  |                                       MeterRegistry meterRegistry, | ||||||
|                                       int maxPoolSize, |                                       int maxPoolSize, | ||||||
|                                       int connectionTimeoutInSeconds, |                                       int connectionTimeoutInSeconds, | ||||||
|                                       int idleTimeoutInSeconds, |                                       int idleTimeoutInSeconds, | ||||||
| @@ -120,6 +140,7 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|       maxLifetimeInSeconds, |       maxLifetimeInSeconds, | ||||||
|       leakDetectionThresholdInSeconds); |       leakDetectionThresholdInSeconds); | ||||||
|     this.readOnlyChecker = readOnlyChecker; |     this.readOnlyChecker = readOnlyChecker; | ||||||
|  |     this.meterRegistry = meterRegistry; | ||||||
|     this.dataSource = new HikariDataSource(config); |     this.dataSource = new HikariDataSource(config); | ||||||
|  |  | ||||||
|     this.objectMapper = objectMapper |     this.objectMapper = objectMapper | ||||||
| @@ -177,9 +198,9 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   public <T> SQLiteQueryableStore<T> getReadOnly(Class<T> clazz, String... parentIds) { |   public <T> QueryableStore<T> getReadOnly(Class<T> clazz, String... parentIds) { | ||||||
|     QueryableTypeDescriptor queryableTypeDescriptor = getQueryableTypeDescriptor(clazz); |     QueryableTypeDescriptor queryableTypeDescriptor = getQueryableTypeDescriptor(clazz); | ||||||
|     return new SQLiteQueryableStore<>( |     SQLiteQueryableStore<T> store = new SQLiteQueryableStore<>( | ||||||
|       objectMapper, |       objectMapper, | ||||||
|       openDefaultConnection(), |       openDefaultConnection(), | ||||||
|       clazz, |       clazz, | ||||||
| @@ -188,12 +209,21 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|       lock, |       lock, | ||||||
|       mustBeReadOnly(queryableTypeDescriptor, parentIds) |       mustBeReadOnly(queryableTypeDescriptor, parentIds) | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     return StoreInvocationMicrometerWrapper.create( | ||||||
|  |       "QueryableStore", | ||||||
|  |       clazz, | ||||||
|  |       parentIds, | ||||||
|  |       QueryableStore.class, | ||||||
|  |       store, | ||||||
|  |       meterRegistry | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   public <T> QueryableMaintenanceStore<T> getForMaintenance(Class<T> clazz, String... parentIds) { |   public <T> QueryableMaintenanceStore<T> getForMaintenance(Class<T> clazz, String... parentIds) { | ||||||
|     QueryableTypeDescriptor queryableTypeDescriptor = getQueryableTypeDescriptor(clazz); |     QueryableTypeDescriptor queryableTypeDescriptor = getQueryableTypeDescriptor(clazz); | ||||||
|     return new SQLiteQueryableStore<>( |     SQLiteQueryableStore<T> store = new SQLiteQueryableStore<>( | ||||||
|       objectMapper, |       objectMapper, | ||||||
|       openDefaultConnection(), |       openDefaultConnection(), | ||||||
|       clazz, |       clazz, | ||||||
| @@ -202,12 +232,21 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|       lock, |       lock, | ||||||
|       mustBeReadOnly(queryableTypeDescriptor, parentIds) |       mustBeReadOnly(queryableTypeDescriptor, parentIds) | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     return StoreInvocationMicrometerWrapper.create( | ||||||
|  |       "QueryableMaintenanceStore", | ||||||
|  |       clazz, | ||||||
|  |       parentIds, | ||||||
|  |       QueryableMaintenanceStore.class, | ||||||
|  |       store, | ||||||
|  |       meterRegistry | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   public <T> SQLiteQueryableMutableStore<T> getMutable(Class<T> clazz, String... parentIds) { |   public <T> QueryableMutableStore<T> getMutable(Class<T> clazz, String... parentIds) { | ||||||
|     QueryableTypeDescriptor queryableTypeDescriptor = getQueryableTypeDescriptor(clazz); |     QueryableTypeDescriptor queryableTypeDescriptor = getQueryableTypeDescriptor(clazz); | ||||||
|     return new SQLiteQueryableMutableStore<>( |     SQLiteQueryableMutableStore<T> store = new SQLiteQueryableMutableStore<>( | ||||||
|       objectMapper, |       objectMapper, | ||||||
|       keyGenerator, |       keyGenerator, | ||||||
|       openDefaultConnection(), |       openDefaultConnection(), | ||||||
| @@ -217,6 +256,15 @@ public class SQLiteQueryableStoreFactory implements QueryableStoreFactory { | |||||||
|       lock, |       lock, | ||||||
|       mustBeReadOnly(queryableTypeDescriptor, parentIds) |       mustBeReadOnly(queryableTypeDescriptor, parentIds) | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     return StoreInvocationMicrometerWrapper.create( | ||||||
|  |       "QueryableMutableStore", | ||||||
|  |       clazz, | ||||||
|  |       parentIds, | ||||||
|  |       QueryableMutableStore.class, | ||||||
|  |       store, | ||||||
|  |       meterRegistry | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private boolean mustBeReadOnly(QueryableTypeDescriptor queryableTypeDescriptor, String... parentIds) { |   private boolean mustBeReadOnly(QueryableTypeDescriptor queryableTypeDescriptor, String... parentIds) { | ||||||
|   | |||||||
| @@ -0,0 +1,309 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 - present Cloudogu GmbH | ||||||
|  |  * | ||||||
|  |  * This program is free software: you can redistribute it and/or modify it under | ||||||
|  |  * the terms of the GNU Affero General Public License as published by the Free | ||||||
|  |  * Software Foundation, version 3. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||||
|  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||||||
|  |  * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | ||||||
|  |  * details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program. If not, see https://www.gnu.org/licenses/. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package sonia.scm.store; | ||||||
|  |  | ||||||
|  | import io.micrometer.core.instrument.MeterRegistry; | ||||||
|  | import io.micrometer.core.instrument.Timer; | ||||||
|  | import org.junit.jupiter.api.BeforeEach; | ||||||
|  | import org.junit.jupiter.api.Nested; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  | import org.junit.jupiter.api.extension.ExtendWith; | ||||||
|  | import org.mockito.Mock; | ||||||
|  | import org.mockito.junit.jupiter.MockitoExtension; | ||||||
|  | import sonia.scm.user.User; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Proxy; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.function.Supplier; | ||||||
|  |  | ||||||
|  | import static org.assertj.core.api.Assertions.assertThat; | ||||||
|  | import static org.mockito.Answers.RETURNS_SELF; | ||||||
|  | import static org.mockito.ArgumentMatchers.any; | ||||||
|  | import static org.mockito.Mockito.only; | ||||||
|  | import static org.mockito.Mockito.verify; | ||||||
|  | import static org.mockito.Mockito.when; | ||||||
|  |  | ||||||
|  | @ExtendWith(MockitoExtension.class) | ||||||
|  | class StoreInvocationMicrometerWrapperTest { | ||||||
|  |  | ||||||
|  |   @Mock | ||||||
|  |   private MeterRegistry meterRegistry; | ||||||
|  |   @Mock(answer = RETURNS_SELF) | ||||||
|  |   private Timer.Builder timerBuilder; | ||||||
|  |   @Mock | ||||||
|  |   private Timer timer; | ||||||
|  |  | ||||||
|  |   @BeforeEach | ||||||
|  |   void setUpTimer() { | ||||||
|  |     when(timerBuilder.register(any())) | ||||||
|  |       .thenReturn(timer); | ||||||
|  |     when(timer.record(any(Supplier.class))) | ||||||
|  |       .thenAnswer(i -> i.getArgument(0, Supplier.class).get()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Nested | ||||||
|  |   class ForSimpleStore { | ||||||
|  |  | ||||||
|  |     @Mock | ||||||
|  |     private DataStore<User> dataStore; | ||||||
|  |     @Mock | ||||||
|  |     private TypedStoreParameters<User> storeParameters; | ||||||
|  |  | ||||||
|  |     @BeforeEach | ||||||
|  |     void setUpStore() { | ||||||
|  |       when(storeParameters.getName()) | ||||||
|  |         .thenReturn("users"); | ||||||
|  |       when(dataStore.get("42")).thenReturn(new User("dent")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void shouldMeasureDirectCallForGlobalStore() { | ||||||
|  |       StoreInvocationMicrometerWrapper handler = | ||||||
|  |         new StoreInvocationMicrometerWrapper( | ||||||
|  |           "data", | ||||||
|  |           storeParameters, | ||||||
|  |           dataStore, | ||||||
|  |           meterRegistry, | ||||||
|  |           () -> timerBuilder | ||||||
|  |         ); | ||||||
|  |       DataStore<User> store = (DataStore<User>) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{DataStore.class}, handler); | ||||||
|  |       User result = store | ||||||
|  |         .get("42"); | ||||||
|  |  | ||||||
|  |       assertThat(result) | ||||||
|  |         .extracting(User::getName) | ||||||
|  |         .isEqualTo("dent"); | ||||||
|  |  | ||||||
|  |       verify(timerBuilder).description("Time taken for store operation in data"); | ||||||
|  |       verify(timerBuilder).tag("method", "get"); | ||||||
|  |       verify(timerBuilder).tag("type", "data"); | ||||||
|  |       verify(timerBuilder).tag("storeName", "users"); | ||||||
|  |       verify(timerBuilder).tag("repositoryId", "none"); | ||||||
|  |       verify(timerBuilder).tag("namespace", "none"); | ||||||
|  |  | ||||||
|  |       verify(timer, only()).record(any(Supplier.class)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void shouldMeasureDirectCallForRepositoryStore() { | ||||||
|  |       when(storeParameters.getRepositoryId()) | ||||||
|  |         .thenReturn("hog"); | ||||||
|  |       StoreInvocationMicrometerWrapper handler = | ||||||
|  |         new StoreInvocationMicrometerWrapper( | ||||||
|  |           "data", | ||||||
|  |           storeParameters, | ||||||
|  |           dataStore, | ||||||
|  |           meterRegistry, | ||||||
|  |           () -> timerBuilder | ||||||
|  |         ); | ||||||
|  |       DataStore<User> store = (DataStore<User>) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{DataStore.class}, handler); | ||||||
|  |       User result = store | ||||||
|  |         .get("42"); | ||||||
|  |  | ||||||
|  |       assertThat(result) | ||||||
|  |         .extracting(User::getName) | ||||||
|  |         .isEqualTo("dent"); | ||||||
|  |  | ||||||
|  |       verify(timerBuilder).description("Time taken for store operation in data"); | ||||||
|  |       verify(timerBuilder).tag("method", "get"); | ||||||
|  |       verify(timerBuilder).tag("repositoryId", "hog"); | ||||||
|  |       verify(timerBuilder).tag("namespace", "none"); | ||||||
|  |  | ||||||
|  |       verify(timer, only()).record(any(Supplier.class)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void shouldMeasureDirectCallForNamespaceStore() { | ||||||
|  |       when(storeParameters.getNamespace()) | ||||||
|  |         .thenReturn("intergalactic"); | ||||||
|  |       StoreInvocationMicrometerWrapper handler = | ||||||
|  |         new StoreInvocationMicrometerWrapper( | ||||||
|  |           "data", | ||||||
|  |           storeParameters, | ||||||
|  |           dataStore, | ||||||
|  |           meterRegistry, | ||||||
|  |           () -> timerBuilder | ||||||
|  |         ); | ||||||
|  |       DataStore<User> store = (DataStore<User>) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{DataStore.class}, handler); | ||||||
|  |       User result = store | ||||||
|  |         .get("42"); | ||||||
|  |  | ||||||
|  |       assertThat(result) | ||||||
|  |         .extracting(User::getName) | ||||||
|  |         .isEqualTo("dent"); | ||||||
|  |  | ||||||
|  |       verify(timerBuilder).description("Time taken for store operation in data"); | ||||||
|  |       verify(timerBuilder).tag("method", "get"); | ||||||
|  |       verify(timerBuilder).tag("namespace", "intergalactic"); | ||||||
|  |       verify(timerBuilder).tag("repositoryId", "none"); | ||||||
|  |  | ||||||
|  |       verify(timer, only()).record(any(Supplier.class)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Nested | ||||||
|  |   class ForQueryableStore { | ||||||
|  |  | ||||||
|  |     @Mock | ||||||
|  |     private QueryableStore<User> queryableStore; | ||||||
|  |     @Mock | ||||||
|  |     private QueryableStore.Query<User, User, ?> query; | ||||||
|  |  | ||||||
|  |     @BeforeEach | ||||||
|  |     void setUpStore() { | ||||||
|  |       when(queryableStore.query()) | ||||||
|  |         .thenAnswer(i -> query); | ||||||
|  |       when(query.findAll()) | ||||||
|  |         .thenReturn(List.of(new User("dent"))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void shouldMeasureDirectCall() { | ||||||
|  |       StoreInvocationMicrometerWrapper handler = | ||||||
|  |         new StoreInvocationMicrometerWrapper( | ||||||
|  |           "queryable", | ||||||
|  |           User.class, | ||||||
|  |           new String[]{"intergalactic", "hog"}, | ||||||
|  |           queryableStore, | ||||||
|  |           meterRegistry, | ||||||
|  |           () -> timerBuilder | ||||||
|  |         ); | ||||||
|  |       QueryableStore<User> store = (QueryableStore<User>) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{QueryableStore.class}, handler); | ||||||
|  |       List<User> result = store | ||||||
|  |         .query() | ||||||
|  |         .findAll(); | ||||||
|  |  | ||||||
|  |       assertThat(result) | ||||||
|  |         .extracting(User::getName) | ||||||
|  |         .containsExactly("dent"); | ||||||
|  |  | ||||||
|  |       verify(timerBuilder).description("Time taken for store operation in queryable"); | ||||||
|  |       verify(timerBuilder).tag("method", "findAll"); | ||||||
|  |       verify(timerBuilder).tag("storeClass", "sonia.scm.user.User"); | ||||||
|  |       verify(timerBuilder).tag("parentIds", "intergalactic,hog"); | ||||||
|  |  | ||||||
|  |       verify(timer, only()).record(any(Supplier.class)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void shouldMeasureCallAfterOrder() { | ||||||
|  |       when(query.orderBy(any(), any(QueryableStore.Order.class))) | ||||||
|  |         .thenAnswer(i -> query); | ||||||
|  |  | ||||||
|  |       StoreInvocationMicrometerWrapper handler = | ||||||
|  |         new StoreInvocationMicrometerWrapper( | ||||||
|  |           "queryable", | ||||||
|  |           User.class, | ||||||
|  |           new String[]{"intergalactic", "hog"}, | ||||||
|  |           queryableStore, | ||||||
|  |           meterRegistry, | ||||||
|  |           () -> timerBuilder | ||||||
|  |         ); | ||||||
|  |       QueryableStore store = (QueryableStore) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{QueryableStore.class}, handler); | ||||||
|  |       List<User> result = store | ||||||
|  |         .query() | ||||||
|  |         .orderBy(new QueryableStore.IdQueryField(), QueryableStore.Order.DESC) | ||||||
|  |         .findAll(); | ||||||
|  |  | ||||||
|  |       assertThat(result) | ||||||
|  |         .extracting(User::getName) | ||||||
|  |         .containsExactly("dent"); | ||||||
|  |  | ||||||
|  |       verify(timerBuilder).description("Time taken for store operation in queryable"); | ||||||
|  |       verify(timerBuilder).tag("method", "findAll"); | ||||||
|  |  | ||||||
|  |       verify(timer, only()).record(any(Supplier.class)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void shouldMeasureCallAfterMultipleIndirection() { | ||||||
|  |       when(query.orderBy(any(), any(QueryableStore.Order.class))) | ||||||
|  |         .thenAnswer(i -> query); | ||||||
|  |       when(query.distinct()) | ||||||
|  |         .thenAnswer(i -> query); | ||||||
|  |       when(query.project(any())) | ||||||
|  |         .thenAnswer(i -> query); | ||||||
|  |  | ||||||
|  |       StoreInvocationMicrometerWrapper handler = | ||||||
|  |         new StoreInvocationMicrometerWrapper( | ||||||
|  |           "queryable", | ||||||
|  |           User.class, | ||||||
|  |           new String[]{"intergalactic", "hog"}, | ||||||
|  |           queryableStore, | ||||||
|  |           meterRegistry, | ||||||
|  |           () -> timerBuilder | ||||||
|  |         ); | ||||||
|  |       QueryableStore store = (QueryableStore) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{QueryableStore.class}, handler); | ||||||
|  |       List<User> result = store | ||||||
|  |         .query() | ||||||
|  |         .orderBy(new QueryableStore.IdQueryField(), QueryableStore.Order.DESC) | ||||||
|  |         .distinct() | ||||||
|  |         .project(new QueryableStore.IdQueryField()) | ||||||
|  |         .findAll(); | ||||||
|  |  | ||||||
|  |       assertThat(result) | ||||||
|  |         .extracting(User::getName) | ||||||
|  |         .containsExactly("dent"); | ||||||
|  |  | ||||||
|  |       verify(timerBuilder).description("Time taken for store operation in queryable"); | ||||||
|  |       verify(timerBuilder).tag("method", "findAll"); | ||||||
|  |  | ||||||
|  |       verify(timer, only()).record(any(Supplier.class)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Nested | ||||||
|  |   class ForQueryableMutableStore { | ||||||
|  |  | ||||||
|  |     @Mock | ||||||
|  |     private QueryableMutableStore<User> queryableMutableStore; | ||||||
|  |     @Mock | ||||||
|  |     private QueryableMutableStore.MutableQuery<User, ?> query; | ||||||
|  |  | ||||||
|  |     @BeforeEach | ||||||
|  |     void setUpStore() { | ||||||
|  |       when(queryableMutableStore.query()) | ||||||
|  |         .thenAnswer(i -> query); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void shouldMeasureFinalCall() { | ||||||
|  |       when(query.orderBy(any(), any(QueryableStore.Order.class))) | ||||||
|  |         .thenAnswer(i -> query); | ||||||
|  |  | ||||||
|  |       StoreInvocationMicrometerWrapper handler = | ||||||
|  |         new StoreInvocationMicrometerWrapper( | ||||||
|  |           "queryable", | ||||||
|  |           User.class, | ||||||
|  |           new String[]{"intergalactic", "hog"}, | ||||||
|  |           queryableMutableStore, | ||||||
|  |           meterRegistry, | ||||||
|  |           () -> timerBuilder | ||||||
|  |         ); | ||||||
|  |       QueryableMutableStore<User> store = (QueryableMutableStore) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{QueryableMutableStore.class}, handler); | ||||||
|  |       store | ||||||
|  |         .query() | ||||||
|  |         .orderBy(new QueryableStore.IdQueryField(), QueryableStore.Order.DESC) | ||||||
|  |         .retain(100); | ||||||
|  |  | ||||||
|  |       verify(timerBuilder).description("Time taken for store operation in queryable"); | ||||||
|  |       verify(timerBuilder).tag("method", "retain"); | ||||||
|  |  | ||||||
|  |       verify(timer, only()).record(any(Supplier.class)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; | |||||||
| import org.junit.jupiter.api.io.TempDir; | import org.junit.jupiter.api.io.TempDir; | ||||||
| import sonia.scm.repository.Repository; | import sonia.scm.repository.Repository; | ||||||
| import sonia.scm.store.QueryableMaintenanceStore; | import sonia.scm.store.QueryableMaintenanceStore; | ||||||
|  | import sonia.scm.store.QueryableMutableStore; | ||||||
| import sonia.scm.user.User; | import sonia.scm.user.User; | ||||||
|  |  | ||||||
| import java.nio.file.Path; | import java.nio.file.Path; | ||||||
| @@ -56,7 +57,7 @@ class SQLiteParallelizationTest { | |||||||
|     ExecutorService executor = Executors.newFixedThreadPool(numThreads); |     ExecutorService executor = Executors.newFixedThreadPool(numThreads); | ||||||
|     List<Future<?>> futures = new ArrayList<>(); |     List<Future<?>> futures = new ArrayList<>(); | ||||||
|  |  | ||||||
|     SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |     QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|  |  | ||||||
|     for (int i = 0; i < numThreads; i++) { |     for (int i = 0; i < numThreads; i++) { | ||||||
|       final String userId = "user-" + i; |       final String userId = "user-" + i; | ||||||
|   | |||||||
| @@ -88,7 +88,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldPutObjectWithAutoIncrementId() { |     void shouldPutObjectWithAutoIncrementId() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString, IdGenerator.AUTO_INCREMENT).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString, IdGenerator.AUTO_INCREMENT).forClassWithIds(Spaceship.class); | ||||||
|       store.put(new Spaceship("42")); |       store.put(new Spaceship("42")); | ||||||
|       store.put(new Spaceship("23")); |       store.put(new Spaceship("23")); | ||||||
|  |  | ||||||
| @@ -101,7 +101,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldPutObjectWithGivenIdsThoughAutoIncrementActivated() { |     void shouldPutObjectWithGivenIdsThoughAutoIncrementActivated() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString, IdGenerator.AUTO_INCREMENT).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString, IdGenerator.AUTO_INCREMENT).forClassWithIds(Spaceship.class); | ||||||
|       store.put("42", new Spaceship("42", SQLiteQueryableStoreTest.Range.INTER_GALACTIC)); |       store.put("42", new Spaceship("42", SQLiteQueryableStoreTest.Range.INTER_GALACTIC)); | ||||||
|       store.put("23", new Spaceship("23", SQLiteQueryableStoreTest.Range.SOLAR_SYSTEM)); |       store.put("23", new Spaceship("23", SQLiteQueryableStoreTest.Range.SOLAR_SYSTEM)); | ||||||
|  |  | ||||||
| @@ -148,7 +148,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldRollback() throws SQLException { |     void shouldRollback() throws SQLException { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|  |  | ||||||
|       store.transactional(() -> { |       store.transactional(() -> { | ||||||
|         store.put("tricia", new User("trillian")); |         store.put("tricia", new User("trillian")); | ||||||
| @@ -163,7 +163,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldDisableAutoCommit() throws SQLException { |     void shouldDisableAutoCommit() throws SQLException { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|  |  | ||||||
|       store.transactional(() -> { |       store.transactional(() -> { | ||||||
|         store.put("tricia", new User("trillian")); |         store.put("tricia", new User("trillian")); | ||||||
| @@ -192,7 +192,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldGetObjectWithoutParent() { |     void shouldGetObjectWithoutParent() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
|       User tricia = store.get("tricia"); |       User tricia = store.get("tricia"); | ||||||
| @@ -205,7 +205,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldReturnForNotExistingValue() { |     void shouldReturnForNotExistingValue() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       User earth = store.get("earth"); |       User earth = store.get("earth"); | ||||||
|  |  | ||||||
|       assertThat(earth) |       assertThat(earth) | ||||||
| @@ -215,7 +215,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|     @Test |     @Test | ||||||
|     void shouldGetObjectWithSingleParent() { |     void shouldGetObjectWithSingleParent() { | ||||||
|       new StoreTestBuilder(connectionString, "sonia.Group").withIds("1337").put("tricia", new User("McMillan")); |       new StoreTestBuilder(connectionString, "sonia.Group").withIds("1337").put("tricia", new User("McMillan")); | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group").withIds("42"); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group").withIds("42"); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
|       User tricia = store.get("tricia"); |       User tricia = store.get("tricia"); | ||||||
| @@ -229,7 +229,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|     @Test |     @Test | ||||||
|     void shouldGetObjectWithMultipleParents() { |     void shouldGetObjectWithMultipleParents() { | ||||||
|       new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "1337").put("tricia", new User("McMillan")); |       new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "1337").put("tricia", new User("McMillan")); | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
|       User tricia = store.get("tricia"); |       User tricia = store.get("tricia"); | ||||||
| @@ -243,7 +243,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|     @Test |     @Test | ||||||
|     void shouldGetAllForSingleEntry() { |     void shouldGetAllForSingleEntry() { | ||||||
|       new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "1337").put("tricia", new User("McMillan")); |       new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "1337").put("tricia", new User("McMillan")); | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
|       Map<String, User> users = store.getAll(); |       Map<String, User> users = store.getAll(); | ||||||
| @@ -257,7 +257,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldGetAllForMultipleEntries() { |     void shouldGetAllForMultipleEntries() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); | ||||||
|       store.put("dent", new User("arthur")); |       store.put("dent", new User("arthur")); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
| @@ -280,7 +280,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldUseIdFromItemOnPut() { |     void shouldUseIdFromItemOnPut() { | ||||||
|       SQLiteQueryableMutableStore<SpaceshipWithId> store = new StoreTestBuilder(connectionString).forClassWithIds(SpaceshipWithId.class); |       QueryableMutableStore<SpaceshipWithId> store = new StoreTestBuilder(connectionString).forClassWithIds(SpaceshipWithId.class); | ||||||
|  |  | ||||||
|       String id = store.put(new SpaceshipWithId("Heart of Gold", 42)); |       String id = store.put(new SpaceshipWithId("Heart of Gold", 42)); | ||||||
|       SpaceshipWithId spaceship = store.get("Heart of Gold"); |       SpaceshipWithId spaceship = store.get("Heart of Gold"); | ||||||
| @@ -291,7 +291,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldSetNewIdInItemOnPut() { |     void shouldSetNewIdInItemOnPut() { | ||||||
|       SQLiteQueryableMutableStore<SpaceshipWithId> store = new StoreTestBuilder(connectionString).forClassWithIds(SpaceshipWithId.class); |       QueryableMutableStore<SpaceshipWithId> store = new StoreTestBuilder(connectionString).forClassWithIds(SpaceshipWithId.class); | ||||||
|  |  | ||||||
|       String id = store.put(new SpaceshipWithId()); |       String id = store.put(new SpaceshipWithId()); | ||||||
|       SpaceshipWithId spaceship = store.get(id); |       SpaceshipWithId spaceship = store.get(id); | ||||||
| @@ -302,7 +302,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldStoreWithNewIdAfterManualChange() { |     void shouldStoreWithNewIdAfterManualChange() { | ||||||
|       SQLiteQueryableMutableStore<SpaceshipWithId> store = new StoreTestBuilder(connectionString).forClassWithIds(SpaceshipWithId.class); |       QueryableMutableStore<SpaceshipWithId> store = new StoreTestBuilder(connectionString).forClassWithIds(SpaceshipWithId.class); | ||||||
|  |  | ||||||
|       store.put(new SpaceshipWithId("Heart of Gold", 42)); |       store.put(new SpaceshipWithId("Heart of Gold", 42)); | ||||||
|       SpaceshipWithId spaceship = store.get("Heart of Gold"); |       SpaceshipWithId spaceship = store.get("Heart of Gold"); | ||||||
| @@ -321,9 +321,9 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|   class Clear { |   class Clear { | ||||||
|     @Test |     @Test | ||||||
|     void shouldClear() { |     void shouldClear() { | ||||||
|       SQLiteQueryableMutableStore<User> uneffectedStore = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "1337"); |       QueryableMutableStore<User> uneffectedStore = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "1337"); | ||||||
|       uneffectedStore.put("tricia", new User("McMillan")); |       uneffectedStore.put("tricia", new User("McMillan")); | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
|       store.clear(); |       store.clear(); | ||||||
| @@ -337,7 +337,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|   class Remove { |   class Remove { | ||||||
|     @Test |     @Test | ||||||
|     void shouldRemove() { |     void shouldRemove() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Company", "sonia.Group").withIds("cloudogu", "42"); | ||||||
|       store.put("dent", new User("arthur")); |       store.put("dent", new User("arthur")); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
| @@ -351,7 +351,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|   class DeleteAll { |   class DeleteAll { | ||||||
|     @Test |     @Test | ||||||
|     void shouldDeleteAllInStoreWithoutSubsequentQuery() { |     void shouldDeleteAllInStoreWithoutSubsequentQuery() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       store.put("1", new Spaceship("1")); |       store.put("1", new Spaceship("1")); | ||||||
|       store.put("2", new Spaceship("2")); |       store.put("2", new Spaceship("2")); | ||||||
|       store.put("3", new Spaceship("3")); |       store.put("3", new Spaceship("3")); | ||||||
| @@ -365,7 +365,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldOnlyDeleteElementsMatchingTheQuery() { |     void shouldOnlyDeleteElementsMatchingTheQuery() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       store.put("1", new Spaceship("1")); |       store.put("1", new Spaceship("1")); | ||||||
|       store.put("2", new Spaceship("2")); |       store.put("2", new Spaceship("2")); | ||||||
|       store.put("3", new Spaceship("3")); |       store.put("3", new Spaceship("3")); | ||||||
| @@ -383,9 +383,9 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|       StoreTestBuilder spaceshipStoreBuilder = new StoreTestBuilder(connectionString); |       StoreTestBuilder spaceshipStoreBuilder = new StoreTestBuilder(connectionString); | ||||||
|       StoreTestBuilder crewmateStoreBuilder = new StoreTestBuilder(connectionString, "Spaceship"); |       StoreTestBuilder crewmateStoreBuilder = new StoreTestBuilder(connectionString, "Spaceship"); | ||||||
|       try ( |       try ( | ||||||
|         SQLiteQueryableMutableStore<Spaceship> spaceshipStore = spaceshipStoreBuilder.forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> spaceshipStore = spaceshipStoreBuilder.forClassWithIds(Spaceship.class); | ||||||
|         SQLiteQueryableMutableStore<Crewmate> crewmateStoreForShipOne = crewmateStoreBuilder.forClassWithIds(Crewmate.class, "1"); |         QueryableMutableStore<Crewmate> crewmateStoreForShipOne = crewmateStoreBuilder.forClassWithIds(Crewmate.class, "1"); | ||||||
|         SQLiteQueryableMutableStore<Crewmate> crewmateStoreForShipTwo = crewmateStoreBuilder.forClassWithIds(Crewmate.class, "2") |         QueryableMutableStore<Crewmate> crewmateStoreForShipTwo = crewmateStoreBuilder.forClassWithIds(Crewmate.class, "2") | ||||||
|       ) { |       ) { | ||||||
|         Spaceship spaceshipOne = new Spaceship("1"); |         Spaceship spaceshipOne = new Spaceship("1"); | ||||||
|         Spaceship spaceshipTwo = new Spaceship("2"); |         Spaceship spaceshipTwo = new Spaceship("2"); | ||||||
| @@ -408,7 +408,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|   class Retain { |   class Retain { | ||||||
|     @Test |     @Test | ||||||
|     void shouldRetainOneWithAscendingOrder() { |     void shouldRetainOneWithAscendingOrder() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       store.put("1", new Spaceship("1")); |       store.put("1", new Spaceship("1")); | ||||||
|       store.put("2", new Spaceship("2")); |       store.put("2", new Spaceship("2")); | ||||||
|       store.put("3", new Spaceship("3")); |       store.put("3", new Spaceship("3")); | ||||||
| @@ -423,7 +423,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldThrowIllegalArgumentExceptionIfKeptElementsIsNegative() { |     void shouldThrowIllegalArgumentExceptionIfKeptElementsIsNegative() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       store.put("1", new Spaceship("1")); |       store.put("1", new Spaceship("1")); | ||||||
|       store.put("2", new Spaceship("2")); |       store.put("2", new Spaceship("2")); | ||||||
|       store.put("3", new Spaceship("3")); |       store.put("3", new Spaceship("3")); | ||||||
| @@ -436,7 +436,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldRetainOneWithDescendingOrder() { |     void shouldRetainOneWithDescendingOrder() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       store.put("1", new Spaceship("1")); |       store.put("1", new Spaceship("1")); | ||||||
|       store.put("2", new Spaceship("2")); |       store.put("2", new Spaceship("2")); | ||||||
|       store.put("3", new Spaceship("3")); |       store.put("3", new Spaceship("3")); | ||||||
| @@ -451,7 +451,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldDeleteUnselectedEntitiesAndRetainKeptElementsFromTheSelectedOnes() { |     void shouldDeleteUnselectedEntitiesAndRetainKeptElementsFromTheSelectedOnes() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|  |  | ||||||
|       Spaceship spaceshipOne = new Spaceship("LazyShip"); |       Spaceship spaceshipOne = new Spaceship("LazyShip"); | ||||||
|       Spaceship spaceshipTwo = new Spaceship("Biblical Ship"); |       Spaceship spaceshipTwo = new Spaceship("Biblical Ship"); | ||||||
| @@ -477,7 +477,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldRetainEverythingIfKeptElementsHigherThanContentQuantity() { |     void shouldRetainEverythingIfKeptElementsHigherThanContentQuantity() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       store.put("1", new Spaceship("1")); |       store.put("1", new Spaceship("1")); | ||||||
|       store.put("2", new Spaceship("2")); |       store.put("2", new Spaceship("2")); | ||||||
|       store.put("3", new Spaceship("3")); |       store.put("3", new Spaceship("3")); | ||||||
| @@ -494,9 +494,9 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|       StoreTestBuilder spaceshipStoreBuilder = new StoreTestBuilder(connectionString); |       StoreTestBuilder spaceshipStoreBuilder = new StoreTestBuilder(connectionString); | ||||||
|       StoreTestBuilder crewmateStoreBuilder = new StoreTestBuilder(connectionString, "Spaceship"); |       StoreTestBuilder crewmateStoreBuilder = new StoreTestBuilder(connectionString, "Spaceship"); | ||||||
|       try ( |       try ( | ||||||
|         SQLiteQueryableMutableStore<Spaceship> spaceshipStore = spaceshipStoreBuilder.forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> spaceshipStore = spaceshipStoreBuilder.forClassWithIds(Spaceship.class); | ||||||
|         SQLiteQueryableMutableStore<Crewmate> crewmateStoreForShipOne = crewmateStoreBuilder.forClassWithIds(Crewmate.class, "1"); |         QueryableMutableStore<Crewmate> crewmateStoreForShipOne = crewmateStoreBuilder.forClassWithIds(Crewmate.class, "1"); | ||||||
|         SQLiteQueryableMutableStore<Crewmate> crewmateStoreForShipTwo = crewmateStoreBuilder.forClassWithIds(Crewmate.class, "2") |         QueryableMutableStore<Crewmate> crewmateStoreForShipTwo = crewmateStoreBuilder.forClassWithIds(Crewmate.class, "2") | ||||||
|       ) { |       ) { | ||||||
|         Spaceship spaceshipOne = new Spaceship("1"); |         Spaceship spaceshipOne = new Spaceship("1"); | ||||||
|         Spaceship spaceshipTwo = new Spaceship("2"); |         Spaceship spaceshipTwo = new Spaceship("2"); | ||||||
| @@ -567,7 +567,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|       Repository.class.getName() + ".class" |       Repository.class.getName() + ".class" | ||||||
|     ).readOnly("42"); |     ).readOnly("42"); | ||||||
|  |  | ||||||
|     SQLiteQueryableMutableStore<User> store = storeTestBuilder.withIds("23"); |     QueryableMutableStore<User> store = storeTestBuilder.withIds("23"); | ||||||
|     store.put("tricia", new User("trillian")); |     store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
|     assertThat(store.get("tricia")).isNotNull(); |     assertThat(store.get("tricia")).isNotNull(); | ||||||
| @@ -580,7 +580,7 @@ class SQLiteQueryableMutableStoreTest { | |||||||
|       Group.class.getName() + ".class" |       Group.class.getName() + ".class" | ||||||
|     ).readOnly("42"); |     ).readOnly("42"); | ||||||
|  |  | ||||||
|     SQLiteQueryableMutableStore<User> store = storeTestBuilder.withIds("42"); |     QueryableMutableStore<User> store = storeTestBuilder.withIds("42"); | ||||||
|     store.put("tricia", new User("trillian")); |     store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
|     assertThat(store.get("tricia")).isNotNull(); |     assertThat(store.get("tricia")).isNotNull(); | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ import sonia.scm.store.Operator; | |||||||
| import sonia.scm.store.QueryableMaintenanceStore; | import sonia.scm.store.QueryableMaintenanceStore; | ||||||
| import sonia.scm.store.QueryableMaintenanceStore.MaintenanceIterator; | import sonia.scm.store.QueryableMaintenanceStore.MaintenanceIterator; | ||||||
| import sonia.scm.store.QueryableMaintenanceStore.MaintenanceStoreEntry; | import sonia.scm.store.QueryableMaintenanceStore.MaintenanceStoreEntry; | ||||||
|  | import sonia.scm.store.QueryableMutableStore; | ||||||
| import sonia.scm.store.QueryableStore; | import sonia.scm.store.QueryableStore; | ||||||
| import sonia.scm.user.User; | import sonia.scm.user.User; | ||||||
|  |  | ||||||
| @@ -71,7 +72,7 @@ class SQLiteQueryableStoreTest { | |||||||
|       @ParameterizedTest |       @ParameterizedTest | ||||||
|       @ValueSource(strings = {"*Of*", "Heart*Gold", "H*", "*d", "*Heart Of Gold*", "Heart Of Gold", "Heart Of *Gold"}) |       @ValueSource(strings = {"*Of*", "Heart*Gold", "H*", "*d", "*Heart Of Gold*", "Heart Of Gold", "Heart Of *Gold"}) | ||||||
|       void shouldWorkWithLikes(String searchString) { |       void shouldWorkWithLikes(String searchString) { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Space Shuttle", Range.SOLAR_SYSTEM)); |         store.put(new Spaceship("Space Shuttle", Range.SOLAR_SYSTEM)); | ||||||
|         store.put(new Spaceship("Heart Of Gold", Range.INTER_GALACTIC)); |         store.put(new Spaceship("Heart Of Gold", Range.INTER_GALACTIC)); | ||||||
|  |  | ||||||
| @@ -87,7 +88,7 @@ class SQLiteQueryableStoreTest { | |||||||
|       @ParameterizedTest |       @ParameterizedTest | ||||||
|       @ValueSource(strings = {"Of", "*of*", "heart of gold"}) |       @ValueSource(strings = {"Of", "*of*", "heart of gold"}) | ||||||
|       void shouldNotFindNotMatchingValuesWithLike() { |       void shouldNotFindNotMatchingValuesWithLike() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Space Shuttle", Range.SOLAR_SYSTEM)); |         store.put(new Spaceship("Space Shuttle", Range.SOLAR_SYSTEM)); | ||||||
|         store.put(new Spaceship("Heart Of Gold", Range.INTER_GALACTIC)); |         store.put(new Spaceship("Heart Of Gold", Range.INTER_GALACTIC)); | ||||||
|  |  | ||||||
| @@ -100,7 +101,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldWorkWithEnums() { |       void shouldWorkWithEnums() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Space Shuttle", Range.SOLAR_SYSTEM)); |         store.put(new Spaceship("Space Shuttle", Range.SOLAR_SYSTEM)); | ||||||
|         store.put(new Spaceship("Heart Of Gold", Range.INTER_GALACTIC)); |         store.put(new Spaceship("Heart Of Gold", Range.INTER_GALACTIC)); | ||||||
|  |  | ||||||
| @@ -114,7 +115,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldWorkWithLongs() { |       void shouldWorkWithLongs() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         User trillian = new User("trillian", "McMillan", "tricia@hog.org"); |         User trillian = new User("trillian", "McMillan", "tricia@hog.org"); | ||||||
|         trillian.setCreationDate(10000000000L); |         trillian.setCreationDate(10000000000L); | ||||||
|         store.put(trillian); |         store.put(trillian); | ||||||
| @@ -134,7 +135,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldWorkWithIntegers() { |       void shouldWorkWithIntegers() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         User trillian = new User("trillian", "McMillan", "tricia@hog.org"); |         User trillian = new User("trillian", "McMillan", "tricia@hog.org"); | ||||||
|         trillian.setCreationDate(42L); |         trillian.setCreationDate(42L); | ||||||
|         store.put(trillian); |         store.put(trillian); | ||||||
| @@ -154,7 +155,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldWorkWithNumberCollection() { |       void shouldWorkWithNumberCollection() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         User trillian = new User("trillian", "McMillan", "tricia@hog.org"); |         User trillian = new User("trillian", "McMillan", "tricia@hog.org"); | ||||||
|         trillian.setActive(true); |         trillian.setActive(true); | ||||||
|         store.put(trillian); |         store.put(trillian); | ||||||
| @@ -174,7 +175,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldCountAndWorkWithNumberCollection() { |       void shouldCountAndWorkWithNumberCollection() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         User trillian = new User("trillian", "McMillan", "tricia@hog.org"); |         User trillian = new User("trillian", "McMillan", "tricia@hog.org"); | ||||||
|         trillian.setActive(true); |         trillian.setActive(true); | ||||||
|         store.put(trillian); |         store.put(trillian); | ||||||
| @@ -197,7 +198,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleCollections() { |       void shouldHandleCollections() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Spaceshuttle", "Buzz", "Anndre")); |         store.put(new Spaceship("Spaceshuttle", "Buzz", "Anndre")); | ||||||
|         store.put(new Spaceship("Heart Of Gold", "Trillian", "Arthur", "Ford", "Zaphod", "Marvin")); |         store.put(new Spaceship("Heart Of Gold", "Trillian", "Arthur", "Ford", "Zaphod", "Marvin")); | ||||||
|  |  | ||||||
| @@ -210,7 +211,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldCountAndHandleCollections() { |       void shouldCountAndHandleCollections() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Spaceshuttle", "Buzz", "Anndre")); |         store.put(new Spaceship("Spaceshuttle", "Buzz", "Anndre")); | ||||||
|         store.put(new Spaceship("Heart Of Gold", "Trillian", "Arthur", "Ford", "Zaphod", "Marvin")); |         store.put(new Spaceship("Heart Of Gold", "Trillian", "Arthur", "Ford", "Zaphod", "Marvin")); | ||||||
|  |  | ||||||
| @@ -223,7 +224,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldCountWithoutConditions() { |       void shouldCountWithoutConditions() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Spaceshuttle", "Buzz", "Anndre")); |         store.put(new Spaceship("Spaceshuttle", "Buzz", "Anndre")); | ||||||
|         store.put(new Spaceship("Heart Of Gold", "Trillian", "Arthur", "Ford", "Zaphod", "Marvin")); |         store.put(new Spaceship("Heart Of Gold", "Trillian", "Arthur", "Ford", "Zaphod", "Marvin")); | ||||||
|  |  | ||||||
| @@ -234,7 +235,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleEmptyCollectionWithMaxString() { |       void shouldHandleEmptyCollectionWithMaxString() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         Integer result = store.query().max( |         Integer result = store.query().max( | ||||||
|           SPACESHIP_FLIGHT_COUNT |           SPACESHIP_FLIGHT_COUNT | ||||||
|         ); |         ); | ||||||
| @@ -245,7 +246,7 @@ class SQLiteQueryableStoreTest { | |||||||
|       @Nested |       @Nested | ||||||
|       class ForAggregations { |       class ForAggregations { | ||||||
|  |  | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store; |         QueryableMutableStore<Spaceship> store; | ||||||
|  |  | ||||||
|         @BeforeEach |         @BeforeEach | ||||||
|         void createData() { |         void createData() { | ||||||
| @@ -318,7 +319,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleCollectionSize() { |       void shouldHandleCollectionSize() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Spaceshuttle", "Buzz", "Anndre")); |         store.put(new Spaceship("Spaceshuttle", "Buzz", "Anndre")); | ||||||
|         store.put(new Spaceship("Heart of Gold", "Trillian", "Arthur", "Ford", "Zaphod", "Marvin")); |         store.put(new Spaceship("Heart of Gold", "Trillian", "Arthur", "Ford", "Zaphod", "Marvin")); | ||||||
|         store.put(new Spaceship("MillenniumFalcon")); |         store.put(new Spaceship("MillenniumFalcon")); | ||||||
| @@ -345,7 +346,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleMap() { |       void shouldHandleMap() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Spaceshuttle", Map.of("moon", true, "earth", true))); |         store.put(new Spaceship("Spaceshuttle", Map.of("moon", true, "earth", true))); | ||||||
|         store.put(new Spaceship("Heart of Gold", Map.of("vogon", true, "earth", true))); |         store.put(new Spaceship("Heart of Gold", Map.of("vogon", true, "earth", true))); | ||||||
|         store.put(new Spaceship("MillenniumFalcon", Map.of("dagobah", false))); |         store.put(new Spaceship("MillenniumFalcon", Map.of("dagobah", false))); | ||||||
| @@ -365,7 +366,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldCountAndHandleMap() { |       void shouldCountAndHandleMap() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Spaceshuttle", Map.of("moon", true, "earth", true))); |         store.put(new Spaceship("Spaceshuttle", Map.of("moon", true, "earth", true))); | ||||||
|         store.put(new Spaceship("Heart of Gold", Map.of("vogon", true, "earth", true))); |         store.put(new Spaceship("Heart of Gold", Map.of("vogon", true, "earth", true))); | ||||||
|         store.put(new Spaceship("MillenniumFalcon", Map.of("dagobah", false))); |         store.put(new Spaceship("MillenniumFalcon", Map.of("dagobah", false))); | ||||||
| @@ -386,7 +387,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleMapSize() { |       void shouldHandleMapSize() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         store.put(new Spaceship("Spaceshuttle", Map.of("moon", true, "earth", true))); |         store.put(new Spaceship("Spaceshuttle", Map.of("moon", true, "earth", true))); | ||||||
|         store.put(new Spaceship("Heart of Gold", Map.of("vogon", true, "earth", true, "dagobah", true))); |         store.put(new Spaceship("Heart of Gold", Map.of("vogon", true, "earth", true, "dagobah", true))); | ||||||
|         store.put(new Spaceship("MillenniumFalcon", Map.of())); |         store.put(new Spaceship("MillenniumFalcon", Map.of())); | ||||||
| @@ -413,7 +414,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldRetrieveTime() { |       void shouldRetrieveTime() { | ||||||
|         SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |         QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|         Spaceship spaceshuttle = new Spaceship("Spaceshuttle", Range.SOLAR_SYSTEM); |         Spaceship spaceshuttle = new Spaceship("Spaceshuttle", Range.SOLAR_SYSTEM); | ||||||
|         spaceshuttle.setInServiceSince(Instant.parse("1981-04-12T10:00:00Z")); |         spaceshuttle.setInServiceSince(Instant.parse("1981-04-12T10:00:00Z")); | ||||||
|         store.put(spaceshuttle); |         store.put(spaceshuttle); | ||||||
| @@ -445,7 +446,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldLimitQuery() { |       void shouldLimitQuery() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put(new User("trillian", "McMillan", "tricia@hog.org")); |         store.put(new User("trillian", "McMillan", "tricia@hog.org")); | ||||||
|         store.put(new User("arthur", "Dent", "arthur@hog.org")); |         store.put(new User("arthur", "Dent", "arthur@hog.org")); | ||||||
|         store.put(new User("zaphod", "Beeblebrox", "zaphod@hog.org")); |         store.put(new User("zaphod", "Beeblebrox", "zaphod@hog.org")); | ||||||
| @@ -461,7 +462,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldOrderResults() { |       void shouldOrderResults() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put(new User("trillian", "McMillan", "tricia@hog.org")); |         store.put(new User("trillian", "McMillan", "tricia@hog.org")); | ||||||
|         store.put(new User("arthur", "Dent", "arthur@hog.org")); |         store.put(new User("arthur", "Dent", "arthur@hog.org")); | ||||||
|         store.put(new User("zaphod", "Beeblebrox Head 1", "zaphod1@hog.org")); |         store.put(new User("zaphod", "Beeblebrox Head 1", "zaphod1@hog.org")); | ||||||
| @@ -480,7 +481,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldOrderResultsAsNumbers() { |       void shouldOrderResultsAsNumbers() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put(new User("1")); |         store.put(new User("1")); | ||||||
|         store.put(new User("2")); |         store.put(new User("2")); | ||||||
|         store.put(new User("10")); |         store.put(new User("10")); | ||||||
| @@ -498,7 +499,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldOrderResultsAsStrings() { |       void shouldOrderResultsAsStrings() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put(new User("1")); |         store.put(new User("1")); | ||||||
|         store.put(new User("2")); |         store.put(new User("2")); | ||||||
|         store.put(new User("10")); |         store.put(new User("10")); | ||||||
| @@ -519,7 +520,7 @@ class SQLiteQueryableStoreTest { | |||||||
|     class QueryLogicalHandling { |     class QueryLogicalHandling { | ||||||
|       @Test |       @Test | ||||||
|       void shouldQueryForId() { |       void shouldQueryForId() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put("1", new User("trillian", "Tricia", "tricia@hog.org")); |         store.put("1", new User("trillian", "Tricia", "tricia@hog.org")); | ||||||
|         store.put("2", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); |         store.put("2", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); | ||||||
|         store.put("3", new User("arthur", "Arthur Dent", "arthur@hog.org")); |         store.put("3", new User("arthur", "Arthur Dent", "arthur@hog.org")); | ||||||
| @@ -536,7 +537,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldQueryForIdAndOrderByDESC() { |       void shouldQueryForIdAndOrderByDESC() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put("1", new User("trish", "Tricia", "tricia@hog.org")); |         store.put("1", new User("trish", "Tricia", "tricia@hog.org")); | ||||||
|         store.put("2", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); |         store.put("2", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); | ||||||
|         store.put("3", new User("arthur", "Arthur Dent", "arthur@hog.org")); |         store.put("3", new User("arthur", "Arthur Dent", "arthur@hog.org")); | ||||||
| @@ -551,7 +552,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldOrderIdsAndPayload() { |       void shouldOrderIdsAndPayload() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put("1", new User("trish", "Tricia", "tricia@hog.org")); |         store.put("1", new User("trish", "Tricia", "tricia@hog.org")); | ||||||
|         store.put("2", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); |         store.put("2", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); | ||||||
|         store.put("3", new User("trillian", "Arthur Dent", "arthur@hog.org")); |         store.put("3", new User("trillian", "Arthur Dent", "arthur@hog.org")); | ||||||
| @@ -576,7 +577,7 @@ class SQLiteQueryableStoreTest { | |||||||
|           .withIds("1337") |           .withIds("1337") | ||||||
|           .put("tricia", new User("trillian", "Trillian McMillan", "tricia@hog.org")); |           .put("tricia", new User("trillian", "Trillian McMillan", "tricia@hog.org")); | ||||||
|  |  | ||||||
|         SQLiteQueryableStore<User> store = new StoreTestBuilder(connectionString, Group.class.getName()).withIds(); |         QueryableStore<User> store = new StoreTestBuilder(connectionString, Group.class.getName()).withIds(); | ||||||
|  |  | ||||||
|         List<User> all = store.query( |         List<User> all = store.query( | ||||||
|             GROUP.eq("42") |             GROUP.eq("42") | ||||||
| @@ -590,7 +591,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleContainsCondition() { |       void shouldHandleContainsCondition() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); |         store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); | ||||||
|         store.put("McMillan", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); |         store.put("McMillan", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); | ||||||
|         store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); |         store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); | ||||||
| @@ -605,7 +606,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleIsNullCondition() { |       void shouldHandleIsNullCondition() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put("tricia", new User("trillian", null, "tricia@hog.org")); |         store.put("tricia", new User("trillian", null, "tricia@hog.org")); | ||||||
|         store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); |         store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); | ||||||
|  |  | ||||||
| @@ -621,7 +622,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleNotNullCondition() { |       void shouldHandleNotNullCondition() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put("tricia", new User("trillian", null, "tricia@hog.org")); |         store.put("tricia", new User("trillian", null, "tricia@hog.org")); | ||||||
|         store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); |         store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); | ||||||
|  |  | ||||||
| @@ -637,7 +638,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleOr() { |       void shouldHandleOr() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); |         store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); | ||||||
|         store.put("McMillan", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); |         store.put("McMillan", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); | ||||||
|         store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); |         store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); | ||||||
| @@ -658,7 +659,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleOrWithMultipleStores() { |       void shouldHandleOrWithMultipleStores() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group").withIds("CoolGroup"); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group").withIds("CoolGroup"); | ||||||
|         User tricia = new User("trillian", "Tricia", "tricia@hog.org"); |         User tricia = new User("trillian", "Tricia", "tricia@hog.org"); | ||||||
|         User mcmillan = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); |         User mcmillan = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); | ||||||
|         User dent = new User("arthur", "Arthur Dent", "arthur@hog.org"); |         User dent = new User("arthur", "Arthur Dent", "arthur@hog.org"); | ||||||
| @@ -666,7 +667,7 @@ class SQLiteQueryableStoreTest { | |||||||
|         store.put("McMillan", mcmillan); |         store.put("McMillan", mcmillan); | ||||||
|         store.put("dent", dent); |         store.put("dent", dent); | ||||||
|  |  | ||||||
|         SQLiteQueryableMutableStore<User> parallelStore = new StoreTestBuilder(connectionString, "sonia.Group").withIds("LameGroup"); |         QueryableMutableStore<User> parallelStore = new StoreTestBuilder(connectionString, "sonia.Group").withIds("LameGroup"); | ||||||
|         parallelStore.put("tricia", new User("trillian", "Trillian IAMINAPARALLELSTORE McMillan", "mcmillan@gmail.com")); |         parallelStore.put("tricia", new User("trillian", "Trillian IAMINAPARALLELSTORE McMillan", "mcmillan@gmail.com")); | ||||||
|  |  | ||||||
|         List<User> result = store.query( |         List<User> result = store.query( | ||||||
| @@ -680,7 +681,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleGroup() { |       void shouldHandleGroup() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") | ||||||
|           .withIds("42"); |           .withIds("42"); | ||||||
|         store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); |         store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); | ||||||
|         new StoreTestBuilder(connectionString, "sonia.Group") |         new StoreTestBuilder(connectionString, "sonia.Group") | ||||||
| @@ -696,7 +697,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleGroupWithCondition() { |       void shouldHandleGroupWithCondition() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") | ||||||
|           .withIds("42"); |           .withIds("42"); | ||||||
|         store |         store | ||||||
|           .put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); |           .put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); | ||||||
| @@ -713,7 +714,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|       @Test |       @Test | ||||||
|       void shouldHandleInArrayCondition() { |       void shouldHandleInArrayCondition() { | ||||||
|         SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |         QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|         store.put(new User("trillian", "McMillan", "tricia@hog.org")); |         store.put(new User("trillian", "McMillan", "tricia@hog.org")); | ||||||
|         store.put(new User("arthur", "Dent", "arthur@hog.org")); |         store.put(new User("arthur", "Dent", "arthur@hog.org")); | ||||||
|         store.put(new User("zaphod", "Beeblebrox", "zaphod@hog.org")); |         store.put(new User("zaphod", "Beeblebrox", "zaphod@hog.org")); | ||||||
| @@ -731,7 +732,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldFindAllObjectsWithoutParentWithoutConditions() { |     void shouldFindAllObjectsWithoutParentWithoutConditions() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|  |  | ||||||
|       List<User> all = store.query().findAll(); |       List<User> all = store.query().findAll(); | ||||||
| @@ -741,7 +742,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldFindAllObjectsWithoutParentWithCondition() { |     void shouldFindAllObjectsWithoutParentWithCondition() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       store.put("tricia", new User("trillian")); |       store.put("tricia", new User("trillian")); | ||||||
|       store.put("dent", new User("arthur")); |       store.put("dent", new User("arthur")); | ||||||
|  |  | ||||||
| @@ -751,7 +752,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldFindAllObjectsWithOneParentAndMultipleConditions() { |     void shouldFindAllObjectsWithOneParentAndMultipleConditions() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group").withIds("CoolGroup"); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group").withIds("CoolGroup"); | ||||||
|       User tricia = new User("trillian", "Tricia", "tricia@hog.org"); |       User tricia = new User("trillian", "Tricia", "tricia@hog.org"); | ||||||
|       User mcmillan = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); |       User mcmillan = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); | ||||||
|       User dent = new User("arthur", "Arthur Dent", "arthur@hog.org"); |       User dent = new User("arthur", "Arthur Dent", "arthur@hog.org"); | ||||||
| @@ -770,7 +771,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldFindAllObjectsWithoutParentWithMultipleConditions() { |     void shouldFindAllObjectsWithoutParentWithMultipleConditions() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); |       store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); | ||||||
|       store.put("McMillan", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); |       store.put("McMillan", new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); | ||||||
|       store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); |       store.put("dent", new User("arthur", "Arthur Dent", "arthur@hog.org")); | ||||||
| @@ -786,7 +787,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldReturnIds() { |     void shouldReturnIds() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, Spaceship.class.getName()) |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, Spaceship.class.getName()) | ||||||
|         .withIds("hog"); |         .withIds("hog"); | ||||||
|       store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); |       store.put("tricia", new User("trillian", "Tricia", "tricia@hog.org")); | ||||||
|  |  | ||||||
| @@ -807,13 +808,13 @@ class SQLiteQueryableStoreTest { | |||||||
|   class FindOne { |   class FindOne { | ||||||
|     @Test |     @Test | ||||||
|     void shouldReturnEmptyOptionalIfNoResultFound() { |     void shouldReturnEmptyOptionalIfNoResultFound() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       assertThat(store.query(SPACESHIP_NAME.eq("Heart Of Gold")).findOne()).isEmpty(); |       assertThat(store.query(SPACESHIP_NAME.eq("Heart Of Gold")).findOne()).isEmpty(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldReturnOneResultIfOneIsGiven() { |     void shouldReturnOneResultIfOneIsGiven() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       Spaceship expectedShip = new Spaceship("Heart Of Gold", Range.INNER_GALACTIC); |       Spaceship expectedShip = new Spaceship("Heart Of Gold", Range.INNER_GALACTIC); | ||||||
|       store.put(expectedShip); |       store.put(expectedShip); | ||||||
|       Spaceship ship = store.query(SPACESHIP_NAME.eq("Heart Of Gold")).findOne().get(); |       Spaceship ship = store.query(SPACESHIP_NAME.eq("Heart Of Gold")).findOne().get(); | ||||||
| @@ -823,7 +824,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldThrowErrorIfMoreThanOneResultIsSaved() { |     void shouldThrowErrorIfMoreThanOneResultIsSaved() { | ||||||
|       SQLiteQueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); |       QueryableMutableStore<Spaceship> store = new StoreTestBuilder(connectionString).forClassWithIds(Spaceship.class); | ||||||
|       Spaceship expectedShip = new Spaceship("Heart Of Gold", Range.INNER_GALACTIC); |       Spaceship expectedShip = new Spaceship("Heart Of Gold", Range.INNER_GALACTIC); | ||||||
|       Spaceship localShip = new Spaceship("Heart Of Gold", Range.SOLAR_SYSTEM); |       Spaceship localShip = new Spaceship("Heart Of Gold", Range.SOLAR_SYSTEM); | ||||||
|       store.put(expectedShip); |       store.put(expectedShip); | ||||||
| @@ -837,7 +838,7 @@ class SQLiteQueryableStoreTest { | |||||||
|   class FindFirst { |   class FindFirst { | ||||||
|     @Test |     @Test | ||||||
|     void shouldFindFirst() { |     void shouldFindFirst() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       User expectedUser = new User("trillian", "Tricia", "tricia@hog.org"); |       User expectedUser = new User("trillian", "Tricia", "tricia@hog.org"); | ||||||
|  |  | ||||||
|       store.put("1", expectedUser); |       store.put("1", expectedUser); | ||||||
| @@ -854,7 +855,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldFindFirstWithMatchingCondition() { |     void shouldFindFirstWithMatchingCondition() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       User expectedUser = new User("trillian", "Trillian McMillan", "mcmillan-alternate@gmail.com"); |       User expectedUser = new User("trillian", "Trillian McMillan", "mcmillan-alternate@gmail.com"); | ||||||
|  |  | ||||||
|       store.put("1", new User("trillian", "Tricia", "tricia@hog.org")); |       store.put("1", new User("trillian", "Tricia", "tricia@hog.org")); | ||||||
| @@ -874,7 +875,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldFindFirstWithMatchingLogicalCondition() { |     void shouldFindFirstWithMatchingLogicalCondition() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       User expectedUser = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); |       User expectedUser = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); | ||||||
|  |  | ||||||
|       store.put("1", new User("trillian-old", "Tricia", "tricia@hog.org")); |       store.put("1", new User("trillian-old", "Tricia", "tricia@hog.org")); | ||||||
| @@ -900,7 +901,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldReturnEmptyOptionalIfNoResultFound() { |     void shouldReturnEmptyOptionalIfNoResultFound() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       Optional<User> user = store.query( |       Optional<User> user = store.query( | ||||||
|           USER_NAME.eq("dave") |           USER_NAME.eq("dave") | ||||||
|         ) |         ) | ||||||
| @@ -933,7 +934,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldReturnDistinctValuesFromObject() { |     void shouldReturnDistinctValuesFromObject() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") | ||||||
|         .withIds(); |         .withIds(); | ||||||
|  |  | ||||||
|       List<Object[]> result = store.query().project(USER_NAME).distinct().findAll(); |       List<Object[]> result = store.query().project(USER_NAME).distinct().findAll(); | ||||||
| @@ -949,7 +950,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldReturnDistinctParentIds() { |     void shouldReturnDistinctParentIds() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") | ||||||
|         .withIds(); |         .withIds(); | ||||||
|  |  | ||||||
|       List<Object[]> result = store.query().project(GROUP).distinct().findAll(); |       List<Object[]> result = store.query().project(GROUP).distinct().findAll(); | ||||||
| @@ -963,7 +964,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldReturnDistinctCount() { |     void shouldReturnDistinctCount() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString, "sonia.Group") | ||||||
|         .withIds(); |         .withIds(); | ||||||
|  |  | ||||||
|       long count = store.query().project(GROUP).distinct().count(); |       long count = store.query().project(GROUP).distinct().count(); | ||||||
| @@ -976,11 +977,12 @@ class SQLiteQueryableStoreTest { | |||||||
|   class ForMaintenance { |   class ForMaintenance { | ||||||
|     @Test |     @Test | ||||||
|     void shouldUpdateRawJson() throws Exception { |     void shouldUpdateRawJson() throws Exception { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       User user = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); |       User user = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); | ||||||
|       store.put("1", user); |       store.put("1", user); | ||||||
|  |  | ||||||
|       try (MaintenanceIterator<User> iterator = store.iterateAll()) { |       QueryableMaintenanceStore<User> maintenanceStore = new StoreTestBuilder(connectionString).forMaintenanceWithSubIds(); | ||||||
|  |       try (MaintenanceIterator<User> iterator = maintenanceStore.iterateAll()) { | ||||||
|         assertThat(iterator.hasNext()).isTrue(); |         assertThat(iterator.hasNext()).isTrue(); | ||||||
|         MaintenanceStoreEntry<User> entry = iterator.next(); |         MaintenanceStoreEntry<User> entry = iterator.next(); | ||||||
|         assertThat(entry.getId()).isEqualTo("1"); |         assertThat(entry.getId()).isEqualTo("1"); | ||||||
| @@ -997,7 +999,7 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldUpdateRawJsonForItemWithParent() throws Exception { |     void shouldUpdateRawJsonForItemWithParent() throws Exception { | ||||||
|       SQLiteQueryableMutableStore<User> subStore = new StoreTestBuilder(connectionString, Group.class.getName()).withIds("hitchhiker"); |       QueryableMutableStore<User> subStore = new StoreTestBuilder(connectionString, Group.class.getName()).withIds("hitchhiker"); | ||||||
|       User user = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); |       User user = new User("trillian", "Trillian McMillan", "mcmillan@gmail.com"); | ||||||
|       subStore.put("1", user); |       subStore.put("1", user); | ||||||
|  |  | ||||||
| @@ -1019,11 +1021,12 @@ class SQLiteQueryableStoreTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void shouldRemoveFromIteratorWithoutParent() { |     void shouldRemoveFromIteratorWithoutParent() { | ||||||
|       SQLiteQueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); |       QueryableMutableStore<User> store = new StoreTestBuilder(connectionString).withIds(); | ||||||
|       store.put(new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); |       store.put(new User("trillian", "Trillian McMillan", "mcmillan@gmail.com")); | ||||||
|       store.put(new User("dent", "Arthur Dent", "dent@gmail.com")); |       store.put(new User("dent", "Arthur Dent", "dent@gmail.com")); | ||||||
|  |  | ||||||
|       for (MaintenanceIterator<User> iter = store.iterateAll(); iter.hasNext(); ) { |       QueryableMaintenanceStore<User> maintenanceStore = new StoreTestBuilder(connectionString).forMaintenanceWithSubIds(); | ||||||
|  |       for (MaintenanceIterator<User> iter = maintenanceStore.iterateAll(); iter.hasNext(); ) { | ||||||
|         MaintenanceStoreEntry<User> next = iter.next(); |         MaintenanceStoreEntry<User> next = iter.next(); | ||||||
|         if (next.get().getName().equals("dent")) { |         if (next.get().getName().equals("dent")) { | ||||||
|           iter.remove(); |           iter.remove(); | ||||||
| @@ -1039,11 +1042,11 @@ class SQLiteQueryableStoreTest { | |||||||
|     @Test |     @Test | ||||||
|     void shouldRemoveFromIteratorWithParents() { |     void shouldRemoveFromIteratorWithParents() { | ||||||
|       StoreTestBuilder testStoreBuilder = new StoreTestBuilder(connectionString, Repository.class.getName(), Group.class.getName()); |       StoreTestBuilder testStoreBuilder = new StoreTestBuilder(connectionString, Repository.class.getName(), Group.class.getName()); | ||||||
|       SQLiteQueryableMutableStore<User> hogStore = testStoreBuilder.withIds("42", "hog"); |       QueryableMutableStore<User> hogStore = testStoreBuilder.withIds("42", "hog"); | ||||||
|       hogStore.put("trisha", new User("trillian", "Trillian McMillan", "mcmillan@hog.com")); |       hogStore.put("trisha", new User("trillian", "Trillian McMillan", "mcmillan@hog.com")); | ||||||
|       hogStore.put("dent", new User("dent", "Arthur Dent", "dent@hog.com")); |       hogStore.put("dent", new User("dent", "Arthur Dent", "dent@hog.com")); | ||||||
|  |  | ||||||
|       SQLiteQueryableMutableStore<User> earthStore = testStoreBuilder.withIds("42", "earth"); |       QueryableMutableStore<User> earthStore = testStoreBuilder.withIds("42", "earth"); | ||||||
|       earthStore.put("dent", new User("dent", "Arthur Dent", "dent@gmail.com")); |       earthStore.put("dent", new User("dent", "Arthur Dent", "dent@gmail.com")); | ||||||
|  |  | ||||||
|       QueryableMaintenanceStore<User> store = testStoreBuilder.forMaintenanceWithSubIds("42"); |       QueryableMaintenanceStore<User> store = testStoreBuilder.forMaintenanceWithSubIds("42"); | ||||||
| @@ -1068,7 +1071,7 @@ class SQLiteQueryableStoreTest { | |||||||
|     @Test |     @Test | ||||||
|     void shouldReadAll() { |     void shouldReadAll() { | ||||||
|       StoreTestBuilder testStoreBuilder = new StoreTestBuilder(connectionString, Repository.class.getName()); |       StoreTestBuilder testStoreBuilder = new StoreTestBuilder(connectionString, Repository.class.getName()); | ||||||
|       SQLiteQueryableMutableStore<User> hogStore = testStoreBuilder.withIds("42"); |       QueryableMutableStore<User> hogStore = testStoreBuilder.withIds("42"); | ||||||
|       hogStore.put("trisha", new User("trillian", "Trillian McMillan", "mcmillan@hog.com")); |       hogStore.put("trisha", new User("trillian", "Trillian McMillan", "mcmillan@hog.com")); | ||||||
|       hogStore.put("dent", new User("dent", "Arthur Dent", "dent@hog.com")); |       hogStore.put("dent", new User("dent", "Arthur Dent", "dent@hog.com")); | ||||||
|  |  | ||||||
| @@ -1091,9 +1094,9 @@ class SQLiteQueryableStoreTest { | |||||||
|     @Test |     @Test | ||||||
|     void shouldReadAllWithMultipleIds() { |     void shouldReadAllWithMultipleIds() { | ||||||
|       StoreTestBuilder testStoreBuilder = new StoreTestBuilder(connectionString, Repository.class.getName(), Group.class.getName()); |       StoreTestBuilder testStoreBuilder = new StoreTestBuilder(connectionString, Repository.class.getName(), Group.class.getName()); | ||||||
|       SQLiteQueryableMutableStore<User> store1 = testStoreBuilder.withIds("42", "astronauts"); |       QueryableMutableStore<User> store1 = testStoreBuilder.withIds("42", "astronauts"); | ||||||
|       store1.put("trisha", new User("trillian", "Trillian McMillan", "mcmillan@hog.com")); |       store1.put("trisha", new User("trillian", "Trillian McMillan", "mcmillan@hog.com")); | ||||||
|       SQLiteQueryableMutableStore<User> store2 = testStoreBuilder.withIds("42", "earthlings"); |       QueryableMutableStore<User> store2 = testStoreBuilder.withIds("42", "earthlings"); | ||||||
|       store2.put("dent", new User("dent", "Arthur Dent", "dent@hog.com")); |       store2.put("dent", new User("dent", "Arthur Dent", "dent@hog.com")); | ||||||
|  |  | ||||||
|       QueryableMaintenanceStore<User> store = testStoreBuilder.forMaintenanceWithSubIds("42"); |       QueryableMaintenanceStore<User> store = testStoreBuilder.forMaintenanceWithSubIds("42"); | ||||||
| @@ -1128,7 +1131,7 @@ class SQLiteQueryableStoreTest { | |||||||
|         ) |         ) | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|       SQLiteQueryableMutableStore<User> hogStore = testStoreBuilder.withIds("42"); |       QueryableMaintenanceStore<User> hogStore = testStoreBuilder.forMaintenanceWithSubIds("42"); | ||||||
|       Collection<QueryableMaintenanceStore.Row<User>> allValues = hogStore.readAll(); |       Collection<QueryableMaintenanceStore.Row<User>> allValues = hogStore.readAll(); | ||||||
|       assertThat(allValues) |       assertThat(allValues) | ||||||
|         .extracting("value") |         .extracting("value") | ||||||
| @@ -1147,7 +1150,7 @@ class SQLiteQueryableStoreTest { | |||||||
|         ) |         ) | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|       SQLiteQueryableMutableStore<User> hogStore = testStoreBuilder.withIds("42"); |       QueryableMaintenanceStore<User> hogStore = testStoreBuilder.forMaintenanceWithSubIds("42"); | ||||||
|       Collection<QueryableMaintenanceStore.Row<User>> allValues = hogStore.readAll(); |       Collection<QueryableMaintenanceStore.Row<User>> allValues = hogStore.readAll(); | ||||||
|       assertThat(allValues) |       assertThat(allValues) | ||||||
|         .extracting("value") |         .extracting("value") | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ import sonia.scm.repository.RepositoryReadOnlyChecker; | |||||||
| import sonia.scm.security.UUIDKeyGenerator; | import sonia.scm.security.UUIDKeyGenerator; | ||||||
| import sonia.scm.store.IdGenerator; | import sonia.scm.store.IdGenerator; | ||||||
| import sonia.scm.store.QueryableMaintenanceStore; | import sonia.scm.store.QueryableMaintenanceStore; | ||||||
|  | import sonia.scm.store.QueryableMutableStore; | ||||||
| import sonia.scm.store.QueryableStore; | import sonia.scm.store.QueryableStore; | ||||||
| import sonia.scm.user.User; | import sonia.scm.user.User; | ||||||
|  |  | ||||||
| @@ -68,7 +69,7 @@ class StoreTestBuilder { | |||||||
|     this.parentClasses = parentClasses; |     this.parentClasses = parentClasses; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SQLiteQueryableMutableStore<User> withIds(String... ids) { |   QueryableMutableStore<User> withIds(String... ids) { | ||||||
|     return forClassWithIds(User.class, ids); |     return forClassWithIds(User.class, ids); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -86,7 +87,7 @@ class StoreTestBuilder { | |||||||
|     return createStoreFactory(User.class).getForMaintenance(User.class, ids); |     return createStoreFactory(User.class).getForMaintenance(User.class, ids); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   <T> SQLiteQueryableMutableStore<T> forClassWithIds(Class<T> clazz, String... ids) { |   <T> QueryableMutableStore<T> forClassWithIds(Class<T> clazz, String... ids) { | ||||||
|     return createStoreFactory(clazz).getMutable(clazz, ids); |     return createStoreFactory(clazz).getMutable(clazz, ids); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user