All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.ght1pc9kc.testy.jooq.WithSampleDataLoaded Maven / Gradle / Ivy

The newest version!
package fr.ght1pc9kc.testy.jooq;

import fr.ght1pc9kc.testy.jooq.model.RelationalDataSet;
import org.jooq.CreateTableElementListStep;
import org.jooq.DSLContext;
import org.jooq.Key;
import org.jooq.Query;
import org.jooq.TableRecord;
import org.jooq.UniqueKey;
import org.jooq.impl.DSL;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolver;

import javax.inject.Named;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

import static java.util.Objects.nonNull;

/**
 * This extension allows you to load test data into a previously created database.
 * 

* These data are imported as a list of jOOQ records. The records are inserted in the same order they have been added * to the extension in order to consider the external key constraints. *

* It is possible to insert data in an empty database thanks to the createTablesIfNotExists option. *

* The concerned tables are emptied and reloaded for each test to keep the consistency, however, for performance * reasons, if the data has not been modified during a test, a tracker allows to signal to the extension that it is * not necessary to refresh the data. This can improve test performance significantly. * *


 * // Declare InMemory database
 * private static final WithInMemoryDatasource wDs = WithInMemoryDatasource.builder().build();
 *
 * // Declare jOOQ DslContext
 * private static final WithDslContext wDslContext = WithDslContext.builder()
 *         .setDatasourceExtension(wDs).build();
 *
 * // Declare records sample for each used tables
 * private static final WithSampleDataLoaded wSamples = WithSampleDataLoaded.builder(wDslContext)
 *         .createTablesIfNotExists()               // Create table if not exist in database
 *         .addDataset(UsersRecordSamples.SAMPLE)
 *         .addDataset(UsersRolesSamples.SAMPLE)
 *         .build();
 *
 * // Use ChainedExtension to chain each declared extensions
 * {@literal @}RegisterExtension
 * static ChainedExtension chain = ChainedExtension.outer(wDs)
 *         .append(wDslContext)
 *         .append(wSamples)
 *         .register();
 * 
*

* You can now write your test *


 * {@literal @}Test
 * void should_get_raw_news(WithSampleDataLoaded.Tracker tracker) { // Inject the modification tracker
 *     tracker.skipNextSampleLoad();    // ask for skipping the next sample reload
 *
 *     // ...
 * }
 * 
*/ public final class WithSampleDataLoaded implements BeforeAllCallback, BeforeEachCallback, ParameterResolver { private static final String P_TRACKER = "sampleTracker_"; private final WithDslContext wDsl; private final List> records; private final boolean createTables; private WithSampleDataLoaded(Extension wDsl, List> records, boolean createTables) { this.wDsl = (WithDslContext) wDsl; this.records = List.copyOf(records); this.createTables = createTables; } @Override public void beforeAll(ExtensionContext context) { final String catalog = getContextCatalog(context); getStore(context).put(P_TRACKER + catalog, new Tracker()); DSLContext dslContext = wDsl.getDslContext(context); dslContext.attach(records); if (createTables) { records.stream() .map(TableRecord::getTable) .distinct() .map(t -> { CreateTableElementListStep steps = dslContext.createTableIfNotExists(t) .columns(t.fields()) .constraints(t.getKeys().stream().map(Key::constraint).toList()); UniqueKey pk = t.getPrimaryKey(); if (nonNull(pk)) { return steps.primaryKey(pk.getFields()); } else { return steps; } }) .forEach(Query::execute); } } @Override public void beforeEach(ExtensionContext context) { final String catalog = getContextCatalog(context); Tracker tracker = getStore(context).get(P_TRACKER + catalog, Tracker.class); if (tracker == null) { throw new IllegalStateException(getClass().getName() + " must be static and package-protected !"); } if (tracker.skipNext.getAndSet(false)) { return; } DSLContext dslContext = wDsl.getDslContext(context); dslContext.transaction(tx -> { DSLContext txDsl = DSL.using(tx); var it = records.listIterator(records.size()); while (it.hasPrevious()) { txDsl.delete(it.previous().getTable()).execute(); } records.forEach(r -> r.changed(true)); txDsl.batchInsert(records).execute(); }); } private ExtensionContext.Store getStore(ExtensionContext context) { return context.getStore(ExtensionContext.Namespace.create(getClass().getName(), getContextCatalog(context))); } private String getContextCatalog(ExtensionContext context) { return Objects.requireNonNull(wDsl.getDatasourceExtension().getCatalog(context), "Catalog not found in context Store !"); } @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { Class type = parameterContext.getParameter().getType(); final String catalog = getContextCatalog(extensionContext); return Tracker.class.equals(type) && catalog.equals(getCatalogForParameter(parameterContext, catalog)); } private String getCatalogForParameter(ParameterContext parameterContext, String catalogDefault) { return parameterContext.findAnnotation(Named.class) .map(Named::value) .orElse(catalogDefault); } @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { Class type = parameterContext.getParameter().getType(); final String catalog = getContextCatalog(extensionContext); if (Tracker.class.equals(type)) { return getStore(extensionContext).get(P_TRACKER + catalog); } throw new NoSuchElementException(P_TRACKER + catalog); } public static SampleLoaderBuilder builder(Extension ex) { return new SampleLoaderBuilder(ex); } public static class SampleLoaderBuilder { private final Extension dslExtension; private final List> records = new ArrayList<>(); private boolean createTables = false; SampleLoaderBuilder(Extension dslExtension) { this.dslExtension = dslExtension; } public > SampleLoaderBuilder addDataset(RelationalDataSet dataset) { records.addAll(dataset.records()); return this; } /** * With this option, the tables corresponding to the sample records will be created if they do not exist. *

* This avoids having to load the complete database schema and can speed up the tests significantly. *

* However, since the schema is not loaded, not all the data it contains is loaded either, * which can cause unexpected behavior. *

* By default, the tables was never created. * * @return the builder instance */ public SampleLoaderBuilder createTablesIfNotExists() { this.createTables = true; return this; } public WithSampleDataLoaded build() { return new WithSampleDataLoaded(dslExtension, records, createTables); } } public static class Tracker { private final AtomicBoolean skipNext = new AtomicBoolean(false); public void skipNextSampleLoad() { skipNext.set(true); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy