import export functionality added

This commit is contained in:
2026-10-28 08:07:42 +01:00
parent bd497aefd5
commit e3e362c2ac
16 changed files with 609 additions and 4 deletions

View File

@@ -0,0 +1,32 @@
package nl.agilecc.agiledancers;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;
@QuarkusTest
class SchoolSnapshotResourceTest {
private static final String BASE = "/api/dance-schools";
@Test void downloadPreviewRestoreAndRejectInvalidFiles() {
long id = given().contentType("application/json").body("{\"name\":\"JSON school\"}").post(BASE)
.then().statusCode(201).extract().jsonPath().getLong("id");
String json = given().get(BASE + "/" + id + "/export").then().statusCode(200)
.header("Content-Disposition", containsString(".json")).body("formatVersion", equalTo(1)).extract().asString();
try {
given().contentType("application/json").body(json).post(BASE + "/import/preview").then().statusCode(200)
.body("existingId", equalTo((int) id)).body("dancers", equalTo(0));
for (String bad : new String[]{"{", "null", json.replace("\"formatVersion\" : 1", "\"formatVersion\" : 2"),
json.replace("\"name\" :", "\"unknown\" :"), json + " {}",
json.replace("\"dancers\" : [ ]", "\"dancers\" : null")}) {
given().contentType("application/json").body(bad).post(BASE + "/import?replaceId=" + id)
.then().statusCode(400).body("message", not(emptyOrNullString()));
}
given().get(BASE + "/" + id + "/export").then().body(equalTo(json));
given().delete(BASE + "/" + id).then().statusCode(204);
id = given().contentType("application/json").body(json).post(BASE + "/import")
.then().statusCode(200).extract().jsonPath().getLong("id");
given().get(BASE + "/" + id + "/export").then().body(equalTo(json));
} finally { given().delete(BASE + "/" + id); }
}
}

View File

@@ -0,0 +1,70 @@
package nl.agilecc.agiledancers;
import nl.agilecc.agiledancers.service.*;
import nl.agilecc.agiledancers.model.specifications.*;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
class SchoolSnapshotTest {
private SchoolSnapshot complete() {
return new SchoolSnapshot(1, UUID.randomUUID(), "Studio", List.of(new SchoolSnapshot.Person("a", "Sam", LocalDate.of(2010,1,1))),
List.of(new SchoolSnapshot.Trainer("c", "Alex")),
List.of(new SchoolSnapshot.Group("g", "Hiphop", DanceGroupType.REGULAR, 0, 99, List.of(DanceGroupStyle.HIPHOP), List.of("a"), "c"),
new SchoolSnapshot.Group("h", "Jazz", DanceGroupType.REGULAR, 0, 99, List.of(), List.of("a"), "c")),
List.of(new SchoolSnapshot.Performance("s", "Show", LocalDate.of(2027,1,1),
List.of(new SchoolSnapshot.Part(DayPartType.MORNING, List.of("g", "h"))))));
}
@Test void roundTripAfterRestartPreservesAllFieldsAndSharedRelationships() {
var snapshot = complete();
var service = new SchoolManagementService();
var existing = service.createSchool("Other");
service.createDancer(existing.getId(), "Other dancer", LocalDate.of(2000,1,1));
var imported = service.importSchool(snapshot, null);
assertEquals(2, imported.getDanceGroups().size());
var dancer = imported.getDancers().iterator().next();
imported.getDanceGroups().forEach(g -> assertSame(dancer, g.getDancers().iterator().next()));
assertEquals(SchoolSnapshot.of(snapshot.restore()), service.exportSchool(imported.getId()));
var restarted = new SchoolManagementService();
var restored = restarted.importSchool(service.exportSchool(imported.getId()), null);
assertEquals(SchoolSnapshot.of(snapshot.restore()), restarted.exportSchool(restored.getId()));
// Membership removal must work after remapping hash-based sets.
service.deleteDancer(imported.getId(), dancer.getId());
imported.getDanceGroups().forEach(g -> assertTrue(g.getDancers().isEmpty()));
assertEquals(1, existing.getDancers().size());
var group = imported.getDanceGroups().iterator().next();
service.deleteGroup(imported.getId(), group.getId());
assertEquals(1, imported.getShows().iterator().next().getDayParts().getFirst().getDanceGroups().size());
assertDoesNotThrow(() -> service.exportSchool(imported.getId()).restore());
}
@Test void previewIsReadOnlyAndReplacementRemovesPreviousConfiguration() {
var service = new SchoolManagementService(); var snapshot = complete();
assertNull(service.preview(snapshot).existingId()); assertTrue(service.schools().isEmpty());
var original = service.importSchool(snapshot, null);
service.createDancer(original.getId(), "Extra", LocalDate.of(2000,1,1));
var other = service.createSchool(snapshot.name());
assertEquals(original.getId(), service.preview(snapshot).existingId());
assertThrows(ManagementException.class, () -> service.importSchool(snapshot, null));
assertEquals(2, original.getDancers().size());
var replaced = service.importSchool(snapshot, original.getId());
assertEquals(SchoolSnapshot.of(snapshot.restore()), service.exportSchool(replaced.getId()));
assertSame(other, service.school(other.getId()));
service.deleteSchool(replaced.getId());
assertEquals(SchoolSnapshot.of(snapshot.restore()), service.exportSchool(service.importSchool(snapshot, null).getId()));
}
@Test void unresolvedReferenceLeavesExistingAndOtherSchoolsIntact() {
var service = new SchoolManagementService(); var valid = complete();
var original = service.importSchool(valid, null);
var bad = new SchoolSnapshot(1, valid.key(), valid.name(), List.of(), valid.coaches(), valid.groups(), valid.shows());
assertThrows(ManagementException.class, () -> service.preview(bad));
assertThrows(ManagementException.class, () -> service.importSchool(bad, original.getId()));
assertEquals(SchoolSnapshot.of(valid.restore()), service.exportSchool(original.getId()));
}
@Test void emptySchoolRoundTrips() {
var service = new SchoolManagementService();
var empty = service.createSchool("Empty"); var snapshot = service.exportSchool(empty.getId());
service.deleteSchool(empty.getId());
assertEquals(SchoolSnapshot.of(snapshot.restore()), service.exportSchool(service.importSchool(snapshot, null).getId()));
}
}