b/161133229  Initialize traversal delegating structure for the extended validator with full Info object traversal

Change-Id: Iaadf3c806e5d87b170ee01fe4328e92593a34d8c
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java
new file mode 100644
index 0000000..9a5ab66
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java
@@ -0,0 +1,23 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import javax.inject.Inject;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+final class BaseExtendedValidator implements ExtendedValidator {
+
+  private final TraversalCoordinator traversalCoordinator;
+  private final TraversalCommandFactory traversalCommandFactory;
+
+  @Inject
+  BaseExtendedValidator(
+      TraversalCoordinator traversalCoordinator, TraversalCommandFactory traversalCommandFactory) {
+    this.traversalCoordinator = traversalCoordinator;
+    this.traversalCommandFactory = traversalCommandFactory;
+  }
+
+  @Override
+  public void validate(OpenApi3 openApiSpec) {
+    traversalCoordinator.handleTraversalCommand(traversalCommandFactory.create(openApiSpec));
+    traversalCoordinator.traverse();
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionValidationIntegrator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionValidationIntegrator.java
new file mode 100644
index 0000000..e30e1a2
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionValidationIntegrator.java
@@ -0,0 +1,13 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.common.collect.ImmutableMap;
+
+class BaseExtensionValidationIntegrator extends ExtensionValidationIntegrator {
+
+  // TODO(b/161441872): Inject ExtensionValidationIntegrator member dependencies.
+
+  @Override
+  public void validateExtensions(ImmutableMap<String, Object> extensions) {
+    // TODO(b/161441872): Add validation logic here.
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinator.java
new file mode 100644
index 0000000..a224306
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinator.java
@@ -0,0 +1,21 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import java.util.ArrayDeque;
+
+/** Executes {@linkplain Traversal TraversalCommands} in a depth first (right to left) fashion. */
+class BaseTraversalCoordinator implements TraversalCoordinator {
+
+  private final ArrayDeque<TraversalCommand> stack = new ArrayDeque<>();
+
+  @Override
+  public void traverse() {
+    while (!stack.isEmpty()) {
+      stack.pop().traverse();
+    }
+  }
+
+  @Override
+  public void handleTraversalCommand(TraversalCommand traversalCommand) {
+    stack.push(traversalCommand);
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java
new file mode 100644
index 0000000..225d327
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java
@@ -0,0 +1,27 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.inject.assistedinject.Assisted;
+import javax.inject.Inject;
+import org.openapi4j.parser.model.v3.Contact;
+
+/** A {@link Traversal} that traverses a {@link Contact} object. */
+class ContactTraversal extends Traversal<Contact> implements ContactTraversalCommand {
+
+  @Inject
+  ContactTraversal(
+      TraversalCoordinator traversalCoordinator,
+      ExtensionValidationIntegrator extensionValidationIntegrator,
+      TraversalCommandFactory traversalCommandFactory,
+      @Assisted Contact openApiSchemaObj) {
+    super(
+        traversalCoordinator,
+        extensionValidationIntegrator,
+        traversalCommandFactory,
+        openApiSchemaObj);
+  }
+
+  @Override
+  public void traverse() {
+    // TODO(b/161441872): Add call to process extensions.
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversalCommand.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversalCommand.java
new file mode 100644
index 0000000..23843e4
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversalCommand.java
@@ -0,0 +1,4 @@
+package com.apigee.security.oas.extendedvalidator;
+
+/** A {@link TraversalCommand} marker interface for {@link ContactTraversal}. */
+public interface ContactTraversalCommand extends TraversalCommand {}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidator.java
new file mode 100644
index 0000000..dae3f6b
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidator.java
@@ -0,0 +1,13 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+/**
+ * Validates the correctness of ApiSecurityTool's OpenApi extensions given an {@link OpenApi3}
+ * object.
+ */
+public interface ExtendedValidator {
+
+  /** Validates ApiSecurityTool's extensions in passed {@link OpenApi3} object. */
+  void validate(OpenApi3 openApi3);
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModule.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModule.java
index 2451529..0ef5547 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModule.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModule.java
@@ -6,9 +6,12 @@
  * An {@link AbstractModule} that installs all required bindings for the ExtendedValidator module.
  */
 public class ExtendedValidatorMainModule extends AbstractModule {
+
   @Override
   protected void configure() {
+    install(new ExtendedValidatorModule());
     install(new ExtensionModule());
+    install(new TraversalCommandFactoryModule());
     binder().requireExplicitBindings();
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java
new file mode 100644
index 0000000..7d7af1e
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java
@@ -0,0 +1,19 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.inject.AbstractModule;
+
+/**
+ * Configures the bindings of inner components to be installed in the {@link
+ * ExtendedValidatorMainModule}.
+ */
+class ExtendedValidatorModule extends AbstractModule {
+
+  @Override
+  protected void configure() {
+    bind(ExtendedValidator.class).to(BaseExtendedValidator.class);
+
+    bind(ExtensionValidationIntegrator.class).to(BaseExtensionValidationIntegrator.class);
+
+    bind(TraversalCoordinator.class).to(BaseTraversalCoordinator.class);
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidationIntegrator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidationIntegrator.java
new file mode 100644
index 0000000..26fcaed
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionValidationIntegrator.java
@@ -0,0 +1,15 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.common.collect.ImmutableMap;
+
+/** Carries out the process of validating ApiSecurityTool's OpenApi extensions. */
+public abstract class ExtensionValidationIntegrator {
+
+  // TODO(b/161441872): Add needed member dependencies to be injected by child classes.
+
+  /**
+   * Validates extensions from a mapping of extension {@linkplain String name} to extension
+   * {@linkplain Object content}.
+   */
+  abstract void validateExtensions(ImmutableMap<String, Object> extensions);
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java
new file mode 100644
index 0000000..504a806
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java
@@ -0,0 +1,32 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.inject.assistedinject.Assisted;
+import javax.inject.Inject;
+import org.openapi4j.parser.model.v3.Info;
+
+/** A {@link Traversal} that traverses an {@link Info} object. */
+class InfoTraversal extends Traversal<Info> implements InfoTraversalCommand {
+
+  @Inject
+  InfoTraversal(
+      TraversalCoordinator traversalCoordinator,
+      ExtensionValidationIntegrator extensionValidationIntegrator,
+      TraversalCommandFactory traversalCommandFactory,
+      @Assisted Info openApiSchemaObj) {
+    super(
+        traversalCoordinator,
+        extensionValidationIntegrator,
+        traversalCommandFactory,
+        openApiSchemaObj);
+  }
+
+  @Override
+  public void traverse() {
+    // TODO(b/161441872): Process extensions.
+
+    traversalCoordinator.handleTraversalCommand(
+        traversalCommandFactory.create(openApiSchemaObj.getContact()));
+    traversalCoordinator.handleTraversalCommand(
+        traversalCommandFactory.create(openApiSchemaObj.getLicense()));
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversalCommand.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversalCommand.java
new file mode 100644
index 0000000..06a524d
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversalCommand.java
@@ -0,0 +1,4 @@
+package com.apigee.security.oas.extendedvalidator;
+
+/** A {@link TraversalCommand} marker interface for {@link InfoTraversal}. */
+public interface InfoTraversalCommand extends TraversalCommand {}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java
new file mode 100644
index 0000000..86fae07
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java
@@ -0,0 +1,27 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.inject.assistedinject.Assisted;
+import javax.inject.Inject;
+import org.openapi4j.parser.model.v3.License;
+
+/** A {@link Traversal} that traverses a {@link License} object. */
+class LicenseTraversal extends Traversal<License> implements LicenseTraversalCommand {
+
+  @Inject
+  LicenseTraversal(
+      TraversalCoordinator traversalCoordinator,
+      ExtensionValidationIntegrator extensionValidationIntegrator,
+      TraversalCommandFactory traversalCommandFactory,
+      @Assisted License openApiSchemaObj) {
+    super(
+        traversalCoordinator,
+        extensionValidationIntegrator,
+        traversalCommandFactory,
+        openApiSchemaObj);
+  }
+
+  @Override
+  public void traverse() {
+    // TODO(b/161441872): Add call to process extensions.
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversalCommand.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversalCommand.java
new file mode 100644
index 0000000..7bdef8d
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversalCommand.java
@@ -0,0 +1,4 @@
+package com.apigee.security.oas.extendedvalidator;
+
+/** A {@link TraversalCommand} marker interface for {@link LicenseTraversal}. */
+public interface LicenseTraversalCommand extends TraversalCommand {}
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
new file mode 100644
index 0000000..e16e5fb
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
@@ -0,0 +1,31 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.inject.assistedinject.Assisted;
+import javax.inject.Inject;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+/** A {@link Traversal} that traverses an {@link OpenApi3} object. */
+class OpenApiSpecificationTraversal extends Traversal<OpenApi3>
+    implements OpenApiSpecificationTraversalCommand {
+
+  @Inject
+  OpenApiSpecificationTraversal(
+      TraversalCoordinator traversalCoordinator,
+      ExtensionValidationIntegrator extensionValidationIntegrator,
+      TraversalCommandFactory traversalCommandFactory,
+      @Assisted OpenApi3 openApiSchemaObj) {
+    super(
+        traversalCoordinator,
+        extensionValidationIntegrator,
+        traversalCommandFactory,
+        openApiSchemaObj);
+  }
+
+  @Override
+  public void traverse() {
+    // TODO(b/161441872): Process extensions.
+
+    traversalCoordinator.handleTraversalCommand(
+        traversalCommandFactory.create(openApiSchemaObj.getInfo()));
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalCommand.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalCommand.java
new file mode 100644
index 0000000..2f65ded
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalCommand.java
@@ -0,0 +1,4 @@
+package com.apigee.security.oas.extendedvalidator;
+
+/** A {@link TraversalCommand} marker interface for {@link OpenApiSpecificationTraversal}. */
+public interface OpenApiSpecificationTraversalCommand extends TraversalCommand {}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java
new file mode 100644
index 0000000..cc1ad65
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java
@@ -0,0 +1,23 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import org.openapi4j.parser.model.OpenApiSchema;
+
+/** Superclass field skeleton for traversing an {@link OpenApiSchema} object. */
+abstract class Traversal<T extends OpenApiSchema> {
+
+  protected T openApiSchemaObj;
+  protected TraversalCoordinator traversalCoordinator;
+  protected ExtensionValidationIntegrator extensionValidationIntegrator;
+  protected TraversalCommandFactory traversalCommandFactory;
+
+  Traversal(
+      TraversalCoordinator traversalCoordinator,
+      ExtensionValidationIntegrator extensionValidationIntegrator,
+      TraversalCommandFactory traversalCommandFactory,
+      T openApiSchemaObj) {
+    this.traversalCoordinator = traversalCoordinator;
+    this.extensionValidationIntegrator = extensionValidationIntegrator;
+    this.traversalCommandFactory = traversalCommandFactory;
+    this.openApiSchemaObj = openApiSchemaObj;
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommand.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommand.java
new file mode 100644
index 0000000..257eb80
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommand.java
@@ -0,0 +1,8 @@
+package com.apigee.security.oas.extendedvalidator;
+
+/** Used in conjunction with {@link Traversal} to traverse an OpenApi4j object. */
+public interface TraversalCommand {
+
+  /** Starts traversal logic. */
+  void traverse();
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactory.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactory.java
new file mode 100644
index 0000000..88ea292
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactory.java
@@ -0,0 +1,21 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import org.openapi4j.parser.model.v3.Contact;
+import org.openapi4j.parser.model.v3.Info;
+import org.openapi4j.parser.model.v3.License;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+/**
+ * Generates {@link TraversalCommand} implementations via Guice's {@link
+ * com.google.inject.assistedinject.FactoryModuleBuilder}.
+ */
+interface TraversalCommandFactory {
+
+  OpenApiSpecificationTraversalCommand create(OpenApi3 openApiSpec);
+
+  InfoTraversalCommand create(Info info);
+
+  ContactTraversalCommand create(Contact contact);
+
+  LicenseTraversalCommand create(License license);
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryModule.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryModule.java
new file mode 100644
index 0000000..592a57b
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryModule.java
@@ -0,0 +1,19 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+
+/** Configures bindings for {@link TraversalCommandFactory}. */
+public class TraversalCommandFactoryModule extends AbstractModule {
+  @Override
+  protected void configure() {
+    install(
+        new FactoryModuleBuilder()
+            .implement(
+                OpenApiSpecificationTraversalCommand.class, OpenApiSpecificationTraversal.class)
+            .implement(InfoTraversalCommand.class, InfoTraversal.class)
+            .implement(ContactTraversalCommand.class, ContactTraversal.class)
+            .implement(LicenseTraversalCommand.class, LicenseTraversal.class)
+            .build(TraversalCommandFactory.class));
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCoordinator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCoordinator.java
new file mode 100644
index 0000000..bb15592
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalCoordinator.java
@@ -0,0 +1,17 @@
+package com.apigee.security.oas.extendedvalidator;
+
+/**
+ * A {@link TraversalCoordinator} serves as the coordinator for otherwise interdependent {@linkplain
+ * TraversalCommand TraversalCommands}.
+ */
+public interface TraversalCoordinator {
+
+  /** Traverses handled {@linkplain Traversal TraversalCommands}. */
+  void traverse();
+
+  /**
+   * Handles given {@link Traversal} in accordance with {@linkplain TraversalCoordinator
+   * Traversal's} procedure.
+   */
+  void handleTraversalCommand(TraversalCommand traversalCommand);
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidatorTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidatorTest.java
new file mode 100644
index 0000000..c03db6e
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidatorTest.java
@@ -0,0 +1,53 @@
+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 com.google.inject.Guice;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+@RunWith(JUnit4.class)
+public class BaseExtendedValidatorTest {
+
+  @Rule public final MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+  @Mock private OpenApi3 openApiSpec;
+
+  @Mock private TraversalCoordinator traversalCoordinator;
+  private BaseExtendedValidator baseExtendedValidator;
+
+  /** Sets up an {@link ExtendedValidator} for testing. */
+  @Before
+  public void setup() {
+    TraversalCommandFactory traversalCommandFactory =
+        Guice.createInjector(new ExtendedValidatorMainModule())
+            .getInstance(TraversalCommandFactory.class);
+
+    baseExtendedValidator =
+        new BaseExtendedValidator(traversalCoordinator, traversalCommandFactory);
+  }
+
+  @Test
+  public void validate_openApiSpec_callsTraversalCoordinatorTraverse() {
+    baseExtendedValidator.validate(openApiSpec);
+
+    verify(traversalCoordinator, atLeastOnce()).traverse();
+  }
+
+  @Test
+  public void validate_openApiSpec_sendsTraversalCommandToTraversalCoordinator() {
+    baseExtendedValidator.validate(openApiSpec);
+
+    verify(traversalCoordinator).handleTraversalCommand(any(TraversalCommand.class));
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinatorTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinatorTest.java
new file mode 100644
index 0000000..2fdd475
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseTraversalCoordinatorTest.java
@@ -0,0 +1,78 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+
+@RunWith(JUnit4.class)
+public class BaseTraversalCoordinatorTest {
+
+  @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+  @Mock private InfoTraversal infoTraversal;
+  @Mock private InfoTraversal infoTraversalChildOne;
+  @Mock private InfoTraversal infoTraversalChildTwo;
+  @Mock private InfoTraversal infoTraversalChildThree;
+  @Mock private InfoTraversal infoTraversalChildFour;
+
+  private TraversalCoordinator traversalCoordinator;
+
+  /** Sets up main module {@link Injector} and live {@link BaseTraversalCoordinator}. */
+  @Before
+  public void setUp() {
+    Injector injector = Guice.createInjector(new ExtendedValidatorMainModule());
+    traversalCoordinator = injector.getInstance(TraversalCoordinator.class);
+  }
+
+  /** Ensures a right to left depth first traversal is taking place. */
+  @Test
+  public void
+      traverse_traversalCommandWithChildren_traversesAllChildrenRightToLeftBeforeMovingToNextParent() {
+
+    doAnswer(
+            invocationOnMock -> {
+              traversalCoordinator.handleTraversalCommand(infoTraversalChildOne);
+              traversalCoordinator.handleTraversalCommand(infoTraversalChildTwo);
+              return null;
+            })
+        .when(infoTraversal)
+        .traverse();
+
+    doAnswer(
+            invocation -> {
+              traversalCoordinator.handleTraversalCommand(infoTraversalChildThree);
+              traversalCoordinator.handleTraversalCommand(infoTraversalChildFour);
+              return null;
+            })
+        .when(infoTraversalChildTwo)
+        .traverse();
+
+    traversalCoordinator.handleTraversalCommand(infoTraversal);
+    traversalCoordinator.traverse();
+
+    InOrder inOrder =
+        inOrder(
+            infoTraversal,
+            infoTraversalChildTwo,
+            infoTraversalChildFour,
+            infoTraversalChildThree,
+            infoTraversalChildOne);
+    inOrder.verify(infoTraversal).traverse();
+    inOrder.verify(infoTraversalChildTwo).traverse();
+    inOrder.verify(infoTraversalChildFour).traverse();
+    inOrder.verify(infoTraversalChildThree).traverse();
+    inOrder.verify(infoTraversalChildOne).traverse();
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java
index 7c020e9..ccf2e2e 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java
@@ -12,17 +12,36 @@
 
 @RunWith(JUnit4.class)
 public class ExtendedValidatorMainModuleTest {
+  
   private Injector injector;
 
-  /** Sets up main module Guice {@link Injector}. */
+  /** Sets up main module {@link Injector}. */
   @Before
   public void setup() {
     injector = Guice.createInjector(new ExtendedValidatorMainModule());
   }
 
   @Test
-  public void createExtensionFactory_shouldNotFail() {
+  public void getInstance_extensionFactory_shouldNotFail() {
     assertThat(catchThrowable(() -> injector.getInstance(ExtensionFactory.class)))
         .doesNotThrowAnyException();
   }
+
+  @Test
+  public void getInstance_traversalCommandFactory_shouldNotFail() {
+    assertThat(catchThrowable(() -> injector.getInstance(TraversalCommandFactory.class)))
+        .doesNotThrowAnyException();
+  }
+
+  @Test
+  public void getInstance_extendedValidator_shouldNotFail() {
+    assertThat(catchThrowable(() -> injector.getInstance(ExtendedValidator.class)))
+        .doesNotThrowAnyException();
+  }
+
+  @Test
+  public void getInstance_traversalCoordinator_shouldNotFail() {
+    assertThat(catchThrowable(() -> injector.getInstance(TraversalCoordinator.class)))
+        .doesNotThrowAnyException();
+  }
 }
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
new file mode 100644
index 0000000..e6dba27
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
@@ -0,0 +1,60 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.inject.Guice;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+import org.openapi4j.parser.model.v3.Contact;
+import org.openapi4j.parser.model.v3.Info;
+import org.openapi4j.parser.model.v3.License;
+
+@RunWith(JUnit4.class)
+public class InfoTraversalTest {
+
+  @Rule public final MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+  @Mock private TraversalCoordinator traversalCoordinator;
+  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
+  @Mock private Info info;
+
+  private InfoTraversal infoTraversal;
+
+  /** Sets up live and mocked instances for testing. */
+  @Before
+  public void setup() {
+    TraversalCommandFactory traversalCommandFactory =
+        Guice.createInjector(new ExtendedValidatorMainModule())
+            .getInstance(TraversalCommandFactory.class);
+
+    infoTraversal =
+        new InfoTraversal(
+            traversalCoordinator, extensionValidationIntegrator, traversalCommandFactory, info);
+
+    when(info.getContact()).thenReturn(new Contact());
+    when(info.getLicense()).thenReturn(new License());
+  }
+
+  @Test
+  public void traverse_sendsTraversalCoordinatorContactTraversalCommand() {
+    infoTraversal.traverse();
+
+    verify(traversalCoordinator).handleTraversalCommand(any(ContactTraversal.class));
+  }
+
+  @Test
+  public void traverse_sendsTraversalCoordinatorLicenseTraversalCommand() {
+    infoTraversal.traverse();
+
+    verify(traversalCoordinator).handleTraversalCommand(any(LicenseTraversal.class));
+  }
+}
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
new file mode 100644
index 0000000..ea3eb4d
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
@@ -0,0 +1,51 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.inject.Guice;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+import org.openapi4j.parser.model.v3.Info;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+public class OpenApiSpecificationTraversalTest {
+
+  @Rule public final MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+  @Mock private TraversalCoordinator traversalCoordinator;
+  @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
+  @Mock private OpenApi3 openApiSpec;
+
+  private OpenApiSpecificationTraversal openApiSpecificationTraversal;
+
+  /** Sets up live and mocked instances for testing. */
+  @Before
+  public void setup() {
+    TraversalCommandFactory traversalCommandFactory =
+        Guice.createInjector(new ExtendedValidatorMainModule())
+            .getInstance(TraversalCommandFactory.class);
+
+    openApiSpecificationTraversal =
+        new OpenApiSpecificationTraversal(
+            traversalCoordinator,
+            extensionValidationIntegrator,
+            traversalCommandFactory,
+            openApiSpec);
+
+    when(openApiSpec.getInfo()).thenReturn(new Info());
+  }
+
+  @Test
+  public void traverse_sendsInfoChildTraversalToTraversalCoordinator() {
+    openApiSpecificationTraversal.traverse();
+
+    verify(traversalCoordinator).handleTraversalCommand(any(InfoTraversal.class));
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryTest.java
new file mode 100644
index 0000000..2cffc83
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryTest.java
@@ -0,0 +1,51 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.inject.Guice;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.openapi4j.parser.model.v3.Contact;
+import org.openapi4j.parser.model.v3.Info;
+import org.openapi4j.parser.model.v3.License;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+@RunWith(JUnit4.class)
+public class TraversalCommandFactoryTest {
+
+  private TraversalCommandFactory traversalCommandFactory;
+
+  /** Sets up live {@link TraversalCommandFactory} instance. */
+  @Before
+  public void setUp() {
+    traversalCommandFactory =
+        Guice.createInjector(new ExtendedValidatorMainModule())
+            .getInstance(TraversalCommandFactory.class);
+  }
+
+  @Test
+  public void
+      create_openApiSpecificationObjectPassed_returnsOpenApiSpecificationTraversalCommand() {
+    assertThat(traversalCommandFactory.create(new OpenApi3()))
+        .isInstanceOf(OpenApiSpecificationTraversalCommand.class);
+  }
+
+  @Test
+  public void create_infoObjectPassed_returnsInfoTraversalCommand() {
+    assertThat(traversalCommandFactory.create(new Info())).isInstanceOf(InfoTraversalCommand.class);
+  }
+
+  @Test
+  public void create_contactObjectPassed_returnsContactTraversalCommand() {
+    assertThat(traversalCommandFactory.create(new Contact()))
+        .isInstanceOf(ContactTraversalCommand.class);
+  }
+
+  @Test
+  public void create_licenseObjectPassed_returnsLicenseTraversalCommand() {
+    assertThat(traversalCommandFactory.create(new License()))
+        .isInstanceOf(LicenseTraversalCommand.class);
+  }
+}