From da98d1ddf5099afa8cee634b45b99e68d3595a6b Mon Sep 17 00:00:00 2001 From: Dion Date: Fri, 18 Oct 2024 17:01:54 +0200 Subject: [PATCH 1/3] Add architecture test --- .../Architecture/ArchitectureTest.cs | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 starsky/starskytest/Architecture/ArchitectureTest.cs diff --git a/starsky/starskytest/Architecture/ArchitectureTest.cs b/starsky/starskytest/Architecture/ArchitectureTest.cs new file mode 100644 index 000000000..dc89d8673 --- /dev/null +++ b/starsky/starskytest/Architecture/ArchitectureTest.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using starsky.foundation.platform.Helpers; + +namespace starskytest.Architecture; + +/// +/// Fitness functions for software architecture are defined as a way of confirming that a +/// particular +/// characteristic of a solution's architecture that is considered important to maintain over time, +/// is in fact still the case. +/// Some of these can be confirmed via automated tests. +/// This class defines tests that ensure the project reference structure as defined by Sitecore +/// Helix principles are being maintained. +/// +/// +/// References: +/// https://www.thoughtworks.com/insights/blog/microservices-evolutionary-architecture +/// http://shop.oreilly.com/product/0636920080237.do +/// http://helix.sitecore.net/introduction/index.html +/// +[TestClass] +public class TheSolutionShould +{ + private const string SolutionName = "starsky.sln"; + + private const string Foundation = "Foundation"; + private const string Feature = "Feature"; + private const string Project = "Project"; + private const string ProjectCli = "cli"; + private const string MainProject = "starsky"; + private const string MainProjectWeb = "starsky.project.web"; + + private const string TestProjectSuffix = "test"; + private const string BuildProject = "_build"; + private const string DocsProject = "documentation"; + + /// + /// Ensures that only appropriate inter-project references are in place within the solution. + /// Specifically: + /// - Foundation projects should only reference other Foundation projects + /// - Feature projects should only reference Foundation projects + /// - Project projects should only reference Foundation or Feature projects + /// + /// + /// Hat-tip for locating projects and references in solution: + /// https://stackoverflow.com/a/17571223/489433 + /// + [TestMethod] + public void ContainNoHelixInvalidProjectReferences() + { + // Arrange - get list of projects in the solution, along with a list of references for each project + var projects = GetProjectsWithReferences(); + + // Act - review project list for invalid references + var invalidFoundationReferences = new List(); + var invalidFeatureReferences = new List(); + var invalidProjectReferences = new List(); + foreach ( var project in projects ) + { + if ( project.Key.Contains(Foundation, StringComparison.OrdinalIgnoreCase) ) + { + GetInvalidReferences(project, new[] { Feature, Project }, + invalidFoundationReferences); + continue; + } + + if ( project.Key.Contains(Feature, StringComparison.OrdinalIgnoreCase) ) + { + GetInvalidReferences(project, new[] { Feature, Project }, + invalidFeatureReferences); + continue; + } + + if ( project.Key.Contains(Project, StringComparison.OrdinalIgnoreCase) && + project.Key != MainProjectWeb && project.Key != MainProject ) + { + GetInvalidReferences(project, new[] { Project }, invalidProjectReferences); + continue; + } + + if ( project.Key.Contains(ProjectCli, StringComparison.OrdinalIgnoreCase) || + project.Key.Contains(TestProjectSuffix, + StringComparison.OrdinalIgnoreCase) || project.Key == BuildProject || + project.Key == DocsProject || project.Key == MainProjectWeb || + project.Key == MainProject ) + { + continue; + } + + throw new MissingFieldException($"Unknown project type {project}"); + } + + // Assert - check if we have any invalid refererences + AssertLayerReferences(invalidFoundationReferences, Foundation); + AssertLayerReferences(invalidFeatureReferences, Feature); + AssertLayerReferences(invalidProjectReferences, Project); + } + + private static Dictionary> GetProjectsWithReferences() + { + var solutionFilePath = GetSolutionFilePath(); + var solutionFileContents = File.ReadAllText(solutionFilePath); + var matches = GetProjectsFromSolutionFileContents(solutionFileContents); + return matches + .Select(x => x.Groups[2].Value) + .ToDictionary(GetProjectFileName, + p => GetReferencedProjects(solutionFilePath, p)); + } + + private static string GetSolutionFilePath() + { + var parent = Directory.GetParent(BaseDirectoryProjectHelper.BaseDirectoryProject) + ?.Parent?.Parent?.Parent?.Parent?.FullName; + if ( string.IsNullOrEmpty(parent) ) + { + throw new DirectoryNotFoundException("Unable to locate solution file"); + } + + return Path.Combine(parent, SolutionName); + } + + private static IList GetPathParts(string directory) + { + return directory + .Split(new[] { "\\" }, StringSplitOptions.None) + .ToList(); + } + + private static IEnumerable GetProjectsFromSolutionFileContents( + string solutionFileContents) + { + var regex = new Regex( + "Project\\(\"\\{[\\w-]*\\}\"\\) = \"([\\w _]*.*)\", \"(.*\\.(cs|vcx|vb)proj)\"", + RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + return regex.Matches(solutionFileContents); + } + + private static string GetProjectFileName(string value) + { + var result = GetPathParts(value).LastOrDefault()?.Replace(".csproj", string.Empty); + result ??= string.Empty; + return result; + } + + private static IList GetReferencedProjects(string solutionFilePath, + string projectFilePath) + { + var rootedProjectFilePath = + Path.Combine(solutionFilePath.Replace(SolutionName, string.Empty), + projectFilePath).Replace("\\", Path.DirectorySeparatorChar.ToString()); + return GetReferencedProjects(rootedProjectFilePath); + } + + private static List GetReferencedProjects(string rootedProjectFilePath) + { + var result = new List(); + + // Load the XML document + var xmlDoc = XDocument.Load(rootedProjectFilePath); + + // Get all ProjectReference elements + var projectReferences = xmlDoc.Descendants("ProjectReference"); + + // Extract the Include attribute value from each ProjectReference element + foreach ( var reference in projectReferences ) + { + var includeValue = reference.Attribute("Include")?.Value; + if ( !string.IsNullOrEmpty(includeValue) ) + { + result.Add(includeValue); + } + } + + return result; + } + + private static void GetInvalidReferences(KeyValuePair> project, + string[] invalidLayers, List invalidReferences) + { + invalidReferences.AddRange(project.Value + .Where(x => ContainsInvalidReference(project.Key, x, invalidLayers)) + .Select(x => $"{project.Key} references {x}")); + } + + private static bool ContainsInvalidReference(string projectName, + string referencedProjectName, IEnumerable invalidLayers) + { + return invalidLayers + .Any(x => ProjectReferencesInvalidLayer(referencedProjectName, x) && + !ReferencedProjectIsFromDedicatedTestProject(projectName, + referencedProjectName) && + !ReferencedProjectIsPartOfSubFeature(projectName, + referencedProjectName)); + } + + private static bool ProjectReferencesInvalidLayer(string referencedProject, + string invalidLayer) + { + return referencedProject.Contains(invalidLayer, + StringComparison.InvariantCultureIgnoreCase); + } + + private static bool ReferencedProjectIsFromDedicatedTestProject(string projectName, + string referencedProject) + { + return projectName == referencedProject + TestProjectSuffix; + } + + private static bool ReferencedProjectIsPartOfSubFeature(string projectName, + string referencedProject) + { + // We have some features that are broken into more than one project. Via naming convention + // we should allow these to be valid. + // E.g. MySolution.Feature.FeatureName.Api references MySolution.Feature.FeatureName.Model + if ( projectName.Split('.').Length <= 3 || + referencedProject.Split('.').Length <= 3 ) + { + // Not a sub-feature + return false; + } + + return projectName.Split('.')[2] == referencedProject.Split('.')[2]; + } + + private static void AssertLayerReferences(IReadOnlyCollection invalidReferences, + string layer) + { + if ( layer == "Feature" && invalidReferences.Any(p => + p.Contains( + @"starsky.feature.trash references ..\starsky.feature.metaupdate\starsky.feature.metaupdate.csproj")) ) + { + // This is a known issue, as the Trash feature references the MetaUpdate feature + // This should be refactored in the future + return; + } + + Assert.AreEqual(0, invalidReferences.Count, + $"There should be no Helix incompatible references in the {layer} layer. " + + $"{invalidReferences.Count} invalid reference{( invalidReferences.Count == 1 ? " was" : "s were" )} " + + $"found: {string.Join(", ", invalidReferences)}. Expected 0"); + } +} From 698fc51d20138bbc943086d6152a86a528a15772 Mon Sep 17 00:00:00 2001 From: Dion Date: Fri, 18 Oct 2024 17:22:19 +0200 Subject: [PATCH 2/3] architecture us // code smells && reformat && add docs --- .../docs/developer-guide/api/readme.md | 6 +- .../developer-guide/testing/_category_.json | 8 + .../testing/architecture-test.md | 82 ++++++++ .../docs/developer-guide/testing/index.md | 38 ++++ documentation/scripts/prestart.js | 197 +++++++++--------- .../Architecture/ArchitectureTest.cs | 22 +- 6 files changed, 241 insertions(+), 112 deletions(-) create mode 100644 documentation/docs/developer-guide/testing/_category_.json create mode 100644 documentation/docs/developer-guide/testing/architecture-test.md create mode 100644 documentation/docs/developer-guide/testing/index.md diff --git a/documentation/docs/developer-guide/api/readme.md b/documentation/docs/developer-guide/api/readme.md index 49ab8d9da..6ff5a7e66 100644 --- a/documentation/docs/developer-guide/api/readme.md +++ b/documentation/docs/developer-guide/api/readme.md @@ -9,7 +9,7 @@ This document is auto generated | Path | Type | Description | |---------------------------------------------------|-------|---------------------------------------------------------------------------------| -| __/api/account/status__ | GET | Check the account status of the current logged in user | +| __/api/account/status__ | GET | Check the account status of the current logged-in user | | __/account/login__ | GET | Login form page (HTML) | | __/api/account/login__ | POST | Login the current HttpContext in | | __/api/account/logout__ | POST | Logout the current HttpContext out | @@ -28,7 +28,7 @@ This document is auto generated | __/api/cache/list__ | GET | Get Database Cache (only the cache) | | __/api/remove-cache__ | GET | Delete Database Cache (only the cache) | | __/api/remove-cache__ | POST | Delete Database Cache (only the cache) | -| __/api/delete__ | DELETE| Remove files from the disk, but the file must contain the !delete! (TrashKeyw...| +| __/api/delete__ | DELETE| Remove files from the disk, but the file must contain the !delete!(TrashKeywo...| | _Parameters: f (subPaths, separated by dot comma), collections (true is to update files with the same name before _ | | _ the extenstion) _ | | __/api/desktop-editor/open__ | POST | Open a file in the default editor or a specific editor on the desktop | @@ -101,7 +101,7 @@ This document is auto generated | __/api/suggest/inflate__ | GET | To fill the cache with the data (only if cache is not already filled) | | __/api/synchronize__ | POST | Faster API to Check if directory is changed (not recursive) | | __/api/synchronize__ | GET | Faster API to Check if directory is changed (not recursive) | -| __/api/thumbnail/small/\{f\}__ | GET | Get thumbnail for index pages (300 px or 150px or 1000px (based on whats there))| +| __/api/thumbnail/small/\{f\}__ | GET | Get thumbnail for index pages (300 px or 150px or 1000px (based on what's the...| | __/api/thumbnail/list-sizes/\{f\}__ | GET | Get overview of what exists by name | | __/api/thumbnail/\{f\}__ | GET | Get thumbnail with fallback to original source image.Return source image when...| | _Parameters: f (one single fileHash (NOT path)), filePath (fallback FilePath), isSingleItem (true = load original), _ | diff --git a/documentation/docs/developer-guide/testing/_category_.json b/documentation/docs/developer-guide/testing/_category_.json new file mode 100644 index 000000000..e3317f05f --- /dev/null +++ b/documentation/docs/developer-guide/testing/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Testing", + "position": 12, + "link": { + "type": "generated-index", + "description": "Overview of Testing in the Platform" + } +} diff --git a/documentation/docs/developer-guide/testing/architecture-test.md b/documentation/docs/developer-guide/testing/architecture-test.md new file mode 100644 index 000000000..3537c0bbc --- /dev/null +++ b/documentation/docs/developer-guide/testing/architecture-test.md @@ -0,0 +1,82 @@ +# Architecture Test Documentation + +## Overview + +The `ArchitectureTest.cs` file contains automated tests to ensure that the project's architecture +adheres to specific guidelines. These guidelines are based on the Sitecore Helix principles, which +help maintain a clean and scalable architecture over time. + +## Key Concepts + +### Fitness Functions + +Fitness functions are tests that confirm whether a particular characteristic of the solution's +architecture is maintained over time. These tests help ensure that the architecture remains +consistent and adheres to predefined rules. + +### Helix Principles + +The Helix principles define a set of guidelines for structuring projects within a solution. The key +rules enforced by these tests are: + +- **Foundation projects** should only reference other Foundation projects. +- **Feature projects** should only reference Foundation projects. +- **Project projects** should only reference Foundation or Feature projects. + +## Test Class: `TheSolutionShould` + +### Purpose + +The `TheSolutionShould` class contains tests that validate the project reference structure within +the solution. It ensures that the inter-project references comply with the Helix principles. + +### Key Methods + +#### `ContainNoHelixInvalidProjectReferences` + +This test method verifies that there are no invalid project references within the solution. It +performs the following steps: + +1. **Arrange**: Retrieves a list of projects and their references. +2. **Act**: Reviews the project list for invalid references. +3. **Assert**: Checks if any invalid references are found and asserts that there should be none. + +#### `GetProjectsWithReferences` + +This method retrieves a dictionary of projects and their references from the solution file. + +#### `GetSolutionFilePath` + +This method locates the solution file path based on the base directory. + +#### `GetProjectsFromSolutionFileContents` + +This method extracts project references from the solution file contents using a regular expression. + +#### `GetReferencedProjects` + +This method retrieves the referenced projects from a given project file. + +#### `GetInvalidReferences` + +This method identifies invalid references for a given project based on the specified invalid layers. + +#### `AssertLayerReferences` + +This method asserts that there are no invalid references for a given layer. + +## Known Issues + +- The `starsky.feature.trash` project references the `starsky.feature.metaupdate` project, which is + a known issue and should be refactored in the future. + +## References + +- [Microservices and Evolutionary Architecture](https://www.thoughtworks.com/insights/blog/microservices-evolutionary-architecture) +- [Building Evolutionary Architectures](http://shop.oreilly.com/product/0636920080237.do) +- [Sitecore Helix Documentation](http://helix.sitecore.net/introduction/index.html) + +--- + +This documentation provides an overview of the purpose and functionality of the +`ArchitectureTest.cs` file, along with explanations of key concepts and methods. \ No newline at end of file diff --git a/documentation/docs/developer-guide/testing/index.md b/documentation/docs/developer-guide/testing/index.md new file mode 100644 index 000000000..438c7872c --- /dev/null +++ b/documentation/docs/developer-guide/testing/index.md @@ -0,0 +1,38 @@ +# Unit tests + +In this project, we use unit tests to verify the correctness of individual components +in. + +- Language: The unit tests are written in C#. +- Framework: The tests use a testing framework such as MSTest. +- Structure: The tests are organized to cover various layers and components of the application, +ensuring comprehensive coverage. +- Test Cases: Each test case is designed to validate specific functionality, edge cases, and error +conditions. +- Automation: The tests can be run automatically as part of a continuous integration (CI) pipeline to +ensure that new changes do not introduce regressions. + +## Here are some best practices for writing unit tests: + +1. **Write Independent Tests**: Each test should be independent and not rely on the outcome of other + tests. +2. **Use Descriptive Names**: Test method names should clearly describe what is being tested and the + expected outcome. +3. **Follow the AAA Pattern**: Arrange, Act, Assert. First, set up the test data and environment ( + Arrange), then execute the code being tested (Act), and finally verify the result (Assert). +4. **Test One Thing at a Time**: Each test should focus on a single piece of functionality or + behavior. +5. **Keep Tests Small and Focused**: Small, focused tests are easier to understand, maintain, and + debug. +6. **Use Mocks and Stubs**: We manually mock services, at the moment we don't use a library for this. +7. **Ensure Tests are Repeatable**: Tests should produce the same results every time they are run, + regardless of the environment or order of execution. +8. **Run Tests Frequently**: Integrate tests into the development process and run them frequently to + catch issues early. +9. **Test Edge Cases and Error Conditions**: Ensure that tests cover edge cases and potential error + conditions. +10. **Maintain Test Code Quality**: Apply the same standards to test code as you do to production + code, including readability, maintainability, and adherence to coding standards. + +By following these best practices, you can create effective and reliable unit tests that help +maintain the quality of your codebase. \ No newline at end of file diff --git a/documentation/scripts/prestart.js b/documentation/scripts/prestart.js index cf93779af..a43caf5c3 100644 --- a/documentation/scripts/prestart.js +++ b/documentation/scripts/prestart.js @@ -2,52 +2,52 @@ const fs = require("fs"); const path = require("path"); -const { parseAndWrite } = require("./openapi"); +const {parseAndWrite} = require("./openapi"); const parentDirectory = path.join(__dirname, "..", ".."); const docsDirectory = path.join(__dirname, "..", "docs"); // /git/starsky/documentation/docs function copyFileSync(input, to) { - const inputPath = path.join(parentDirectory, input); + const inputPath = path.join(parentDirectory, input); - const relativeParentInputFolder = path.dirname(to); + const relativeParentInputFolder = path.dirname(to); - if (relativeParentInputFolder != ".") { - const relativeParentInputFolderPath = path.join( - docsDirectory, - relativeParentInputFolder - ); - fs.mkdirSync(relativeParentInputFolderPath, { recursive: true }); - } + if (relativeParentInputFolder !== ".") { + const relativeParentInputFolderPath = path.join( + docsDirectory, + relativeParentInputFolder + ); + fs.mkdirSync(relativeParentInputFolderPath, {recursive: true}); + } - fs.copyFileSync(inputPath, path.join(docsDirectory, to)); + fs.copyFileSync(inputPath, path.join(docsDirectory, to)); } function touchSync(to) { - const filename = path.join(docsDirectory, to); - - const relativeParentInputFolder = path.dirname(to); - - if (relativeParentInputFolder !== ".") { - const relativeParentInputFolderPath = path.join( - docsDirectory, - relativeParentInputFolder - ); - fs.mkdirSync(relativeParentInputFolderPath, { recursive: true }); - } - - const time = new Date(); - try { - fs.utimesSync(filename, time, time); - } catch (e) { - let fd = fs.openSync(filename, "a"); - fs.closeSync(fd); - } + const filename = path.join(docsDirectory, to); + + const relativeParentInputFolder = path.dirname(to); + + if (relativeParentInputFolder !== ".") { + const relativeParentInputFolderPath = path.join( + docsDirectory, + relativeParentInputFolder + ); + fs.mkdirSync(relativeParentInputFolderPath, {recursive: true}); + } + + const time = new Date(); + try { + fs.utimesSync(filename, time, time); + } catch (e) { + let fd = fs.openSync(filename, "a"); + fs.closeSync(fd); + } } function writeFile(to, content) { - const filename = path.join(docsDirectory, to); - fs.writeFileSync(filename, content); + const filename = path.join(docsDirectory, to); + fs.writeFileSync(filename, content); } copyFileSync("history.md", "advanced-options/history.md"); @@ -58,67 +58,67 @@ touchSync("advanced-options/__do_not_edit_history_md"); touchSync("advanced-options/starsky/__do_not_edit_this__folder"); writeFile( - "advanced-options/starsky/_category_.json", - JSON.stringify({ - label: "Applications Guide", - position: 2, - link: { - type: "generated-index", - description: "CLI tools/ Applications", - }, - }) + "advanced-options/starsky/_category_.json", + JSON.stringify({ + label: "Applications Guide", + position: 2, + link: { + type: "generated-index", + description: "CLI tools/ Applications", + }, + }) ); copyFileSync("starsky/readme.md", "advanced-options/starsky/readme.md"); copyFileSync("starsky/starsky/readme.md", "advanced-options/starsky/starsky/readme.md"); copyFileSync( - "starsky/starsky/readme-docker-hub.md", - "advanced-options/starsky/starsky/readme-docker-hub.md" + "starsky/starsky/readme-docker-hub.md", + "advanced-options/starsky/starsky/readme-docker-hub.md" ); copyFileSync( - "starsky/starsky/readme-docker-development.md", - "advanced-options/starsky/starsky/readme-docker-development.md" + "starsky/starsky/readme-docker-development.md", + "advanced-options/starsky/starsky/readme-docker-development.md" ); copyFileSync( - "starsky/starsky/clientapp/readme.md", - "advanced-options/starsky/starsky/clientapp/readme.md" + "starsky/starsky/clientapp/readme.md", + "advanced-options/starsky/starsky/clientapp/readme.md" ); copyFileSync( - "starsky/starskyimportercli/readme.md", - "advanced-options/starsky/starskyimportercli/readme.md" + "starsky/starskyimportercli/readme.md", + "advanced-options/starsky/starskyimportercli/readme.md" ); copyFileSync( - "starsky/starskygeocli/readme.md", - "advanced-options/starsky/starskygeocli/readme.md" + "starsky/starskygeocli/readme.md", + "advanced-options/starsky/starskygeocli/readme.md" ); copyFileSync( - "starsky/starskywebhtmlcli/readme.md", - "advanced-options/starsky/starskywebhtmlcli/readme.md" + "starsky/starskywebhtmlcli/readme.md", + "advanced-options/starsky/starskywebhtmlcli/readme.md" ); copyFileSync( - "starsky/starskywebftpcli/readme.md", - "advanced-options/starsky/starskywebftpcli/readme.md" + "starsky/starskywebftpcli/readme.md", + "advanced-options/starsky/starskywebftpcli/readme.md" ); copyFileSync( - "starsky/starskyadmincli/readme.md", - "advanced-options/starsky/starskyadmincli/readme.md" + "starsky/starskyadmincli/readme.md", + "advanced-options/starsky/starskyadmincli/readme.md" ); copyFileSync( - "starsky/starskysynchronizecli/readme.md", - "advanced-options/starsky/starskysynchronizecli/readme.md" + "starsky/starskysynchronizecli/readme.md", + "advanced-options/starsky/starskysynchronizecli/readme.md" ); copyFileSync( - "starsky/starskythumbnailcli/readme.md", - "advanced-options/starsky/starskythumbnailcli/readme.md" + "starsky/starskythumbnailcli/readme.md", + "advanced-options/starsky/starskythumbnailcli/readme.md" ); copyFileSync( - "starsky/starskybusinesslogic/readme.md", - "advanced-options/starsky/starskybusinesslogic/readme.md" + "starsky/starskybusinesslogic/readme.md", + "advanced-options/starsky/starskybusinesslogic/readme.md" ); copyFileSync("starskydesktop/readme.md", "advanced-options/starskydesktop/readme.md"); @@ -126,13 +126,13 @@ copyFileSync("starskydesktop/readme.md", "advanced-options/starskydesktop/readme touchSync("advanced-options/starskydesktop/__do_not_edit_this__folder"); copyFileSync( - "starskydesktop/docs-assets/starskyapp-mac-gatekeeper.jpg", - "advanced-options/starskydesktop/docs-assets/starskyapp-mac-gatekeeper.jpg" + "starskydesktop/docs-assets/starskyapp-mac-gatekeeper.jpg", + "advanced-options/starskydesktop/docs-assets/starskyapp-mac-gatekeeper.jpg" ); copyFileSync( - "starskydesktop/docs-assets/starskyapp-remote-options-v040.jpg", - "advanced-options/starskydesktop/docs-assets/starskyapp-remote-options-v040.jpg" + "starskydesktop/docs-assets/starskyapp-remote-options-v040.jpg", + "advanced-options/starskydesktop/docs-assets/starskyapp-remote-options-v040.jpg" ); copyFileSync("starsky/starskytest/readme.md", "advanced-options/starsky/starskytest/readme.md"); @@ -142,23 +142,23 @@ copyFileSync("starsky-tools/readme.md", "advanced-options/starsky-tools/readme.m touchSync("advanced-options/starsky-tools/__do_not_edit_this__folder"); copyFileSync( - "starsky-tools/build-tools/readme.md", - "advanced-options/starsky-tools/build-tools/readme.md" + "starsky-tools/build-tools/readme.md", + "advanced-options/starsky-tools/build-tools/readme.md" ); copyFileSync( - "starsky-tools/dropbox-import/readme.md", - "advanced-options/starsky-tools/dropbox-import/readme.md" + "starsky-tools/dropbox-import/readme.md", + "advanced-options/starsky-tools/dropbox-import/readme.md" ); copyFileSync( - "starsky-tools/end2end/readme.md", - "advanced-options/starsky-tools/end2end/readme.md" + "starsky-tools/end2end/readme.md", + "advanced-options/starsky-tools/end2end/readme.md" ); copyFileSync( - "starsky-tools/localtunnel/readme.md", - "advanced-options/starsky-tools/localtunnel/readme.md" + "starsky-tools/localtunnel/readme.md", + "advanced-options/starsky-tools/localtunnel/readme.md" ); copyFileSync("starsky-tools/mail/readme.md", "advanced-options/starsky-tools/mail/readme.md"); @@ -167,18 +167,18 @@ copyFileSync("starsky-tools/mock/readme.md", "advanced-options/starsky-tools/moc copyFileSync( - "starsky-tools/thumbnail/readme.md", - "advanced-options/starsky-tools/thumbnail/readme.md" + "starsky-tools/thumbnail/readme.md", + "advanced-options/starsky-tools/thumbnail/readme.md" ); copyFileSync( - "starsky-tools/release-tools/readme.md", - "advanced-options/starsky-tools/release-tools/readme.md" + "starsky-tools/release-tools/readme.md", + "advanced-options/starsky-tools/release-tools/readme.md" ); copyFileSync( - "starsky-tools/slack-notification/readme.md", - "advanced-options/starsky-tools/slack-notification/readme.md" + "starsky-tools/slack-notification/readme.md", + "advanced-options/starsky-tools/slack-notification/readme.md" ); copyFileSync("starsky-tools/sync/readme.md", "advanced-options/starsky-tools/sync/readme.md"); @@ -187,37 +187,36 @@ copyFileSync("starsky-tools/sync/readme.md", "advanced-options/starsky-tools/syn touchSync("developer-guide/contributing/__do_not_edit_this__folder"); writeFile( - "developer-guide/contributing/_category_.json", - JSON.stringify({ - label: "Contributing", - position: 8, - link: { - type: "generated-index", - description: "contributing instructions" - }, - }) + "developer-guide/contributing/_category_.json", + JSON.stringify({ + label: "Contributing", + position: 8, + link: { + type: "generated-index", + description: "contributing instructions" + }, + }) ); copyFileSync( - "CONTRIBUTING.md", - "developer-guide/contributing/CONTRIBUTING.md" + "CONTRIBUTING.md", + "developer-guide/contributing/CONTRIBUTING.md" ); copyFileSync( - "SECURITY.md", - "developer-guide/contributing/SECURITY.md" + "SECURITY.md", + "developer-guide/contributing/SECURITY.md" ); copyFileSync( - "PULL_REQUEST_TEMPLATE.md", - "developer-guide/contributing/PULL_REQUEST_TEMPLATE.md" + "PULL_REQUEST_TEMPLATE.md", + "developer-guide/contributing/PULL_REQUEST_TEMPLATE.md" ); copyFileSync( - "HACKING.md", - "developer-guide/contributing/HACKING.md" + "HACKING.md", + "developer-guide/contributing/HACKING.md" ); - parseAndWrite(); diff --git a/starsky/starskytest/Architecture/ArchitectureTest.cs b/starsky/starskytest/Architecture/ArchitectureTest.cs index dc89d8673..b81cf5074 100644 --- a/starsky/starskytest/Architecture/ArchitectureTest.cs +++ b/starsky/starskytest/Architecture/ArchitectureTest.cs @@ -25,7 +25,7 @@ namespace starskytest.Architecture; /// http://helix.sitecore.net/introduction/index.html /// [TestClass] -public class TheSolutionShould +public partial class TheSolutionShould { private const string SolutionName = "starsky.sln"; @@ -102,7 +102,7 @@ public void ContainNoHelixInvalidProjectReferences() AssertLayerReferences(invalidProjectReferences, Project); } - private static Dictionary> GetProjectsWithReferences() + private static Dictionary> GetProjectsWithReferences() { var solutionFilePath = GetSolutionFilePath(); var solutionFileContents = File.ReadAllText(solutionFilePath); @@ -127,17 +127,13 @@ private static string GetSolutionFilePath() private static IList GetPathParts(string directory) { - return directory - .Split(new[] { "\\" }, StringSplitOptions.None) - .ToList(); + return [.. directory.Split(["\\"], StringSplitOptions.None)]; } private static IEnumerable GetProjectsFromSolutionFileContents( string solutionFileContents) { - var regex = new Regex( - "Project\\(\"\\{[\\w-]*\\}\"\\) = \"([\\w _]*.*)\", \"(.*\\.(cs|vcx|vb)proj)\"", - RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + var regex = ProjectFileNameRegex(); return regex.Matches(solutionFileContents); } @@ -148,7 +144,7 @@ private static string GetProjectFileName(string value) return result; } - private static IList GetReferencedProjects(string solutionFilePath, + private static List GetReferencedProjects(string solutionFilePath, string projectFilePath) { var rootedProjectFilePath = @@ -180,7 +176,7 @@ private static List GetReferencedProjects(string rootedProjectFilePath) return result; } - private static void GetInvalidReferences(KeyValuePair> project, + private static void GetInvalidReferences(KeyValuePair> project, string[] invalidLayers, List invalidReferences) { invalidReferences.AddRange(project.Value @@ -237,6 +233,9 @@ private static void AssertLayerReferences(IReadOnlyCollection invalidRef { // This is a known issue, as the Trash feature references the MetaUpdate feature // This should be refactored in the future + + // when update also update docs!!! + return; } @@ -245,4 +244,7 @@ private static void AssertLayerReferences(IReadOnlyCollection invalidRef $"{invalidReferences.Count} invalid reference{( invalidReferences.Count == 1 ? " was" : "s were" )} " + $"found: {string.Join(", ", invalidReferences)}. Expected 0"); } + + [GeneratedRegex("Project\\(\"\\{[\\w-]*\\}\"\\) = \"([\\w _]*.*)\", \"(.*\\.(cs|vcx|vb)proj)\"", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] + private static partial Regex ProjectFileNameRegex(); } From 1c7f640c25f818f8bad1bc986b2512f8101e0a06 Mon Sep 17 00:00:00 2001 From: Dion Date: Fri, 18 Oct 2024 17:31:51 +0200 Subject: [PATCH 3/3] architecture code smell --- starsky/starskytest/Architecture/ArchitectureTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starsky/starskytest/Architecture/ArchitectureTest.cs b/starsky/starskytest/Architecture/ArchitectureTest.cs index b81cf5074..18a7f92c0 100644 --- a/starsky/starskytest/Architecture/ArchitectureTest.cs +++ b/starsky/starskytest/Architecture/ArchitectureTest.cs @@ -130,7 +130,7 @@ private static IList GetPathParts(string directory) return [.. directory.Split(["\\"], StringSplitOptions.None)]; } - private static IEnumerable GetProjectsFromSolutionFileContents( + private static MatchCollection GetProjectsFromSolutionFileContents( string solutionFileContents) { var regex = ProjectFileNameRegex();