blob: 4e0c7038d1405b829efc17331da6fe7821fdc433 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.directory.scim.server.provider;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.enterprise.inject.Instance;
import org.apache.directory.scim.server.rest.ObjectMapperFactory;
import org.apache.directory.scim.server.schema.Registry;
import org.apache.directory.scim.server.utility.ExampleObjectExtension;
import org.apache.directory.scim.server.utility.Subobject;
import org.apache.directory.scim.spec.extension.EnterpriseExtension;
import org.apache.directory.scim.spec.extension.EnterpriseExtension.Manager;
import org.apache.directory.scim.spec.extension.ScimExtensionRegistry;
import org.apache.directory.scim.spec.phonenumber.PhoneNumberParseException;
import org.apache.directory.scim.spec.protocol.data.PatchOperation;
import org.apache.directory.scim.spec.protocol.data.PatchOperation.Type;
import org.apache.directory.scim.spec.protocol.data.PatchOperationPath;
import org.apache.directory.scim.spec.protocol.filter.FilterParseException;
import org.apache.directory.scim.spec.resources.Address;
import org.apache.directory.scim.spec.resources.Email;
import org.apache.directory.scim.spec.resources.Name;
import org.apache.directory.scim.spec.resources.PhoneNumber;
import org.apache.directory.scim.spec.resources.PhoneNumber.GlobalPhoneNumberBuilder;
import org.apache.directory.scim.spec.resources.Photo;
import org.apache.directory.scim.spec.resources.ScimUser;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import static org.assertj.core.groups.Tuple.tuple;
@Slf4j
@ExtendWith(MockitoExtension.class)
public class UpdateRequestTest {
private static final String FIRST = "first";
private static final String SECOND = "second";
private static final String THIRD = "third";
private static final String FOURTH = "fourth";
private static final String A = "A";
private static final String B = "B";
private static final String C = "C";
private Registry registry;
@Mock
Provider<ScimUser> provider;
ProviderRegistry providerRegistry;
@BeforeEach
public void initialize() throws Exception {
registry = new Registry();
providerRegistry = new ProviderRegistry(registry, ScimExtensionRegistry.getInstance(), null);
Mockito.when(provider.getExtensionList())
.thenReturn(Stream.of(EnterpriseExtension.class,ExampleObjectExtension.class).collect(Collectors.toList()));
providerRegistry.registerProvider(ScimUser.class, provider);
}
@Test
public void testResourcePassthrough() throws Exception {
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", createUser1(), createUser1(), registry);
ScimUser result = updateRequest.getResource();
log.info("testResourcePassthrough: " + result);
Assertions.assertThat(result)
.isNotNull();
}
@Test
public void testPatchPassthrough() throws Exception {
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", createUser1(), createUser1PatchOps(), registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
log.info("testPatchPassthrough: " + result);
Assertions.assertThat(result)
.isNotNull();
}
@Test
public void testPatchToUpdate() throws Exception {
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", createUser1(), createUser1PatchOps(), registry);
assertThrows(UnsupportedOperationException.class, () -> updateRequest.getResource());
}
@Test
public void testAddSingleAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.setNickName("Jon");
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.ADD, "nickName", "Jon");
}
@Test
public void testAddSingleExtension() throws Exception {
ScimUser user1 = createUser1();
EnterpriseExtension ext = user1.removeExtension(EnterpriseExtension.class);
ScimUser user2 = copy(user1);
user2.addExtension(ext);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.ADD, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", ext);
}
@Test
public void testAddComplexAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.getName()
.setHonorificPrefix("Dr.");
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.ADD, "name.honorificPrefix", "Dr.");
}
@Test
public void testAddMultiValuedAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
PhoneNumber mobilePhone = new GlobalPhoneNumberBuilder().globalNumber("+1(814)867-5306").build();
mobilePhone.setType("mobile");
mobilePhone.setPrimary(false);
user2.getPhoneNumbers().add(mobilePhone);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.ADD, "phoneNumbers", mobilePhone);
}
/**
* This unit test is to replicate the issue where a replace is sent back
* from the differencing engine for a collection that is currently empty
* but is having an object added to it. This should produce an ADD with an
* ArrayList of objects to add.
*/
@Test
public void testAddObjectToEmptyCollection() throws Exception {
ScimUser user1 = createUser1();
user1.setPhoneNumbers(new ArrayList<>());
ScimUser user2 = copy(user1);
PhoneNumber mobilePhone = new GlobalPhoneNumberBuilder().globalNumber("+1(814)867-5306").build();
mobilePhone.setType("mobile");
mobilePhone.setPrimary(true);
user2.getPhoneNumbers().add(mobilePhone);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
assertNotNull(operations);
assertEquals(1, operations.size());
PatchOperation operation = operations.get(0);
assertNotNull(operation.getValue());
assertEquals(Type.ADD, operation.getOperation());
assertEquals(PhoneNumber.class, operation.getValue().getClass());
}
@Test
public void testAddObjectsToEmptyCollection() throws Exception {
ScimUser user1 = createUser1();
user1.setPhoneNumbers(new ArrayList<>());
ScimUser user2 = copy(user1);
PhoneNumber mobilePhone = new GlobalPhoneNumberBuilder().globalNumber("+1(814)867-5306").build();
mobilePhone.setType("mobile");
mobilePhone.setPrimary(true);
PhoneNumber homePhone = new GlobalPhoneNumberBuilder().globalNumber("+1(814)867-5307").build();
homePhone.setType("home");
homePhone.setPrimary(true);
user2.getPhoneNumbers().add(mobilePhone);
user2.getPhoneNumbers().add(homePhone);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
assertNotNull(operations);
assertEquals(2, operations.size());
PatchOperation operation = operations.get(0);
assertNotNull(operation.getValue());
assertEquals(Type.ADD, operation.getOperation());
assertEquals(PhoneNumber.class, operation.getValue().getClass());
operation = operations.get(1);
assertNotNull(operation.getValue());
assertEquals(Type.ADD, operation.getOperation());
assertEquals(PhoneNumber.class, operation.getValue().getClass());
}
@Test
public void testReplaceSingleAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.setActive(false);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REPLACE, "active", false);
}
@Test
public void testReplaceExtensionSingleAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.getExtension(EnterpriseExtension.class).setDepartment("Dept XYZ.");
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REPLACE, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department", "Dept XYZ.");
}
@Test
public void testReplaceComplexAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.getName()
.setFamilyName("Nobody");
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REPLACE, "name.familyName", "Nobody");
}
@Test
public void testReplaceMultiValuedAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.getEmails()
.stream()
.filter(e -> e.getType()
.equals("work"))
.forEach(e -> e.setValue("nobody@example.com"));
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REPLACE, "emails[type EQ \"work\"].value", "nobody@example.com");
}
@Test
public void testRemoveSingleAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.setUserName(null);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REMOVE, "userName", null);
}
@Test
public void testRemoveSingleExtension() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.removeExtension(EnterpriseExtension.class);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REMOVE, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", null);
}
@Test
public void testRemoveComplexAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.getName()
.setMiddleName(null);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REMOVE, "name.middleName", null);
}
@Test
public void testRemoveFullComplexAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user2.setName(null);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REMOVE, "name", null);
}
@Test
public void testRemoveMultiValuedAttribute() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
List<Email> newEmails = user2.getEmails()
.stream()
.filter(e -> e.getType()
.equals("work"))
.collect(Collectors.toList());
user2.setEmails(newEmails);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REMOVE, "emails[type EQ \"home\"]", null);
}
@Test
public void testRemoveMultiValuedAttributeWithSorting() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
Address localAddress = new Address();
localAddress.setStreetAddress("123 Main Street");
localAddress.setLocality("State College");
localAddress.setRegion("PA");
localAddress.setCountry("USA");
localAddress.setType("local");
user1.getAddresses().add(localAddress);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
PatchOperation actual = assertSingleResult(result);
checkAssertions(actual, Type.REMOVE, "addresses[type EQ \"local\"]", null);
}
@Test
public void testAddMultiValuedAttributeWithSorting() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
Address localAddress = new Address();
localAddress.setStreetAddress("123 Main Street");
localAddress.setLocality("State College");
localAddress.setRegion("PA");
localAddress.setCountry("USA");
localAddress.setType("local");
user2.getAddresses().add(localAddress);
user1.getAddresses().get(0).setKey("asdf");
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> result = updateRequest.getPatchOperations();
assertEquals(2, result.size());
checkAssertions(result.get(1), Type.ADD, "addresses", localAddress);
}
@Test
public void verifyEmptyArraysDoNotCauseMove() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
user1.setPhotos(new ArrayList<>());
ExampleObjectExtension ext1 = new ExampleObjectExtension();
user1.addExtension(ext1);
ExampleObjectExtension ext2 = new ExampleObjectExtension();
ext2.setList(new ArrayList<>());
user2.addExtension(ext2);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
assertTrue(operations.isEmpty(), "Empty Arrays caused a diff");
}
@Test
public void verifyEmptyArraysAreNulled() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
//Set empty list on root object and verify no differences
user1.setPhotos(new ArrayList<>());
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
assertTrue(operations.isEmpty(), "Empty Arrays are not being nulled out");
//Reset user 1 and empty list on Extension and verify no differences
user1 = createUser1();
ExampleObjectExtension ext = new ExampleObjectExtension();
ext.setList(new ArrayList<>());
operations = updateRequest.getPatchOperations();
assertTrue(operations.isEmpty(), "Empty Arrays are not being nulled out");
//Reset extension and set empty list on element of extension then verify no differences
Subobject subobject = new Subobject();
subobject.setList1(new ArrayList<>());
ext = new ExampleObjectExtension();
ext.setSubobject(subobject);
operations = updateRequest.getPatchOperations();
assertTrue(operations.isEmpty(), "Empty Arrays are not being nulled out");
}
/**
* This unit test is to replicate the issue where
*/
@Test
public void testAddArray() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
Photo photo = new Photo();
photo.setType("photo");
photo.setValue("photo1.png");
user2.setPhotos(Stream.of(photo).collect(Collectors.toList()));
ExampleObjectExtension ext1 = new ExampleObjectExtension();
ext1.setList(null);
user1.addExtension(ext1);
ExampleObjectExtension ext2 = new ExampleObjectExtension();
ext2.setList(Stream.of(FIRST,SECOND).collect(Collectors.toList()));
user2.addExtension(ext2);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
Assertions.assertThat(operations)
.hasSize(3)
.extracting("operation","value")
.contains(
tuple(Type.ADD, photo),
tuple(Type.ADD, "first"),
tuple(Type.ADD, "second"));
}
@Test
public void testRemoveArray() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
Photo photo = new Photo();
photo.setType("photo");
photo.setValue("photo1.png");
user1.setPhotos(Stream.of(photo).collect(Collectors.toList()));
ExampleObjectExtension ext1 = new ExampleObjectExtension();
ext1.setList(Stream.of(FIRST,SECOND).collect(Collectors.toList()));
user1.addExtension(ext1);
ExampleObjectExtension ext2 = new ExampleObjectExtension();
ext2.setList(null);
user2.addExtension(ext2);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
assertNotNull(operations);
assertEquals(2, operations.size());
PatchOperation operation = operations.get(0);
assertEquals(Type.REMOVE, operation.getOperation());
assertNull(operation.getValue());
}
@Test
@Disabled
//TODO: do asserts
public void testNonTypedAttributeListGetUseablePath() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
ExampleObjectExtension ext1 = new ExampleObjectExtension();
ext1.setList(Stream.of(FIRST,SECOND,THIRD).collect(Collectors.toList()));
user1.addExtension(ext1);
ExampleObjectExtension ext2 = new ExampleObjectExtension();
ext2.setList(Stream.of(FIRST,SECOND,FOURTH).collect(Collectors.toList()));
user2.addExtension(ext2);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
//TODO: perform assert that proper add and remove paths are correct
}
@Test
@Disabled
//TODO: do asserts
public void testMoveFormatNameToNicknamePart1() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
String nickname = "John Xander Anyman";
user1.setNickName(nickname);
user2.getName().setFormatted(nickname);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
}
@Test
@Disabled
//TODO: do asserts
public void testMoveFormatNameToNicknamePart2() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
String nickname = "John Xander Anyman";
user1.setNickName(nickname);
user2.setNickName("");
user2.getName().setFormatted(nickname);
user1.getName().setFormatted("");
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
}
@Test
@Disabled
//TODO: do asserts
public void testMoveFormatNameToNicknamePart3() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
String nickname = "John Xander Anyman";
user1.setNickName(nickname);
user2.setNickName(null);
user2.getName().setFormatted(nickname);
user1.getName().setFormatted("");
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
}
@Test
@Disabled
//TODO: do asserts
public void testMoveFormatNameToNicknamePart4() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
String nickname = "John Xander Anyman";
user1.setNickName(nickname);
user2.setNickName("");
user2.getName().setFormatted(nickname);
user1.getName().setFormatted(null);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
}
@Test
@Disabled
//TODO: do asserts
public void testMoveFormatNameToNicknamePart5() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
String nickname = "John Xander Anyman";
user1.setNickName("");
user2.setNickName(nickname);
user2.getName().setFormatted(null);
user1.getName().setFormatted(nickname);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
}
@ParameterizedTest
@MethodSource("testListOfStringsParameters")
public void testListOfStringsParameterized(List<String> list1, List<String> list2, List<ExpectedPatchOperation> ops) throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
ExampleObjectExtension ext1 = new ExampleObjectExtension();
ext1.setList(list1);
user1.addExtension(ext1);
ExampleObjectExtension ext2 = new ExampleObjectExtension();
ext2.setList(list2);
user2.addExtension(ext2);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
assertEquals(ops.size(), operations.size());
for(int i = 0; i < operations.size(); i++) {
PatchOperation actualOp = operations.get(i);
ExpectedPatchOperation expectedOp = ops.get(i);
assertEquals(expectedOp.getOp(), actualOp.getOperation().toString());
assertEquals(expectedOp.getPath(), actualOp.getPath().toString());
if (expectedOp.getValue() == null) {
assertNull(actualOp.getValue());
} else {
assertEquals(expectedOp.getValue(), actualOp.getValue().toString());
}
}
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
}
@SuppressWarnings("unused")
private static Object[] testListOfStringsParameters() throws Exception {
List<Object> params = new ArrayList<>();
String nickName = "John Xander Anyman";
//Parameter order
//1 Original list of Strings
//2 Update list of Strings
//3 Array of Expected Operations
// 3a Operation
// 3b Path
// 3c Value
List<ExpectedPatchOperation> multipleOps = new ArrayList<ExpectedPatchOperation>();
multipleOps.add(new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", "A"));
multipleOps.add(new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", "B"));
multipleOps.add(new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", "C"));
params.add(new Object[] {Stream.of(A).collect(Collectors.toList()), new ArrayList<String>(), Stream.of(new ExpectedPatchOperation("REMOVE", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", null)).collect(Collectors.toList())});
params.add(new Object[] {Stream.of(A).collect(Collectors.toList()), null, Stream.of(new ExpectedPatchOperation("REMOVE", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", null)).collect(Collectors.toList())});
params.add(new Object[] {null, Stream.of(A).collect(Collectors.toList()), Stream.of(new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", "A")).collect(Collectors.toList())});
params.add(new Object[] {null, Stream.of(C,B,A).collect(Collectors.toList()), multipleOps});
params.add(new Object[] {Stream.of(A,B,C).collect(Collectors.toList()), new ArrayList<String>(), Stream.of(new ExpectedPatchOperation("REMOVE", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", null)).collect(Collectors.toList())});
params.add(new Object[] {Stream.of(C,B,A).collect(Collectors.toList()), new ArrayList<String>(), Stream.of(new ExpectedPatchOperation("REMOVE", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", null)).collect(Collectors.toList())});
params.add(new Object[] {new ArrayList<String>(), Stream.of(A).collect(Collectors.toList()), Stream.of(new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", "A")).collect(Collectors.toList())});
params.add(new Object[] {new ArrayList<String>(), Stream.of(C,B,A).collect(Collectors.toList()), multipleOps});
params.add(new Object[] {Stream.of(A, B).collect(Collectors.toList()), Stream.of(B).collect(Collectors.toList()), Stream.of(new ExpectedPatchOperation("REMOVE", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list[value EQ \"A\"]", null)).collect(Collectors.toList())});
params.add(new Object[] {Stream.of(B, A).collect(Collectors.toList()), Stream.of(B).collect(Collectors.toList()), Stream.of(new ExpectedPatchOperation("REMOVE", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list[value EQ \"A\"]", null)).collect(Collectors.toList())});
params.add(new Object[] {Stream.of(B).collect(Collectors.toList()), Stream.of(A,B).collect(Collectors.toList()), Stream.of(new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", A)).collect(Collectors.toList())});
params.add(new Object[] {Stream.of(B).collect(Collectors.toList()), Stream.of(B,A).collect(Collectors.toList()), Stream.of(new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", A)).collect(Collectors.toList())});
params.add(new Object[] {Stream.of(A).collect(Collectors.toList()), Stream.of(A,B,C).collect(Collectors.toList()), Stream.of(new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", B),new ExpectedPatchOperation("ADD", "urn:ietf:params:scim:schemas:extension:example:2.0:Object:list", C)).collect(Collectors.toList())});
return params.toArray();
}
@Test
//TODO: do parameterized test
public void offsetTest1() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
ExampleObjectExtension ext1 = new ExampleObjectExtension();
ext1.setList(Stream.of("D","M","Y","Z","Z","Z","Z","Z").collect(Collectors.toList()));
user1.addExtension(ext1);
ExampleObjectExtension ext2 = new ExampleObjectExtension();
//ext2.setList(Stream.of("A","A","B","B","D","F","N","Q","Z").collect(Collectors.toList()));
ext2.setList(Stream.of("A","Z").collect(Collectors.toList()));
user2.addExtension(ext2);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
}
@Test
public void testMoveFormatNameToNicknamePart6() throws Exception {
ScimUser user1 = createUser1();
ScimUser user2 = copy(user1);
String nickname = "John Xander Anyman";
user1.setNickName(null);
user2.setNickName(nickname);
user2.getName().setFormatted("");
user1.getName().setFormatted(nickname);
UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
List<PatchOperation> operations = updateRequest.getPatchOperations();
System.out.println("Number of operations: "+operations.size());
operations.stream().forEach(op -> System.out.println(op));
}
/**
* This is used to test an error condition. In this scenario a user has multiple phone numbers where home is marked primary and work is not. A SCIM update
* is performed in which the new user only contains a work phone number where the type is null. When this happens it should only only be a single DELETE
* operation. Instead it creates four operations: replace value of the home number with the work number value, replace the home type to work,
* remove the primary flag, and remove the work number
*/
@Test
@Disabled
public void testShowBugWhereDeleteIsTreatedAsMultipleReplace() throws Exception {
// final int expectedNumberOfOperationsWithoutBug = 1;
// final int expectedNumberOfOperationsWithBug = 4;
//
// ScimUser user1 = createUser1();
// ScimUser user2 = copy(user1);
// user2.getPhoneNumbers().removeIf(p -> p.getType().equals("home"));
//
// PhoneNumber workNumber = user2.getPhoneNumbers().stream().filter(p -> p.getType().equals("work")).findFirst().orElse(null);
// workNumber.setType("home");
// assertNotNull(workNumber);
//
// UpdateRequest<ScimUser> updateRequest = new UpdateRequest<>("1234", user1, user2, registry);
// List<PatchOperation> operations = updateRequest.getPatchOperations();
// assertNotNull(operations);
// assertEquals(expectedNumberOfOperationsWithBug, operations.size());
// assertNotEquals(expectedNumberOfOperationsWithoutBug, operations.size());
}
private PatchOperation assertSingleResult(List<PatchOperation> result) {
Assertions.assertThat(result)
.isNotNull();
Assertions.assertThat(result)
.hasSize(1);
PatchOperation actual = result.get(0);
return actual;
}
private void checkAssertions(PatchOperation actual, Type op, String path, Object value) throws FilterParseException {
Assertions.assertThat(actual.getOperation())
.isEqualTo(op);
Assertions.assertThat(actual.getPath()
.toString())
.isEqualTo(path);
Assertions.assertThat(actual.getValue())
.isEqualTo(value);
}
@Data
@AllArgsConstructor
private static class ExpectedPatchOperation {
private String op;
private String path;
private String value;
}
public static final Address createHomeAddress() {
Address homeAddress = new Address();
homeAddress.setType("home");
homeAddress.setStreetAddress("123 Fake Street");
homeAddress.setLocality("State College");
homeAddress.setRegion("Pennsylvania");
homeAddress.setCountry("USA");
homeAddress.setPostalCode("16801");
return homeAddress;
}
public static ScimUser createUser1() throws PhoneNumberParseException {
ScimUser user = new ScimUser();
user.setId("912345678");
user.setExternalId("912345678");
user.setActive(true);
user.setDisplayName("John Anyman");
user.setTitle("Professor");
user.setUserName("jxa123");
Name name = new Name();
name.setGivenName("John");
name.setMiddleName("Xander");
name.setFamilyName("Anyman");
name.setHonorificSuffix("Jr.");
user.setName(name);
Address homeAddress = new Address();
homeAddress.setType("home");
homeAddress.setStreetAddress("123 Fake Street");
homeAddress.setLocality("State College");
homeAddress.setRegion("Pennsylvania");
homeAddress.setCountry("USA");
homeAddress.setPostalCode("16801");
Address workAddress = new Address();
workAddress.setType("work");
workAddress.setStreetAddress("2 Old Main");
workAddress.setLocality("State College");
workAddress.setRegion("Pennsylvania");
workAddress.setCountry("USA");
workAddress.setPostalCode("16802");
List<Address> address = Stream.of(workAddress, homeAddress)
.collect(Collectors.toList());
user.setAddresses(address);
Email workEmail = new Email();
workEmail.setPrimary(true);
workEmail.setType("work");
workEmail.setValue("jxa123@psu.edu");
workEmail.setDisplay("jxa123@psu.edu");
Email homeEmail = new Email();
homeEmail.setPrimary(true);
homeEmail.setType("home");
homeEmail.setValue("john@gmail.com");
homeEmail.setDisplay("john@gmail.com");
Email otherEmail = new Email();
otherEmail.setPrimary(true);
otherEmail.setType("other");
otherEmail.setValue("outside@version.net");
otherEmail.setDisplay("outside@version.net");
List<Email> emails = Stream.of(homeEmail, workEmail)
.collect(Collectors.toList());
user.setEmails(emails);
//"+1(814)867-5309"
PhoneNumber homePhone = new GlobalPhoneNumberBuilder().globalNumber("+1(814)867-5309").build();
homePhone.setType("home");
homePhone.setPrimary(true);
//"+1(814)867-5307"
PhoneNumber workPhone = new GlobalPhoneNumberBuilder().globalNumber("+1(814)867-5307").build();
workPhone.setType("work");
workPhone.setPrimary(false);
List<PhoneNumber> phones = Stream.of(homePhone, workPhone)
.collect(Collectors.toList());
user.setPhoneNumbers(phones);
EnterpriseExtension enterpriseExtension = new EnterpriseExtension();
enterpriseExtension.setEmployeeNumber("7865");
enterpriseExtension.setDepartment("Dept B.");
Manager manager = new Manager();
manager.setValue("Pointy Haired Boss");
manager.setRef("45353");
enterpriseExtension.setManager(manager);
user.addExtension(enterpriseExtension);
return user;
}
private ScimUser copy(ScimUser scimUser) throws IOException {
ObjectMapper objMapper = new ObjectMapperFactory(registry).createObjectMapper();
String json = objMapper.writeValueAsString(scimUser);
return objMapper.readValue(json, ScimUser.class);
}
private List<PatchOperation> createUser1PatchOps() throws FilterParseException {
List<PatchOperation> patchOperations = new ArrayList<>();
PatchOperation removePhoneNumberOp = new PatchOperation();
removePhoneNumberOp.setOperation(Type.REMOVE);
removePhoneNumberOp.setPath(new PatchOperationPath("phoneNumbers[type eq \"home\"]"));
patchOperations.add(removePhoneNumberOp);
return patchOperations;
}
}