S16 postgresql opslag added
All checks were successful
Build, test and publish / verify (push) Successful in 7m31s
All checks were successful
Build, test and publish / verify (push) Successful in 7m31s
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package nl.agilecc.agiledancers;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import nl.agilecc.agiledancers.model.Coach;
|
||||
import nl.agilecc.agiledancers.model.DanceGroup;
|
||||
import nl.agilecc.agiledancers.model.DanceSchool;
|
||||
import nl.agilecc.agiledancers.model.Dancer;
|
||||
import nl.agilecc.agiledancers.model.DayPart;
|
||||
import nl.agilecc.agiledancers.model.Show;
|
||||
import nl.agilecc.agiledancers.model.specifications.DanceGroupStyle;
|
||||
import nl.agilecc.agiledancers.model.specifications.DayPartType;
|
||||
import nl.agilecc.agiledancers.rest.apimodel.GroupView;
|
||||
import nl.agilecc.agiledancers.rest.apimodel.SchoolView;
|
||||
import nl.agilecc.agiledancers.rest.apimodel.ShowView;
|
||||
import nl.agilecc.agiledancers.rest.mapping.DomainViewMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
class DomainViewMapperTest {
|
||||
private @Inject DomainViewMapper views;
|
||||
|
||||
public @Test void relationshipsBecomeOrderedIdsAndUnlinkedCoachesRetainNull() {
|
||||
final DanceSchool school =
|
||||
DanceSchool.builder().name("School").revision(4).build();
|
||||
school.setId(7L);
|
||||
final Dancer first = dancer(9L);
|
||||
final Dancer second = dancer(3L);
|
||||
school.addDancer(first);
|
||||
school.addDancer(second);
|
||||
final SchoolView mapped = views.school(school);
|
||||
assertEquals(List.of(9L, 3L), mapped.dancerIds());
|
||||
assertEquals(4, mapped.revision());
|
||||
final Coach coach = Coach.builder().name("Coach").dancer(first).build();
|
||||
coach.setId(2L);
|
||||
assertEquals(9L, views.coach(coach).dancerId());
|
||||
coach.setDancer(null);
|
||||
assertNull(views.coach(coach).dancerId());
|
||||
school.removeDancer(first);
|
||||
assertEquals(List.of(9L, 3L), mapped.dancerIds());
|
||||
assertThrows(
|
||||
UnsupportedOperationException.class, () -> mapped.dancerIds().clear());
|
||||
}
|
||||
|
||||
public @Test void nestedViewsKeepAssignmentOrderAndExplicitValidationWithoutSharingCollections() {
|
||||
final DanceGroup first = DanceGroup.builder().category("First").build();
|
||||
first.setId(8L);
|
||||
final DanceGroup second = DanceGroup.builder().category("Second").build();
|
||||
second.setId(2L);
|
||||
final List<DanceGroup> assigned = new ArrayList<>(List.of(first, second));
|
||||
final DayPart part = DayPart.builder()
|
||||
.date(LocalDate.of(2027, 1, 1))
|
||||
.type(DayPartType.MORNING)
|
||||
.danceGroups(assigned)
|
||||
.build();
|
||||
final List<String> violations = new ArrayList<>(List.of("Draft validation"));
|
||||
final Show show = Show.builder()
|
||||
.name("Show")
|
||||
.dayParts(new ArrayList<>(List.of(part)))
|
||||
.appearances(Map.of())
|
||||
.build();
|
||||
show.setId(1L);
|
||||
final ShowView mapped = views.show(show, violations);
|
||||
assertEquals(1, mapped.preferredDayParts());
|
||||
assertEquals(List.of(8L, 2L), mapped.dayParts().getFirst().danceGroupIds());
|
||||
assigned.clear();
|
||||
violations.clear();
|
||||
show.getDayParts().clear();
|
||||
assertEquals(List.of(8L, 2L), mapped.dayParts().getFirst().danceGroupIds());
|
||||
assertEquals(List.of("Draft validation"), mapped.violations());
|
||||
assertThrows(
|
||||
UnsupportedOperationException.class,
|
||||
() -> mapped.dayParts().getFirst().danceGroupIds().clear());
|
||||
}
|
||||
|
||||
public @Test void preferredDefaultsUseTheExistingRuleAndStylesAreSnapshotValues() {
|
||||
final DayPart part = DayPart.builder()
|
||||
.date(LocalDate.of(2027, 1, 1))
|
||||
.type(DayPartType.MORNING)
|
||||
.danceGroups(List.of())
|
||||
.build();
|
||||
final Show show = Show.builder()
|
||||
.dayParts(List.of(part, part))
|
||||
.appearances(Map.of())
|
||||
.build();
|
||||
show.setId(1L);
|
||||
assertEquals(2, views.show(show, List.of()).preferredDayParts());
|
||||
show.setPreferredDayParts(1);
|
||||
assertEquals(1, views.show(show, List.of()).preferredDayParts());
|
||||
final DanceGroup group = DanceGroup.builder().category("Group").build();
|
||||
group.setId(1L);
|
||||
group.addStyle(DanceGroupStyle.JAZZ);
|
||||
final Dancer dancer = dancer(4L);
|
||||
group.addDancer(dancer);
|
||||
final GroupView mapped = views.group(group);
|
||||
group.replaceStyles(Set.of(DanceGroupStyle.MODERN));
|
||||
group.removeDancer(dancer);
|
||||
assertEquals(Set.of(DanceGroupStyle.JAZZ), mapped.styles());
|
||||
assertEquals(List.of(4L), mapped.dancerIds());
|
||||
}
|
||||
|
||||
private static Dancer dancer(final long id) {
|
||||
final Dancer dancer = Dancer.builder()
|
||||
.name("Dancer")
|
||||
.birthDate(LocalDate.of(2000, 1, 1))
|
||||
.build();
|
||||
dancer.setId(id);
|
||||
return dancer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
package nl.agilecc.agiledancers;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import nl.agilecc.agiledancers.model.DanceSchool;
|
||||
import nl.agilecc.agiledancers.model.DayPart;
|
||||
import nl.agilecc.agiledancers.model.Show;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingPhase;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingProgress;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingRun;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingState;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingSummary;
|
||||
import nl.agilecc.agiledancers.model.specifications.DanceGroupStyle;
|
||||
import nl.agilecc.agiledancers.model.specifications.DanceGroupType;
|
||||
import nl.agilecc.agiledancers.model.specifications.DayPartType;
|
||||
import nl.agilecc.agiledancers.repository.Database;
|
||||
import nl.agilecc.agiledancers.repository.RunRepository;
|
||||
import nl.agilecc.agiledancers.repository.SchoolReader;
|
||||
import nl.agilecc.agiledancers.repository.SchoolWriter;
|
||||
import nl.agilecc.agiledancers.repository.StorageException;
|
||||
import nl.agilecc.agiledancers.service.SchoolManagementService;
|
||||
import nl.agilecc.agiledancers.service.SchoolTransactions;
|
||||
import nl.agilecc.agiledancers.service.model.GroupDetails;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
class PersistenceTest {
|
||||
private @Inject SchoolTransactions transactions;
|
||||
private @Inject SchoolManagementService schools;
|
||||
private @Inject Database database;
|
||||
private @Inject SchoolReader reader;
|
||||
private @Inject SchoolWriter writer;
|
||||
private @Inject RunRepository runs;
|
||||
private @Inject jakarta.persistence.EntityManager entities;
|
||||
|
||||
public @Test void simultaneousEditsRequireTheLoadedRevision() throws Exception {
|
||||
final long id = create();
|
||||
final String path = "/api/dance-schools/" + id;
|
||||
try (final java.util.concurrent.ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
||||
final java.util.concurrent.Future<Integer> first = executor.submit(() -> rename(path, "First", 0));
|
||||
final java.util.concurrent.Future<Integer> second = executor.submit(() -> rename(path, "Second", 0));
|
||||
final List<Integer> statuses = List.of(first.get(10, TimeUnit.SECONDS), second.get(10, TimeUnit.SECONDS));
|
||||
assertTrue(statuses.containsAll(List.of(200, 409)));
|
||||
given().contentType("application/json")
|
||||
.body("{\"name\":\"Missing revision\"}")
|
||||
.put(path)
|
||||
.then()
|
||||
.statusCode(409);
|
||||
given().get(path + "/data").then().statusCode(200);
|
||||
assertEquals(1, given().get(path).jsonPath().getInt("revision"));
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void detachedRelationalGraphRetainsIdsReferencesDraftAndFinalState() throws Exception {
|
||||
final long id = create();
|
||||
try {
|
||||
final Show show = fixture(id);
|
||||
transactions.background(() -> {
|
||||
final DanceSchool loaded = schools.school(id);
|
||||
assertEquals(1, loaded.getDancers().size());
|
||||
assertEquals(
|
||||
Set.of(DanceGroupStyle.JAZZ),
|
||||
loaded.getDanceGroups().iterator().next().getStyles());
|
||||
assertEquals(show.getId(), loaded.getShows().iterator().next().getId());
|
||||
assertTrue(loaded.getShows().iterator().next().isFinalized());
|
||||
assertEquals(
|
||||
loaded.getDanceGroups().iterator().next(),
|
||||
loaded.getShows()
|
||||
.iterator()
|
||||
.next()
|
||||
.getDayParts()
|
||||
.getFirst()
|
||||
.getDanceGroups()
|
||||
.getFirst());
|
||||
return null;
|
||||
});
|
||||
given().get("/api/dance-schools/" + id + "/export").then().statusCode(200);
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void deferredConstraintFailureRollsBackDomainAndCompletedStatusTogether() throws Exception {
|
||||
final long id = create();
|
||||
try {
|
||||
final Show show = fixture(id);
|
||||
final SchedulingRun run = SchedulingRun.started(id, show.getId(), show.getName());
|
||||
database.transaction(() -> {
|
||||
runs.save(run);
|
||||
return null;
|
||||
});
|
||||
final java.util.concurrent.atomic.AtomicBoolean published = new java.util.concurrent.atomic.AtomicBoolean();
|
||||
assertThrows(StorageException.class, () -> damageResult(id, show, run, published));
|
||||
assertFalse(published.get());
|
||||
database.transaction(() -> {
|
||||
assertEquals(
|
||||
"Persistent school",
|
||||
reader.load().stream()
|
||||
.filter((final DanceSchool school) -> school.getId() == id)
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
.getName());
|
||||
assertEquals(
|
||||
1,
|
||||
entities.find(
|
||||
nl.agilecc.agiledancers.repository.entity.DayPartEntity.class,
|
||||
new nl.agilecc.agiledancers.repository.entity.DayPartId(id, show.getId(), 0))
|
||||
.getGroupIds()
|
||||
.size());
|
||||
assertEquals(
|
||||
SchedulingState.RUNNING,
|
||||
runs.load().stream()
|
||||
.filter((final SchedulingRun item) ->
|
||||
item.runId().equals(run.runId()))
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
.state());
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void recoveryRetainsRunIdentityProgressAndExistingAssignments() throws Exception {
|
||||
final long id = create();
|
||||
try {
|
||||
final Show show = fixture(id);
|
||||
final SchedulingRun run = SchedulingRun.started(id, show.getId(), show.getName())
|
||||
.progressed(new SchedulingProgress(SchedulingPhase.SPREAD, true));
|
||||
database.transaction(() -> {
|
||||
runs.save(run);
|
||||
return null;
|
||||
});
|
||||
database.transaction(() -> {
|
||||
runs.recover();
|
||||
return null;
|
||||
});
|
||||
database.transaction(() -> {
|
||||
final SchedulingRun recovered = runs.load().stream()
|
||||
.filter((final SchedulingRun item) -> item.runId().equals(run.runId()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
assertEquals(SchedulingState.INTERRUPTED, recovered.state());
|
||||
assertEquals(run.progress(), recovered.progress());
|
||||
assertTrue(recovered.finishedAt() != null);
|
||||
assertTrue(recovered.message().contains("behouden"));
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void aFailedReplacementImportRetainsThePreviousGraphAndRevision() throws Exception {
|
||||
final long id = create();
|
||||
try {
|
||||
fixture(id);
|
||||
final nl.agilecc.agiledancers.service.model.SchoolSnapshot before =
|
||||
transactions.background(() -> schools.exportSchool(id));
|
||||
assertThrows(
|
||||
nl.agilecc.agiledancers.service.ManagementException.class,
|
||||
() -> transactions.request(id, 1L, true, () -> {
|
||||
final DanceSchool imported = schools.importSchool(before, id);
|
||||
imported.getDancers().iterator().next().setBirthDate(null);
|
||||
return imported;
|
||||
}));
|
||||
transactions.background(() -> {
|
||||
assertEquals(before, schools.exportSchool(id));
|
||||
assertEquals(1, schools.school(id).getRevision());
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void changedSchoolInputsRejectAnOlderSolverSnapshot() throws Exception {
|
||||
final long id = create();
|
||||
try {
|
||||
final Show show = fixture(id);
|
||||
final nl.agilecc.agiledancers.model.scheduling.SchedulingInput input =
|
||||
transactions.background(() -> schools.beginScheduling(id, show.getId()));
|
||||
transactions.request(id, 1L, true, () -> schools.updateSchool(id, "Changed"));
|
||||
final nl.agilecc.agiledancers.service.ManagementException failure = assertThrows(
|
||||
nl.agilecc.agiledancers.service.ManagementException.class,
|
||||
() -> transactions.background(() -> {
|
||||
schools.finishScheduling(
|
||||
id,
|
||||
input,
|
||||
new nl.agilecc.agiledancers.model.scheduling.SchedulingResult(
|
||||
true, List.of(List.of())));
|
||||
return null;
|
||||
}));
|
||||
assertEquals(nl.agilecc.agiledancers.service.ManagementExceptionKind.CONFLICT, failure.getKind());
|
||||
transactions.background(() -> {
|
||||
assertEquals(
|
||||
1,
|
||||
schools.show(id, show.getId())
|
||||
.getDayParts()
|
||||
.getFirst()
|
||||
.getDanceGroups()
|
||||
.size());
|
||||
schools.endScheduling(show.getId());
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void hibernateVersionRejectsDetachedStaleEntities() throws Exception {
|
||||
final long id = create();
|
||||
try {
|
||||
final nl.agilecc.agiledancers.repository.entity.SchoolEntity stale = database.transaction(
|
||||
() -> entities.find(nl.agilecc.agiledancers.repository.entity.SchoolEntity.class, id));
|
||||
database.transaction(() -> {
|
||||
entities.find(nl.agilecc.agiledancers.repository.entity.SchoolEntity.class, id)
|
||||
.setName("Confirmed");
|
||||
return null;
|
||||
});
|
||||
stale.setName("Stale");
|
||||
assertThrows(
|
||||
nl.agilecc.agiledancers.repository.RevisionConflictException.class,
|
||||
() -> database.transaction(() -> entities.merge(stale)));
|
||||
transactions.background(() -> {
|
||||
assertEquals("Confirmed", schools.school(id).getName());
|
||||
assertEquals(1, schools.school(id).getRevision());
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void childOnlyEditsAdvanceTheAggregateVersionOnce() throws Exception {
|
||||
final long id = create();
|
||||
try {
|
||||
fixture(id);
|
||||
transactions.request(id, 1L, true, () -> {
|
||||
final long dancerId = schools.dancers(id).getFirst().getId();
|
||||
return schools.updateDancer(id, dancerId, "Updated dancer", LocalDate.of(2000, 1, 1));
|
||||
});
|
||||
transactions.background(() -> {
|
||||
assertEquals(2, schools.school(id).getRevision());
|
||||
assertEquals("Updated dancer", schools.dancers(id).getFirst().getName());
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void hibernateRetainsAssignmentsWhenDayPartsAreReordered() throws Exception {
|
||||
final long id = create();
|
||||
try {
|
||||
final Show show = fixture(id);
|
||||
final DayPart added = DayPart.builder()
|
||||
.date(LocalDate.of(2027, 1, 2))
|
||||
.type(DayPartType.EVENING)
|
||||
.danceGroups(List.of())
|
||||
.build();
|
||||
transactions.request(
|
||||
id,
|
||||
1L,
|
||||
true,
|
||||
() -> schools.updateShow(
|
||||
id,
|
||||
show.getId(),
|
||||
"Two parts",
|
||||
List.of(show.getDayParts().getFirst(), added),
|
||||
null));
|
||||
transactions.request(id, 2L, true, () -> {
|
||||
final Show current = schools.show(id, show.getId());
|
||||
return schools.updateShow(
|
||||
id,
|
||||
show.getId(),
|
||||
"Swapped",
|
||||
List.of(
|
||||
current.getDayParts().getLast(),
|
||||
current.getDayParts().getFirst()),
|
||||
null);
|
||||
});
|
||||
transactions.background(() -> {
|
||||
final Show restored = schools.show(id, show.getId());
|
||||
assertEquals(added.getDate(), restored.getDayParts().getFirst().getDate());
|
||||
assertEquals(
|
||||
1, restored.getDayParts().getLast().getDanceGroups().size());
|
||||
assertEquals(3, schools.school(id).getRevision());
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void damageResult(
|
||||
final long id,
|
||||
final Show show,
|
||||
final SchedulingRun run,
|
||||
final java.util.concurrent.atomic.AtomicBoolean published)
|
||||
throws Exception {
|
||||
database.transaction(() -> {
|
||||
entities.find(nl.agilecc.agiledancers.repository.entity.SchoolEntity.class, id)
|
||||
.setName("Uncommitted");
|
||||
entities.find(
|
||||
nl.agilecc.agiledancers.repository.entity.DayPartEntity.class,
|
||||
new nl.agilecc.agiledancers.repository.entity.DayPartId(id, show.getId(), 0))
|
||||
.getGroupIds()
|
||||
.clear();
|
||||
runs.save(run.completed(SchedulingState.OPTIMAL, "Uncommitted", new SchedulingSummary(0, 0, 0)));
|
||||
database.afterCommit(() -> published.set(true));
|
||||
// This dancer FK is deferred: work returns normally, and commit itself must fail.
|
||||
entities.find(
|
||||
nl.agilecc.agiledancers.repository.entity.GroupEntity.class,
|
||||
new nl.agilecc.agiledancers.repository.entity.SchoolMemberId(
|
||||
id,
|
||||
show.getDayParts()
|
||||
.getFirst()
|
||||
.getDanceGroups()
|
||||
.getFirst()
|
||||
.getId()))
|
||||
.getDancerIds()
|
||||
.add(999999L);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private long create() throws Exception {
|
||||
return (Long) transactions.request(
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
() -> schools.createSchool("Persistent school").getId());
|
||||
}
|
||||
|
||||
private Show fixture(final long id) throws Exception {
|
||||
return (Show) transactions.request(id, 0L, true, () -> {
|
||||
final long dancer =
|
||||
schools.createDancer(id, "Dancer", LocalDate.of(2000, 1, 1)).getId();
|
||||
final long group = schools.createGroup(
|
||||
id, new GroupDetails("Group", DanceGroupType.REGULAR, 0, 99, Set.of(DanceGroupStyle.JAZZ)))
|
||||
.getId();
|
||||
schools.addGroupDancer(id, group, dancer);
|
||||
final Show show = schools.createShow(
|
||||
id,
|
||||
"Saved show",
|
||||
List.of(DayPart.builder()
|
||||
.date(LocalDate.of(2027, 1, 1))
|
||||
.type(DayPartType.MORNING)
|
||||
.danceGroups(List.of(schools.group(id, group)))
|
||||
.build()),
|
||||
null);
|
||||
show.setFinalized(true);
|
||||
return show;
|
||||
});
|
||||
}
|
||||
|
||||
private int rename(final String path, final String name, final long revision) {
|
||||
return given().header("X-School-Revision", revision)
|
||||
.contentType("application/json")
|
||||
.body(java.util.Map.of("name", name))
|
||||
.put(path)
|
||||
.statusCode();
|
||||
}
|
||||
|
||||
private void delete(final long id) {
|
||||
transactions.background(() -> {
|
||||
writer.delete(id);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package nl.agilecc.agiledancers;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.response.Response;
|
||||
import io.restassured.specification.FilterableRequestSpecification;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** Existing endpoint scenarios use fresh revisions; concurrency tests deliberately send explicit stale revisions. */
|
||||
public class RevisionRequests {
|
||||
private static final Pattern SCHOOL = Pattern.compile("/api/dance-schools/(\\d+)(?:/|$)");
|
||||
private static final Pattern REPLACEMENT = Pattern.compile("[?&]replaceId=(\\d+)");
|
||||
|
||||
public static RequestSpecification given() {
|
||||
return RestAssured.given()
|
||||
.filter(
|
||||
(final FilterableRequestSpecification request,
|
||||
final io.restassured.specification.FilterableResponseSpecification response,
|
||||
final io.restassured.filter.FilterContext context) -> {
|
||||
addRevision(request);
|
||||
return context.next(request, response);
|
||||
});
|
||||
}
|
||||
|
||||
private static void addRevision(final FilterableRequestSpecification request) {
|
||||
final String id = schoolId(request);
|
||||
if (!request.getMethod().equals("GET")
|
||||
&& id != null
|
||||
&& !request.getHeaders().hasHeaderWithName("X-School-Revision")) {
|
||||
final Response school = RestAssured.given().get("/api/dance-schools/" + id);
|
||||
if (school.statusCode() == 200) {
|
||||
request.header("X-School-Revision", school.jsonPath().getLong("revision"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String schoolId(final FilterableRequestSpecification request) {
|
||||
final Matcher school = SCHOOL.matcher(request.getURI());
|
||||
if (school.find()) {
|
||||
return school.group(1);
|
||||
}
|
||||
final Matcher replacement = REPLACEMENT.matcher(request.getURI());
|
||||
if (replacement.find()) {
|
||||
return replacement.group(1);
|
||||
}
|
||||
final Object parameter = request.getQueryParams().get("replaceId");
|
||||
return parameter == null ? null : parameter.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package nl.agilecc.agiledancers;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static nl.agilecc.agiledancers.RevisionRequests.given;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package nl.agilecc.agiledancers;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static nl.agilecc.agiledancers.RevisionRequests.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -36,12 +36,14 @@ import org.junit.jupiter.api.Test;
|
||||
class SchedulingEventsTest {
|
||||
private @Inject SchoolManagementService schools;
|
||||
private @Inject ObjectMapper json;
|
||||
private @Inject nl.agilecc.agiledancers.service.SchoolTransactions transactions;
|
||||
private @TestHTTPResource URI base;
|
||||
|
||||
public @Test void streamStartsWithSnapshotAndPushesCompletionAndReconnectSnapshot() throws Exception {
|
||||
final long school = schools.createSchool("SSE school").getId();
|
||||
final long school = (Long) transactions.request(
|
||||
null, null, true, () -> schools.createSchool("SSE school").getId());
|
||||
try {
|
||||
final Show show = show(school);
|
||||
final Show show = (Show) transactions.request(school, 0L, true, () -> show(school));
|
||||
final String path = "/api/dance-schools/" + school + "/shows/" + show.getId() + "/schedule";
|
||||
given().get(path).then().statusCode(204);
|
||||
try (final HttpClient client = HttpClient.newHttpClient();
|
||||
@@ -64,7 +66,7 @@ class SchedulingEventsTest {
|
||||
}
|
||||
assertReconnectContains(show.getId());
|
||||
} finally {
|
||||
schools.deleteSchool(school);
|
||||
given().delete("/api/dance-schools/" + school);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package nl.agilecc.agiledancers;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static nl.agilecc.agiledancers.RevisionRequests.given;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package nl.agilecc.agiledancers;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static nl.agilecc.agiledancers.RevisionRequests.given;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.emptyOrNullString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@@ -19,11 +19,11 @@ import nl.agilecc.agiledancers.model.Show;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingFailureKind;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingInput;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingResult;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingRun;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingState;
|
||||
import nl.agilecc.agiledancers.model.specifications.DanceGroupType;
|
||||
import nl.agilecc.agiledancers.model.specifications.DayPartType;
|
||||
import nl.agilecc.agiledancers.service.model.GroupDetails;
|
||||
import nl.agilecc.agiledancers.service.model.SchedulingRun;
|
||||
import nl.agilecc.agiledancers.service.model.SchedulingState;
|
||||
import nl.agilecc.agiledancers.service.port.SchedulingEngine;
|
||||
import nl.agilecc.agiledancers.solver.OrToolsSchedulingEngine;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
Reference in New Issue
Block a user