diff --git a/README.md b/README.md
index 1439356b..b30cfe66 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ or you can add a maven dependency since it is now available in Maven central:
com.mmnaseri.utils
spring-data-mock
- ${latest-version}
+ ${spring-data-mock.version}
test
@@ -52,7 +52,7 @@ using [Maven exclusions](https://maven.apache.org/guides/introduction/introducti
com.mmnaseri.utils
spring-data-mock
- ${latest-version}
+ ${spring-data-mock.version}
test
diff --git a/spring-data-mock-sample-jpa/pom.xml b/spring-data-mock-sample-jpa/pom.xml
index 1d32911c..13814e9e 100644
--- a/spring-data-mock-sample-jpa/pom.xml
+++ b/spring-data-mock-sample-jpa/pom.xml
@@ -35,7 +35,7 @@
1.12.1.RELEASE
1.10.1.RELEASE
1.0.2
- 1.1.3
+ 1.1.4
6.9.6
1.3
UTF-8
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/Group.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/Group.java
new file mode 100644
index 00000000..6e303bb7
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/Group.java
@@ -0,0 +1,33 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:03 PM)
+ */
+@Entity
+public class Group {
+
+ @Id
+ private String id;
+ private String name;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/Membership.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/Membership.java
new file mode 100644
index 00000000..c91bee86
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/Membership.java
@@ -0,0 +1,45 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:04 PM)
+ */
+@Entity
+public class Membership {
+
+ @Id
+ private String id;
+ @ManyToOne
+ private User user;
+ @ManyToOne
+ private Group group;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public void setUser(User user) {
+ this.user = user;
+ }
+
+ public Group getGroup() {
+ return group;
+ }
+
+ public void setGroup(Group group) {
+ this.group = group;
+ }
+
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/User.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/User.java
new file mode 100644
index 00000000..f76dc40f
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/model/User.java
@@ -0,0 +1,51 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:03 PM)
+ */
+@Entity
+public class User {
+
+ @Id
+ private String id;
+ private String username;
+ private String email;
+ private String passwordHash;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getPasswordHash() {
+ return passwordHash;
+ }
+
+ public void setPasswordHash(String passwordHash) {
+ this.passwordHash = passwordHash;
+ }
+
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/GroupRepository.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/GroupRepository.java
new file mode 100644
index 00000000..5098bf79
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/GroupRepository.java
@@ -0,0 +1,11 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.repository;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Group;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:05 PM)
+ */
+public interface GroupRepository extends JpaRepository {
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/MembershipRepository.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/MembershipRepository.java
new file mode 100644
index 00000000..1a90510f
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/MembershipRepository.java
@@ -0,0 +1,21 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.repository;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Group;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Membership;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:06 PM)
+ */
+public interface MembershipRepository extends JpaRepository {
+
+ List findByUser(User user);
+
+ List findByGroup(Group group);
+
+ Membership findByUserAndGroup(User user, Group group);
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/UserRepository.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/UserRepository.java
new file mode 100644
index 00000000..4064c84b
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/repository/UserRepository.java
@@ -0,0 +1,18 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.repository;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:05 PM)
+ */
+public interface UserRepository extends JpaRepository {
+
+ User findByUsernameOrEmailAllIgnoreCase(String username, String email);
+
+ User findByUsernameIgnoreCase(String username);
+
+ User findByEmailIgnoreCase(String email);
+
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/GroupService.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/GroupService.java
new file mode 100644
index 00000000..3bbf9081
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/GroupService.java
@@ -0,0 +1,25 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.service;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Group;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.User;
+
+import java.util.List;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 5:26 PM)
+ */
+public interface GroupService {
+
+ Group createGroup(String name);
+
+ void deleteGroup(Group group);
+
+ void join(Group group, User user);
+
+ void leave(Group group, User user);
+
+ List members(Group group);
+
+ List groups(User user);
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/UserService.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/UserService.java
new file mode 100644
index 00000000..6f79f8d3
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/UserService.java
@@ -0,0 +1,21 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.service;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.User;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:06 PM)
+ */
+public interface UserService {
+
+ User createUser(String username, String email, String password);
+
+ void updatePassword(String handle, String oldPassword, String newPassword);
+
+ void deleteUser(String handle);
+
+ User lookup(String handle);
+
+ User authenticate(String handle, String password);
+
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultGroupService.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultGroupService.java
new file mode 100644
index 00000000..cd179897
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultGroupService.java
@@ -0,0 +1,81 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.service.impl;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Group;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Membership;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.User;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.GroupRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.MembershipRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.service.GroupService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/30/16, 9:16 AM)
+ */
+public class DefaultGroupService implements GroupService {
+
+ private final GroupRepository groupRepository;
+ private final MembershipRepository membershipRepository;
+
+ public DefaultGroupService(GroupRepository groupRepository, MembershipRepository membershipRepository) {
+ this.groupRepository = groupRepository;
+ this.membershipRepository = membershipRepository;
+ }
+
+ @Override
+ public Group createGroup(String name) {
+ final Group group = new Group();
+ group.setName(name);
+ return groupRepository.save(group);
+ }
+
+ @Override
+ public void deleteGroup(Group group) {
+ final List memberships = membershipRepository.findByGroup(group);
+ membershipRepository.delete(memberships);
+ groupRepository.delete(group);
+ }
+
+ @Override
+ public void join(Group group, User user) {
+ if (membershipRepository.findByUserAndGroup(user, group) != null) {
+ return;
+ }
+ final Membership membership = new Membership();
+ membership.setGroup(group);
+ membership.setUser(user);
+ membershipRepository.save(membership);
+ }
+
+ @Override
+ public void leave(Group group, User user) {
+ final Membership membership = membershipRepository.findByUserAndGroup(user, group);
+ if (membership == null) {
+ return;
+ }
+ membershipRepository.delete(membership);
+ }
+
+ @Override
+ public List members(Group group) {
+ final List memberships = membershipRepository.findByGroup(group);
+ final List users = new ArrayList<>();
+ for (Membership membership : memberships) {
+ users.add(membership.getUser());
+ }
+ return users;
+ }
+
+ @Override
+ public List groups(User user) {
+ final List memberships = membershipRepository.findByUser(user);
+ final List groups = new ArrayList<>();
+ for (Membership membership : memberships) {
+ groups.add(membership.getGroup());
+ }
+ return groups;
+ }
+
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultUserService.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultUserService.java
new file mode 100644
index 00000000..4266c98f
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultUserService.java
@@ -0,0 +1,78 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.service.impl;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Group;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.User;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.UserRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.service.GroupService;
+import com.mmnaseri.utils.samples.spring.data.jpa.service.UserService;
+import com.mmnaseri.utils.samples.spring.data.jpa.utils.EncryptionUtils;
+
+import java.util.List;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:09 PM)
+ */
+public class DefaultUserService implements UserService {
+
+ private final UserRepository repository;
+ private final GroupService groupService;
+
+ public DefaultUserService(UserRepository repository, GroupService groupService) {
+ this.repository = repository;
+ this.groupService = groupService;
+ }
+
+ @Override
+ public User createUser(String username, String email, String password) {
+ if (repository.findByUsernameIgnoreCase(username) != null || repository.findByEmailIgnoreCase(email) != null) {
+ throw new IllegalArgumentException();
+ }
+ final User user = new User();
+ user.setUsername(username);
+ user.setEmail(email);
+ user.setPasswordHash(EncryptionUtils.encrypt(password));
+ return repository.save(user);
+ }
+
+ @Override
+ public void updatePassword(String handle, String oldPassword, String newPassword) {
+ final User user = authenticate(handle, oldPassword);
+ if (user == null) {
+ throw new IllegalStateException();
+ }
+ user.setPasswordHash(EncryptionUtils.encrypt(newPassword));
+ repository.save(user);
+ }
+
+ @Override
+ public void deleteUser(String handle) {
+ final User user = lookup(handle);
+ if (user == null) {
+ throw new IllegalStateException();
+ }
+ final List groups = groupService.groups(user);
+ for (Group group : groups) {
+ groupService.leave(group, user);
+ }
+ repository.delete(user);
+ }
+
+ @Override
+ public User lookup(String handle) {
+ return repository.findByUsernameOrEmailAllIgnoreCase(handle, handle);
+ }
+
+ @Override
+ public User authenticate(String handle, String password) {
+ final User user = lookup(handle);
+ if (user == null) {
+ return null;
+ }
+ if (user.getPasswordHash().equals(EncryptionUtils.encrypt(password))) {
+ return user;
+ }
+ return null;
+ }
+
+}
diff --git a/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/utils/EncryptionUtils.java b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/utils/EncryptionUtils.java
new file mode 100644
index 00000000..d150f9f6
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/main/java/com/mmnaseri/utils/samples/spring/data/jpa/utils/EncryptionUtils.java
@@ -0,0 +1,21 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.utils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:10 PM)
+ */
+public class EncryptionUtils {
+
+ public static String encrypt(String text) {
+ try {
+ final MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ return new String(digest.digest(text.getBytes()));
+ } catch (NoSuchAlgorithmException e) {
+ return text;
+ }
+ }
+
+}
diff --git a/spring-data-mock-sample-jpa/src/test/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultGroupServiceTest.java b/spring-data-mock-sample-jpa/src/test/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultGroupServiceTest.java
new file mode 100644
index 00000000..6900fc1a
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/test/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultGroupServiceTest.java
@@ -0,0 +1,143 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.service.impl;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Group;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Membership;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.User;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.GroupRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.MembershipRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.UserRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.service.GroupService;
+import com.mmnaseri.utils.samples.spring.data.jpa.service.UserService;
+import com.mmnaseri.utils.spring.data.dsl.factory.RepositoryFactoryBuilder;
+import com.mmnaseri.utils.spring.data.dsl.factory.Start;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/30/16, 9:15 AM)
+ */
+public class DefaultGroupServiceTest {
+
+ private GroupService service;
+ private GroupRepository groupRepository;
+ private UserService userService;
+ private MembershipRepository membershipRepository;
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ final Start builder = RepositoryFactoryBuilder.builder();
+ groupRepository = builder.mock(GroupRepository.class);
+ membershipRepository = builder.mock(MembershipRepository.class);
+ final UserRepository userRepository = builder.mock(UserRepository.class);
+ service = new DefaultGroupService(groupRepository, membershipRepository);
+ userService = new DefaultUserService(userRepository, service);
+ }
+
+ @Test
+ public void testCreatingAGroup() throws Exception {
+ assertThat(groupRepository.count(), is(0L));
+ final String name = "My Group";
+ final Group group = service.createGroup(name);
+ assertThat(group, is(notNullValue()));
+ assertThat(group.getName(), is(name));
+ assertThat(groupRepository.count(), is(1L));
+ final Group found = groupRepository.findOne(group.getId());
+ assertThat(found, is(notNullValue()));
+ assertThat(found.getName(), is(name));
+ }
+
+ @Test
+ public void testDeletingAnEmptyGroup() throws Exception {
+ Group group = new Group();
+ group.setName("My Group");
+ group = groupRepository.save(group);
+ service.deleteGroup(group);
+ assertThat(groupRepository.count(), is(0L));
+ }
+
+ @Test
+ public void testEstablishingMembership() throws Exception {
+ Group group = new Group();
+ group.setName("My Group");
+ group = groupRepository.save(group);
+ final User user = userService.createUser("milad", "milad@domain.com", "123456");
+ service.join(group, user);
+ assertThat(membershipRepository.count(), is(1L));
+ final Membership membership = membershipRepository.findAll().get(0);
+ assertThat(membership, is(notNullValue()));
+ assertThat(membership.getGroup(), is(notNullValue()));
+ assertThat(membership.getGroup().getId(), is(group.getId()));
+ assertThat(membership.getUser(), is(notNullValue()));
+ assertThat(membership.getUser().getId(), is(user.getId()));
+ }
+
+ @Test
+ public void testBreakingAMembership() throws Exception {
+ Group group = new Group();
+ group.setName("My Group");
+ group = groupRepository.save(group);
+ final User user = userService.createUser("milad", "milad@domain.com", "123456");
+ final Membership membership = new Membership();
+ membership.setUser(user);
+ membership.setGroup(group);
+ membershipRepository.save(membership);
+ service.leave(group, user);
+ assertThat(membershipRepository.count(), is(0L));
+ }
+
+ @Test
+ public void testListingGroupMembers() throws Exception {
+ Group group = new Group();
+ group.setName("My Group");
+ group = groupRepository.save(group);
+ final User user = userService.createUser("milad", "milad@domain.com", "123456");
+ final Membership membership = new Membership();
+ membership.setUser(user);
+ membership.setGroup(group);
+ membershipRepository.save(membership);
+ final List users = service.members(group);
+ assertThat(users, is(notNullValue()));
+ assertThat(users, hasSize(1));
+ assertThat(users.get(0), is(notNullValue()));
+ assertThat(users.get(0).getId(), is(user.getId()));
+ }
+
+ @Test
+ public void testListingUserGroups() throws Exception {
+ Group group = new Group();
+ group.setName("My Group");
+ group = groupRepository.save(group);
+ final User user = userService.createUser("milad", "milad@domain.com", "123456");
+ final Membership membership = new Membership();
+ membership.setUser(user);
+ membership.setGroup(group);
+ membershipRepository.save(membership);
+ final List groups = service.groups(user);
+ assertThat(groups, is(notNullValue()));
+ assertThat(groups, hasSize(1));
+ assertThat(groups.get(0), is(notNullValue()));
+ assertThat(groups.get(0).getId(), is(group.getId()));
+ }
+
+ @Test
+ public void testDeletingAGroupWithMembers() throws Exception {
+ Group group = new Group();
+ group.setName("My Group");
+ group = groupRepository.save(group);
+ final User user = userService.createUser("milad", "milad@domain.com", "123456");
+ final Membership membership = new Membership();
+ membership.setUser(user);
+ membership.setGroup(group);
+ membershipRepository.save(membership);
+ service.deleteGroup(group);
+ assertThat(groupRepository.count(), is(0L));
+ assertThat(membershipRepository.count(), is(0L));
+ }
+
+}
diff --git a/spring-data-mock-sample-jpa/src/test/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultUserServiceTest.java b/spring-data-mock-sample-jpa/src/test/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultUserServiceTest.java
new file mode 100644
index 00000000..b2d0e4b4
--- /dev/null
+++ b/spring-data-mock-sample-jpa/src/test/java/com/mmnaseri/utils/samples/spring/data/jpa/service/impl/DefaultUserServiceTest.java
@@ -0,0 +1,146 @@
+package com.mmnaseri.utils.samples.spring.data.jpa.service.impl;
+
+import com.mmnaseri.utils.samples.spring.data.jpa.model.Group;
+import com.mmnaseri.utils.samples.spring.data.jpa.model.User;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.GroupRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.MembershipRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.repository.UserRepository;
+import com.mmnaseri.utils.samples.spring.data.jpa.service.GroupService;
+import com.mmnaseri.utils.spring.data.dsl.factory.RepositoryFactoryBuilder;
+import com.mmnaseri.utils.spring.data.dsl.factory.Start;
+import org.hamcrest.Matchers;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+/**
+ * @author Milad Naseri (milad.naseri@cdk.com)
+ * @since 1.0 (6/29/16, 4:11 PM)
+ */
+public class DefaultUserServiceTest {
+
+ private DefaultUserService service;
+ private UserRepository repository;
+ private GroupService groupService;
+ private GroupRepository groupRepository;
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ final Start builder = RepositoryFactoryBuilder.builder();
+ groupRepository = builder.mock(GroupRepository.class);
+ final MembershipRepository membershipRepository = builder.mock(MembershipRepository.class);
+ groupService = new DefaultGroupService(groupRepository, membershipRepository);
+ repository = builder.mock(UserRepository.class);
+ service = new DefaultUserService(repository, groupService);
+ }
+
+ @Test
+ public void testCreatingAUser() throws Exception {
+ assertThat(repository.count(), is(0L));
+ final String username = "milad";
+ final String email = "milad@mmnaseri.com";
+ final String password = "123456";
+ final User user = service.createUser(username, email, password);
+ assertThat(user, is(notNullValue()));
+ assertThat(user.getId(), is(notNullValue()));
+ assertThat(user.getUsername(), is(username));
+ assertThat(user.getEmail(), is(email));
+ assertThat(user.getPasswordHash(), is(not(password)));
+ assertThat(repository.count(), is(1L));
+ final User found = repository.findOne(user.getId());
+ assertThat(found, is(notNullValue()));
+ assertThat(found.getUsername(), is(username));
+ assertThat(found.getEmail(), is(email));
+ assertThat(found.getPasswordHash(), is(user.getPasswordHash()));
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testCreatingADuplicateUser() throws Exception {
+ service.createUser("milad", "email1", "123456");
+ service.createUser("milad", "email2", "123456");
+ }
+
+ @Test
+ public void testLookingUpAUserByEmail() throws Exception {
+ final String id = service.createUser("milad", "milad@domain.com", "123456").getId();
+ final User found = service.lookup("MILAD@domain.com");
+ assertThat(found, is(notNullValue()));
+ assertThat(found.getId(), is(id));
+ }
+
+ @Test
+ public void testLookingUpAUserByUsername() throws Exception {
+ final String id = service.createUser("milad", "milad@domain.com", "123456").getId();
+ final User found = service.lookup("MILAD");
+ assertThat(found, is(notNullValue()));
+ assertThat(found.getId(), is(id));
+ }
+
+ @Test
+ public void testLookingForNonExistentUser() throws Exception {
+ final User user = service.lookup("milad");
+ assertThat(user, is(nullValue()));
+ }
+
+ @Test
+ public void testAuthenticatingWithUsername() throws Exception {
+ final String id = service.createUser("milad", "milad@domain.com", "123456").getId();
+ final User user = service.authenticate("Milad", "123456");
+ assertThat(user, is(notNullValue()));
+ assertThat(user.getId(), is(id));
+ }
+ @Test
+ public void testAuthenticatingWithEmail() throws Exception {
+ final String id = service.createUser("milad", "milad@domain.com", "123456").getId();
+ final User user = service.authenticate("milad@DOMAIN.com", "123456");
+ assertThat(user, is(notNullValue()));
+ assertThat(user.getId(), is(id));
+ }
+
+ @Test
+ public void testAuthenticatingWithWrongHandle() throws Exception {
+ service.createUser("milad", "milad@domain.com", "123456").getId();
+ final User user = service.authenticate("milad@DOMAIN", "123456");
+ assertThat(user, is(nullValue()));
+ }
+
+ @Test
+ public void testAuthenticatingWithWrongPassword() throws Exception {
+ service.createUser("milad", "milad@domain.com", "123456").getId();
+ final User user = service.authenticate("milad", "987654");
+ assertThat(user, is(nullValue()));
+ }
+
+ @Test
+ public void testDeletingAUser() throws Exception {
+ service.createUser("milad", "milad@mmaseri.com", "123456");
+ assertThat(repository.count(), is(1L));
+ service.deleteUser("milad");
+ assertThat(repository.count(), is(0L));
+ }
+
+ @Test
+ public void testChangingUserPassword() throws Exception {
+ service.createUser("milad", "milad@mmnaseri.com", "123456");
+ assertThat(service.authenticate("milad", "123456"), is(notNullValue()));
+ service.updatePassword("milad", "123456", "987654");
+ assertThat(service.authenticate("milad", "123456"), is(nullValue()));
+ assertThat(service.authenticate("milad", "987654"), is(notNullValue()));
+ }
+
+ @Test
+ public void testDeletingAUserThatIsPartOfMultipleGroups() throws Exception {
+ final User user = service.createUser("milad", "milad@mmnaseri.com", "123456");
+ groupService.join(groupService.createGroup("Group 1"), user);
+ groupService.join(groupService.createGroup("Group 2"), user);
+ groupService.join(groupService.createGroup("Group 3"), user);
+ groupService.join(groupService.createGroup("Group 4"), user);
+ assertThat(groupService.groups(user), hasSize(4));
+ service.deleteUser(user.getUsername());
+ assertThat(groupService.groups(user), is(Matchers.empty()));
+ assertThat(groupRepository.count(), is(4L));
+ }
+
+}
\ No newline at end of file
diff --git a/spring-data-mock/pom.xml b/spring-data-mock/pom.xml
index cf9c348f..b34c88eb 100644
--- a/spring-data-mock/pom.xml
+++ b/spring-data-mock/pom.xml
@@ -27,7 +27,7 @@
com.mmnaseri.utils
spring-data-mock
- 1.1.3
+ 1.1.4
Spring Data Mock
A framework for mocking Spring Data repositories
@@ -71,22 +71,22 @@
1.2
2.9.4
- 6.9.11
+ 6.9.12
1.3
1.6
2.10.4
- 3.0.0
+ 3.0.1
2.8.2
1.6.7
2.7
4.2.0
- 1.12.1.RELEASE
- 1.8.1.RELEASE
- 1.10.1.RELEASE
+ 1.12.2.RELEASE
+ 1.8.2.RELEASE
+ 1.10.2.RELEASE
4.1.2
- 3.2.2
+ 3.2.4
1.0.2
diff --git a/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/dsl/factory/RepositoryFactoryBuilder.java b/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/dsl/factory/RepositoryFactoryBuilder.java
index 5fe97cdf..4a83b027 100644
--- a/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/dsl/factory/RepositoryFactoryBuilder.java
+++ b/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/dsl/factory/RepositoryFactoryBuilder.java
@@ -77,18 +77,22 @@ public static Start builder() {
*/
public static Start given(RepositoryFactoryConfiguration configuration) {
final RepositoryFactoryBuilder builder = new RepositoryFactoryBuilder();
- builder.metadataResolver = configuration.getRepositoryMetadataResolver() != null ? configuration.getRepositoryMetadataResolver() : builder.metadataResolver;
- builder.queryDescriptionExtractor = configuration.getDescriptionExtractor() != null ? configuration.getDescriptionExtractor() : builder.queryDescriptionExtractor;
- builder.functionRegistry = configuration.getFunctionRegistry() != null ? configuration.getFunctionRegistry() : builder.functionRegistry;
- builder.dataStoreRegistry = configuration.getDataStoreRegistry() != null ? configuration.getDataStoreRegistry() : builder.dataStoreRegistry;
- builder.resultAdapterContext = configuration.getResultAdapterContext() != null ? configuration.getResultAdapterContext() : builder.resultAdapterContext;
- builder.typeMappingContext = configuration.getTypeMappingContext() != null ? configuration.getTypeMappingContext() : builder.typeMappingContext;
- builder.eventListenerContext = configuration.getEventListenerContext() != null ? configuration.getEventListenerContext() : builder.eventListenerContext;
- builder.operationInvocationHandler = configuration.getOperationInvocationHandler() != null ? configuration.getOperationInvocationHandler() : builder.operationInvocationHandler;
- builder.defaultKeyGenerator = configuration.getDefaultKeyGenerator() != null ? configuration.getDefaultKeyGenerator() : builder.defaultKeyGenerator;
+ builder.metadataResolver = getOrDefault(configuration.getRepositoryMetadataResolver(), builder.metadataResolver);
+ builder.queryDescriptionExtractor = getOrDefault(configuration.getDescriptionExtractor(), builder.queryDescriptionExtractor);
+ builder.functionRegistry = getOrDefault(configuration.getFunctionRegistry(), builder.functionRegistry);
+ builder.dataStoreRegistry = getOrDefault(configuration.getDataStoreRegistry(), builder.dataStoreRegistry);
+ builder.resultAdapterContext = getOrDefault(configuration.getResultAdapterContext(), builder.resultAdapterContext);
+ builder.typeMappingContext = getOrDefault(configuration.getTypeMappingContext(), builder.typeMappingContext);
+ builder.eventListenerContext = getOrDefault(configuration.getEventListenerContext(), builder.eventListenerContext);
+ builder.operationInvocationHandler = getOrDefault(configuration.getOperationInvocationHandler(), builder.operationInvocationHandler);
+ builder.defaultKeyGenerator = getOrDefault(configuration.getDefaultKeyGenerator(), builder.defaultKeyGenerator);
return builder;
}
+ private static E getOrDefault(E value, E defaultValue) {
+ return value != null ? value : defaultValue;
+ }
+
/**
* @return the default factory
*/
diff --git a/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/proxy/impl/DefaultRepositoryFactory.java b/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/proxy/impl/DefaultRepositoryFactory.java
index 9f9edbf4..470c809f 100644
--- a/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/proxy/impl/DefaultRepositoryFactory.java
+++ b/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/proxy/impl/DefaultRepositoryFactory.java
@@ -58,16 +58,19 @@ public DefaultRepositoryFactory(RepositoryFactoryConfiguration configuration) {
@Override
public E getInstance(KeyGenerator extends Serializable> keyGenerator, Class repositoryInterface, Class... implementations) {
+ final KeyGenerator extends Serializable> actualKeyGenerator;
if (keyGenerator == null) {
if (configuration.getDefaultKeyGenerator() != null) {
//if no key generator is passed and there is a default key generator specified, we fall back to that
- keyGenerator = configuration.getDefaultKeyGenerator();
+ actualKeyGenerator = configuration.getDefaultKeyGenerator();
} else {
//otherwise, let's assume that not key generation is required
- keyGenerator = new NoOpKeyGenerator<>();
+ actualKeyGenerator = new NoOpKeyGenerator<>();
}
+ } else {
+ actualKeyGenerator = keyGenerator;
}
- log.info("We are going to create a proxy instance of type " + repositoryInterface + " using key generator " + keyGenerator + " and binding the implementations to " + Arrays.toString(implementations));
+ log.info("We are going to create a proxy instance of type " + repositoryInterface + " using key generator " + actualKeyGenerator + " and binding the implementations to " + Arrays.toString(implementations));
//figure out the repository metadata
log.info("Resolving repository metadata for " + repositoryInterface);
final RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
@@ -76,7 +79,7 @@ public E getInstance(KeyGenerator extends Serializable> keyGenerator, Clas
final DataStore dataStore = getDataStore(metadata);
//figure out type mappings
log.info("Trying to find all the proper type mappings for entity repository " + repositoryInterface);
- final List> typeMappings = getTypeMappings(metadata, dataStore, keyGenerator, implementations);
+ final List> typeMappings = getTypeMappings(metadata, dataStore, actualKeyGenerator, implementations);
//set up the data operation resolver
final DataOperationResolver operationResolver = new DefaultDataOperationResolver(typeMappings, descriptionExtractor, metadata, functionRegistry, configuration);
//get all of this repository's methods
@@ -90,7 +93,7 @@ public E getInstance(KeyGenerator extends Serializable> keyGenerator, Clas
boundImplementations.add(mapping.getType());
}
//set up the repository configuration
- final RepositoryConfiguration repositoryConfiguration = new ImmutableRepositoryConfiguration(metadata, keyGenerator, boundImplementations);
+ final RepositoryConfiguration repositoryConfiguration = new ImmutableRepositoryConfiguration(metadata, actualKeyGenerator, boundImplementations);
//create the interceptor
//noinspection unchecked
final InvocationHandler interceptor = new DataOperationInvocationHandler(repositoryConfiguration, invocationMappings, dataStore, adapterContext, operationInvocationHandler);
diff --git a/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/query/impl/DefaultQueryDescriptor.java b/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/query/impl/DefaultQueryDescriptor.java
index 2d79a971..7873a8ce 100644
--- a/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/query/impl/DefaultQueryDescriptor.java
+++ b/spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/query/impl/DefaultQueryDescriptor.java
@@ -91,11 +91,19 @@ public boolean matches(Object entity, Invocation invocation) {
for (List branch : branches) {
boolean matches = true;
for (Parameter parameter : branch) {
- final Object value = wrapper.getPropertyValue(parameter.getPath());
+ final boolean ignoreCase;
+ ignoreCase = parameter.getModifiers().contains(Modifier.IGNORE_CASE);
+ Object value = wrapper.getPropertyValue(parameter.getPath());
+ if (ignoreCase && value != null && value instanceof String) {
+ value = ((String) value).toLowerCase();
+ }
final Operator operator = parameter.getOperator();
final Object[] properties = new Object[operator.getOperands()];
for (int i = 0; i < operator.getOperands(); i++) {
properties[i] = invocation.getArguments()[parameter.getIndices()[i]];
+ if (ignoreCase && properties[i] != null && properties[i] instanceof String) {
+ properties[i] = ((String) properties[i]).toLowerCase();
+ }
}
if (!operator.getMatcher().matches(parameter, value, properties)) {
matches = false;