package br.gov.servicos.editor.conteudo.cartas; import br.gov.servicos.editor.config.SlugifyConfig; import br.gov.servicos.editor.conteudo.TipoPagina; import br.gov.servicos.editor.utils.Unchecked; import lombok.SneakyThrows; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import static br.gov.servicos.editor.conteudo.TipoPagina.SERVICO; import static java.util.stream.Collectors.joining; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; public class FluxosEdicaoIntegrationTest extends RepositorioGitIntegrationTest { ExecutorService execucaoParalela; @Before public void setup() { execucaoParalela = Executors.newCachedThreadPool(); setupBase() .carta(id("a"), carta1("A")) .carta(id("b"), carta1("B")) .carta(id("c"), carta1("C")) .carta(id("d"), carta1("D")) .carta(id("e"), carta1("E")) .build(); } @After public void tearDown() { execucaoParalela.shutdown(); } @Test public void equipesListandoEditandoSalvandoAoMesmoTempoDeveManterRepositorioFuncional() throws Exception { paralelizar( () -> fluxoListarSalvarEditar(SERVICO, id("a"), carta1("A"), carta2("A")), () -> fluxoListarSalvarEditar(SERVICO, id("b"), carta1("B"), carta2("B")), () -> fluxoListarSalvarEditar(SERVICO, id("c"), carta1("C"), carta2("C"))); } @Test public void fluxoDelecaoMesmoServicoNaoDeveDarErros() throws Exception { paralelizar( () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a")), () -> api.excluirPagina(SERVICO, id("a"))); api.editarCarta(id("a")) .andExpect(status().isNotFound()); } @Test public void fluxoDeletarPaginaRepositorioNaoDeveFicarInconsistente() { try { paralelizar( () -> api.excluirPagina(SERVICO, id("a")).andExpect(status().isOk()), () -> api.excluirPagina(SERVICO, id("b")).andExpect(status().isOk()), () -> api.excluirPagina(SERVICO, id("c")).andExpect(status().isOk())); paralelizar( () -> api.excluirPagina(SERVICO, id("a")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("b")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("c")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("a")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("b")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("c")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("a")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("b")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("c")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("a")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("b")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("c")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("a")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("b")).andExpect(status().isNotFound()), () -> api.excluirPagina(SERVICO, id("c")).andExpect(status().isNotFound())); } finally { execucaoParalela.shutdown(); } } @Test public void edicaoConcorrenteDoMesmoDocumentoDeveManterAUltimaVersaoSemErros() throws Exception { paralelizar( () -> salvarDocumentos(SERVICO, id("a"), carta1("A"), carta2("A")), () -> salvarDocumentos(SERVICO, id("a"), carta1("A"), carta2("A")), () -> salvarDocumentos(SERVICO, id("a"), carta1("A"), carta2("A")), () -> salvarDocumentos(SERVICO, id("a"), carta1("A"), carta2("A")), () -> salvarDocumentos(SERVICO, id("a"), carta1("A"), carta2("A")), () -> salvarDocumentos(SERVICO, id("a"), carta1("A"), carta2("A")), () -> salvarDocumentos(SERVICO, id("a"), carta1("A"), carta2("A")), () -> salvarDocumentos(SERVICO, id("a"), carta1("A"), carta2("A"))); String ultimo = "<servico><nome>Carta A</nome><descricao>Uma descricao</descricao></servico>"; api.salvarCarta(id("a"), ultimo) .andExpect(status().is3xxRedirection()); api.editarCarta(id("a")) .andExpect(status().isOk()) .andExpect(content().xml(ultimo)); } @Test public void equipesEditandoAoMesmoTempoDeveManterRepositorioFuncional() { paralelizar( () -> fluxoListarExcluirRenomearSalvarSalvarPublicar("a", "b", "c", "d", "e"), () -> fluxoListarEditarSalvarPublicarDescartarDespublicarListar("z"), () -> fluxoListarExcluirRenomearSalvarSalvarPublicar("f", "g", "h", "i", "j"), () -> fluxoListarEditarSalvarPublicarDescartarDespublicarListar("x"), () -> fluxoListarExcluirRenomearSalvarSalvarPublicar("k", "l", "m", "n", "o"), () -> fluxoListarEditarSalvarPublicarDescartarDespublicarListar("v"), () -> fluxoListarExcluirRenomearSalvarSalvarPublicar("p", "q", "r", "s", "t"), () -> fluxoListarEditarSalvarPublicarDescartarDespublicarListar("1"), () -> fluxoListarExcluirRenomearSalvarSalvarPublicar("2", "3", "4", "5", "6"), () -> fluxoListarEditarSalvarPublicarDescartarDespublicarListar("y")); } @SneakyThrows private void fluxoListarExcluirRenomearSalvarSalvarPublicar(String id1, String id2, String id3, String id4, String id5) { String id3r = id3 + " r"; String id4r = id4 + " r"; String id5r = id5 + " r"; api.listar() .andExpect(status().isOk()); api.salvarCarta(id(id1), carta1(id1)); api.salvarCarta(id(id2), carta1(id2)); api.salvarCarta(id(id3), carta1(id3)); api.salvarCarta(id(id4), carta1(id4)); api.salvarCarta(id(id5), carta1(id5)); api.excluirPagina(SERVICO, id(id1)) .andExpect(status().isOk()); api.excluirPagina(SERVICO, id(id2)) .andExpect(status().isOk()); api.renomearCarta(id(id3), id(id3r)) .andExpect(status().isOk()); api.renomearCarta(id(id4), id(id4r)) .andExpect(status().isOk()); api.renomearCarta(id(id5), id(id5r)) .andExpect(status().isOk()); api.salvarCarta(id(id1), carta1(id1)) .andExpect(status().is3xxRedirection()); api.salvarCarta(id(id2), carta1(id2)) .andExpect(status().is3xxRedirection()); api.salvarCarta(id(id3r), carta2(id3r)) .andExpect(status().is3xxRedirection()); api.salvarCarta(id(id4r), carta2(id4r)) .andExpect(status().is3xxRedirection()); api.salvarCarta(id(id5r), carta2(id5r)) .andExpect(status().is3xxRedirection()); api.publicarCarta(id(id1)) .andExpect(status().isOk()); api.publicarCarta(id(id2)) .andExpect(status().isOk()); api.publicarCarta(id(id3r)) .andExpect(status().isOk()); api.publicarCarta(id(id4r)) .andExpect(status().isOk()); api.publicarCarta(id(id4r)) .andExpect(status().isOk()); api.listar() .andExpect(jsonPath("$[*].id") .value(Matchers.hasItems(id(id1), id(id2), id(id3r), id(id4r), id(id5r)))); } @SneakyThrows private void fluxoListarEditarSalvarPublicarDescartarDespublicarListar(String id) { id = id(id); api.listar() .andExpect(status().isOk()); api.salvarCarta(id, carta2(id)) .andExpect(status().is3xxRedirection()); api.publicarCarta(id) .andExpect(status().isOk()); api.salvarCarta(id, carta3(id)) .andExpect(status().is3xxRedirection()); api.descartarCarta(id) .andExpect(status().isOk()); api.editarCarta(id) .andExpect(status().isOk()) .andExpect(content().xml(carta2(id))); api.salvarCarta(id, carta4(id)) .andExpect(status().is3xxRedirection()); api.publicarCarta(id) .andExpect(status().isOk()); api.despublicarCarta(id) .andExpect(status().isOk()); api.listar() .andExpect(status().isOk()); api.editarCarta(id) .andExpect(status().isOk()) .andExpect(content().xml(carta4(id))); } @SneakyThrows private void listar() { api.listar() .andExpect(status().isOk()); } @SneakyThrows private void salvarDocumentos(TipoPagina tipo, String id, String conteudo1, String conteudo2) { api.salvarPagina(tipo, id, conteudo1) .andExpect(status().is3xxRedirection()); api.salvarPagina(tipo, id, conteudo2) .andExpect(status().is3xxRedirection()); } @SneakyThrows private void fluxoListarSalvarEditar(TipoPagina tipo, String id, String conteudo1, String conteudo2) { for (int i = 0; i < 10; i++) { api.listar() .andExpect(status().isOk()); api.salvarPagina(tipo, id, conteudo1) .andExpect(status().is3xxRedirection()); api.listar() .andExpect(status().isOk()); api.editarPagina(tipo, id) .andExpect(status().isOk()) .andExpect(content().xml(conteudo1)); api.listar() .andExpect(status().isOk()); api.salvarPagina(tipo, id, conteudo2) .andExpect(status().is3xxRedirection()); api.listar() .andExpect(status().isOk()); api.editarPagina(tipo, id) .andExpect(status().isOk()) .andExpect(content().xml(conteudo2)); api.listar() .andExpect(status().isOk()); } api.descartarPagina(tipo, id) .andExpect(status().isOk()); api.listar() .andExpect(status().isOk()); api.editarPagina(tipo, id) .andExpect(status().isOk()) .andExpect(content().xml(conteudo1)); api.listar() .andExpect(status().isOk()); } @SafeVarargs private final <T> void paralelizar(Unchecked.Supplier<T>... fs) { Arrays.asList(fs) .stream() .map(f -> execucaoParalela.submit((Callable<Object>) f::get)) .forEach(FluxosEdicaoIntegrationTest::get); } private void paralelizar(Runnable... fs) { Arrays.asList(fs) .stream() .map(f -> execucaoParalela.submit(f::run)) .forEach(FluxosEdicaoIntegrationTest::get); } private static <T> Object get(Future<T> f) { try { return f.get(); } catch (Throwable t) { throw new RuntimeException(t); } } private static String carta1(String base) { return "<servico><nome>Carta " + base + "</nome></servico>"; } private static String carta2(String base) { return "<servico><nome>Carta " + base + "</nome><sigla>C" + base.charAt(0) + "</sigla></servico>"; } private static String carta3(String base) { return "<servico><nome>Carta " + base + "</nome><sigla>C" + base.charAt(0) + "</sigla><descricao>" + base + "</descricao></servico>"; } private static String carta4(String base) { String palavrasChave = "<palavras-chave>" + Arrays.asList(base.split("")) .stream() .map(s -> "<item>" + s + "</item>") .collect(joining("")) + "</palavras-chave>"; return "<servico><nome>Carta " + base + "</nome><sigla>C" + base.charAt(0) + "</sigla>" + palavrasChave + "</servico>"; } private static String id(String base) { return SlugifyConfig.slugify("carta1-" + base); } }