From dcc0070dbbd2233a401dd076c2b0dcbf499acd18 Mon Sep 17 00:00:00 2001 From: Kevin Schneider Date: Tue, 18 Jun 2024 12:31:30 +0200 Subject: [PATCH] fix #55: - create AVPRIndex.Hash module - add tests for string/byte array/file hashing - use this function exclusively in AVPRIndex and API - remove the API endpoint for direct hash creation - return http 422 if package content contains any CR characters - handle PackageContentHash creation for new packages automatically --- src/AVPRIndex/AVPRIndex.fsproj | 15 +- src/AVPRIndex/Domain.fs | 12 +- src/AVPRIndex/MD5Hash.fs | 32 +++++ src/AVPRIndex/RELEASE_NOTES.md | 4 + .../API/Endpoints/VerificationEndpointsV1.cs | 9 +- .../API/Handlers/PackageHandlers.cs | 8 +- .../API/Handlers/VerificationHandlers.cs | 34 ++--- .../Data/DataInitializer.cs | 8 +- .../Models/ValidationPackage.cs | 13 +- .../Models/ValidationPackageDb.cs | 21 +++ .../OpenAPI/OperationMetadataProcessor.cs | 14 +- tests/IndexTests/HashTests.fs | 128 ++++++++++++++++++ tests/IndexTests/IndexTests.fsproj | 5 + tests/IndexTests/ReferenceObjects.fs | 47 ++++++- tests/IndexTests/Utils.fs | 3 +- .../Frontmatter/Binding/valid@1.0.0.fsx | 45 +----- 16 files changed, 290 insertions(+), 108 deletions(-) create mode 100644 src/AVPRIndex/MD5Hash.fs create mode 100644 tests/IndexTests/HashTests.fs diff --git a/src/AVPRIndex/AVPRIndex.fsproj b/src/AVPRIndex/AVPRIndex.fsproj index 64ea858..309f850 100644 --- a/src/AVPRIndex/AVPRIndex.fsproj +++ b/src/AVPRIndex/AVPRIndex.fsproj @@ -7,8 +7,8 @@ Kevin Schneider - Type system for the indexing backend of avpr.nfdi4plants.org - Type system for the indexing backend of avpr.nfdi4plants.org + Type system and utils for the indexing backend of avpr.nfdi4plants.org + Type system and utils for the indexing backend of avpr.nfdi4plants.org MIT C# F# ARC annotated-research-context rdm research-data-management validation https://github.com/nfdi4plants/arc-validate-package-registry @@ -16,22 +16,23 @@ git $([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/RELEASE_NOTES.md")) README.md - 0.1.1 + 0.1.2 - + - + + - - + + diff --git a/src/AVPRIndex/Domain.fs b/src/AVPRIndex/Domain.fs index b59f3cf..8d224a0 100644 --- a/src/AVPRIndex/Domain.fs +++ b/src/AVPRIndex/Domain.fs @@ -212,21 +212,11 @@ module Domain = lastUpdated: System.DateTimeOffset, metadata: ValidationPackageMetadata ) = - - let md5 = MD5.Create() - ValidationPackageIndex.create( repoPath = repoPath, fileName = Path.GetFileName(repoPath), lastUpdated = lastUpdated, - contentHash = ( - repoPath - |> File.ReadAllText - |> fun s -> s.ReplaceLineEndings("\n") - |> Encoding.UTF8.GetBytes - |> md5.ComputeHash - |> Convert.ToHexString - ), + contentHash = Hash.hashFile repoPath, metadata = metadata ) diff --git a/src/AVPRIndex/MD5Hash.fs b/src/AVPRIndex/MD5Hash.fs new file mode 100644 index 0000000..f12bc23 --- /dev/null +++ b/src/AVPRIndex/MD5Hash.fs @@ -0,0 +1,32 @@ +namespace AVPRIndex + +open System +open System.IO +open System.Text +open System.Security.Cryptography + +type Hash = + + // This is the function used as the first point of entry, as it is used when parsing packages that do not exist in the prioduction DB + // unifying line endings is crucial to ensure that the hash is the same on all platforms + + /// calculates a md5 hash of the given byte array and returns it as a hex string + static member hashContent (content: byte array) = + let md5 = MD5.Create() + content + |> md5.ComputeHash + |> Convert.ToHexString + + /// calculates a md5 hash of the given string with line endings unified to `\n` and returns it as a hex string + static member hashString (content: string) = + content + |> fun s -> s.ReplaceLineEndings("\n") + |> Encoding.UTF8.GetBytes + |> Hash.hashContent + + /// calculates a md5 hash of the file at the given path with line endings unified to `\n` and returns it as a hex string + static member hashFile (path: string) = + path + |> File.ReadAllText + |> Hash.hashString + diff --git a/src/AVPRIndex/RELEASE_NOTES.md b/src/AVPRIndex/RELEASE_NOTES.md index a0af89a..5f0f679 100644 --- a/src/AVPRIndex/RELEASE_NOTES.md +++ b/src/AVPRIndex/RELEASE_NOTES.md @@ -1,3 +1,7 @@ +## v0.1.2 + +Add `PackageContentHash` module to unify package hash calculation across downstream libraries + ## v0.1.1 Add `CQCHookEndpoint` field to `ValidationPackageMetadata` diff --git a/src/PackageRegistryService/API/Endpoints/VerificationEndpointsV1.cs b/src/PackageRegistryService/API/Endpoints/VerificationEndpointsV1.cs index d93895a..395785b 100644 --- a/src/PackageRegistryService/API/Endpoints/VerificationEndpointsV1.cs +++ b/src/PackageRegistryService/API/Endpoints/VerificationEndpointsV1.cs @@ -11,10 +11,11 @@ public static RouteGroupBuilder MapVerificationApiV1(this RouteGroupBuilder grou .WithOpenApi() .WithName("VerifyPackageContent"); - group.MapPost("/hashes", VerificationHandlers.CreateContentHash) - .WithOpenApi() - .WithName("CreatePackageContentHash") - .AddEndpointFilter(); // creating hashes via post requests requires an API key! + // remove this at it is safer to create hash entries automatically on posted packages + //group.MapPost("/hashes", VerificationHandlers.CreateContentHash) + // .WithOpenApi() + // .WithName("CreatePackageContentHash") + // .AddEndpointFilter(); // creating hashes via post requests requires an API key! return group.WithTags("Content Verification"); } diff --git a/src/PackageRegistryService/API/Handlers/PackageHandlers.cs b/src/PackageRegistryService/API/Handlers/PackageHandlers.cs index 6414e07..c425068 100644 --- a/src/PackageRegistryService/API/Handlers/PackageHandlers.cs +++ b/src/PackageRegistryService/API/Handlers/PackageHandlers.cs @@ -86,7 +86,7 @@ public static async Task, NotFound, Conflict< return TypedResults.Ok(package); } - public static async Task, Conflict, UnauthorizedHttpResult>> CreatePackage(ValidationPackage package, ValidationPackageDb database) + public static async Task, Conflict, UnauthorizedHttpResult, UnprocessableEntity>> CreatePackage(ValidationPackage package, ValidationPackageDb database) { var existing = await database.ValidationPackages.FindAsync(package.Name, package.MajorVersion, package.MinorVersion, package.PatchVersion); if (existing != null) @@ -94,6 +94,12 @@ public static async Task, Conflict, UnauthorizedHt return TypedResults.Conflict(); } + if (package.ContentContainsCarriageReturn()) + { + return TypedResults.UnprocessableEntity("package content contained non-LF line endings"); + } + + ValidationPackageDb.CreatePackageContentHash(package, database); database.ValidationPackages.Add(package); await database.SaveChangesAsync(); diff --git a/src/PackageRegistryService/API/Handlers/VerificationHandlers.cs b/src/PackageRegistryService/API/Handlers/VerificationHandlers.cs index b5e193a..b9ccd52 100644 --- a/src/PackageRegistryService/API/Handlers/VerificationHandlers.cs +++ b/src/PackageRegistryService/API/Handlers/VerificationHandlers.cs @@ -38,27 +38,27 @@ public static async Task> Verify(Pack return TypedResults.Ok(); } - public static async Task, Conflict, UnauthorizedHttpResult>> CreateContentHash(PackageContentHash hashedPackage, ValidationPackageDb database) - { + //public static async Task, Conflict, UnauthorizedHttpResult>> CreateContentHash(PackageContentHash hashedPackage, ValidationPackageDb database) + //{ - var existing = await - database.Hashes.FindAsync( - hashedPackage.PackageName, - hashedPackage.PackageMajorVersion, - hashedPackage.PackageMinorVersion, - hashedPackage.PackagePatchVersion - ); + // var existing = await + // database.Hashes.FindAsync( + // hashedPackage.PackageName, + // hashedPackage.PackageMajorVersion, + // hashedPackage.PackageMinorVersion, + // hashedPackage.PackagePatchVersion + // ); - if (existing != null) - { - return TypedResults.Conflict(); - } + // if (existing != null) + // { + // return TypedResults.Conflict(); + // } - database.Hashes.Add(hashedPackage); - await database.SaveChangesAsync(); + // database.Hashes.Add(hashedPackage); + // await database.SaveChangesAsync(); - return TypedResults.Ok(hashedPackage); + // return TypedResults.Ok(hashedPackage); - } + //} } } diff --git a/src/PackageRegistryService/Data/DataInitializer.cs b/src/PackageRegistryService/Data/DataInitializer.cs index 4cf4369..1a6f7da 100644 --- a/src/PackageRegistryService/Data/DataInitializer.cs +++ b/src/PackageRegistryService/Data/DataInitializer.cs @@ -18,8 +18,6 @@ public class DataInitializer { public static void SeedData(ValidationPackageDb context) { - MD5 md5 = MD5.Create(); - if (!context.ValidationPackages.Any()) { var index = AVPRRepo.getPreviewIndex(); @@ -67,11 +65,9 @@ public static void SeedData(ValidationPackageDb context) Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"StagingArea/{i.Metadata.Name}/{i.FileName}" ); - var content = - File.ReadAllText(path) - .ReplaceLineEndings("\n"); - var hash = Convert.ToHexString(md5.ComputeHash(Encoding.UTF8.GetBytes(content))); + var hash = AVPRIndex.Hash.hashFile(path); + if (hash != i.ContentHash) { throw new Exception($"Hash collision for indexed hash vs content hash: {$"StagingArea/{i.Metadata.Name}/{i.FileName}"}"); diff --git a/src/PackageRegistryService/Models/ValidationPackage.cs b/src/PackageRegistryService/Models/ValidationPackage.cs index 0f478f0..1fbf8c2 100644 --- a/src/PackageRegistryService/Models/ValidationPackage.cs +++ b/src/PackageRegistryService/Models/ValidationPackage.cs @@ -93,10 +93,15 @@ public class ValidationPackage /// A string containing the package content public string GetPackageContentHash() { - using (var md5 = MD5.Create()) - { - return Convert.ToHexString(md5.ComputeHash(PackageContent)); - } + return AVPRIndex.Hash.hashContent(PackageContent); + } + /// + /// Returns whether the package content CR characters - meaning its is has not been unified to only use LF. + /// + /// true or false + public bool ContentContainsCarriageReturn() + { + return GetPackageScriptContent().Contains("\r"); } } } diff --git a/src/PackageRegistryService/Models/ValidationPackageDb.cs b/src/PackageRegistryService/Models/ValidationPackageDb.cs index 46666f5..82b032a 100644 --- a/src/PackageRegistryService/Models/ValidationPackageDb.cs +++ b/src/PackageRegistryService/Models/ValidationPackageDb.cs @@ -39,7 +39,28 @@ public static bool ValidatePackageContent(ValidationPackage package, ValidationP var packageHash = package.GetPackageContentHash(); return hash.Hash == packageHash; } + public static bool CreatePackageContentHash(ValidationPackage package, ValidationPackageDb database) + { + var result = database.Hashes.SingleOrDefault(d => d.PackageName == package.Name && d.PackageMajorVersion == package.MajorVersion && d.PackageMinorVersion == package.MinorVersion && d.PackagePatchVersion == package.PatchVersion); + if (result != null) + { + return false; // there is an existing hash! + } + else + { + var h = new PackageContentHash + { + PackageName = package.Name, + PackageMajorVersion = package.MajorVersion, + PackageMinorVersion = package.MinorVersion, + PackagePatchVersion = package.PatchVersion, + Hash = package.GetPackageContentHash() + }; + database.Hashes.Add(h); + return true; + } + } public static void IncrementDownloadCount(ValidationPackage package, ValidationPackageDb database) { var result = database.Downloads.SingleOrDefault(d => d.PackageName == package.Name && d.PackageMajorVersion == package.MajorVersion && d.PackageMinorVersion == package.MinorVersion && d.PackagePatchVersion == package.PatchVersion); diff --git a/src/PackageRegistryService/OpenAPI/OperationMetadataProcessor.cs b/src/PackageRegistryService/OpenAPI/OperationMetadataProcessor.cs index 10b67dc..846f6e9 100644 --- a/src/PackageRegistryService/OpenAPI/OperationMetadataProcessor.cs +++ b/src/PackageRegistryService/OpenAPI/OperationMetadataProcessor.cs @@ -43,13 +43,13 @@ public class OperationMetadataProcessor : IOperationProcessor { "Description", "Verify a content hash for a given package. Hashes are MD5 hex fingerprints." } } }, - { - "CreatePackageContentHash", new Dictionary - { - { "Summary", "Create a content hash for a given package." }, - { "Description", "Create a content hash for a given package. Hashes are MD5 hex fingerprints. This Endpoint requires API Key authentication." } - } - }, + //{ + // "CreatePackageContentHash", new Dictionary + // { + // { "Summary", "Create a content hash for a given package." }, + // { "Description", "Create a content hash for a given package. Hashes are MD5 hex fingerprints. This Endpoint requires API Key authentication." } + // } + //}, { "GetAllDownloads", new Dictionary { diff --git a/tests/IndexTests/HashTests.fs b/tests/IndexTests/HashTests.fs new file mode 100644 index 0000000..b9de4a4 --- /dev/null +++ b/tests/IndexTests/HashTests.fs @@ -0,0 +1,128 @@ +namespace PackageContentHashTests + +open System +open System.IO +open System.Text +open Xunit +open AVPRIndex +open AVPRIndex.Domain +open ReferenceObjects + + + module hashString = + + [] + let ``String with no line endings`` () = + let actual = Hash.hashString Hash.Input.noLineEndings + Assert.Equal(Hash.Hashes.noLineEndings, actual) + + [] + let ``String with CLRF`` () = + let actual = Hash.hashString Hash.Input.windowsLineEndings + Assert.Equal(Hash.Hashes.windowsLineEndings, actual) + + [] + let ``String with LF`` () = + let actual = Hash.hashString Hash.Input.unixLineEndings + Assert.Equal(Hash.Hashes.unixLineEndings, actual) + + [] + let ``String with mixed line endings`` () = + let actual = Hash.hashString Hash.Input.mixedLineEndings + Assert.Equal(Hash.Hashes.mixedLineEndings, actual) + + module hashContent = + + [] + let ``mandatory binding frontmatter file`` () = + let actual = + "fixtures/Frontmatter/Binding/valid@1.0.0.fsx" + |> File.ReadAllText + |> fun s -> s.ReplaceLineEndings("\n") + |> Encoding.UTF8.GetBytes + |> Hash.hashContent + Assert.Equal(Hash.Hashes.BindingFrontmatter.validMandatoryFrontmatter, actual) + + [] + let ``full binding frontmatter file`` () = + let actual = + "fixtures/Frontmatter/Binding/valid@2.0.0.fsx" + |> File.ReadAllText + |> fun s -> s.ReplaceLineEndings("\n") + |> Encoding.UTF8.GetBytes + |> Hash.hashContent + + Assert.Equal(Hash.Hashes.BindingFrontmatter.validFullFrontmatter, actual) + + [] + let ``invalid binding frontmatter file`` () = + let actual = + "fixtures/Frontmatter/Binding/invalid@0.0.fsx" + |> File.ReadAllText + |> fun s -> s.ReplaceLineEndings("\n") + |> Encoding.UTF8.GetBytes + |> Hash.hashContent + Assert.Equal(Hash.Hashes.BindingFrontmatter.invalidMissingMandatoryFrontmatter, actual) + + [] + let ``mandatory comment frontmatter file`` () = + let actual = + "fixtures/Frontmatter/Comment/valid@1.0.0.fsx" + |> File.ReadAllText + |> fun s -> s.ReplaceLineEndings("\n") + |> Encoding.UTF8.GetBytes + |> Hash.hashContent + + Assert.Equal(Hash.Hashes.CommentFrontmatter.validMandatoryFrontmatter, actual) + + [] + let ``full comment frontmatter file`` () = + let actual = + "fixtures/Frontmatter/Comment/valid@2.0.0.fsx" + |> File.ReadAllText + |> fun s -> s.ReplaceLineEndings("\n") + |> Encoding.UTF8.GetBytes + |> Hash.hashContent + Assert.Equal(Hash.Hashes.CommentFrontmatter.validFullFrontmatter, actual) + + [] + let ``invalid comment frontmatter file`` () = + let actual = + "fixtures/Frontmatter/Comment/invalid@0.0.fsx" + |> File.ReadAllText + |> fun s -> s.ReplaceLineEndings("\n") + |> Encoding.UTF8.GetBytes + |> Hash.hashContent + Assert.Equal(Hash.Hashes.CommentFrontmatter.invalidMissingMandatoryFrontmatter, actual) + + module hashFile = + + [] + let ``mandatory binding frontmatter file`` () = + let actual = "fixtures/Frontmatter/Binding/valid@1.0.0.fsx" |> Hash.hashFile + Assert.Equal(Hash.Hashes.BindingFrontmatter.validMandatoryFrontmatter, actual) + + [] + let ``full binding frontmatter file`` () = + let actual = "fixtures/Frontmatter/Binding/valid@2.0.0.fsx" |> Hash.hashFile + Assert.Equal(Hash.Hashes.BindingFrontmatter.validFullFrontmatter, actual) + + [] + let ``invalid binding frontmatter file`` () = + let actual = "fixtures/Frontmatter/Binding/invalid@0.0.fsx" |> Hash.hashFile + Assert.Equal(Hash.Hashes.BindingFrontmatter.invalidMissingMandatoryFrontmatter, actual) + + [] + let ``mandatory comment frontmatter file`` () = + let actual = "fixtures/Frontmatter/Comment/valid@1.0.0.fsx" |> Hash.hashFile + Assert.Equal(Hash.Hashes.CommentFrontmatter.validMandatoryFrontmatter, actual) + + [] + let ``full comment frontmatter file`` () = + let actual = "fixtures/Frontmatter/Comment/valid@2.0.0.fsx" |> Hash.hashFile + Assert.Equal(Hash.Hashes.CommentFrontmatter.validFullFrontmatter, actual) + + [] + let ``invalid comment frontmatter file`` () = + let actual = "fixtures/Frontmatter/Comment/invalid@0.0.fsx" |> Hash.hashFile + Assert.Equal(Hash.Hashes.CommentFrontmatter.invalidMissingMandatoryFrontmatter, actual) \ No newline at end of file diff --git a/tests/IndexTests/IndexTests.fsproj b/tests/IndexTests/IndexTests.fsproj index 88077ae..3608c26 100644 --- a/tests/IndexTests/IndexTests.fsproj +++ b/tests/IndexTests/IndexTests.fsproj @@ -13,6 +13,7 @@ + @@ -38,4 +39,8 @@ + + + + diff --git a/tests/IndexTests/ReferenceObjects.fs b/tests/IndexTests/ReferenceObjects.fs index 5ceca44..6835977 100644 --- a/tests/IndexTests/ReferenceObjects.fs +++ b/tests/IndexTests/ReferenceObjects.fs @@ -6,6 +6,41 @@ open AVPRIndex.Domain let testDate = System.DateTimeOffset.Parse("01/01/2024") +module Hash = + + module Input = + + let noLineEndings = "This is a test string with no line endings." + + let windowsLineEndings = "This is a test string with Windows line endings.\r\nanother one" + + let unixLineEndings = "This is a test string with Unix line endings.\nanother one" + + let mixedLineEndings = "This is a test string with mixed line endings.\r\nanother one\nand another one" + + module Hashes = + + // note that these hashes represent the input with unified line endings! + + let noLineEndings = "810F403210CD3D056F2DF13676D9385A" + let windowsLineEndings = "259AA1C2F8EE8F0A12A6077E60176973" + let unixLineEndings = "A85A58F412C6358B3FF1638876579FC6" + let mixedLineEndings = "EC425DD2233B497BCD9B9FDDFD35FA84" + + module CommentFrontmatter = + + let validMandatoryFrontmatter = "2A29D85A29D908C7DE214D56119DE207" + let validFullFrontmatter = "E3742447779570EC372DD5DA9C56846F" + let invalidMissingMandatoryFrontmatter = "4331EE804414463D7E6DE9B8B6A3D49C" + + module BindingFrontmatter = + + let validMandatoryFrontmatter = "FC9558E6681A4114794BA912925FC283" + let validFullFrontmatter = "7996D96B4690896224B8D1D1FB621FC7" + let invalidMissingMandatoryFrontmatter = "94C704CFD2538A819CC2C0FFA406A355" + + + module Author = let mandatoryFields = Author(FullName = "test") @@ -420,7 +455,7 @@ module ValidationPackageIndex = repoPath = "fixtures/Frontmatter/Comment/valid@1.0.0.fsx", fileName = "valid@1.0.0.fsx", lastUpdated = testDate, - contentHash = (Frontmatter.Comment.validMandatoryFrontmatter |> md5hash), + contentHash = (Frontmatter.Comment.validMandatoryFrontmatter |> md5hashNoReplace), metadata = Metadata.validMandatoryFrontmatter ) @@ -429,7 +464,7 @@ module ValidationPackageIndex = repoPath = "fixtures/Frontmatter/Comment/valid@2.0.0.fsx", fileName = "valid@2.0.0.fsx", lastUpdated = testDate, - contentHash = (Frontmatter.Comment.validFullFrontmatter |> md5hash), + contentHash = (Frontmatter.Comment.validFullFrontmatter |> md5hashNoReplace), metadata = Metadata.validFullFrontmatter ) @@ -438,7 +473,7 @@ module ValidationPackageIndex = repoPath = "fixtures/Frontmatter/Comment/invalid@0.0.fsx", fileName = "invalid@0.0.fsx", lastUpdated = testDate, - contentHash = (Frontmatter.Comment.invalidMissingMandatoryFrontmatter |> md5hash), + contentHash = (Frontmatter.Comment.invalidMissingMandatoryFrontmatter |> md5hashNoReplace), metadata = Metadata.invalidMissingMandatoryFrontmatter ) @@ -449,7 +484,7 @@ module ValidationPackageIndex = repoPath = "fixtures/Frontmatter/Binding/valid@1.0.0.fsx", fileName = "valid@1.0.0.fsx", lastUpdated = testDate, - contentHash = (Frontmatter.Binding.validMandatoryFrontmatter |> md5hash), + contentHash = (Frontmatter.Binding.validMandatoryFrontmatter |> md5hashNoReplace), metadata = Metadata.validMandatoryFrontmatter ) @@ -458,7 +493,7 @@ module ValidationPackageIndex = repoPath = "fixtures/Frontmatter/Binding/valid@2.0.0.fsx", fileName = "valid@2.0.0.fsx", lastUpdated = testDate, - contentHash = (Frontmatter.Binding.validFullFrontmatter |> md5hash), + contentHash = (Frontmatter.Binding.validFullFrontmatter |> md5hashNoReplace), metadata = Metadata.validFullFrontmatter ) @@ -467,6 +502,6 @@ module ValidationPackageIndex = repoPath = "fixtures/Frontmatter/Binding/invalid@0.0.fsx", fileName = "invalid@0.0.fsx", lastUpdated = testDate, - contentHash = (Frontmatter.Binding.invalidMissingMandatoryFrontmatter |> md5hash), + contentHash = (Frontmatter.Binding.invalidMissingMandatoryFrontmatter |> md5hashNoReplace), metadata = Metadata.invalidMissingMandatoryFrontmatter ) \ No newline at end of file diff --git a/tests/IndexTests/Utils.fs b/tests/IndexTests/Utils.fs index 51ec3be..51052ac 100644 --- a/tests/IndexTests/Utils.fs +++ b/tests/IndexTests/Utils.fs @@ -8,7 +8,7 @@ open System.Text open System.IO open System.Security.Cryptography -let md5hash (content: string) = +let md5hashNoReplace (content: string) = let md5 = MD5.Create() content |> Encoding.UTF8.GetBytes @@ -42,4 +42,5 @@ type Assert with Assert.Equivalent(expected.Authors, actual.Authors, strict = true) Assert.Equivalent(expected.Tags, actual.Tags, strict = true) Assert.Equal(expected.ReleaseNotes, actual.ReleaseNotes) + Assert.Equal(expected.CQCHookEndpoint, actual.CQCHookEndpoint) Assert.Equivalent(expected, actual, strict = true) \ No newline at end of file diff --git a/tests/IndexTests/fixtures/Frontmatter/Binding/valid@1.0.0.fsx b/tests/IndexTests/fixtures/Frontmatter/Binding/valid@1.0.0.fsx index 51c5ab8..4e07c98 100644 --- a/tests/IndexTests/fixtures/Frontmatter/Binding/valid@1.0.0.fsx +++ b/tests/IndexTests/fixtures/Frontmatter/Binding/valid@1.0.0.fsx @@ -10,47 +10,4 @@ Description: | It does it very good, it does it very well. It does it very fast, it does it very swell. --- -*)""" - -open System.IO -open System -open System.Text - -let f = - File.ReadAllText(@"C:\Users\schne\source\repos\nfdi4plants\arc-validate-package-registry\tests\IndexTests\fixtures\Frontmatter\Binding\valid@2.0.0.fsx") - .ReplaceLineEndings("\n") - -/// the frontmatter start string if the package uses yaml frontmatter as comment -let [] frontMatterCommentStart = "(*\n---" -/// the frontmatter end string if the package uses yaml frontmatter as comment -let [] frontMatterCommentEnd = "---\n*)" - -/// the frontmatter start string if the package uses yaml frontmatter as a string binding to be re-used in the package code -let [] frontmatterBindingStart = "let []PACKAGE_METADATA = \"\"\"(*\n---" -/// the frontmatter end string if the package uses yaml frontmatter as a string binding to be re-used in the package code -let [] frontmatterBindingEnd = "---\n*)\"\"\"" - - -let containsCommentFrontmatter (str: string) = - str.StartsWith(frontMatterCommentStart, StringComparison.Ordinal) && str.Contains(frontMatterCommentEnd) - -let containsBindingFrontmatter (str: string) = - str.StartsWith(frontmatterBindingStart, StringComparison.Ordinal) && str.Contains(frontmatterBindingEnd) - -let tryExtractFromString (str: string) = - let norm = str.ReplaceLineEndings("\n") - if containsCommentFrontmatter norm then - norm.Substring( - frontMatterCommentStart.Length, - (norm.IndexOf(frontMatterCommentEnd, StringComparison.Ordinal) - frontMatterCommentStart.Length)) - |> Some - elif containsBindingFrontmatter norm then - norm.Substring( - frontmatterBindingStart.Length, - (norm.IndexOf(frontmatterBindingEnd, StringComparison.Ordinal) - frontmatterBindingStart.Length)) - |> Some - else - None - -containsBindingFrontmatter f -tryExtractFromString f \ No newline at end of file +*)""" \ No newline at end of file