/*
* Copyright (c) 2015-present, Parse, LLC.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.parse;
import android.os.Parcel;
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = TestHelper.ROBOLECTRIC_SDK_VERSION)
public class ParseACLTest {
private final static String UNRESOLVED_KEY = "*unresolved";
private static final String READ_PERMISSION = "read";
private static final String WRITE_PERMISSION = "write";
@Before
public void setUp() {
ParseObject.registerSubclass(ParseRole.class);
ParseObject.registerSubclass(ParseUser.class);
}
@After
public void tearDown() {
ParseObject.unregisterSubclass(ParseRole.class);
ParseObject.unregisterSubclass(ParseUser.class);
}
//region testConstructor
@Test
public void testConstructor() throws Exception {
ParseACL acl = new ParseACL();
assertEquals(0, acl.getPermissionsById().size());
}
@Test
public void testConstructorWithUser() throws Exception {
ParseUser user = new ParseUser();
user.setObjectId("test");
ParseACL acl = new ParseACL(user);
assertTrue(acl.getReadAccess("test"));
assertTrue(acl.getWriteAccess("test"));
}
//endregion
//region testCopy
@Test
public void testCopy() throws Exception {
ParseACL acl = new ParseACL();
final ParseUser unresolvedUser = mock(ParseUser.class);
when(unresolvedUser.isLazy()).thenReturn(true);
// This will set unresolvedUser and permissionsById
acl.setReadAccess(unresolvedUser, true);
acl.setWriteAccess(unresolvedUser, true);
// We need to reset unresolvedUser since registerSaveListener will be triggered once in
// setReadAccess()
reset(unresolvedUser);
ParseACL copiedACL = new ParseACL(acl);
assertEquals(1, copiedACL.getPermissionsById().size());
assertTrue(copiedACL.getPermissionsById().containsKey(UNRESOLVED_KEY));
assertTrue(copiedACL.getReadAccess(unresolvedUser));
assertTrue(copiedACL.getWriteAccess(unresolvedUser));
assertFalse(copiedACL.isShared());
assertSame(unresolvedUser, copiedACL.getUnresolvedUser());
verify(unresolvedUser, times(1)).registerSaveListener(any(GetCallback.class));
}
@Test
public void testCopyWithSaveListener() throws Exception {
ParseACL acl = new ParseACL();
final ParseUser unresolvedUser = mock(ParseUser.class);
when(unresolvedUser.isLazy()).thenReturn(true);
// This will set unresolvedUser and permissionsById
acl.setReadAccess(unresolvedUser, true);
acl.setWriteAccess(unresolvedUser, true);
// We need to reset unresolvedUser since registerSaveListener will be triggered once in
// setReadAccess()
reset(unresolvedUser);
ParseACL copiedACL = new ParseACL(acl);
// Make sure the callback is called
ArgumentCaptor<GetCallback> callbackCaptor = ArgumentCaptor.forClass(GetCallback.class);
verify(unresolvedUser, times(1)).registerSaveListener(callbackCaptor.capture());
// Trigger the callback
GetCallback callback = callbackCaptor.getValue();
// Manually set userId and not lazy, mock user is saved
when(unresolvedUser.getObjectId()).thenReturn("userId");
when(unresolvedUser.isLazy()).thenReturn(false);
callback.done(unresolvedUser, null);
// Makre sure we unregister the callback
verify(unresolvedUser, times(1)).unregisterSaveListener(any(GetCallback.class));
assertEquals(1, copiedACL.getPermissionsById().size());
assertTrue(copiedACL.getReadAccess(unresolvedUser));
assertTrue(copiedACL.getWriteAccess(unresolvedUser));
assertFalse(copiedACL.isShared());
// No more unresolved permissions since it has been resolved in the callback.
assertFalse(copiedACL.getPermissionsById().containsKey(UNRESOLVED_KEY));
assertNull(copiedACL.getUnresolvedUser());
}
//endregion
//region toJson
@Test
public void testToJson() throws Exception {
ParseACL acl = new ParseACL();
acl.setReadAccess("userId", true);
ParseUser unresolvedUser = new ParseUser();
setLazy(unresolvedUser);
acl.setReadAccess(unresolvedUser, true);
// Mock decoder
ParseEncoder mockEncoder = mock(ParseEncoder.class);
when(mockEncoder.encode(eq(unresolvedUser))).thenReturn("unresolvedUserJson");
JSONObject aclJson = acl.toJSONObject(mockEncoder);
assertEquals("unresolvedUserJson", aclJson.getString("unresolvedUser"));
assertEquals(aclJson.getJSONObject("userId").getBoolean("read"), true);
assertEquals(aclJson.getJSONObject("userId").has("write"), false);
assertEquals(aclJson.getJSONObject("*unresolved").getBoolean("read"), true);
assertEquals(aclJson.getJSONObject("*unresolved").has("write"), false);
assertEquals(aclJson.length(), 3);
}
//endregion
//region parcelable
@Test
public void testParcelable() throws Exception {
ParseACL acl = new ParseACL();
acl.setReadAccess("userId", true);
ParseUser user = new ParseUser();
user.setObjectId("userId2");
acl.setReadAccess(user, true);
acl.setRoleWriteAccess("role", true);
acl.setShared(true);
Parcel parcel = Parcel.obtain();
acl.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
acl = ParseACL.CREATOR.createFromParcel(parcel);
assertTrue(acl.getReadAccess("userId"));
assertTrue(acl.getReadAccess(user));
assertTrue(acl.getRoleWriteAccess("role"));
assertTrue(acl.isShared());
assertFalse(acl.getPublicReadAccess());
assertFalse(acl.getPublicWriteAccess());
}
@Test
public void testParcelableWithUnresolvedUser() throws Exception {
ParseFieldOperations.registerDefaultDecoders(); // Needed for unparceling ParseObjects
ParseACL acl = new ParseACL();
ParseUser unresolved = new ParseUser();
setLazy(unresolved);
acl.setReadAccess(unresolved, true);
// unresolved users need a local id when parcelling and unparcelling.
// Since we don't have an Android environment, local id creation will fail.
unresolved.localId = "localId";
Parcel parcel = Parcel.obtain();
acl.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
// Do not user ParseObjectParcelDecoder because it requires local ids
acl = new ParseACL(parcel, new ParseParcelDecoder());
assertTrue(acl.getReadAccess(unresolved));
}
//endregion
//region testCreateACLFromJSONObject
@Test
public void testCreateACLFromJSONObject() throws Exception {
JSONObject aclJson = new JSONObject();
JSONObject permission = new JSONObject();
permission.put(READ_PERMISSION, true);
permission.put(WRITE_PERMISSION, true);
aclJson.put("userId", permission);
ParseUser unresolvedUser = new ParseUser();
JSONObject unresolvedUserJson = new JSONObject();
aclJson.put("unresolvedUser", unresolvedUserJson);
// Mock decoder
ParseDecoder mockDecoder = mock(ParseDecoder.class);
when(mockDecoder.decode(eq(unresolvedUserJson))).thenReturn(unresolvedUser);
ParseACL acl = ParseACL.createACLFromJSONObject(aclJson, mockDecoder);
assertSame(unresolvedUser, acl.getUnresolvedUser());
assertTrue(acl.getReadAccess("userId"));
assertTrue(acl.getWriteAccess("userId"));
assertEquals(1, acl.getPermissionsById().size());
}
//endregion
//region testResolveUser
@Test
public void testResolveUserWithNewUser() throws Exception {
ParseUser unresolvedUser = new ParseUser();
setLazy(unresolvedUser);
ParseACL acl = new ParseACL();
acl.setReadAccess(unresolvedUser, true);
ParseUser other = new ParseUser();
// local id creation fails if we don't have Android environment
unresolvedUser.localId = "someId";
other.localId = "someOtherId";
acl.resolveUser(other);
// Make sure unresolvedUser is not changed
assertSame(unresolvedUser, acl.getUnresolvedUser());
}
@Test
public void testResolveUserWithUnresolvedUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser unresolvedUser = new ParseUser();
setLazy(unresolvedUser);
// This will set the unresolvedUser in acl
acl.setReadAccess(unresolvedUser, true);
acl.setWriteAccess(unresolvedUser, true);
unresolvedUser.setObjectId("test");
acl.resolveUser(unresolvedUser);
assertNull(acl.getUnresolvedUser());
assertTrue(acl.getReadAccess(unresolvedUser));
assertTrue(acl.getWriteAccess(unresolvedUser));
assertEquals(1, acl.getPermissionsById().size());
assertFalse(acl.getPermissionsById().containsKey(UNRESOLVED_KEY));
}
//endregion
//region testSetAccess
@Test
public void testSetAccessWithNoPermissionAndNotAllowed() throws Exception {
ParseACL acl = new ParseACL();
acl.setReadAccess("userId", false);
// Make sure noting is set
assertEquals(0, acl.getPermissionsById().size());
}
@Test
public void testSetAccessWithAllowed() throws Exception {
ParseACL acl = new ParseACL();
acl.setReadAccess("userId", true);
assertTrue(acl.getReadAccess("userId"));
assertEquals(1, acl.getPermissionsById().size());
}
@Test
public void testSetAccessWithPermissionsAndNotAllowed() throws Exception {
ParseACL acl = new ParseACL();
acl.setReadAccess("userId", true);
acl.setReadAccess("userId", false);
// Make sure we remove the read access
assertFalse(acl.getReadAccess("userId"));
assertEquals(0, acl.getPermissionsById().size());
}
@Test
public void testSetPublicReadAccessAllowed() throws Exception {
ParseACL acl = new ParseACL();
acl.setPublicReadAccess(true);
assertTrue(acl.getPublicReadAccess());
}
@Test
public void testSetPublicReadAccessNotAllowed() throws Exception {
ParseACL acl = new ParseACL();
acl.setPublicReadAccess(false);
// Make sure noting is set
assertEquals(0, acl.getPermissionsById().size());
}
@Test
public void testSetPublicWriteAccessAllowed() throws Exception {
ParseACL acl = new ParseACL();
acl.setPublicWriteAccess(true);
assertTrue(acl.getPublicWriteAccess());
assertEquals(1, acl.getPermissionsById().size());
}
@Test
public void testSetPublicWriteAccessNotAllowed() throws Exception {
ParseACL acl = new ParseACL();
acl.setPublicWriteAccess(false);
// Make sure noting is set
assertEquals(0, acl.getPermissionsById().size());
}
@Test(expected = IllegalArgumentException.class)
public void testSetReadAccessWithNullUserId() throws Exception {
ParseACL acl = new ParseACL();
String userId = null;
acl.setReadAccess(userId, true);
}
@Test(expected = IllegalArgumentException.class)
public void testSetWriteAccessWithNullUserId() throws Exception {
ParseACL acl = new ParseACL();
String userId = null;
acl.setWriteAccess(userId, true);
}
@Test(expected = IllegalArgumentException.class)
public void testSetRoleReadAccessWithInvalidRole() throws Exception {
ParseRole role = new ParseRole();
role.setName("Player");
ParseACL acl = new ParseACL();
acl.setRoleReadAccess(role, true);
}
@Test
public void testSetRoleReadAccess() throws Exception {
ParseRole role = new ParseRole();
role.setName("Player");
role.setObjectId("test");
ParseACL acl = new ParseACL();
acl.setRoleReadAccess(role, true);
assertTrue(acl.getRoleReadAccess(role));
assertEquals(1, acl.getPermissionsById().size());
}
@Test(expected = IllegalArgumentException.class)
public void testSetRoleWriteAccessWithInvalidRole() throws Exception {
ParseRole role = new ParseRole();
role.setName("Player");
ParseACL acl = new ParseACL();
acl.setRoleWriteAccess(role, true);
}
@Test
public void testSetRoleWriteAccess() throws Exception {
ParseRole role = new ParseRole();
role.setName("Player");
role.setObjectId("test");
ParseACL acl = new ParseACL();
acl.setRoleWriteAccess(role, true);
assertTrue(acl.getRoleWriteAccess(role));
assertEquals(1, acl.getPermissionsById().size());
}
@Test(expected = IllegalArgumentException.class)
public void testSetUserReadAccessWithNotSavedNotLazyUser() throws Exception {
ParseUser user = new ParseUser();
ParseACL acl = new ParseACL();
acl.setReadAccess(user, true);
}
@Test
public void testSetUserReadAccessWithLazyUser() throws Exception {
ParseUser unresolvedUser = mock(ParseUser.class);
when(unresolvedUser.isLazy()).thenReturn(true);
ParseACL acl = new ParseACL();
acl.setReadAccess(unresolvedUser, true);
assertSame(unresolvedUser, acl.getUnresolvedUser());
verify(unresolvedUser, times(1)).registerSaveListener(any(GetCallback.class));
assertTrue(acl.getPermissionsById().containsKey(UNRESOLVED_KEY));
assertTrue(acl.getReadAccess(unresolvedUser));
assertEquals(1, acl.getPermissionsById().size());
}
@Test
public void testSetUserReadAccessWithNormalUser() throws Exception {
ParseUser user = new ParseUser();
user.setObjectId("test");
ParseACL acl = new ParseACL();
acl.setReadAccess(user, true);
assertTrue(acl.getReadAccess(user));
assertEquals(1, acl.getPermissionsById().size());
}
@Test(expected = IllegalArgumentException.class)
public void testSetUserWriteAccessWithNotSavedNotLazyUser() throws Exception {
ParseUser user = new ParseUser();
ParseACL acl = new ParseACL();
acl.setWriteAccess(user, true);
}
@Test
public void testSetUserWriteAccessWithLazyUser() throws Exception {
ParseUser user = mock(ParseUser.class);
when(user.isLazy()).thenReturn(true);
ParseACL acl = new ParseACL();
acl.setWriteAccess(user, true);
assertSame(user, acl.getUnresolvedUser());
verify(user, times(1)).registerSaveListener(any(GetCallback.class));
assertTrue(acl.getPermissionsById().containsKey(UNRESOLVED_KEY));
assertTrue(acl.getWriteAccess(user));
assertEquals(1, acl.getPermissionsById().size());
}
@Test
public void testSetUserWriteAccessWithNormalUser() throws Exception {
ParseUser user = new ParseUser();
user.setObjectId("test");
ParseACL acl = new ParseACL();
acl.setWriteAccess(user, true);
assertTrue(acl.getWriteAccess(user));
assertEquals(1, acl.getPermissionsById().size());
}
//endregion
//region testGetAccess
@Test
public void testGetAccessWithNoPermission() throws Exception {
ParseACL acl = new ParseACL();
assertFalse(acl.getReadAccess("userId"));
}
@Test
public void testGetAccessWithNoAccessType() throws Exception {
ParseACL acl = new ParseACL();
acl.setReadAccess("userId", true);
assertFalse(acl.getWriteAccess("userId"));
}
@Test
public void testGetAccessWithPermission() throws Exception {
ParseACL acl = new ParseACL();
acl.setReadAccess("userId", true);
assertTrue(acl.getReadAccess("userId"));
}
@Test
public void testGetPublicReadAccess() throws Exception {
ParseACL acl = new ParseACL();
acl.setPublicWriteAccess(true);
assertTrue(acl.getPublicWriteAccess());
}
@Test
public void testGetPublicWriteAccess() throws Exception {
ParseACL acl = new ParseACL();
acl.setPublicWriteAccess(true);
assertTrue(acl.getPublicWriteAccess());
}
@Test(expected = IllegalArgumentException.class)
public void testGetReadAccessWithNullUserId() throws Exception {
ParseACL acl = new ParseACL();
String userId = null;
acl.getReadAccess(userId);
}
@Test(expected = IllegalArgumentException.class)
public void testGetWriteAccessWithNullUserId() throws Exception {
ParseACL acl = new ParseACL();
String userId = null;
acl.getWriteAccess(userId);
}
@Test(expected = IllegalArgumentException.class)
public void testGetRoleReadAccessWithInvalidRole() throws Exception {
ParseACL acl = new ParseACL();
ParseRole role = new ParseRole();
role.setName("Player");
acl.getRoleReadAccess(role);
}
@Test
public void testGetRoleReadAccess() throws Exception {
ParseACL acl = new ParseACL();
ParseRole role = new ParseRole();
role.setName("Player");
role.setObjectId("test");
acl.setRoleReadAccess(role, true);
assertTrue(acl.getRoleReadAccess(role));
}
@Test(expected = IllegalArgumentException.class)
public void testGetRoleWriteAccessWithInvalidRole() throws Exception {
ParseACL acl = new ParseACL();
ParseRole role = new ParseRole();
role.setName("Player");
acl.getRoleWriteAccess(role);
}
@Test
public void testGetRoleWriteAccess() throws Exception {
ParseACL acl = new ParseACL();
ParseRole role = new ParseRole();
role.setName("Player");
role.setObjectId("test");
acl.setRoleWriteAccess(role, true);
assertTrue(acl.getRoleWriteAccess(role));
}
@Test
public void testGetUserReadAccessWithUnresolvedUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = new ParseUser();
setLazy(user);
// Since user is a lazy user, this will set the acl's unresolved user and give it read access
acl.setReadAccess(user ,true);
assertTrue(acl.getReadAccess(user));
}
@Test
public void testGetUserReadAccessWithLazyUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = new ParseUser();
setLazy(user);
assertFalse(acl.getReadAccess(user));
}
@Test(expected = IllegalArgumentException.class)
public void testGetUserReadAccessWithNotSavedUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = new ParseUser();
assertFalse(acl.getReadAccess(user));
}
@Test
public void testGetUserReadAccessWithNormalUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = new ParseUser();
user.setObjectId("test");
acl.setReadAccess(user, true);
assertTrue(acl.getReadAccess(user));
}
@Test
public void testGetUserWriteAccessWithUnresolvedUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = new ParseUser();
setLazy(user);
// Since user is a lazy user, this will set the acl's unresolved user and give it write access
acl.setWriteAccess(user, true);
assertTrue(acl.getWriteAccess(user));
}
@Test
public void testGetUserWriteAccessWithLazyUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = mock(ParseUser.class);
when(user.isLazy()).thenReturn(true);
assertFalse(acl.getWriteAccess(user));
}
@Test(expected = IllegalArgumentException.class)
public void testGetUserWriteAccessWithNotSavedUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = new ParseUser();
assertFalse(acl.getWriteAccess(user));
}
@Test
public void testGetUserWriteAccessWithNormalUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = new ParseUser();
user.setObjectId("test");
acl.setWriteAccess(user, true);
assertTrue(acl.getWriteAccess(user));
}
//endregion
//region testGetter/Setter
@Test
public void testIsShared() throws Exception {
ParseACL acl = new ParseACL();
acl.setShared(true);
assertTrue(acl.isShared());
}
@Test
public void testUnresolvedUser() throws Exception {
ParseACL acl = new ParseACL();
ParseUser user = new ParseUser();
setLazy(user);
// This will set unresolvedUser in acl
acl.setReadAccess(user, true);
assertTrue(acl.hasUnresolvedUser());
assertSame(user, acl.getUnresolvedUser());
}
//endregion
private static void setLazy(ParseUser user) {
Map<String, String> anonymousAuthData = new HashMap<>();
anonymousAuthData.put("anonymousToken", "anonymousTest");
user.putAuthData(ParseAnonymousUtils.AUTH_TYPE, anonymousAuthData);
}
}