예제 #1
0
        public static void GetCyclePaths_GivenEmptyTables_ReturnsEmptyCollection()
        {
            var cycleDetector = new CycleDetector();
            var result        = cycleDetector.GetCyclePaths(Array.Empty <IRelationalDatabaseTable>());

            Assert.That(result, Is.Empty);
        }
        /// <summary>
        /// Analyses database tables. Reports messages when a set of foreign key relationships forms a cycle.
        /// </summary>
        /// <param name="tables">A set of database tables.</param>
        /// <param name="cancellationToken">A cancellation token used to interrupt analysis.</param>
        /// <returns>A set of linting messages used for reporting. An empty set indicates no issues discovered.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="tables"/> is <c>null</c>.</exception>
        public IAsyncEnumerable <IRuleMessage> AnalyseTables(IEnumerable <IRelationalDatabaseTable> tables, CancellationToken cancellationToken = default)
        {
            if (tables == null)
            {
                throw new ArgumentNullException(nameof(tables));
            }

            var cycleDetector = new CycleDetector();
            var cycles        = cycleDetector.GetCyclePaths(tables.ToList());

            return(cycles.Select(BuildMessage).ToAsyncEnumerable());
        }
예제 #3
0
        public async Task RenderAsync(CancellationToken cancellationToken = default)
        {
            if (!ExportDirectory.Exists)
            {
                ExportDirectory.Create();
            }

            var cycleDetector = new CycleDetector();
            var cycles        = cycleDetector.GetCyclePaths(Tables);
            var hasCycles     = cycles.Count > 0;

            // TODO acutlly use the canellation tokens here
            await ExportInsertionOrderAsync(hasCycles, cancellationToken).ConfigureAwait(false);
            await ExportDeletionOrderAsync(hasCycles, cancellationToken).ConfigureAwait(false);
        }
예제 #4
0
        public static void GetCyclePaths_GivenTablesWithCycle_ReturnsEmptyCollection()
        {
            var cycleDetector = new CycleDetector();

            var mockChildKey = new Mock <IDatabaseKey>();

            mockChildKey.Setup(c => c.KeyType).Returns(DatabaseKeyType.Foreign);
            var childKey = mockChildKey.Object;

            var mockParentKey = new Mock <IDatabaseKey>();

            mockParentKey.Setup(p => p.KeyType).Returns(DatabaseKeyType.Primary);
            var parentKey = mockParentKey.Object;

            // create tables with no cycle where the path is a -> b -> c -> a
            var tableAMock = new Mock <IRelationalDatabaseTable>();

            tableAMock.Setup(t => t.Name).Returns(Identifier.CreateQualifiedIdentifier("a"));
            tableAMock.Setup(t => t.ParentKeys).Returns(new[]
            {
                new DatabaseRelationalKey(
                    Identifier.CreateQualifiedIdentifier("a"),
                    childKey,
                    Identifier.CreateQualifiedIdentifier("b"),
                    parentKey,
                    ReferentialAction.NoAction,
                    ReferentialAction.NoAction
                    )
            });

            var tableBMock = new Mock <IRelationalDatabaseTable>();

            tableBMock.Setup(t => t.Name).Returns(Identifier.CreateQualifiedIdentifier("b"));
            tableBMock.Setup(t => t.ParentKeys).Returns(new[]
            {
                new DatabaseRelationalKey(
                    Identifier.CreateQualifiedIdentifier("b"),
                    childKey,
                    Identifier.CreateQualifiedIdentifier("c"),
                    parentKey,
                    ReferentialAction.NoAction,
                    ReferentialAction.NoAction
                    )
            });

            var tableCMock = new Mock <IRelationalDatabaseTable>();

            tableCMock.Setup(t => t.Name).Returns(Identifier.CreateQualifiedIdentifier("c"));
            tableCMock.Setup(t => t.ParentKeys).Returns(new[]
            {
                new DatabaseRelationalKey(
                    Identifier.CreateQualifiedIdentifier("c"),
                    childKey,
                    Identifier.CreateQualifiedIdentifier("a"),
                    parentKey,
                    ReferentialAction.NoAction,
                    ReferentialAction.NoAction
                    )
            });

            var tables = new[]
            {
                tableAMock.Object,
                tableBMock.Object,
                tableCMock.Object
            };

            var result = cycleDetector.GetCyclePaths(tables);

            var cycleTableNames = result.SelectMany(c => c.Select(t => t.LocalName)).ToList();
            var expectedCycle   = new[] { "a", "b", "c" };

            Assert.Multiple(() =>
            {
                Assert.That(result, Has.Exactly(1).Items);
                Assert.That(cycleTableNames, Has.Exactly(3).Items);
                Assert.That(cycleTableNames, Is.EquivalentTo(expectedCycle));
            });
        }
예제 #5
0
        public static void GetCyclePaths_GivenTablesWithNoCycle_ReturnsEmptyCollection()
        {
            var cycleDetector = new CycleDetector();

            var mockChildKey = new Mock <IDatabaseKey>();

            mockChildKey.Setup(c => c.KeyType).Returns(DatabaseKeyType.Foreign);
            var childKey = mockChildKey.Object;

            var mockParentKey = new Mock <IDatabaseKey>();

            mockParentKey.Setup(p => p.KeyType).Returns(DatabaseKeyType.Primary);
            var parentKey = mockParentKey.Object;

            // create tables with no cycle where the path is a -> b -> c
            var tableAMock = new Mock <IRelationalDatabaseTable>();

            tableAMock.Setup(t => t.Name).Returns(Identifier.CreateQualifiedIdentifier("a"));
            tableAMock.Setup(t => t.ParentKeys).Returns(new[]
            {
                new DatabaseRelationalKey(
                    Identifier.CreateQualifiedIdentifier("a"),
                    childKey,
                    Identifier.CreateQualifiedIdentifier("b"),
                    parentKey,
                    ReferentialAction.NoAction,
                    ReferentialAction.NoAction
                    )
            });

            var tableBMock = new Mock <IRelationalDatabaseTable>();

            tableBMock.Setup(t => t.Name).Returns(Identifier.CreateQualifiedIdentifier("b"));
            tableBMock.Setup(t => t.ParentKeys).Returns(new[]
            {
                new DatabaseRelationalKey(
                    Identifier.CreateQualifiedIdentifier("b"),
                    childKey,
                    Identifier.CreateQualifiedIdentifier("c"),
                    parentKey,
                    ReferentialAction.NoAction,
                    ReferentialAction.NoAction
                    )
            });

            var tableCMock = new Mock <IRelationalDatabaseTable>();

            tableCMock.Setup(t => t.Name).Returns(Identifier.CreateQualifiedIdentifier("c"));
            tableCMock.Setup(t => t.ParentKeys).Returns(Array.Empty <IDatabaseRelationalKey>());

            var tables = new[]
            {
                tableAMock.Object,
                tableBMock.Object,
                tableCMock.Object
            };

            var result = cycleDetector.GetCyclePaths(tables);

            Assert.That(result, Is.Empty);
        }
예제 #6
0
        public static void GetCyclePaths_GivenNullTables_ThrowsArgumentNullException()
        {
            var cycleDetector = new CycleDetector();

            Assert.That(() => cycleDetector.GetCyclePaths(null), Throws.ArgumentNullException);
        }