public void Remove_Test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Delete a node
            tree.Remove(this.e2_L1.Id).Wait();

            // Auth-chains for everything in its subtree should be invalidated
            Assert.False(tree.GetAuthChain(this.e2_L1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e4_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.leaf2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.mod2.Id).Result.HasValue);

            // Delete the rest of the subtree
            tree.Remove(this.e3_L2.Id).Wait();
            tree.Remove(this.e4_L2.Id).Wait();
            tree.Remove(this.leaf2.Id).Wait();
            tree.Remove(this.mod2.Id).Wait();

            // Nothing under e2_L1 should remain
            Assert.False(tree.Get(this.e2_L1.Id).Result.HasValue);
            Assert.False(tree.Get(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.Get(this.e4_L2.Id).Result.HasValue);
            Assert.False(tree.Get(this.leaf2.Id).Result.HasValue);
            Assert.False(tree.Get(this.mod2.Id).Result.HasValue);
        }
        public void Update_Test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Re-insert the same node, nothing should have changed
            tree.AddOrUpdate(this.e2_L2).Wait();
            this.CheckValidAuthChains(tree);

            // Re-parent e3_L2 from e2_L1 to e1_L1
            ServiceIdentity updatedIdentity = CreateServiceIdentity(
                this.e3_L2.DeviceId,
                null,
                this.e3_L2.DeviceScope.Expect(() => new InvalidOperationException()),
                this.e1_L1.DeviceScope.Expect(() => new InvalidOperationException()),
                true);

            tree.AddOrUpdate(updatedIdentity).Wait();

            // Equality check
            Option <ServiceIdentity> roundTripIdentity = tree.Get(updatedIdentity.Id).Result;

            Assert.True(roundTripIdentity.Contains(updatedIdentity));

            // The child of e3_L2, leaf2, should also go through a different path for authchain now
            Option <string> authChainActual          = tree.GetAuthChain(this.leaf2.Id).Result;
            string          leaf2_authchain_expected =
                this.leaf2.Id + ";" +
                this.e3_L2.Id + ";" +
                this.e1_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(leaf2_authchain_expected));
        }
        public void GetAuthChain_Test()
        {
            // Setup our tree
            ServiceIdentityTree tree = this.SetupTree();

            // Check for valid auth chains
            this.CheckValidAuthChains(tree);

            // Check non-existent auth chain
            Assert.False(tree.GetAuthChain("nonexistent").Result.HasValue);

            // Insert an orphaned node and check for its invalid auth chain
            ServiceIdentity orphan = CreateServiceIdentity("orphan", null, null, null, false);

            tree.AddOrUpdate(orphan).Wait();
            Assert.False(tree.GetAuthChain(orphan.Id).Result.HasValue);
        }
        internal void CheckValidAuthChains(ServiceIdentityTree tree)
        {
            // Check leaf1
            Option <string> authChainActual          = tree.GetAuthChain(this.leaf1.Id).Result;
            string          leaf1_authchain_expected =
                this.leaf1.Id + ";" +
                this.e1_L2.Id + ";" +
                this.e1_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(leaf1_authchain_expected));

            // Check leaf2
            authChainActual = tree.GetAuthChain(this.leaf2.Id).Result;
            string leaf2_authchain_expected =
                this.leaf2.Id + ";" +
                this.e3_L2.Id + ";" +
                this.e2_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(leaf2_authchain_expected));

            // Check mod1
            authChainActual = tree.GetAuthChain(this.mod1.Id).Result;
            string mod1_authchain_expected =
                this.mod1.Id + ";" +
                this.e2_L2.Id + ";" +
                this.e1_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(mod1_authchain_expected));

            // Check mod2
            authChainActual = tree.GetAuthChain(this.mod2.Id).Result;
            string mod2_authchain_expected =
                this.mod2.Id + ";" +
                this.e4_L2.Id + ";" +
                this.e2_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(mod2_authchain_expected));
        }
        public void MaxDepth_test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Create an orphaned chain
            ServiceIdentity e1_L3 = CreateServiceIdentity("e1_L3", null, "e1_L3_scope", null, true);
            ServiceIdentity e1_L4 = CreateServiceIdentity("e1_L4", null, "e1_L4_scope", "e1_L3_scope", true);
            ServiceIdentity e1_L5 = CreateServiceIdentity("e1_L5", null, "e1_L5_scope", "e1_L4_scope", true);

            tree.AddOrUpdate(e1_L3).Wait();
            tree.AddOrUpdate(e1_L4).Wait();
            tree.AddOrUpdate(e1_L5).Wait();

            // Merge this chain into the main tree, this exceeds the maximum depth,
            // and e1_L5 should have no valid auth chain
            e1_L3 = CreateServiceIdentity("e1_L3", null, "e1_L3_scope", "e1_L2_scope", true);
            tree.AddOrUpdate(e1_L3).Wait();
            Assert.False(tree.GetAuthChain(e1_L5.Id).Result.HasValue);

            // Try explicitly adding yet another layer with an Edge device, this shouldn't yield a valid chain
            tree.AddOrUpdate(e1_L5).Wait();
            Assert.False(tree.GetAuthChain(e1_L5.Id).Result.HasValue);

            // But we should still be able to add a leaf device
            ServiceIdentity leaf = CreateServiceIdentity("leaf", null, null, "e1_L4_scope", false);

            tree.AddOrUpdate(leaf).Wait();

            Option <string> authChainActual         = tree.GetAuthChain(leaf.Id).Result;
            string          leaf_authchain_expected =
                leaf.Id + ";" +
                e1_L4.Id + ";" +
                e1_L3.Id + ";" +
                this.e1_L2.Id + ";" +
                this.e1_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(leaf_authchain_expected));
        }
        public void GetAuthChain_DisabledDevice_Test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Add another branch with a disabled Edge
            ServiceIdentity edge_L2 = CreateServiceIdentity("edge_L2", null, "edge_L2_scope", "e1_L1_scope", true, false);
            ServiceIdentity leaf    = CreateServiceIdentity("leaf", null, null, "edge_L2_scope", false);

            tree.AddOrUpdate(edge_L2).Wait();
            tree.AddOrUpdate(leaf).Wait();

            // Act
            Option <string> authChain = tree.GetAuthChain(leaf.Id).Result;

            // Assert
            Assert.False(authChain.HasValue);
        }
        public void Insertion_OutOfOrder_Test()
        {
            var tree = new ServiceIdentityTree(this.root.Id);

            // Insert L2 identities
            tree.AddOrUpdate(this.e1_L2).Wait();
            tree.AddOrUpdate(this.e2_L2).Wait();
            tree.AddOrUpdate(this.e3_L2).Wait();
            tree.AddOrUpdate(this.e4_L2).Wait();

            // Should have no valid auth chains
            Assert.False(tree.GetAuthChain(this.e1_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e4_L2.Id).Result.HasValue);

            // Insert L1 identities
            tree.AddOrUpdate(this.e1_L1).Wait();
            tree.AddOrUpdate(this.e2_L1).Wait();

            // Should have no valid auth chains
            Assert.False(tree.GetAuthChain(this.e1_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e4_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e1_L1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L1.Id).Result.HasValue);

            // Insert leaf identities
            tree.AddOrUpdate(this.leaf1).Wait();
            tree.AddOrUpdate(this.leaf2).Wait();
            tree.AddOrUpdate(this.mod1).Wait();
            tree.AddOrUpdate(this.mod2).Wait();

            // Should have no valid auth chains
            Assert.False(tree.GetAuthChain(this.e1_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e4_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e1_L1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.leaf1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.leaf2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.mod1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.mod2.Id).Result.HasValue);

            // Insert root
            tree.AddOrUpdate(this.root).Wait();

            // All auth chains should now be valid because root is available
            this.CheckValidAuthChains(tree);
        }