diff --git a/src/DemaConsulting.FileAssert/Modeling/FileAssertFile.cs b/src/DemaConsulting.FileAssert/Modeling/FileAssertFile.cs
index c474b89..aa78040 100644
--- a/src/DemaConsulting.FileAssert/Modeling/FileAssertFile.cs
+++ b/src/DemaConsulting.FileAssert/Modeling/FileAssertFile.cs
@@ -39,13 +39,7 @@ internal sealed class FileAssertFile
/// The exact number of files that must match, or null for no constraint.
/// The minimum file size in bytes, or null for no constraint.
/// The maximum file size in bytes, or null for no constraint.
- /// The text assert unit, or null when no text: block is declared.
- /// The PDF assert unit, or null when no pdf: block is declared.
- /// The XML assert unit, or null when no xml: block is declared.
- /// The HTML assert unit, or null when no html: block is declared.
- /// The YAML assert unit, or null when no yaml: block is declared.
- /// The JSON assert unit, or null when no json: block is declared.
- /// The zip assert unit, or null when no zip: block is declared.
+ /// The file-type assert units, each null when the corresponding block is not declared.
private FileAssertFile(
string pattern,
int? min,
@@ -53,13 +47,7 @@ private FileAssertFile(
int? count,
long? minSize,
long? maxSize,
- FileAssertTextAssert? textAssert,
- FileAssertPdfAssert? pdfAssert,
- FileAssertXmlAssert? xmlAssert,
- FileAssertHtmlAssert? htmlAssert,
- FileAssertYamlAssert? yamlAssert,
- FileAssertJsonAssert? jsonAssert,
- FileAssertZipAssert? zipAssert)
+ FileTypeAsserts asserts)
{
// Store all validated properties for use during execution
Pattern = pattern;
@@ -68,15 +56,35 @@ private FileAssertFile(
Count = count;
MinSize = minSize;
MaxSize = maxSize;
- TextAssert = textAssert;
- PdfAssert = pdfAssert;
- XmlAssert = xmlAssert;
- HtmlAssert = htmlAssert;
- YamlAssert = yamlAssert;
- JsonAssert = jsonAssert;
- ZipAssert = zipAssert;
+ TextAssert = asserts.TextAssert;
+ PdfAssert = asserts.PdfAssert;
+ XmlAssert = asserts.XmlAssert;
+ HtmlAssert = asserts.HtmlAssert;
+ YamlAssert = asserts.YamlAssert;
+ JsonAssert = asserts.JsonAssert;
+ ZipAssert = asserts.ZipAssert;
}
+ ///
+ /// Groups the optional file-type assert units so they can be passed to the
+ /// constructor as a single parameter.
+ ///
+ /// The text assert unit, or null when no text: block is declared.
+ /// The PDF assert unit, or null when no pdf: block is declared.
+ /// The XML assert unit, or null when no xml: block is declared.
+ /// The HTML assert unit, or null when no html: block is declared.
+ /// The YAML assert unit, or null when no yaml: block is declared.
+ /// The JSON assert unit, or null when no json: block is declared.
+ /// The zip assert unit, or null when no zip: block is declared.
+ private readonly record struct FileTypeAsserts(
+ FileAssertTextAssert? TextAssert,
+ FileAssertPdfAssert? PdfAssert,
+ FileAssertXmlAssert? XmlAssert,
+ FileAssertHtmlAssert? HtmlAssert,
+ FileAssertYamlAssert? YamlAssert,
+ FileAssertJsonAssert? JsonAssert,
+ FileAssertZipAssert? ZipAssert);
+
///
/// Gets the glob pattern used to match files.
///
@@ -172,7 +180,7 @@ internal static FileAssertFile Create(FileAssertFileData data)
// Return the fully constructed file assertion
return new FileAssertFile(
data.Pattern, data.Min, data.Max, data.Count, data.MinSize, data.MaxSize,
- textAssert, pdfAssert, xmlAssert, htmlAssert, yamlAssert, jsonAssert, zipAssert);
+ new FileTypeAsserts(textAssert, pdfAssert, xmlAssert, htmlAssert, yamlAssert, jsonAssert, zipAssert));
}
///
diff --git a/src/DemaConsulting.FileAssert/Modeling/FileAssertPdfAssert.cs b/src/DemaConsulting.FileAssert/Modeling/FileAssertPdfAssert.cs
index 113af30..21009c0 100644
--- a/src/DemaConsulting.FileAssert/Modeling/FileAssertPdfAssert.cs
+++ b/src/DemaConsulting.FileAssert/Modeling/FileAssertPdfAssert.cs
@@ -316,7 +316,8 @@ private void RunDocumentAssertions(IContext context, string displayPath, PdfDocu
}
else
{
- _pages?.Apply(context, displayPath, document.GetPages().Count());
+ // When we reach here _text is empty, so the guard above guarantees _pages is non-null
+ _pages!.Apply(context, displayPath, document.GetPages().Count());
}
}
@@ -326,7 +327,7 @@ private void RunDocumentAssertions(IContext context, string displayPath, PdfDocu
///
/// The ordered list of pages from the PDF document.
/// A single string containing all page text joined with newline separators.
- private static string BuildPageText(IReadOnlyList pages)
+ private static string BuildPageText(List pages)
{
var sb = new StringBuilder();
for (var i = 0; i < pages.Count; i++)
diff --git a/src/DemaConsulting.FileAssert/SelfTest/ValidationPdf.cs b/src/DemaConsulting.FileAssert/SelfTest/ValidationPdf.cs
index 845bdfe..7f36047 100644
--- a/src/DemaConsulting.FileAssert/SelfTest/ValidationPdf.cs
+++ b/src/DemaConsulting.FileAssert/SelfTest/ValidationPdf.cs
@@ -49,7 +49,7 @@ private static void RunPdfTest(Context context, DemaConsulting.TestResults.TestR
var configFile = tempDir.GetFilePath(".fileassert.yaml");
// Build a minimal PDF in memory using PdfPig's writer API
- var builder = new PdfDocumentBuilder();
+ using var builder = new PdfDocumentBuilder();
builder.DocumentInformation.Title = "Test PDF";
var font = builder.AddStandard14Font(Standard14Font.Helvetica);
var page = builder.AddPage(PageSize.A4);
diff --git a/test/DemaConsulting.FileAssert.Tests/Cli/ContextNewPropertiesTests.cs b/test/DemaConsulting.FileAssert.Tests/Cli/ContextNewPropertiesTests.cs
index 38aa754..b8f1141 100644
--- a/test/DemaConsulting.FileAssert.Tests/Cli/ContextNewPropertiesTests.cs
+++ b/test/DemaConsulting.FileAssert.Tests/Cli/ContextNewPropertiesTests.cs
@@ -94,8 +94,8 @@ public void Context_Create_MixedArguments_ParsesCorrectly()
// Assert
Assert.True(context.Silent);
Assert.Equal("cfg.yaml", context.ConfigFile);
- Assert.Single(context.Filters);
- Assert.Equal("my-filter", context.Filters[0]);
+ var filter = Assert.Single(context.Filters);
+ Assert.Equal("my-filter", filter);
}
///
diff --git a/test/DemaConsulting.FileAssert.Tests/Configuration/ConfigurationTests.cs b/test/DemaConsulting.FileAssert.Tests/Configuration/ConfigurationTests.cs
index a840e2b..835e710 100644
--- a/test/DemaConsulting.FileAssert.Tests/Configuration/ConfigurationTests.cs
+++ b/test/DemaConsulting.FileAssert.Tests/Configuration/ConfigurationTests.cs
@@ -57,13 +57,11 @@ public void Configuration_LoadYaml_BuildsCompleteTestHierarchy()
var config = FileAssertConfig.ReadFromFile(configPath);
// Assert - the full hierarchy is correctly constructed
- Assert.Single(config.Tests);
- var test = config.Tests[0];
+ var test = Assert.Single(config.Tests);
Assert.Equal("License Check", test.Name);
- Assert.Single(test.Tags);
- Assert.Equal("license", test.Tags[0]);
- Assert.Single(test.Files);
- var file = test.Files[0];
+ var tag = Assert.Single(test.Tags);
+ Assert.Equal("license", tag);
+ var file = Assert.Single(test.Files);
Assert.Equal("**/*.txt", file.Pattern);
Assert.Equal(1, file.Min);
Assert.Single(file.TextAssert!.Rules);
diff --git a/test/DemaConsulting.FileAssert.Tests/Configuration/FileAssertConfigTests.cs b/test/DemaConsulting.FileAssert.Tests/Configuration/FileAssertConfigTests.cs
index 4d0c60c..855dcb8 100644
--- a/test/DemaConsulting.FileAssert.Tests/Configuration/FileAssertConfigTests.cs
+++ b/test/DemaConsulting.FileAssert.Tests/Configuration/FileAssertConfigTests.cs
@@ -64,8 +64,8 @@ public void FileAssertConfig_ReadFromFile_ValidFile_ReturnsConfig()
var config = FileAssertConfig.ReadFromFile(configPath);
// Assert
- Assert.Single(config.Tests);
- Assert.Equal("Sample Test", config.Tests[0].Name);
+ var test = Assert.Single(config.Tests);
+ Assert.Equal("Sample Test", test.Name);
}
@@ -285,11 +285,9 @@ public void FileAssertConfig_ReadFromFile_PdfAssertConfig_ParsesCorrectly()
var config = FileAssertConfig.ReadFromFile(configPath);
// Assert - one test was parsed with one file assertion and populated PDF settings
- Assert.Single(config.Tests);
- Assert.Equal("PDF Check", config.Tests[0].Name);
- Assert.Single(config.Tests[0].Files);
-
- var fileAssertion = config.Tests[0].Files[0];
+ var test = Assert.Single(config.Tests);
+ Assert.Equal("PDF Check", test.Name);
+ var fileAssertion = Assert.Single(test.Files);
Assert.Equal("report.pdf", fileAssertion.Pattern);
Assert.NotNull(fileAssertion.PdfAssert);
diff --git a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertHtmlAssertTests.cs b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertHtmlAssertTests.cs
index a6e948a..0d06859 100644
--- a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertHtmlAssertTests.cs
+++ b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertHtmlAssertTests.cs
@@ -460,8 +460,8 @@ public void FileAssertHtmlAssert_Run_UnauthorizedAccess_WritesError()
htmlAssert.Run(context, container, "page.html");
// Assert: the IO failure is reported
- Assert.Single(context.Errors);
- Assert.Contains("could not be parsed as an HTML document", context.Errors[0]);
+ var error = Assert.Single(context.Errors);
+ Assert.Contains("could not be parsed as an HTML document", error);
}
///
diff --git a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertJsonAssertTests.cs b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertJsonAssertTests.cs
index 4e74505..f088e47 100644
--- a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertJsonAssertTests.cs
+++ b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertJsonAssertTests.cs
@@ -364,8 +364,8 @@ public void FileAssertJsonAssert_Run_InvalidJson_WritesParseError()
jsonAssert.Run(context, container, fileName);
// Assert: the error identifies a parse failure, not an IO failure
- Assert.Single(context.Errors);
- Assert.Contains("could not be parsed as a JSON document", context.Errors[0]);
+ var error = Assert.Single(context.Errors);
+ Assert.Contains("could not be parsed as a JSON document", error);
}
finally
{
@@ -389,8 +389,8 @@ public void FileAssertJsonAssert_Run_IOError_WritesReadError()
jsonAssert.Run(context, container, "data.json");
// Assert: the error identifies an IO failure, not a parse failure
- Assert.Single(context.Errors);
- Assert.Contains("could not be read", context.Errors[0]);
+ var error = Assert.Single(context.Errors);
+ Assert.Contains("could not be read", error);
}
///
diff --git a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertRuleTests.cs b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertRuleTests.cs
index 6a31bf1..aa47cec 100644
--- a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertRuleTests.cs
+++ b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertRuleTests.cs
@@ -43,8 +43,8 @@ public void FileAssertRule_Create_WithContains_ReturnsContainsRule()
var rule = FileAssertRule.Create(data);
// Assert
- Assert.IsType(rule);
- Assert.Equal("expected text", ((FileAssertContainsRule)rule).Value);
+ var containsRule = Assert.IsType(rule);
+ Assert.Equal("expected text", containsRule.Value);
}
///
@@ -60,8 +60,8 @@ public void FileAssertRule_Create_WithMatches_ReturnsMatchesRule()
var rule = FileAssertRule.Create(data);
// Assert
- Assert.IsType(rule);
- Assert.Equal(@"\d+", ((FileAssertMatchesRule)rule).Pattern);
+ var matchesRule = Assert.IsType(rule);
+ Assert.Equal(@"\d+", matchesRule.Pattern);
}
///
@@ -172,8 +172,8 @@ public void FileAssertRule_Create_WithDoesNotContain_ReturnsDoesNotContainRule()
var rule = FileAssertRule.Create(data);
// Assert
- Assert.IsType(rule);
- Assert.Equal("forbidden text", ((FileAssertDoesNotContainRule)rule).Value);
+ var doesNotContainRule = Assert.IsType(rule);
+ Assert.Equal("forbidden text", doesNotContainRule.Value);
}
///
@@ -189,8 +189,8 @@ public void FileAssertRule_Create_WithDoesNotContainRegex_ReturnsDoesNotMatchRul
var rule = FileAssertRule.Create(data);
// Assert
- Assert.IsType(rule);
- Assert.Equal(@"FATAL|ERROR", ((FileAssertDoesNotMatchRule)rule).Pattern);
+ var doesNotMatchRule = Assert.IsType(rule);
+ Assert.Equal(@"FATAL|ERROR", doesNotMatchRule.Pattern);
}
///
diff --git a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertYamlAssertTests.cs b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertYamlAssertTests.cs
index fd2deac..93c1d91 100644
--- a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertYamlAssertTests.cs
+++ b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertYamlAssertTests.cs
@@ -438,8 +438,8 @@ public void FileAssertYamlAssert_Run_InvalidFile_RemainingAssertionsSkipped()
yamlAssert.Run(context, container, fileName);
// Assert - exactly one error (the parse failure); the queries are not evaluated
- Assert.Single(context.Errors);
- Assert.Contains("could not be parsed", context.Errors[0]);
+ var error = Assert.Single(context.Errors);
+ Assert.Contains("could not be parsed", error);
}
finally
{
diff --git a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertZipAssertTests.cs b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertZipAssertTests.cs
index 883b0b6..8838afe 100644
--- a/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertZipAssertTests.cs
+++ b/test/DemaConsulting.FileAssert.Tests/Modeling/FileAssertZipAssertTests.cs
@@ -20,6 +20,7 @@
using System.Collections.ObjectModel;
using System.IO.Compression;
+using System.Linq;
using DemaConsulting.FileAssert.Cli;
using DemaConsulting.FileAssert.Configuration;
using DemaConsulting.FileAssert.Modeling;
@@ -82,12 +83,13 @@ private static void CreateZipFile(string path, IEnumerable entries)
File.Delete(path);
using var archive = ZipFile.Open(path, ZipArchiveMode.Create);
- foreach (var entry in entries)
+ foreach (var stream in entries.Select(entry => archive.CreateEntry(entry).Open()))
{
- using var stream = archive.CreateEntry(entry).Open();
-
- // Write a single placeholder byte so the entry is not an empty-stream edge case
- stream.WriteByte(0x00);
+ using (stream)
+ {
+ // Write a single placeholder byte so the entry is not an empty-stream edge case
+ stream.WriteByte(0x00);
+ }
}
}
@@ -238,10 +240,10 @@ public void FileAssertZipAssert_Create_ValidData_CreatesZipAssert()
// Assert
Assert.NotNull(zipAssert);
- Assert.Single(zipAssert.Files);
- Assert.Equal("lib/**/*.dll", zipAssert.Files[0].Pattern);
- Assert.Equal(1, zipAssert.Files[0].Min);
- Assert.Null(zipAssert.Files[0].Max);
+ var file = Assert.Single(zipAssert.Files);
+ Assert.Equal("lib/**/*.dll", file.Pattern);
+ Assert.Equal(1, file.Min);
+ Assert.Null(file.Max);
}
///