From f739d617660efe42a3cd40e927960badef590e17 Mon Sep 17 00:00:00 2001 From: Miguel Reboiro-Jato Date: Tue, 27 Oct 2015 11:02:59 +0100 Subject: [PATCH] Adds the REST layer implementation partially tested The REST layer has been added but with incomplete tests. Students will have to complete the tests. This commit includes an important refactorization regarding tests. Test classes and datasets have been moved from the "service" project to a new "tests" project. This way, any project can include this "tests" project as a dependency in order to have access to these common testing resources. --- .gitignore | 3 + README.md | 28 +- .../xcs/domain/entities/Administrator.java | 2 + .../uvigo/esei/xcs/domain/entities/Owner.java | 6 +- .../uvigo/esei/xcs/domain/entities/Pet.java | 9 +- pom.xml | 54 ++- rest/.gitignore | 1 + rest/pom.xml | 69 +++ .../es/uvigo/esei/xcs/rest/CORSFilter.java | 21 + .../rest/IllegalArgumentExceptionMapper.java | 18 + .../es/uvigo/esei/xcs/rest/OwnerResource.java | 145 ++++++ .../es/uvigo/esei/xcs/rest/PetResource.java | 147 +++++++ .../xcs/rest/SecurityExceptionMapper.java | 17 + .../main/resources/META-INF/persistence.xml | 20 + rest/src/main/webapp/WEB-INF/beans.xml | 6 + rest/src/main/webapp/WEB-INF/jboss-web.xml | 4 + rest/src/main/webapp/WEB-INF/web.xml | 50 +++ .../es/uvigo/esei/xcs/rest/GenericTypes.java | 20 + .../esei/xcs/rest/OwnerResourceRestTest.java | 305 +++++++++++++ .../esei/xcs/rest/OwnerResourceUnitTest.java | 198 +++++++++ .../resources-wildfly-embedded-h2/beans.xml | 11 + .../cleanup-autoincrement.sql | 1 + .../jboss-web.xml | 4 + .../standalone.xml | 415 ++++++++++++++++++ .../test-persistence.xml | 17 + .../beans.xml | 11 + .../cleanup-autoincrement.sql | 1 + .../jboss-web.xml | 4 + .../mysql-ds.xml | 17 + .../standalone.xml | 415 ++++++++++++++++++ .../test-persistence.xml | 17 + rest/src/test/resources/arquillian.xml | 25 ++ rest/src/test/resources/web.xml | 50 +++ service/pom.xml | 5 + .../es/uvigo/esei/xcs/service/PetService.java | 2 +- tests/pom.xml | 37 ++ .../xcs/domain/entities/IsEqualsToEntity.java | 0 .../xcs/domain/entities/IsEqualsToOwner.java | 0 .../xcs/domain/entities/IsEqualsToPet.java | 0 .../xcs/domain/entities/OwnersDataset.java | 34 +- .../util/security/AdminRoleCaller.java | 0 .../util/security/OwnerRoleCaller.java | 0 .../xcs/service/util/security/RoleCaller.java | 0 .../service/util/security/TestPrincipal.java | 0 .../resources/datasets/owners-create-pet.xml | 0 .../datasets/owners-create-with-pets.xml | 0 .../datasets/owners-create-without-pets.xml | 0 .../resources/datasets/owners-remove-pet.xml | 0 .../datasets/owners-remove-with-pets.xml | 0 .../datasets/owners-remove-without-pets.xml | 0 .../datasets/owners-update-password.xml | 0 .../resources/datasets/owners-update-pet.xml | 0 .../src/main}/resources/datasets/owners.xml | 0 .../src/main}/resources/scripts/cleanup.sql | 0 54 files changed, 2155 insertions(+), 34 deletions(-) create mode 100644 rest/.gitignore create mode 100644 rest/pom.xml create mode 100644 rest/src/main/java/es/uvigo/esei/xcs/rest/CORSFilter.java create mode 100644 rest/src/main/java/es/uvigo/esei/xcs/rest/IllegalArgumentExceptionMapper.java create mode 100644 rest/src/main/java/es/uvigo/esei/xcs/rest/OwnerResource.java create mode 100644 rest/src/main/java/es/uvigo/esei/xcs/rest/PetResource.java create mode 100644 rest/src/main/java/es/uvigo/esei/xcs/rest/SecurityExceptionMapper.java create mode 100644 rest/src/main/resources/META-INF/persistence.xml create mode 100644 rest/src/main/webapp/WEB-INF/beans.xml create mode 100644 rest/src/main/webapp/WEB-INF/jboss-web.xml create mode 100644 rest/src/main/webapp/WEB-INF/web.xml create mode 100644 rest/src/test/java/es/uvigo/esei/xcs/rest/GenericTypes.java create mode 100644 rest/src/test/java/es/uvigo/esei/xcs/rest/OwnerResourceRestTest.java create mode 100644 rest/src/test/java/es/uvigo/esei/xcs/rest/OwnerResourceUnitTest.java create mode 100644 rest/src/test/resources-wildfly-embedded-h2/beans.xml create mode 100644 rest/src/test/resources-wildfly-embedded-h2/cleanup-autoincrement.sql create mode 100644 rest/src/test/resources-wildfly-embedded-h2/jboss-web.xml create mode 100644 rest/src/test/resources-wildfly-embedded-h2/standalone.xml create mode 100644 rest/src/test/resources-wildfly-embedded-h2/test-persistence.xml create mode 100644 rest/src/test/resources-wildfly-embedded-mysql/beans.xml create mode 100644 rest/src/test/resources-wildfly-embedded-mysql/cleanup-autoincrement.sql create mode 100644 rest/src/test/resources-wildfly-embedded-mysql/jboss-web.xml create mode 100644 rest/src/test/resources-wildfly-embedded-mysql/mysql-ds.xml create mode 100644 rest/src/test/resources-wildfly-embedded-mysql/standalone.xml create mode 100644 rest/src/test/resources-wildfly-embedded-mysql/test-persistence.xml create mode 100644 rest/src/test/resources/arquillian.xml create mode 100644 rest/src/test/resources/web.xml create mode 100644 tests/pom.xml rename {service/src/test => tests/src/main}/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToEntity.java (100%) rename {service/src/test => tests/src/main}/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToOwner.java (100%) rename {service/src/test => tests/src/main}/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToPet.java (100%) rename {service/src/test => tests/src/main}/java/es/uvigo/esei/xcs/domain/entities/OwnersDataset.java (77%) rename {service/src/test => tests/src/main}/java/es/uvigo/esei/xcs/service/util/security/AdminRoleCaller.java (100%) rename {service/src/test => tests/src/main}/java/es/uvigo/esei/xcs/service/util/security/OwnerRoleCaller.java (100%) rename {service/src/test => tests/src/main}/java/es/uvigo/esei/xcs/service/util/security/RoleCaller.java (100%) rename {service/src/test => tests/src/main}/java/es/uvigo/esei/xcs/service/util/security/TestPrincipal.java (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners-create-pet.xml (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners-create-with-pets.xml (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners-create-without-pets.xml (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners-remove-pet.xml (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners-remove-with-pets.xml (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners-remove-without-pets.xml (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners-update-password.xml (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners-update-pet.xml (100%) rename {service/src/test => tests/src/main}/resources/datasets/owners.xml (100%) rename {service/src/test => tests/src/main}/resources/scripts/cleanup.sql (100%) diff --git a/.gitignore b/.gitignore index e1d8b8b..75d7805 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ #Maven target + +#General +bak diff --git a/README.md b/README.md index 13ec199..c8b071b 100644 --- a/README.md +++ b/README.md @@ -42,22 +42,26 @@ Eclipse should then import 2 projects (`xcs-sample` and `domain`) ## Sample 1: Testing entities Using JUnit and Hamcrest, we will see how to test JPA entities or any other -basic Java class. +Java class. This libraries are the base for every test done in the application. -## Sample 2: Testing with test doubles -Coming soon.... +## Sample 2: Testing EJBs +Using Arquillian and Arquillian Persistence, the EJBs are tested. We wouldn't do +unit testing in this layer, as we don't want to mock the `EntityManager`. -## Sample 3: Testing EJBs -Coming soon... +In this layer we will use some workarounds to set the desired role and principal +in the tests. -## Sample 4: Testing JAX-RS -Coming soon... +## Sample 3: Testing with test doubles +Using EasyMock, we will mock the EJBs to test the REST classes isolated from the +underlying layer. -## Sample 5: Testing AngularJS -Coming soon... +## Sample 4: Testing JAX-RS +Using Arquillian REST Client, we will test the REST API accessing it as real +HTTP clients. -## Sample 6: Testing JSF -Coming soon... +## Sample 5: Testing JSF +Using Arquillian Drone, Arquillian Graphene and Selenium, we will test the JSF +web interface accessing it as real Web clients. -## Sample 7: Additional Testing Tools +## Sample 6: Additional Testing Tools Coming soon... diff --git a/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Administrator.java b/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Administrator.java index c320166..e279bb1 100644 --- a/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Administrator.java +++ b/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Administrator.java @@ -4,6 +4,7 @@ import java.io.Serializable; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; +import javax.xml.bind.annotation.XmlRootElement; /** * An administrator of the application. @@ -12,6 +13,7 @@ import javax.persistence.Entity; */ @Entity @DiscriminatorValue("ADMIN") +@XmlRootElement(name = "admin", namespace = "http://entities.domain.xcs.esei.uvigo.es") public class Administrator extends User implements Serializable { private static final long serialVersionUID = 1L; diff --git a/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Owner.java b/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Owner.java index 312bd79..decf5df 100644 --- a/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Owner.java +++ b/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Owner.java @@ -11,7 +11,9 @@ import java.util.Collection; import javax.persistence.CascadeType; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.OneToMany; +import javax.xml.bind.annotation.XmlRootElement; /** * A pet owner. @@ -20,6 +22,7 @@ import javax.persistence.OneToMany; */ @Entity @DiscriminatorValue("OWNER") +@XmlRootElement(name = "owner", namespace = "http://entities.domain.xcs.esei.uvigo.es") public class Owner extends User implements Serializable { private static final long serialVersionUID = 1L; @@ -27,7 +30,8 @@ public class Owner extends User implements Serializable { mappedBy = "owner", targetEntity = Pet.class, cascade = CascadeType.ALL, - orphanRemoval = true + orphanRemoval = true, + fetch = FetchType.EAGER ) private Collection pets; diff --git a/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Pet.java b/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Pet.java index 1f0996c..0fe3e20 100644 --- a/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Pet.java +++ b/domain/src/main/java/es/uvigo/esei/xcs/domain/entities/Pet.java @@ -17,6 +17,10 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; /** * A pet. @@ -24,6 +28,8 @@ import javax.persistence.TemporalType; * @author Miguel Reboiro-Jato */ @Entity(name = "Pet") +@XmlRootElement(name = "pet", namespace = "http://entities.domain.xcs.esei.uvigo.es") +@XmlAccessorType(XmlAccessType.FIELD) public class Pet implements Serializable { private static final long serialVersionUID = 1L; @@ -43,7 +49,8 @@ public class Pet implements Serializable { private Date birth; @ManyToOne - @JoinColumn(name = "owner", referencedColumnName = "login") + @JoinColumn(name = "owner", referencedColumnName = "login", nullable = false) + @XmlTransient private Owner owner; // Required for JPA. diff --git a/pom.xml b/pom.xml index 662d4c3..f3223c7 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,9 @@ domain + tests service + rest @@ -24,6 +26,8 @@ 7.0 1.1.9.Final + 1.0.0.Alpha3 + 2.47.1 8.2.1.Final @@ -36,8 +40,8 @@ 5.1.21 1.5.10 3.4 - 1.0.0.CR4 - 4.1 + 3.0.13.Final + 2.1.0.Alpha2 1.1.0.Alpha1 @@ -61,6 +65,13 @@ pom import + + org.jboss.arquillian.selenium + selenium-bom + ${arquillian.selenium.version} + pom + import + @@ -73,6 +84,17 @@ service ${project.version} + + ${project.groupId} + rest + ${project.version} + + + ${project.groupId} + tests + ${project.version} + test + @@ -102,6 +124,23 @@ arquillian-persistence-dbunit ${arquillian.persistence.dbunit.version} + + org.jboss.arquillian.extension + arquillian-rest-client-impl-3x + ${arquillian.rest.version} + + + org.jboss.resteasy + resteasy-jackson-provider + ${resteasy.version} + test + + + org.jboss.arquillian.graphene + graphene-webdriver + ${graphene.webdrive.version} + pom + mysql mysql-connector-java @@ -132,17 +171,6 @@ wildfly-arquillian-container-embedded ${wildfly.version} - - - org.jboss.arquillian.container - arquillian-glassfish-embedded-3.1 - ${arquillian.glassfish.embedded.version} - - - org.glassfish.main.extras - glassfish-embedded-all - ${glassfish.embedded.all.version} - diff --git a/rest/.gitignore b/rest/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/rest/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/rest/pom.xml b/rest/pom.xml new file mode 100644 index 0000000..001dee5 --- /dev/null +++ b/rest/pom.xml @@ -0,0 +1,69 @@ + + 4.0.0 + + es.uvigo.esei.xcs + sample + 0.0.1-SNAPSHOT + + rest + war + + REST + XCS Sample - REST + + + + + javax + javaee-api + provided + + + es.uvigo.esei.xcs + service + + + + + es.uvigo.esei.xcs + tests + test + + + junit + junit + test + + + org.hamcrest + java-hamcrest + test + + + org.easymock + easymock + test + + + org.jboss.arquillian.junit + arquillian-junit-container + test + + + org.jboss.arquillian.extension + arquillian-persistence-dbunit + test + + + org.jboss.arquillian.extension + arquillian-rest-client-impl-3x + test + + + org.jboss.resteasy + resteasy-jackson-provider + test + + + \ No newline at end of file diff --git a/rest/src/main/java/es/uvigo/esei/xcs/rest/CORSFilter.java b/rest/src/main/java/es/uvigo/esei/xcs/rest/CORSFilter.java new file mode 100644 index 0000000..4603875 --- /dev/null +++ b/rest/src/main/java/es/uvigo/esei/xcs/rest/CORSFilter.java @@ -0,0 +1,21 @@ +package es.uvigo.esei.xcs.rest; + +import java.io.IOException; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.ext.Provider; + +@Provider +public class CORSFilter implements ContainerResponseFilter { + @Override + public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext cres) + throws IOException { + cres.getHeaders().add("Access-Control-Allow-Origin", "*"); + cres.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); + cres.getHeaders().add("Access-Control-Allow-Credentials", "true"); + cres.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); + cres.getHeaders().add("Access-Control-Max-Age", "1209600"); + } +} \ No newline at end of file diff --git a/rest/src/main/java/es/uvigo/esei/xcs/rest/IllegalArgumentExceptionMapper.java b/rest/src/main/java/es/uvigo/esei/xcs/rest/IllegalArgumentExceptionMapper.java new file mode 100644 index 0000000..3fc49bd --- /dev/null +++ b/rest/src/main/java/es/uvigo/esei/xcs/rest/IllegalArgumentExceptionMapper.java @@ -0,0 +1,18 @@ +package es.uvigo.esei.xcs.rest; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +@Provider +public class IllegalArgumentExceptionMapper +implements ExceptionMapper { + @Override + public Response toResponse(IllegalArgumentException e) { + return Response.status(Response.Status.BAD_REQUEST) + .entity(e.getMessage()) + .type(MediaType.TEXT_PLAIN) + .build(); + } +} diff --git a/rest/src/main/java/es/uvigo/esei/xcs/rest/OwnerResource.java b/rest/src/main/java/es/uvigo/esei/xcs/rest/OwnerResource.java new file mode 100644 index 0000000..6d92173 --- /dev/null +++ b/rest/src/main/java/es/uvigo/esei/xcs/rest/OwnerResource.java @@ -0,0 +1,145 @@ +package es.uvigo.esei.xcs.rest; + +import java.net.URI; + +import javax.ejb.EJB; +import javax.persistence.EntityExistsException; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import es.uvigo.esei.xcs.domain.entities.Owner; +import es.uvigo.esei.xcs.domain.entities.Pet; +import es.uvigo.esei.xcs.service.OwnerService; + +/** + * Resource that represents the owners in the application. + * + * @author Miguel Reboiro Jato + */ +@Path("owner") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class OwnerResource { + @EJB + private OwnerService service; + + @Context + private UriInfo uriInfo; + + /** + * Returns the owner identified by the login. + * + * @param login the login of an owner. + * @return an {@code OK} response containing the {@link Owner} with the + * provided login. + * @throws IllegalArgumentException if {@code login} is {@code null} or + * if it does not correspond with any owner. + */ + @Path("{login}") + @GET + public Response get(@PathParam("login") String login) { + if (login == null) + throw new IllegalArgumentException("login can't be null"); + + final Owner owner = this.service.get(login); + + if (owner == null) + throw new IllegalArgumentException("Owner not found: " + login); + else + return Response.ok(owner).build(); + } + + /** + * Returns the list of owners stored in the application. + * + * @return an {@code OK} response containing the list of owners stored in + * the application. + */ + @GET + public Response list() { + return Response.ok(this.service.list()).build(); + } + + /** + * Creates a new owner. This owner may include a list of pets, that will be + * also created. + * + * @param owner a new owner to be stored. + * @return a {@code CREATED} response with the URI of the new owner in the + * {@code Location} header. + * @throws IllegalArgumentException if owner is {@code null} or if an owner + * with the same login already exists. + */ + @POST + public Response create(Owner owner) { + // Pets are serialized without owner. + assignOwnerToPets(owner); + + try { + final Owner newOwner = this.service.create(owner); + final URI ownerUri = uriInfo.getAbsolutePathBuilder() + .path(newOwner.getLogin()) + .build(); + + return Response.created(ownerUri).build(); + } catch (EntityExistsException eee) { + throw new IllegalArgumentException("The owner already exists"); + } + } + + /** + * Updates an owner. This owner may include a list of pets, that will be + * also created or updated. If the owner does not exists it will be created. + * + * @param owner an owner to be updated. + * @return an empty {@code OK} response. + * @throws IllegalArgumentException if owner is {@code null}. + */ + @PUT + public Response update(Owner owner) { + // Pets are serialized without owner. + assignOwnerToPets(owner); + + this.service.update(owner); + + return Response.ok().build(); + } + + /** + * Deletes an owner. + * + * @param login the login of the owner to be deleted. + * @throws IllegalArgumentException if {@code login} is {@code null} or if + * it does not identifies a valid owner. + */ + @Path("{login}") + @DELETE + public Response delete(@PathParam("login") String login) { + if (login == null) + throw new IllegalArgumentException("login can't be null"); + + this.service.remove(login); + + return Response.ok().build(); + } + + private static void assignOwnerToPets(Owner owner) { + if (owner == null) + throw new IllegalArgumentException("owner can't be null"); + + for (Pet pet : owner.getPets()) { + if (pet.getOwner() != owner) + pet.setOwner(owner); + } + } +} diff --git a/rest/src/main/java/es/uvigo/esei/xcs/rest/PetResource.java b/rest/src/main/java/es/uvigo/esei/xcs/rest/PetResource.java new file mode 100644 index 0000000..55ac0f1 --- /dev/null +++ b/rest/src/main/java/es/uvigo/esei/xcs/rest/PetResource.java @@ -0,0 +1,147 @@ +package es.uvigo.esei.xcs.rest; + +import java.net.URI; + +import javax.ejb.EJB; +import javax.ejb.EJBAccessException; +import javax.persistence.EntityExistsException; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import es.uvigo.esei.xcs.domain.entities.Pet; +import es.uvigo.esei.xcs.service.PetService; + +/** + * Resource that represents the pets in the application. + * + * @author Miguel Reboiro Jato + */ +@Path("pet") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class PetResource { + @EJB + private PetService service; + + @Context + private UriInfo uriInfo; + + /** + * Returns the owner identified by the login. + * + * @param id the identified of a pet. + * @return an {@code OK} response containing the {@link Pet} with the + * provided identifier. + * @throws SecurityException if the current owner does not owns the pet. + */ + @Path("{id}") + @GET + public Response get(@PathParam("id") int id) throws SecurityException { + try { + final Pet pet = this.service.get(id); + + if (pet == null) + throw new IllegalArgumentException("Pet not found: " + id); + else + return Response.ok(pet).build(); + } catch (EJBAccessException eae) { + throw new SecurityException(eae); + } + } + + /** + * Returns the complete list of pets of the current owner. + * + * @return an {@code OK} response containing the complete list of pets of + * the current owner. + */ + @GET + public Response list() { + return Response.ok(this.service.list()).build(); + } + + /** + * Creates a new pet owned by the current user. + * + * @param pet a new owner to be stored. + * @return a {@code CREATED} response with the URI of the new pet in the + * {@code Location} header. + * @throws IllegalArgumentException if pet is {@code null} or if a pet with + * the same identifier already exists. + * @throws SecurityException if the pet already has an owner and it is not + * the current user. If the pet has no owner, this exception will be never + * thrown. + */ + @POST + public Response create(Pet pet) throws SecurityException { + if (pet == null) + throw new IllegalArgumentException("pet can't be null"); + + try { + final Pet newPet = this.service.create(pet); + final URI petUri = uriInfo.getAbsolutePathBuilder() + .path(Integer.toString(newPet.getId())) + .build(); + + return Response.created(petUri).build(); + } catch (EntityExistsException eee) { + throw new IllegalArgumentException("The pet already exists"); + } catch (EJBAccessException eae) { + throw new SecurityException(eae); + } + } + + /** + * Updates the information of a pet. If the pet is not stored, it will be + * created. + * + * @param pet a pet to be updated. + * @return an empty {@code OK} response. + * @throws IllegalArgumentException if pet is {@code null} of it has no + * owner. + * @throws SecurityException if the pet's owner is not the current user. + */ + @PUT + public Response update(Pet pet) throws SecurityException { + if (pet == null) + throw new IllegalArgumentException("pet can't be null"); + + try { + this.service.update(pet); + + return Response.ok().build(); + } catch (EJBAccessException eae) { + throw new SecurityException(eae); + } + } + + /** + * Deletes a pet. + * + * @param id the identifier of the pet to be deleted. + * @throws IllegalArgumentException if there is no pet with the provided + * identifier. + * @throws SecurityException if the pet's owner is not the current user. + */ + @Path("{id}") + @DELETE + public Response delete(@PathParam("id") int id) throws SecurityException { + try { + this.service.remove(id); + + return Response.ok().build(); + } catch (EJBAccessException eae) { + throw new SecurityException(eae); + } + } +} diff --git a/rest/src/main/java/es/uvigo/esei/xcs/rest/SecurityExceptionMapper.java b/rest/src/main/java/es/uvigo/esei/xcs/rest/SecurityExceptionMapper.java new file mode 100644 index 0000000..76edd12 --- /dev/null +++ b/rest/src/main/java/es/uvigo/esei/xcs/rest/SecurityExceptionMapper.java @@ -0,0 +1,17 @@ +package es.uvigo.esei.xcs.rest; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +@Provider +public class SecurityExceptionMapper implements ExceptionMapper { + @Override + public Response toResponse(SecurityException e) { + return Response.status(Response.Status.FORBIDDEN) + .entity(e.getMessage()) + .type(MediaType.TEXT_PLAIN) + .build(); + } +} diff --git a/rest/src/main/resources/META-INF/persistence.xml b/rest/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000..6f685f2 --- /dev/null +++ b/rest/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,20 @@ + + + + java:jboss/datasources/xcs + + es.uvigo.esei.xcs.domain.entities.Owner + es.uvigo.esei.xcs.domain.entities.Pet + es.uvigo.esei.xcs.domain.entities.Administrator + false + + + + + + + \ No newline at end of file diff --git a/rest/src/main/webapp/WEB-INF/beans.xml b/rest/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000..2777559 --- /dev/null +++ b/rest/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,6 @@ + + + diff --git a/rest/src/main/webapp/WEB-INF/jboss-web.xml b/rest/src/main/webapp/WEB-INF/jboss-web.xml new file mode 100644 index 0000000..68c9282 --- /dev/null +++ b/rest/src/main/webapp/WEB-INF/jboss-web.xml @@ -0,0 +1,4 @@ + + + xcs-sample-security-domain + \ No newline at end of file diff --git a/rest/src/main/webapp/WEB-INF/web.xml b/rest/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..f8bf7f3 --- /dev/null +++ b/rest/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,50 @@ + + Pet Store REST + + + javax.ws.rs.core.Application + /api/* + + + + + + admin + /api/owner/* + OPTIONS + + + ADMIN + + + + + + owner + /api/pet/* + OPTIONS + + + OWNER + + + + + + + ADMIN + + + + OWNER + + + + + BASIC + default + + \ No newline at end of file diff --git a/rest/src/test/java/es/uvigo/esei/xcs/rest/GenericTypes.java b/rest/src/test/java/es/uvigo/esei/xcs/rest/GenericTypes.java new file mode 100644 index 0000000..655bb16 --- /dev/null +++ b/rest/src/test/java/es/uvigo/esei/xcs/rest/GenericTypes.java @@ -0,0 +1,20 @@ +package es.uvigo.esei.xcs.rest; + +import java.util.List; + +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.Response; + +import es.uvigo.esei.xcs.domain.entities.Owner; + +public final class GenericTypes { + private GenericTypes() {} + + public static class ListOwnerType extends GenericType> { + public static ListOwnerType INSTANCE = new ListOwnerType(); + + public static List readEntity(Response response) { + return response.readEntity(INSTANCE); + } + } +} diff --git a/rest/src/test/java/es/uvigo/esei/xcs/rest/OwnerResourceRestTest.java b/rest/src/test/java/es/uvigo/esei/xcs/rest/OwnerResourceRestTest.java new file mode 100644 index 0000000..45db6db --- /dev/null +++ b/rest/src/test/java/es/uvigo/esei/xcs/rest/OwnerResourceRestTest.java @@ -0,0 +1,305 @@ +package es.uvigo.esei.xcs.rest; + +import static es.uvigo.esei.xcs.domain.entities.IsEqualsToOwner.containsOwnersInAnyOrder; +import static es.uvigo.esei.xcs.domain.entities.IsEqualsToOwner.equalsToOwner; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.EXISTENT_LOGIN; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.NON_EXISTENT_LOGIN; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.OWNER_WITHOUT_PETS_LOGIN; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.OWNER_WITH_PETS_LOGIN; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.newOwnerWithFreshPets; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.newOwnerWithPersistentPets; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.newOwnerWithoutPets; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.owner; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.owners; +import static javax.ws.rs.client.Entity.json; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.List; + +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.extension.rest.client.ArquillianResteasyResource; +import org.jboss.arquillian.extension.rest.client.Header; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.junit.InSequence; +import org.jboss.arquillian.persistence.Cleanup; +import org.jboss.arquillian.persistence.CleanupUsingScript; +import org.jboss.arquillian.persistence.ShouldMatchDataSet; +import org.jboss.arquillian.persistence.TestExecutionPhase; +import org.jboss.arquillian.persistence.UsingDataSet; +import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +import es.uvigo.esei.xcs.domain.entities.Owner; +import es.uvigo.esei.xcs.domain.entities.OwnersDataset; +import es.uvigo.esei.xcs.rest.GenericTypes.ListOwnerType; +import es.uvigo.esei.xcs.service.OwnerService; + +@RunWith(Arquillian.class) +public class OwnerResourceRestTest { + private final static String BASE_PATH = "api/owner/"; + private static final String BASIC_AUTHORIZATION = "Basic am9zZTpqb3NlcGFzcw="; + + @Deployment + public static Archive createDeployment() { + final WebArchive archive = ShrinkWrap.create(WebArchive.class, "test.war") + .addClass(OwnerResource.class) + .addClasses(CORSFilter.class, IllegalArgumentExceptionMapper.class, SecurityExceptionMapper.class) + .addPackage(OwnerService.class.getPackage()) + .addPackage(Owner.class.getPackage()) + .addAsResource("test-persistence.xml", "META-INF/persistence.xml") + .addAsWebInfResource("jboss-web.xml") + .addAsWebInfResource("web.xml") + .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); + + return archive; + } + + @Test @InSequence(1) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeGet() {} + + @Test @InSequence(2) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testGet( + @ArquillianResteasyResource(BASE_PATH + EXISTENT_LOGIN) ResteasyWebTarget webTarget + ) throws Exception { + final Response response = webTarget.request().get(); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + + final Owner owner = response.readEntity(Owner.class); + final Owner expected = owner("pepe"); + + assertThat(owner, is(equalsToOwner(expected))); + } + + @Test @InSequence(3) + @ShouldMatchDataSet("owners.xml") + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterGet() {} + + + + @Test @InSequence(4) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeGetNonExistent() {} + + @Test @InSequence(5) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testGetNonExistent( + @ArquillianResteasyResource(BASE_PATH + NON_EXISTENT_LOGIN) ResteasyWebTarget webTarget + ) throws Exception { + final Response response = webTarget.request().get(); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.BAD_REQUEST))); + } + + @Test @InSequence(6) + @ShouldMatchDataSet("owners.xml") + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterGetNonExistent() {} + + + + @Test @InSequence(10) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeList() {} + + @Test @InSequence(11) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testList( + @ArquillianResteasyResource(BASE_PATH) ResteasyWebTarget webTarget + ) throws Exception { + final Response response = webTarget.request().get(); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + + final List list = ListOwnerType.readEntity(response); + assertThat(list, containsOwnersInAnyOrder(owners())); + } + + @Test @InSequence(12) + @ShouldMatchDataSet("owners.xml") + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterList() {} + + + + @Test @InSequence(20) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeCreate() {} + + @Test @InSequence(21) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testCreate( + @ArquillianResteasyResource(BASE_PATH) ResteasyWebTarget webTarget + ) throws Exception { + testCreateOwner(webTarget, newOwnerWithoutPets()); + } + + @Test @InSequence(22) + @ShouldMatchDataSet({"owners.xml", "owners-create-without-pets.xml"}) + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterCreate() {} + + + + @Test @InSequence(23) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeCreateWithPets() {} + + @Test @InSequence(24) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testCreateWithPets( + @ArquillianResteasyResource(BASE_PATH) ResteasyWebTarget webTarget + ) throws Exception { + testCreateOwner(webTarget, newOwnerWithFreshPets(), newOwnerWithPersistentPets()); + } + + @Test @InSequence(25) + @ShouldMatchDataSet({"owners.xml", "owners-create-with-pets.xml"}) + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterCreateWithPets() {} + + private void testCreateOwner(WebTarget webTarget, Owner newOwner) { + testCreateOwner(webTarget, newOwner, newOwner); + } + + private void testCreateOwner(WebTarget webTarget, Owner newOwner, Owner persistentOwner) { + final Response response = webTarget.request().post(json(newOwner)); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.CREATED))); + + final String location = response.getHeaderString("Location"); + + final Response responseGet = authorizedJsonRequestGet(location); + final Owner owner = responseGet.readEntity(Owner.class); + assertThat(owner, is(equalsToOwner(persistentOwner))); + } + + + + @Test @InSequence(30) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeUpdatePassword() {} + + @Test @InSequence(31) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testUpdatePassword( + @ArquillianResteasyResource(BASE_PATH) ResteasyWebTarget webTarget + ) throws Exception { + final Owner owner = OwnersDataset.anyOwner(); + owner.changePassword("newpassword"); + + final Response response = webTarget.request().put(json(owner)); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + } + + @Test @InSequence(32) + @ShouldMatchDataSet("owners-update-password.xml") + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterUpdatePassword() {} + + + + @Test @InSequence(40) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeDeleteWithoutPets() {} + + @Test @InSequence(41) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testDeleteWithoutPets( + @ArquillianResteasyResource(BASE_PATH + OWNER_WITHOUT_PETS_LOGIN) ResteasyWebTarget webTarget + ) throws Exception { + final Response response = webTarget.request().delete(); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + } + + @Test @InSequence(42) + @ShouldMatchDataSet("owners-remove-without-pets.xml") + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterDeleteWithoutPets() {} + + + + @Test @InSequence(43) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeDeleteWithPets() {} + + @Test @InSequence(44) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testDeleteWithPets( + @ArquillianResteasyResource(BASE_PATH + OWNER_WITH_PETS_LOGIN) ResteasyWebTarget webTarget + ) throws Exception { + final Response response = webTarget.request().delete(); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + } + + @Test @InSequence(45) + @ShouldMatchDataSet("owners-remove-with-pets.xml") + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterDeleteWithPets() {} + + + + @Test @InSequence(46) + @UsingDataSet("owners.xml") + @Cleanup(phase = TestExecutionPhase.NONE) + public void beforeDeleteNoLogin() {} + + @Test @InSequence(47) + @RunAsClient + @Header(name = "Authorization", value = BASIC_AUTHORIZATION) + public void testDeleteNoLogin( + @ArquillianResteasyResource(BASE_PATH) ResteasyWebTarget webTarget + ) throws Exception { + final Response response = webTarget.request().delete(); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.METHOD_NOT_ALLOWED))); + } + + @Test @InSequence(48) + @ShouldMatchDataSet("owners.xml") + @CleanupUsingScript({ "cleanup.sql", "cleanup-autoincrement.sql" }) + public void afterDeleteNoLogin() {} + + + private static Response authorizedJsonRequestGet(String uri) { + return ClientBuilder.newClient().target(uri) + .request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", BASIC_AUTHORIZATION) + .get(); + } +} diff --git a/rest/src/test/java/es/uvigo/esei/xcs/rest/OwnerResourceUnitTest.java b/rest/src/test/java/es/uvigo/esei/xcs/rest/OwnerResourceUnitTest.java new file mode 100644 index 0000000..a635711 --- /dev/null +++ b/rest/src/test/java/es/uvigo/esei/xcs/rest/OwnerResourceUnitTest.java @@ -0,0 +1,198 @@ +package es.uvigo.esei.xcs.rest; + +import static es.uvigo.esei.xcs.domain.entities.IsEqualsToOwner.containsOwnersInAnyOrder; +import static es.uvigo.esei.xcs.domain.entities.IsEqualsToOwner.equalsToOwner; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.anyLogin; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.anyOwner; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.newOwnerWithFreshPets; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.newOwnerWithPersistentPets; +import static es.uvigo.esei.xcs.domain.entities.OwnersDataset.owners; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.collection.IsEmptyCollection.empty; +import static org.junit.Assert.assertThat; + +import java.net.URI; +import java.util.List; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.easymock.EasyMockRunner; +import org.easymock.EasyMockSupport; +import org.easymock.Mock; +import org.easymock.TestSubject; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import es.uvigo.esei.xcs.domain.entities.Owner; +import es.uvigo.esei.xcs.domain.entities.OwnersDataset; +import es.uvigo.esei.xcs.service.OwnerService; + +@RunWith(EasyMockRunner.class) +public class OwnerResourceUnitTest extends EasyMockSupport { + @TestSubject + private OwnerResource resource = new OwnerResource(); + + @Mock + private OwnerService facade; + + @Mock + private UriInfo uriInfo; + + @Mock + private UriBuilder uriBuilder; + + @After + public void tearDown() throws Exception { + verifyAll(); + } + + @Test + public void testGet() { + final Owner owner = OwnersDataset.anyOwner(); + + expect(facade.get(owner.getLogin())) + .andReturn(owner); + + replayAll(); + + final Response response = resource.get(owner.getLogin()); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + assertThat(response.getEntity(), is(instanceOf(Owner.class))); + assertThat((Owner) response.getEntity(), is(equalsToOwner(owner))); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetNull() { + replayAll(); + + resource.get(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetMissing() { + final String login = anyLogin(); + + expect(facade.get(login)) + .andReturn(null); + + replayAll(); + + resource.get(login); + } + + @Test + @SuppressWarnings("unchecked") + public void testList() { + final Owner[] owners = owners(); + + expect(facade.list()) + .andReturn(asList(owners)); + + replayAll(); + + final Response response = resource.list(); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + assertThat(response.getEntity(), is(instanceOf(List.class))); + assertThat((List) response.getEntity(), containsOwnersInAnyOrder(owners)); + } + + @Test + @SuppressWarnings("unchecked") + public void testListEmpty() { + expect(facade.list()) + .andReturn(emptyList()); + + replayAll(); + + final Response response = resource.list(); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + assertThat(response.getEntity(), is(instanceOf(List.class))); + assertThat((List) response.getEntity(), is(empty())); + } + + @Test + public void testCreate() throws Exception { + final Owner newOwner = newOwnerWithFreshPets(); + final Owner createdOwner = newOwnerWithPersistentPets(); + + final URI mockUri = new URI("http://host/api/owner/" + newOwner.getLogin()); + + expect(facade.create(newOwner)) + .andReturn(createdOwner); + + expect(uriInfo.getAbsolutePathBuilder()) + .andReturn(uriBuilder); + expect(uriBuilder.path(newOwner.getLogin())) + .andReturn(uriBuilder); + expect(uriBuilder.build()) + .andReturn(mockUri); + + replayAll(); + + final Response response = resource.create(newOwner); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.CREATED))); + assertThat(response.getHeaderString("Location"), is(equalTo(mockUri.toString()))); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateNull() { + replayAll(); + + resource.create(null); + } + + @Test + public void testUpdate() { + final Owner owner = anyOwner(); + + facade.update(owner); + + expectLastCall().andReturn(owner); + + replayAll(); + + final Response response = resource.update(owner); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + } + + @Test(expected = IllegalArgumentException.class) + public void testUpdateNull() { + replayAll(); + + resource.update(null); + } + + @Test + public void testDelete() { + final String login = OwnersDataset.anyLogin(); + + facade.remove(login); + + replayAll(); + + final Response response = resource.delete(login); + + assertThat(response.getStatusInfo(), is(equalTo(Response.Status.OK))); + } + + @Test(expected = IllegalArgumentException.class) + public void testDeleteNull() { + replayAll(); + + resource.delete(null); + } +} diff --git a/rest/src/test/resources-wildfly-embedded-h2/beans.xml b/rest/src/test/resources-wildfly-embedded-h2/beans.xml new file mode 100644 index 0000000..01d52d1 --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-h2/beans.xml @@ -0,0 +1,11 @@ + + + + es.uvigo.esei.xcs.service.util.security.TestPrincipal + + + diff --git a/rest/src/test/resources-wildfly-embedded-h2/cleanup-autoincrement.sql b/rest/src/test/resources-wildfly-embedded-h2/cleanup-autoincrement.sql new file mode 100644 index 0000000..245049c --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-h2/cleanup-autoincrement.sql @@ -0,0 +1 @@ +ALTER TABLE Pet ALTER COLUMN id RESTART WITH 1; \ No newline at end of file diff --git a/rest/src/test/resources-wildfly-embedded-h2/jboss-web.xml b/rest/src/test/resources-wildfly-embedded-h2/jboss-web.xml new file mode 100644 index 0000000..68c9282 --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-h2/jboss-web.xml @@ -0,0 +1,4 @@ + + + xcs-sample-security-domain + \ No newline at end of file diff --git a/rest/src/test/resources-wildfly-embedded-h2/standalone.xml b/rest/src/test/resources-wildfly-embedded-h2/standalone.xml new file mode 100644 index 0000000..fd979b9 --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-h2/standalone.xml @@ -0,0 +1,415 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + h2 + + sa + sa + + + + + org.h2.jdbcx.JdbcDataSource + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${jboss.bind.address:127.0.0.1} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rest/src/test/resources-wildfly-embedded-h2/test-persistence.xml b/rest/src/test/resources-wildfly-embedded-h2/test-persistence.xml new file mode 100644 index 0000000..ae6b5d3 --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-h2/test-persistence.xml @@ -0,0 +1,17 @@ + + + + java:jboss/datasources/ExampleDS + + false + + + + + + + \ No newline at end of file diff --git a/rest/src/test/resources-wildfly-embedded-mysql/beans.xml b/rest/src/test/resources-wildfly-embedded-mysql/beans.xml new file mode 100644 index 0000000..01d52d1 --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-mysql/beans.xml @@ -0,0 +1,11 @@ + + + + es.uvigo.esei.xcs.service.util.security.TestPrincipal + + + diff --git a/rest/src/test/resources-wildfly-embedded-mysql/cleanup-autoincrement.sql b/rest/src/test/resources-wildfly-embedded-mysql/cleanup-autoincrement.sql new file mode 100644 index 0000000..a51d23b --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-mysql/cleanup-autoincrement.sql @@ -0,0 +1 @@ +ALTER TABLE Pet AUTO_INCREMENT = 1; \ No newline at end of file diff --git a/rest/src/test/resources-wildfly-embedded-mysql/jboss-web.xml b/rest/src/test/resources-wildfly-embedded-mysql/jboss-web.xml new file mode 100644 index 0000000..68c9282 --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-mysql/jboss-web.xml @@ -0,0 +1,4 @@ + + + xcs-sample-security-domain + \ No newline at end of file diff --git a/rest/src/test/resources-wildfly-embedded-mysql/mysql-ds.xml b/rest/src/test/resources-wildfly-embedded-mysql/mysql-ds.xml new file mode 100644 index 0000000..e35c79b --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-mysql/mysql-ds.xml @@ -0,0 +1,17 @@ + + + + jdbc:mysql://localhost:3306/xcs + mysql-connector-java-${mysql.version}.jar + + 30 + + + xcs + xcs + + + + \ No newline at end of file diff --git a/rest/src/test/resources-wildfly-embedded-mysql/standalone.xml b/rest/src/test/resources-wildfly-embedded-mysql/standalone.xml new file mode 100644 index 0000000..28088fd --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-mysql/standalone.xml @@ -0,0 +1,415 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + h2 + + sa + sa + + + + + org.h2.jdbcx.JdbcDataSource + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${jboss.bind.address:127.0.0.1} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rest/src/test/resources-wildfly-embedded-mysql/test-persistence.xml b/rest/src/test/resources-wildfly-embedded-mysql/test-persistence.xml new file mode 100644 index 0000000..1d6b2be --- /dev/null +++ b/rest/src/test/resources-wildfly-embedded-mysql/test-persistence.xml @@ -0,0 +1,17 @@ + + + + java:jboss/datasources/xcs + + false + + + + + + + \ No newline at end of file diff --git a/rest/src/test/resources/arquillian.xml b/rest/src/test/resources/arquillian.xml new file mode 100644 index 0000000..dd1edb8 --- /dev/null +++ b/rest/src/test/resources/arquillian.xml @@ -0,0 +1,25 @@ + + + + + CLEAN_INSERT + + + + + target/wildfly-${wildfly.version} + target/wildfly-${wildfly.version}/modules + + + + + + target/wildfly-${wildfly.version} + target/wildfly-${wildfly.version}/modules + + + \ No newline at end of file diff --git a/rest/src/test/resources/web.xml b/rest/src/test/resources/web.xml new file mode 100644 index 0000000..f8bf7f3 --- /dev/null +++ b/rest/src/test/resources/web.xml @@ -0,0 +1,50 @@ + + Pet Store REST + + + javax.ws.rs.core.Application + /api/* + + + + + + admin + /api/owner/* + OPTIONS + + + ADMIN + + + + + + owner + /api/pet/* + OPTIONS + + + OWNER + + + + + + + ADMIN + + + + OWNER + + + + + BASIC + default + + \ No newline at end of file diff --git a/service/pom.xml b/service/pom.xml index e94ef22..65877b2 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -25,6 +25,11 @@ + + es.uvigo.esei.xcs + tests + test + junit junit diff --git a/service/src/main/java/es/uvigo/esei/xcs/service/PetService.java b/service/src/main/java/es/uvigo/esei/xcs/service/PetService.java index f57aa26..37d29d1 100644 --- a/service/src/main/java/es/uvigo/esei/xcs/service/PetService.java +++ b/service/src/main/java/es/uvigo/esei/xcs/service/PetService.java @@ -89,7 +89,7 @@ public class PetService { * Updates the information of a pet. If the pet is not stored, it will be * created. * - * @param pet the pet to be updated. + * @param pet a pet to be updated. * @throws IllegalArgumentException if the pet has no owner. * @throws EJBAccessException if the pet's owner is not the current user. */ diff --git a/tests/pom.xml b/tests/pom.xml new file mode 100644 index 0000000..b34c7e3 --- /dev/null +++ b/tests/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + es.uvigo.esei.xcs + sample + 0.0.1-SNAPSHOT + + tests + jar + + Tests + XCS Sample - Test Utility Classes + + + + javax + javaee-api + compile + + + ${project.groupId} + domain + compile + + + junit + junit + compile + + + org.hamcrest + java-hamcrest + compile + + + diff --git a/service/src/test/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToEntity.java b/tests/src/main/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToEntity.java similarity index 100% rename from service/src/test/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToEntity.java rename to tests/src/main/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToEntity.java diff --git a/service/src/test/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToOwner.java b/tests/src/main/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToOwner.java similarity index 100% rename from service/src/test/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToOwner.java rename to tests/src/main/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToOwner.java diff --git a/service/src/test/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToPet.java b/tests/src/main/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToPet.java similarity index 100% rename from service/src/test/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToPet.java rename to tests/src/main/java/es/uvigo/esei/xcs/domain/entities/IsEqualsToPet.java diff --git a/service/src/test/java/es/uvigo/esei/xcs/domain/entities/OwnersDataset.java b/tests/src/main/java/es/uvigo/esei/xcs/domain/entities/OwnersDataset.java similarity index 77% rename from service/src/test/java/es/uvigo/esei/xcs/domain/entities/OwnersDataset.java rename to tests/src/main/java/es/uvigo/esei/xcs/domain/entities/OwnersDataset.java index 6946a51..2b4bf11 100644 --- a/service/src/test/java/es/uvigo/esei/xcs/domain/entities/OwnersDataset.java +++ b/tests/src/main/java/es/uvigo/esei/xcs/domain/entities/OwnersDataset.java @@ -8,6 +8,11 @@ import java.util.Date; import java.util.Set; public class OwnersDataset { + public static final String EXISTENT_LOGIN = "pepe"; + public static final String NON_EXISTENT_LOGIN = "non-existent"; + public static final String OWNER_WITH_PETS_LOGIN = "juan"; + public static final String OWNER_WITHOUT_PETS_LOGIN = "lorena"; + public static Owner owner(String login) { return stream(owners()) .filter(owner -> owner.getLogin().equals(login)) @@ -32,9 +37,9 @@ public class OwnersDataset { public static Owner[] owners() { return new Owner[] { - new Owner("pepe", "pepepass", + new Owner(EXISTENT_LOGIN, "pepepass", new Pet(1, "Pepecat", AnimalType.CAT, new Date(946684861000L))), - new Owner("juan", "juanpass", + new Owner(OWNER_WITH_PETS_LOGIN, "juanpass", new Pet(2, "Max", AnimalType.CAT, new Date(946684861000L)), new Pet(3, "Juandog", AnimalType.DOG, new Date(946684861000L)) ), @@ -43,7 +48,7 @@ public class OwnersDataset { new Pet(5, "Max", AnimalType.DOG, new Date(946684861000L)), new Pet(6, "Anabird", AnimalType.BIRD, new Date(946684861000L)) ), - new Owner("lorena", "lorenapass") + new Owner(OWNER_WITHOUT_PETS_LOGIN, "lorenapass") }; } @@ -79,11 +84,11 @@ public class OwnersDataset { } public static Owner ownerWithPets() { - return owners()[1]; + return owner(OWNER_WITH_PETS_LOGIN); } public static Owner ownerWithoutPets() { - return owners()[3]; + return owner(OWNER_WITHOUT_PETS_LOGIN); } public static Pet anyPet() { @@ -98,14 +103,26 @@ public class OwnersDataset { return new Pet("Lorenacat", AnimalType.CAT, new Date(946684861000L), owner); } + public static String anyLogin() { + return EXISTENT_LOGIN; + } + public static String existentLogin() { - return "pepe"; + return EXISTENT_LOGIN; + } + + public static String nonExistentLogin() { + return NON_EXISTENT_LOGIN; } public static String existentPetName() { return "Pepecat"; } + public static String nonExistentPetName() { + return "NonExistentPet"; + } + public static int existentPetId() { return 2; } @@ -113,4 +130,9 @@ public class OwnersDataset { public static int nonExistentPetId() { return 1000000; } + + public static Owner nonExistentOwner() { + final String login = nonExistentLogin(); + return new Owner(login, login + "pass"); + } } diff --git a/service/src/test/java/es/uvigo/esei/xcs/service/util/security/AdminRoleCaller.java b/tests/src/main/java/es/uvigo/esei/xcs/service/util/security/AdminRoleCaller.java similarity index 100% rename from service/src/test/java/es/uvigo/esei/xcs/service/util/security/AdminRoleCaller.java rename to tests/src/main/java/es/uvigo/esei/xcs/service/util/security/AdminRoleCaller.java diff --git a/service/src/test/java/es/uvigo/esei/xcs/service/util/security/OwnerRoleCaller.java b/tests/src/main/java/es/uvigo/esei/xcs/service/util/security/OwnerRoleCaller.java similarity index 100% rename from service/src/test/java/es/uvigo/esei/xcs/service/util/security/OwnerRoleCaller.java rename to tests/src/main/java/es/uvigo/esei/xcs/service/util/security/OwnerRoleCaller.java diff --git a/service/src/test/java/es/uvigo/esei/xcs/service/util/security/RoleCaller.java b/tests/src/main/java/es/uvigo/esei/xcs/service/util/security/RoleCaller.java similarity index 100% rename from service/src/test/java/es/uvigo/esei/xcs/service/util/security/RoleCaller.java rename to tests/src/main/java/es/uvigo/esei/xcs/service/util/security/RoleCaller.java diff --git a/service/src/test/java/es/uvigo/esei/xcs/service/util/security/TestPrincipal.java b/tests/src/main/java/es/uvigo/esei/xcs/service/util/security/TestPrincipal.java similarity index 100% rename from service/src/test/java/es/uvigo/esei/xcs/service/util/security/TestPrincipal.java rename to tests/src/main/java/es/uvigo/esei/xcs/service/util/security/TestPrincipal.java diff --git a/service/src/test/resources/datasets/owners-create-pet.xml b/tests/src/main/resources/datasets/owners-create-pet.xml similarity index 100% rename from service/src/test/resources/datasets/owners-create-pet.xml rename to tests/src/main/resources/datasets/owners-create-pet.xml diff --git a/service/src/test/resources/datasets/owners-create-with-pets.xml b/tests/src/main/resources/datasets/owners-create-with-pets.xml similarity index 100% rename from service/src/test/resources/datasets/owners-create-with-pets.xml rename to tests/src/main/resources/datasets/owners-create-with-pets.xml diff --git a/service/src/test/resources/datasets/owners-create-without-pets.xml b/tests/src/main/resources/datasets/owners-create-without-pets.xml similarity index 100% rename from service/src/test/resources/datasets/owners-create-without-pets.xml rename to tests/src/main/resources/datasets/owners-create-without-pets.xml diff --git a/service/src/test/resources/datasets/owners-remove-pet.xml b/tests/src/main/resources/datasets/owners-remove-pet.xml similarity index 100% rename from service/src/test/resources/datasets/owners-remove-pet.xml rename to tests/src/main/resources/datasets/owners-remove-pet.xml diff --git a/service/src/test/resources/datasets/owners-remove-with-pets.xml b/tests/src/main/resources/datasets/owners-remove-with-pets.xml similarity index 100% rename from service/src/test/resources/datasets/owners-remove-with-pets.xml rename to tests/src/main/resources/datasets/owners-remove-with-pets.xml diff --git a/service/src/test/resources/datasets/owners-remove-without-pets.xml b/tests/src/main/resources/datasets/owners-remove-without-pets.xml similarity index 100% rename from service/src/test/resources/datasets/owners-remove-without-pets.xml rename to tests/src/main/resources/datasets/owners-remove-without-pets.xml diff --git a/service/src/test/resources/datasets/owners-update-password.xml b/tests/src/main/resources/datasets/owners-update-password.xml similarity index 100% rename from service/src/test/resources/datasets/owners-update-password.xml rename to tests/src/main/resources/datasets/owners-update-password.xml diff --git a/service/src/test/resources/datasets/owners-update-pet.xml b/tests/src/main/resources/datasets/owners-update-pet.xml similarity index 100% rename from service/src/test/resources/datasets/owners-update-pet.xml rename to tests/src/main/resources/datasets/owners-update-pet.xml diff --git a/service/src/test/resources/datasets/owners.xml b/tests/src/main/resources/datasets/owners.xml similarity index 100% rename from service/src/test/resources/datasets/owners.xml rename to tests/src/main/resources/datasets/owners.xml diff --git a/service/src/test/resources/scripts/cleanup.sql b/tests/src/main/resources/scripts/cleanup.sql similarity index 100% rename from service/src/test/resources/scripts/cleanup.sql rename to tests/src/main/resources/scripts/cleanup.sql -- 2.18.1