b/162837037 Handle traversal null collections

Change-Id: Ib14c3965a9646786ca4184852bdd98bf2a76d0f4
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java
index 5319099..a10f139 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java
@@ -88,7 +88,8 @@
   }
 
   @Override
-  public void sendServerVariableTraversals(ImmutableMap<String, ServerVariable> serverVariablesMap) {
+  public void sendServerVariableTraversals(
+      ImmutableMap<String, ServerVariable> serverVariablesMap) {
     Optional.ofNullable(serverVariablesMap)
         .ifPresent(
             serverVariables -> {
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Immutables.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Immutables.java
new file mode 100644
index 0000000..e39b609
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Immutables.java
@@ -0,0 +1,24 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** Set of utility methods for safely transforming a collection into an immutable collection. */
+public final class Immutables {
+
+  /** Safely converts {@link List} to {@link ImmutableList}. */
+  public static final <T> ImmutableList<T> toImmutableList(@Nullable List<T> list) {
+    return Optional.ofNullable(list).map(ImmutableList::copyOf).orElse(ImmutableList.of());
+  }
+
+  /** Safely converts {@link Map} to {@link ImmutableMap}. */
+  public static final <K, V> ImmutableMap<K, V> toImmutableMap(@Nullable Map<K, V> map) {
+    return Optional.ofNullable(map).map(ImmutableMap::copyOf).orElse(ImmutableMap.of());
+  }
+
+  private Immutables() {}
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
index 3096ba5..1709d93 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
@@ -1,5 +1,7 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableList;
+
 import com.google.common.collect.ImmutableList;
 import com.google.inject.assistedinject.Assisted;
 import javax.inject.Inject;
@@ -25,8 +27,8 @@
 
     traversalHelper.sendExternalDocsTraversal(openApiSchemaObj.getExternalDocs());
     traversalHelper.sendInfoTraversal(openApiSchemaObj.getInfo());
-    traversalHelper.sendServerTraversals(ImmutableList.copyOf(openApiSchemaObj.getServers()));
-    traversalHelper.sendTagTraversals(ImmutableList.copyOf(openApiSchemaObj.getTags()));
+    traversalHelper.sendServerTraversals(toImmutableList(openApiSchemaObj.getServers()));
+    traversalHelper.sendTagTraversals(toImmutableList(openApiSchemaObj.getTags()));
     // TODO(b/162508047): Add path object traversal and callback object traversal logic.
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
index c7c3967..c464d7a 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
@@ -1,7 +1,8 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.Immutables.toImmutableMap;
+
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -28,7 +29,6 @@
 
     TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
 
-    traversalHelper.sendServerVariableTraversals(
-        ImmutableMap.copyOf(openApiSchemaObj.getVariables()));
+    traversalHelper.sendServerVariableTraversals(toImmutableMap(openApiSchemaObj.getVariables()));
   }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ImmutablesTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ImmutablesTest.java
new file mode 100644
index 0000000..db39a19
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ImmutablesTest.java
@@ -0,0 +1,50 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ImmutablesTest {
+
+  @Test
+  public void toImmutableList_validMutableList_returnsReplicaImmutableList() {
+    List<Integer> list = new ArrayList<>(3);
+    list.add(3);
+    list.add(2);
+    list.add(1);
+
+    assertThat(Immutables.toImmutableList(list)).isEqualTo(list);
+  }
+
+  @Test
+  public void toImmutableList_null_returnsEmptyImmutableList() {
+    assertThat(Immutables.toImmutableList(null)).isEqualTo(ImmutableList.of());
+  }
+
+  @Test
+  public void toImmutableMap_validMutableMap_returnsReplicaImmutableMap() {
+    NavigableMap<String, Integer> map = new TreeMap<>();
+    map.put("one", 1);
+    map.put("two", 2);
+    map.put("three", 3);
+    ImmutableSortedMap<String, Integer> immutableMap =
+        ImmutableSortedMap.of("one", 1, "two", 2, "three", 3);
+
+    assertThat(map).isEqualTo(immutableMap);
+  }
+
+  @Test
+  public void toImmutableMap_null_returnsEmptyImmutableMap() {
+    assertThat(Immutables.toImmutableMap(null)).isEqualTo(ImmutableMap.of());
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
index bb23e34..462db49 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
@@ -1,7 +1,6 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -51,7 +50,6 @@
   public void traverse_sendsContactToTraversalHelper() {
     infoTraversal.traverse();
 
-    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
     verify(traversalHelper).sendContactTraversal(contact);
   }
 
@@ -59,7 +57,6 @@
   public void traverse_sendsLicenseToTraversalHelper() {
     infoTraversal.traverse();
 
-    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
     verify(traversalHelper).sendLicenseTraversal(license);
   }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
index 7451a2f..4df4f5d 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
@@ -78,15 +78,31 @@
   public void traverse_sendsServersToTraversalHelper() {
     openApiSpecificationTraversal.traverse();
 
-    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
     verify(traversalHelper).sendServerTraversals(servers);
   }
 
   @Test
+  public void traverse_nullServersMember_doesNotFail() {
+    when(openApiSpec.getServers()).thenReturn(null);
+
+    openApiSpecificationTraversal.traverse();
+
+    verify(traversalHelper, atLeastOnce()).sendServerTraversals(any());
+  }
+
+  @Test
   public void traverse_sendsTagsToTraversalHelper() {
     openApiSpecificationTraversal.traverse();
 
-    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
     verify(traversalHelper).sendTagTraversals(tags);
   }
+
+  @Test
+  public void traverse_nullTagsMember_doesNotFail() {
+    when(openApiSpec.getTags()).thenReturn(null);
+
+    openApiSpecificationTraversal.traverse();
+
+    verify(traversalHelper, atLeastOnce()).sendTagTraversals(any());
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
index 24294a8..5887005 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
@@ -52,10 +52,18 @@
   }
 
   @Test
-  public void traverse_sendsServerVariableChildrenTraversalsToTraversalCommander() {
+  public void traverse_sendsServerVariablesToTraversalHelper() {
     serverTraversal.traverse();
 
-    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
     verify(traversalHelper).sendServerVariableTraversals(ImmutableMap.copyOf(serverVariables));
   }
+
+  @Test
+  public void traverse_nullServerVariablesMember_doesNotFail() {
+    when(server.getVariables()).thenReturn(null);
+
+    serverTraversal.traverse();
+
+    verify(traversalHelper, atLeastOnce()).sendServerVariableTraversals(any());
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
index 3e57ee7..ab86592 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
@@ -1,7 +1,6 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -44,10 +43,9 @@
   }
 
   @Test
-  public void traverse_sendsTraversalCommanderExternalDocsTraversalCommand() {
+  public void traverse_sendsExternalDocsToTraversalHelper() {
     tagTraversal.traverse();
 
-    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
     verify(traversalHelper).sendExternalDocsTraversal(externalDocs);
   }
 }