From 3e4e11c14b8cd5e7387da6d9f98f1bb04dac7440 Mon Sep 17 00:00:00 2001 From: Litichevskiy Dmitriy Date: Thu, 27 Jun 2024 21:41:28 +0300 Subject: [PATCH] Tests for Structurizr JSON parser were added --- Byndyusoft.ArchitectureTesting.sln | 9 +- ...yusoft.ArchitectureTesting.sln.DotSettings | 3 +- .../ArchitectureFiles/musicality-labs.dsl | 69 ++++++ .../ArchitectureFiles/musicality-labs.json | 209 ++++++++++++++++++ .../JsonParserTests.cs | 74 +++++++ .../StructurizrParser.Tests.csproj | 38 ++++ 6 files changed, 400 insertions(+), 2 deletions(-) create mode 100644 tests/StructurizrParser.Tests/ArchitectureFiles/musicality-labs.dsl create mode 100644 tests/StructurizrParser.Tests/ArchitectureFiles/musicality-labs.json create mode 100644 tests/StructurizrParser.Tests/JsonParserTests.cs create mode 100644 tests/StructurizrParser.Tests/StructurizrParser.Tests.csproj diff --git a/Byndyusoft.ArchitectureTesting.sln b/Byndyusoft.ArchitectureTesting.sln index caaa244..170289c 100644 --- a/Byndyusoft.ArchitectureTesting.sln +++ b/Byndyusoft.ArchitectureTesting.sln @@ -18,7 +18,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abstractions", "src\Abstractions\Abstractions.csproj", "{0B01C1F0-B144-4772-872A-D056C3F03F9A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructurizrParser", "src\StructurizrParser\StructurizrParser.csproj", "{DD449432-9A79-49A7-82CA-27FC3A8A356D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StructurizrParser", "src\StructurizrParser\StructurizrParser.csproj", "{DD449432-9A79-49A7-82CA-27FC3A8A356D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructurizrParser.Tests", "tests\StructurizrParser.Tests\StructurizrParser.Tests.csproj", "{01B6A354-9B5A-4ACC-92E5-6DED1E88AFF7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -34,6 +36,10 @@ Global {DD449432-9A79-49A7-82CA-27FC3A8A356D}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD449432-9A79-49A7-82CA-27FC3A8A356D}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD449432-9A79-49A7-82CA-27FC3A8A356D}.Release|Any CPU.Build.0 = Release|Any CPU + {01B6A354-9B5A-4ACC-92E5-6DED1E88AFF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01B6A354-9B5A-4ACC-92E5-6DED1E88AFF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01B6A354-9B5A-4ACC-92E5-6DED1E88AFF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01B6A354-9B5A-4ACC-92E5-6DED1E88AFF7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -41,6 +47,7 @@ Global GlobalSection(NestedProjects) = preSolution {0B01C1F0-B144-4772-872A-D056C3F03F9A} = {9D5D61C4-D494-4124-A184-59C12C2BB2BC} {DD449432-9A79-49A7-82CA-27FC3A8A356D} = {9D5D61C4-D494-4124-A184-59C12C2BB2BC} + {01B6A354-9B5A-4ACC-92E5-6DED1E88AFF7} = {ECEA017E-7CB0-4E1C-8D29-9E38D39CD3C8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A79E4AD9-244B-42E6-BD30-255B9C3DEC46} diff --git a/Byndyusoft.ArchitectureTesting.sln.DotSettings b/Byndyusoft.ArchitectureTesting.sln.DotSettings index 7e1aca7..c7a15b1 100644 --- a/Byndyusoft.ArchitectureTesting.sln.DotSettings +++ b/Byndyusoft.ArchitectureTesting.sln.DotSettings @@ -30,13 +30,14 @@ ONLY_FOR_MULTILINE ONLY_FOR_MULTILINE True - NEXT_LINE_SHIFTED_2 + NEXT_LINE NEVER NEVER False NEVER False NEVER + False LINE_BREAK False True diff --git a/tests/StructurizrParser.Tests/ArchitectureFiles/musicality-labs.dsl b/tests/StructurizrParser.Tests/ArchitectureFiles/musicality-labs.dsl new file mode 100644 index 0000000..762efe3 --- /dev/null +++ b/tests/StructurizrParser.Tests/ArchitectureFiles/musicality-labs.dsl @@ -0,0 +1,69 @@ +workspace { + + model { + + changesMQ = element "musicality_labs.data.changes" "RabbitMQ" { + tags "MQ" + } + + storage = softwareSystem "Storage" { + storageWorker = container "Storage Worker" { + tags "Worker" + technology ".NET Core" + url "https://github.com/MusicalityLabs/musicality-labs-storage-worker" + } + storageApi = container "Storage Api" { + tags "WebApi" + technology ".NET Core" + url "https://github.com/MusicalityLabs/musicality-labs-storage-api" + + storageWorker -> this "Changes" "REST" "Sync" + } + storageDatabase = container "storageapi" { + tags "Database" + technology "PostgreSQL" + + storageApi -> this "Changes" "SQL" "Sync" + } + } + + changesMQ -> storageWorker "Changes" "AMQP" "Async" + } + + views { + systemLandscape { + include * + autolayout lr + } + + container storage { + include * + autolayout lr + } + + styles { + element "ExternalSystem" { + background #7f7f7f + } + element "Database" { + shape cylinder + } + element "MQ" { + shape pipe + } + relationship "Sync" { + dashed false + } + relationship "Async" { + dashed true + } + relationship "TechDebt" { + color red + } + } + + theme default + + } + +} \ No newline at end of file diff --git a/tests/StructurizrParser.Tests/ArchitectureFiles/musicality-labs.json b/tests/StructurizrParser.Tests/ArchitectureFiles/musicality-labs.json new file mode 100644 index 0000000..1428ae6 --- /dev/null +++ b/tests/StructurizrParser.Tests/ArchitectureFiles/musicality-labs.json @@ -0,0 +1,209 @@ +{ + "configuration" : { }, + "description" : "Description", + "documentation" : { }, + "id" : 1, + "lastModifiedAgent" : "structurizr-javascript", + "lastModifiedDate" : "2024-06-27T18:15:36Z", + "model" : { + "customElements" : [ { + "id" : "1", + "metadata" : "RabbitMQ", + "name" : "musicality_labs.data.changes", + "properties" : { + "structurizr.dsl.identifier" : "changesmq" + }, + "relationships" : [ { + "description" : "Changes", + "destinationId" : "3", + "id" : "8", + "properties" : { + "structurizr.dsl.identifier" : "cd68ff6f-c752-46dd-8e1f-b23e59be94b1" + }, + "sourceId" : "1", + "tags" : "Relationship,Async", + "technology" : "AMQP" + }, { + "description" : "Changes", + "destinationId" : "2", + "id" : "9", + "linkedRelationshipId" : "8", + "sourceId" : "1", + "technology" : "AMQP" + } ], + "tags" : "Element,MQ" + } ], + "softwareSystems" : [ { + "containers" : [ { + "documentation" : { }, + "id" : "3", + "name" : "Storage Worker", + "properties" : { + "structurizr.dsl.identifier" : "storageworker" + }, + "relationships" : [ { + "description" : "Changes", + "destinationId" : "4", + "id" : "5", + "properties" : { + "structurizr.dsl.identifier" : "640e21c6-67d2-4c8f-96ba-abcb80762a5f" + }, + "sourceId" : "3", + "tags" : "Relationship,Sync", + "technology" : "REST" + } ], + "tags" : "Element,Container,Worker", + "technology" : ".NET Core", + "url" : "https://github.com/MusicalityLabs/musicality-labs-storage-worker" + }, { + "documentation" : { }, + "id" : "4", + "name" : "Storage Api", + "properties" : { + "structurizr.dsl.identifier" : "storageapi" + }, + "relationships" : [ { + "description" : "Changes", + "destinationId" : "6", + "id" : "7", + "properties" : { + "structurizr.dsl.identifier" : "55219b68-88b9-49c8-9003-903725a1b00b" + }, + "sourceId" : "4", + "tags" : "Relationship,Sync", + "technology" : "SQL" + } ], + "tags" : "Element,Container,WebApi", + "technology" : ".NET Core", + "url" : "https://github.com/MusicalityLabs/musicality-labs-storage-api" + }, { + "documentation" : { }, + "id" : "6", + "name" : "storageapi", + "properties" : { + "structurizr.dsl.identifier" : "storagedatabase" + }, + "tags" : "Element,Container,Database", + "technology" : "PostgreSQL" + } ], + "documentation" : { }, + "id" : "2", + "location" : "Unspecified", + "name" : "Storage", + "properties" : { + "structurizr.dsl.identifier" : "storage" + }, + "tags" : "Element,Software System" + } ] + }, + "name" : "Name", + "properties" : { + "structurizr.dsl" : "d29ya3NwYWNlIHsKCiAgICBtb2RlbCB7CiAgICAgICAgCiAgICAgICAgY2hhbmdlc01RID0gZWxlbWVudCAibXVzaWNhbGl0eV9sYWJzLmRhdGEuY2hhbmdlcyIgIlJhYmJpdE1RIiB7CiAgICAgICAgICAgIHRhZ3MgIk1RIiAKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgc3RvcmFnZSA9IHNvZnR3YXJlU3lzdGVtICJTdG9yYWdlIiB7CiAgICAgICAgICAgIHN0b3JhZ2VXb3JrZXIgPSBjb250YWluZXIgIlN0b3JhZ2UgV29ya2VyIiB7CiAgICAgICAgICAgICAgICB0YWdzICJXb3JrZXIiIAoJCQkJdGVjaG5vbG9neSAiLk5FVCBDb3JlIgoJCQkJdXJsICJodHRwczovL2dpdGh1Yi5jb20vTXVzaWNhbGl0eUxhYnMvbXVzaWNhbGl0eS1sYWJzLXN0b3JhZ2Utd29ya2VyIgogICAgICAgICAgICB9CiAgICAgICAgICAgIHN0b3JhZ2VBcGkgPSBjb250YWluZXIgIlN0b3JhZ2UgQXBpIiB7CiAgICAgICAgICAgICAgICB0YWdzICJXZWJBcGkiIAoJCQkJdGVjaG5vbG9neSAiLk5FVCBDb3JlIgoJCQkJdXJsICJodHRwczovL2dpdGh1Yi5jb20vTXVzaWNhbGl0eUxhYnMvbXVzaWNhbGl0eS1sYWJzLXN0b3JhZ2Utd29ya2VyIgoJCQkJCiAgICAgICAgICAgICAgICBzdG9yYWdlV29ya2VyIC0+IHRoaXMgIkNoYW5nZXMiICJSRVNUIiAiU3luYyIKICAgICAgICAgICAgfQoJCQlzdG9yYWdlRGF0YWJhc2UgPSBjb250YWluZXIgInN0b3JhZ2VhcGkiIHsKICAgICAgICAgICAgICAgIHRhZ3MgIkRhdGFiYXNlIiAKCQkJCXRlY2hub2xvZ3kgIlBvc3RncmVTUUwiCgkJCQkKICAgICAgICAgICAgICAgIHN0b3JhZ2VBcGkgLT4gdGhpcyAiQ2hhbmdlcyIgIlNRTCIgIlN5bmMiCiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgY2hhbmdlc01RIC0+IHN0b3JhZ2VXb3JrZXIgIkNoYW5nZXMiICJBTVFQIiAiQXN5bmMiCiAgICB9CgogICAgdmlld3MgewogICAgICAgIHN5c3RlbUxhbmRzY2FwZSB7CiAgICAgICAgICAgIGluY2x1ZGUgKiAKCQkJYXV0b2xheW91dCBscgogICAgICAgIH0KCQkKCQljb250YWluZXIgc3RvcmFnZSB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhdXRvbGF5b3V0IGxyCiAgICAgICAgfQoJCSAgICAgICAgCiAgICAgICAgc3R5bGVzIHsKICAgICAgICAgICAgZWxlbWVudCAiRXh0ZXJuYWxTeXN0ZW0iIHsKICAgICAgICAgICAgICAgIGJhY2tncm91bmQgIzdmN2Y3ZgogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsZW1lbnQgIkRhdGFiYXNlIiB7CiAgICAgICAgICAgICAgICBzaGFwZSBjeWxpbmRlcgogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsZW1lbnQgIk1RIiB7CiAgICAgICAgICAgICAgICBzaGFwZSBwaXBlCiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmVsYXRpb25zaGlwICJTeW5jIiB7CiAgICAgICAgICAgICAgICBkYXNoZWQgZmFsc2UKICAgICAgICAgICAgfQoJCQlyZWxhdGlvbnNoaXAgIkFzeW5jIiB7CiAgICAgICAgICAgICAgICBkYXNoZWQgdHJ1ZQogICAgICAgICAgICB9CgkJCXJlbGF0aW9uc2hpcCAiVGVjaERlYnQiIHsKCQkJCWNvbG9yIHJlZAoJCQl9CiAgICAgICAgfQogICAgICAgCiAgICAgICAgdGhlbWUgZGVmYXVsdAoKICAgIH0KICAgIAp9Cg==" + }, + "views" : { + "configuration" : { + "branding" : { }, + "lastSavedView" : "Container-001", + "metadataSymbols" : "SquareBrackets", + "styles" : { + "elements" : [ { + "background" : "#7f7f7f", + "tag" : "ExternalSystem" + }, { + "shape" : "Cylinder", + "tag" : "Database" + }, { + "shape" : "Pipe", + "tag" : "MQ" + } ], + "relationships" : [ { + "dashed" : false, + "tag" : "Sync" + }, { + "dashed" : true, + "tag" : "Async" + }, { + "color" : "#ff0000", + "tag" : "TechDebt" + } ] + }, + "terminology" : { }, + "themes" : [ "https://static.structurizr.com/themes/default/theme.json" ] + }, + "containerViews" : [ { + "automaticLayout" : { + "applied" : true, + "edgeSeparation" : 0, + "implementation" : "Graphviz", + "nodeSeparation" : 300, + "rankDirection" : "LeftRight", + "rankSeparation" : 300, + "vertices" : false + }, + "dimensions" : { + "height" : 811, + "width" : 3120 + }, + "elements" : [ { + "id" : "1", + "x" : 199, + "y" : 219 + }, { + "id" : "3", + "x" : 949, + "y" : 219 + }, { + "id" : "4", + "x" : 1699, + "y" : 219 + }, { + "id" : "6", + "x" : 2449, + "y" : 219 + } ], + "externalSoftwareSystemBoundariesVisible" : false, + "generatedKey" : true, + "key" : "Container-001", + "order" : 2, + "relationships" : [ { + "id" : "5" + }, { + "id" : "7" + }, { + "id" : "8" + } ], + "softwareSystemId" : "2" + } ], + "systemLandscapeViews" : [ { + "automaticLayout" : { + "applied" : true, + "edgeSeparation" : 0, + "implementation" : "Graphviz", + "nodeSeparation" : 300, + "rankDirection" : "LeftRight", + "rankSeparation" : 300, + "vertices" : false + }, + "dimensions" : { + "height" : 700, + "width" : 1600 + }, + "elements" : [ { + "id" : "1", + "x" : 200, + "y" : 200 + }, { + "id" : "2", + "x" : 950, + "y" : 200 + } ], + "enterpriseBoundaryVisible" : true, + "generatedKey" : true, + "key" : "SystemLandscape-001", + "order" : 1, + "relationships" : [ { + "id" : "9" + } ] + } ] + } +} \ No newline at end of file diff --git a/tests/StructurizrParser.Tests/JsonParserTests.cs b/tests/StructurizrParser.Tests/JsonParserTests.cs new file mode 100644 index 0000000..ede379e --- /dev/null +++ b/tests/StructurizrParser.Tests/JsonParserTests.cs @@ -0,0 +1,74 @@ +namespace Byndyusoft.ArchitectureTesting.StructurizrParser.Tests +{ + using System; + using System.IO; + using Abstractions.ServiceContracts; + using Abstractions.ServiceContracts.Dependencies; + using FluentAssertions; + using Xunit; + + public class StructurizrJsonParsingTestCase + { + public string JsonFileName { get; set; } + + public Func ServiceNameMatcher { get; set; } + + public ServiceContract[] ExpectedServiceContracts { get; set; } + } + + public class JsonParserTests + { + public static readonly TheoryData StructurizrJsonParsingTestCases; + + static JsonParserTests() + { + StructurizrJsonParsingTestCases + = new TheoryData + { + new StructurizrJsonParsingTestCase + { + JsonFileName = @".\ArchitectureFiles\musicality-labs.json", + ServiceNameMatcher = x => x.StartsWith("musicality-labs", StringComparison.InvariantCultureIgnoreCase), + ExpectedServiceContracts + = new[] + { + new ServiceContract + { + Name = "musicality-labs-storage-api", + Dependencies + = new DependencyBase[] + { + new DbDependency {Name = "storageapi"} + } + }, + new ServiceContract + { + Name = "musicality-labs-storage-worker", + Dependencies + = new DependencyBase[] + { + new RabbitDependency {Name = "musicality_labs.data.changes", Direction = MqDependencyDirection.Incoming}, + new ApiDependency {Name = "musicality-labs-storage-api"} + } + } + } + } + }; + } + + [Theory] + [MemberData(nameof(StructurizrJsonParsingTestCases))] + public void ShouldParseStructurizrJson(StructurizrJsonParsingTestCase testCase) + { + // Given + var jsonString = File.ReadAllText(testCase.JsonFileName); + var parser = new JsonParser(testCase.ServiceNameMatcher); + + // When + var actualServiceContracts = parser.Parse(jsonString); + + // Then + actualServiceContracts.Should().BeEquivalentTo(testCase.ExpectedServiceContracts); + } + } +} \ No newline at end of file diff --git a/tests/StructurizrParser.Tests/StructurizrParser.Tests.csproj b/tests/StructurizrParser.Tests/StructurizrParser.Tests.csproj new file mode 100644 index 0000000..65954b9 --- /dev/null +++ b/tests/StructurizrParser.Tests/StructurizrParser.Tests.csproj @@ -0,0 +1,38 @@ + + + + + + net6.0 + enable + false + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + +