diff --git a/FirstClassErrors.GenDoc.UnitTests/CatalogDiffFormatterTests.cs b/FirstClassErrors.GenDoc.UnitTests/CatalogDiffFormatterTests.cs index 462820d..787f79d 100644 --- a/FirstClassErrors.GenDoc.UnitTests/CatalogDiffFormatterTests.cs +++ b/FirstClassErrors.GenDoc.UnitTests/CatalogDiffFormatterTests.cs @@ -16,13 +16,13 @@ public sealed class CatalogDiffFormatterTests { private static CatalogDiff SampleDiff() { CatalogSnapshot baseline = new() { Errors = [ - new CatalogSnapshotEntry { Code = "PAYMENT.DECLINED", Title = "Payment declined" }, + new CatalogSnapshotEntry { Code = "PAYMENT_DECLINED", Title = "Payment declined" }, new CatalogSnapshotEntry { Code = "SOME_CODE", Title = "Old title" } ] }; CatalogSnapshot current = new() { Errors = [ - new CatalogSnapshotEntry { Code = "PAYMENT.REFUSED", Title = "Payment declined" }, + new CatalogSnapshotEntry { Code = "PAYMENT_REFUSED", Title = "Payment declined" }, new CatalogSnapshotEntry { Code = "SOME_CODE", Title = "New title" } ] }; @@ -43,9 +43,9 @@ public void TheTextReportGroupsTheChangesByImpact() { Check.That(report).Contains("Breaking changes (1):"); Check.That(report).Contains("Compatible changes (1):"); Check.That(report).Contains("Documentation changes (1):"); - Check.That(report).Contains("[removed] PAYMENT.DECLINED"); - Check.That(report).Contains("possibly renamed to 'PAYMENT.REFUSED'"); - Check.That(report).Contains("[added] PAYMENT.REFUSED"); + Check.That(report).Contains("[removed] PAYMENT_DECLINED"); + Check.That(report).Contains("possibly renamed to 'PAYMENT_REFUSED'"); + Check.That(report).Contains("[added] PAYMENT_REFUSED"); Check.That(report).Contains("[title] SOME_CODE"); } @@ -65,7 +65,7 @@ public void TheMarkdownReportHasOneSectionPerImpact() { Check.That(report).Contains("### 💥 Breaking (1)"); Check.That(report).Contains("### ✅ Compatible (1)"); Check.That(report).Contains("### ℹ️ Documentation (1)"); - Check.That(report).Contains("- **`PAYMENT.DECLINED`**"); + Check.That(report).Contains("- **`PAYMENT_DECLINED`**"); } [Fact(DisplayName = "The Markdown report of an empty diff says so under the heading.")] @@ -96,7 +96,7 @@ public void TheJsonReportExposesCountsFlagAndChanges() { Check.That(changes.GetArrayLength()).IsEqualTo(3); JsonElement removal = changes.EnumerateArray().Single(change => change.GetProperty("kind").GetString() == "errorRemoved"); Check.That(removal.GetProperty("impact").GetString()).IsEqualTo("breaking"); - Check.That(removal.GetProperty("code").GetString()).IsEqualTo("PAYMENT.DECLINED"); + Check.That(removal.GetProperty("code").GetString()).IsEqualTo("PAYMENT_DECLINED"); } [Fact(DisplayName = "The JSON report of an empty diff has zero counts and no changes.")] diff --git a/FirstClassErrors.GenDoc.UnitTests/CatalogDifferTests.cs b/FirstClassErrors.GenDoc.UnitTests/CatalogDifferTests.cs index af8d71d..8b24a65 100644 --- a/FirstClassErrors.GenDoc.UnitTests/CatalogDifferTests.cs +++ b/FirstClassErrors.GenDoc.UnitTests/CatalogDifferTests.cs @@ -26,8 +26,8 @@ private static CatalogSnapshotContextKey Key(string key, string valueType) { [Fact(DisplayName = "Two identical snapshots produce an empty diff.")] public void TwoIdenticalSnapshotsProduceAnEmptyDiff() { // Setup - CatalogSnapshot baseline = Snapshot(Entry("PAYMENT.DECLINED", "Payment declined", "Payment", Key("DealId", "System.String"))); - CatalogSnapshot current = Snapshot(Entry("PAYMENT.DECLINED", "Payment declined", "Payment", Key("DealId", "System.String"))); + CatalogSnapshot baseline = Snapshot(Entry("PAYMENT_DECLINED", "Payment declined", "Payment", Key("DealId", "System.String"))); + CatalogSnapshot current = Snapshot(Entry("PAYMENT_DECLINED", "Payment declined", "Payment", Key("DealId", "System.String"))); // Exercise CatalogDiff diff = CatalogDiffer.Diff(baseline, current); @@ -40,20 +40,20 @@ public void TwoIdenticalSnapshotsProduceAnEmptyDiff() { [Fact(DisplayName = "A removed error code is a breaking change.")] public void ARemovedErrorCodeIsABreakingChange() { // Exercise - CatalogDiff diff = CatalogDiffer.Diff(Snapshot(Entry("PAYMENT.DECLINED")), Snapshot()); + CatalogDiff diff = CatalogDiffer.Diff(Snapshot(Entry("PAYMENT_DECLINED")), Snapshot()); // Verify Check.That(diff.BreakingChanges).HasSize(1); CatalogChange change = diff.BreakingChanges[0]; Check.That(change.Kind).IsEqualTo(CatalogChangeKind.ErrorRemoved); - Check.That(change.Code).IsEqualTo("PAYMENT.DECLINED"); + Check.That(change.Code).IsEqualTo("PAYMENT_DECLINED"); } [Fact(DisplayName = "A removal whose title matches a single added error carries a 'possibly renamed' hint.")] public void ARemovalMatchingASingleAdditionCarriesARenameHint() { // Setup - CatalogSnapshot baseline = Snapshot(Entry("PAYMENT.DECLINED", "Payment declined")); - CatalogSnapshot current = Snapshot(Entry("PAYMENT.REFUSED", "Payment declined")); + CatalogSnapshot baseline = Snapshot(Entry("PAYMENT_DECLINED", "Payment declined")); + CatalogSnapshot current = Snapshot(Entry("PAYMENT_REFUSED", "Payment declined")); // Exercise CatalogDiff diff = CatalogDiffer.Diff(baseline, current); @@ -61,7 +61,7 @@ public void ARemovalMatchingASingleAdditionCarriesARenameHint() { // Verify: the removal stays breaking — consumers know the old code — but the hint points at the new one. CatalogChange removal = diff.Changes.Single(change => change.Kind == CatalogChangeKind.ErrorRemoved); Check.That(removal.Impact).IsEqualTo(CatalogChangeImpact.Breaking); - Check.That(removal.Description).Contains("possibly renamed to 'PAYMENT.REFUSED'"); + Check.That(removal.Description).Contains("possibly renamed to 'PAYMENT_REFUSED'"); } [Fact(DisplayName = "No rename hint is emitted when several added errors share the removed error's title.")] @@ -81,7 +81,7 @@ public void NoRenameHintIsEmittedWhenSeveralAdditionsShareTheTitle() { [Fact(DisplayName = "A new error code is a compatible change.")] public void ANewErrorCodeIsACompatibleChange() { // Exercise - CatalogDiff diff = CatalogDiffer.Diff(Snapshot(), Snapshot(Entry("INVENTORY.OUT_OF_STOCK", "Out of stock", "Inventory"))); + CatalogDiff diff = CatalogDiffer.Diff(Snapshot(), Snapshot(Entry("INVENTORY_OUT_OF_STOCK", "Out of stock", "Inventory"))); // Verify Check.That(diff.CompatibleChanges).HasSize(1); @@ -181,8 +181,8 @@ public void ChangesAreOrderedByErrorCode() { [Fact(DisplayName = "Identities and titles are compared normalized: whitespace-only differences produce no change.")] public void WhitespaceOnlyDifferencesProduceNoChange() { // Setup: same contract, but every identity and the title differ only by surrounding whitespace. - CatalogSnapshot baseline = Snapshot(Entry("PAYMENT.DECLINED", "Payment declined", "Payment", Key("DealId", "System.String"))); - CatalogSnapshot current = Snapshot(Entry(" PAYMENT.DECLINED ", " Payment declined ", " Payment ", Key(" DealId ", "System.String"))); + CatalogSnapshot baseline = Snapshot(Entry("PAYMENT_DECLINED", "Payment declined", "Payment", Key("DealId", "System.String"))); + CatalogSnapshot current = Snapshot(Entry(" PAYMENT_DECLINED ", " Payment declined ", " Payment ", Key(" DealId ", "System.String"))); // Exercise CatalogDiff diff = CatalogDiffer.Diff(baseline, current); @@ -194,15 +194,15 @@ public void WhitespaceOnlyDifferencesProduceNoChange() { [Fact(DisplayName = "The rename hint matches titles that differ only by whitespace.")] public void TheRenameHintMatchesTitlesThatDifferOnlyByWhitespace() { // Setup - CatalogSnapshot baseline = Snapshot(Entry("PAYMENT.DECLINED", "Payment declined")); - CatalogSnapshot current = Snapshot(Entry("PAYMENT.REFUSED", " Payment declined ")); + CatalogSnapshot baseline = Snapshot(Entry("PAYMENT_DECLINED", "Payment declined")); + CatalogSnapshot current = Snapshot(Entry("PAYMENT_REFUSED", " Payment declined ")); // Exercise CatalogDiff diff = CatalogDiffer.Diff(baseline, current); // Verify CatalogChange removal = diff.Changes.Single(change => change.Kind == CatalogChangeKind.ErrorRemoved); - Check.That(removal.Description).Contains("possibly renamed to 'PAYMENT.REFUSED'"); + Check.That(removal.Description).Contains("possibly renamed to 'PAYMENT_REFUSED'"); } [Fact(DisplayName = "Null snapshots are rejected.")] diff --git a/FirstClassErrors.Testing/ErrorAssertion.cs b/FirstClassErrors.Testing/ErrorAssertion.cs index a048644..d05e8af 100644 --- a/FirstClassErrors.Testing/ErrorAssertion.cs +++ b/FirstClassErrors.Testing/ErrorAssertion.cs @@ -9,7 +9,7 @@ namespace FirstClassErrors.Testing; /// /// /// outcome.ShouldFail() -/// .WithCode("PAYMENT.DECLINED") +/// .WithCode("PAYMENT_DECLINED") /// .WithContextEntry("CardNetwork", "VISA"); /// /// diff --git a/FirstClassErrors.UnitTests/OutcomeAssertionsTests.cs b/FirstClassErrors.UnitTests/OutcomeAssertionsTests.cs index fa5f14d..80d11ed 100644 --- a/FirstClassErrors.UnitTests/OutcomeAssertionsTests.cs +++ b/FirstClassErrors.UnitTests/OutcomeAssertionsTests.cs @@ -28,7 +28,7 @@ public void Dispose() { #region Statics members declarations - private static DomainError AnError(string code = "PAYMENT.DECLINED", string diagnostic = "diagnostic", string @short = "short") { + private static DomainError AnError(string code = "PAYMENT_DECLINED", string diagnostic = "diagnostic", string @short = "short") { return DomainError.Create(ErrorCode.Create(code), diagnostic).WithPublicMessage(@short); } @@ -46,7 +46,7 @@ public void ShouldSucceedOnFailureThrowsNamingTheCode() { Outcome outcome = Outcome.Failure(AnError()); OutcomeAssertionException exception = Assert.Throws(() => outcome.ShouldSucceed()); - Check.That(exception.Message).Contains("PAYMENT.DECLINED"); + Check.That(exception.Message).Contains("PAYMENT_DECLINED"); } [Fact(DisplayName = "ShouldFail on a failed outcome returns a fluent handle that matches the code.")] @@ -54,7 +54,7 @@ public void ShouldFailReturnsFluentHandleMatchingTheCode() { Outcome outcome = Outcome.Failure(AnError()); outcome.ShouldFail() - .WithCode("PAYMENT.DECLINED") + .WithCode("PAYMENT_DECLINED") .WithDiagnosticMessage("diagnostic") .WithShortMessage("short"); } @@ -63,7 +63,7 @@ public void ShouldFailReturnsFluentHandleMatchingTheCode() { public void ShouldFailWithMismatchingCodeThrows() { Outcome outcome = Outcome.Failure(AnError()); - Assert.Throws(() => outcome.ShouldFail().WithCode("SOMETHING.ELSE")); + Assert.Throws(() => outcome.ShouldFail().WithCode("SOMETHING_ELSE")); } [Fact(DisplayName = "ShouldFail on a successful outcome throws.")] @@ -76,7 +76,7 @@ public void ShouldFailOnSuccessThrows() { [Fact(DisplayName = "The non-generic Outcome assertions behave the same way.")] public void NonGenericOutcomeAssertions() { Outcome.Success.ShouldSucceed(); - Outcome.Failure(AnError()).ShouldFail().WithCode("PAYMENT.DECLINED"); + Outcome.Failure(AnError()).ShouldFail().WithCode("PAYMENT_DECLINED"); Assert.Throws(() => Outcome.Success.ShouldFail()); Assert.Throws(() => Outcome.Failure(AnError()).ShouldSucceed()); @@ -85,7 +85,7 @@ public void NonGenericOutcomeAssertions() { [Fact(DisplayName = "WithContextEntry checks the presence and value of a context entry.")] public void WithContextEntryChecksPresenceAndValue() { ErrorContextKey cardNetwork = ErrorContextKey.Create("CardNetwork", "The card network."); - DomainError error = DomainError.Create(ErrorCode.Create("PAYMENT.DECLINED"), "diagnostic", context => context.Add(cardNetwork, "VISA")) + DomainError error = DomainError.Create(ErrorCode.Create("PAYMENT_DECLINED"), "diagnostic", context => context.Add(cardNetwork, "VISA")) .WithPublicMessage("short"); Outcome outcome = Outcome.Failure(error); diff --git a/FirstClassErrors/Error.cs b/FirstClassErrors/Error.cs index 4df2b16..e469767 100644 --- a/FirstClassErrors/Error.cs +++ b/FirstClassErrors/Error.cs @@ -262,8 +262,8 @@ protected Error(ErrorCode code, /// /// /// Unlike , this value is shared across all occurrences - /// of the same logical error and is intended for grouping, monitoring, and alerting. Examples: PAYMENT.DECLINED - /// or INVENTORY.OUT_OF_STOCK. + /// of the same logical error and is intended for grouping, monitoring, and alerting. Examples: PAYMENT_DECLINED + /// or INVENTORY_OUT_OF_STOCK. /// public ErrorCode Code { get; } diff --git a/doc/Testing.en.md b/doc/Testing.en.md index cd9dc49..3ce29ba 100644 --- a/doc/Testing.en.md +++ b/doc/Testing.en.md @@ -45,7 +45,7 @@ usually means unwrapping it by hand and reaching for the null-forgiving Outcome outcome = checkout.Pay(order); Assert.True(outcome.IsFailure); -Assert.Equal("PAYMENT.DECLINED", outcome.Error!.Code.ToString()); +Assert.Equal("PAYMENT_DECLINED", outcome.Error!.Code.ToString()); ``` With the testing package, the intent comes first and the boilerplate disappears: @@ -54,7 +54,7 @@ With the testing package, the intent comes first and the boilerplate disappears: // 🙂 After checkout.Pay(order) .ShouldFail() - .WithCode("PAYMENT.DECLINED"); + .WithCode("PAYMENT_DECLINED"); ``` ### Successes return their value @@ -92,7 +92,7 @@ public void Declining_a_payment_reports_a_diagnosable_error() Outcome outcome = checkout.Pay(declinedCard); outcome.ShouldFail() - .WithCode("PAYMENT.DECLINED") + .WithCode("PAYMENT_DECLINED") .WithShortMessage("Your payment was declined.") .WithDiagnosticMessage("Issuer refused authorization (code 51).") .WithContextEntry("CardNetwork", "VISA"); @@ -113,7 +113,7 @@ Need something the fluent surface doesn't cover? `Subject` gives you the raw `Error` back: ```csharp -Error error = outcome.ShouldFail().WithCode("ORDER.NOT_FOUND").Subject; +Error error = outcome.ShouldFail().WithCode("ORDER_NOT_FOUND").Subject; Assert.Empty(error.InnerErrors); ``` @@ -125,11 +125,11 @@ with a message that tells you what actually happened — not the domain's own exception: ```text -Expected the outcome to succeed, but it failed with [PAYMENT.DECLINED]: Issuer refused authorization (code 51). +Expected the outcome to succeed, but it failed with [PAYMENT_DECLINED]: Issuer refused authorization (code 51). ``` ```text -Expected the error to have code "ORDER.NOT_FOUND", but it was "ORDER.LOCKED". +Expected the error to have code "ORDER_NOT_FOUND", but it was "ORDER_LOCKED". ``` --- @@ -161,7 +161,7 @@ public void An_error_records_when_it_occurred() using (Clock.UseFixed(instant)) { DomainError error = DomainError - .Create(ErrorCode.Create("ORDER.NOT_FOUND"), "Order 42 was not found.") + .Create(ErrorCode.Create("ORDER_NOT_FOUND"), "Order 42 was not found.") .WithPublicMessage("This order does not exist."); Assert.Equal(instant, error.OccurredAt); @@ -268,7 +268,7 @@ public void Looking_up_a_missing_order_fails_deterministically() Outcome outcome = orders.Find(missingId); ErrorAssertion failure = outcome.ShouldFail() - .WithCode("ORDER.NOT_FOUND") + .WithCode("ORDER_NOT_FOUND") .WithContextEntry("OrderId", missingId); Assert.Equal(instant, failure.Subject.OccurredAt); diff --git a/doc/Testing.fr.md b/doc/Testing.fr.md index 7041cff..505b606 100644 --- a/doc/Testing.fr.md +++ b/doc/Testing.fr.md @@ -48,7 +48,7 @@ C'est la partie que vous utiliserez tous les jours. Tester du code qui renvoie u Outcome outcome = checkout.Pay(order); Assert.True(outcome.IsFailure); -Assert.Equal("PAYMENT.DECLINED", outcome.Error!.Code.ToString()); +Assert.Equal("PAYMENT_DECLINED", outcome.Error!.Code.ToString()); ``` Avec le package de test, l'intention passe en premier et le boilerplate disparaît : @@ -57,7 +57,7 @@ Avec le package de test, l'intention passe en premier et le boilerplate dispara // 🙂 Après checkout.Pay(order) .ShouldFail() - .WithCode("PAYMENT.DECLINED"); + .WithCode("PAYMENT_DECLINED"); ``` ### Les succès renvoient leur valeur @@ -95,7 +95,7 @@ public void Un_paiement_refuse_remonte_une_erreur_diagnosable() Outcome outcome = checkout.Pay(declinedCard); outcome.ShouldFail() - .WithCode("PAYMENT.DECLINED") + .WithCode("PAYMENT_DECLINED") .WithShortMessage("Votre paiement a été refusé.") .WithDiagnosticMessage("L'émetteur a refusé l'autorisation (code 51).") .WithContextEntry("CardNetwork", "VISA"); @@ -116,7 +116,7 @@ Besoin de quelque chose que la surface fluente ne couvre pas ? `Subject` vous rend l'`Error` brute : ```csharp -Error error = outcome.ShouldFail().WithCode("ORDER.NOT_FOUND").Subject; +Error error = outcome.ShouldFail().WithCode("ORDER_NOT_FOUND").Subject; Assert.Empty(error.InnerErrors); ``` @@ -128,11 +128,11 @@ Quand une attente n'est pas satisfaite, les assertions lèvent une et non l'exception propre au domaine : ```text -Expected the outcome to succeed, but it failed with [PAYMENT.DECLINED]: L'émetteur a refusé l'autorisation (code 51). +Expected the outcome to succeed, but it failed with [PAYMENT_DECLINED]: L'émetteur a refusé l'autorisation (code 51). ``` ```text -Expected the error to have code "ORDER.NOT_FOUND", but it was "ORDER.LOCKED". +Expected the error to have code "ORDER_NOT_FOUND", but it was "ORDER_LOCKED". ``` --- @@ -164,7 +164,7 @@ public void Une_erreur_enregistre_quand_elle_survient() using (Clock.UseFixed(instant)) { DomainError error = DomainError - .Create(ErrorCode.Create("ORDER.NOT_FOUND"), "La commande 42 est introuvable.") + .Create(ErrorCode.Create("ORDER_NOT_FOUND"), "La commande 42 est introuvable.") .WithPublicMessage("Cette commande n'existe pas."); Assert.Equal(instant, error.OccurredAt); @@ -274,7 +274,7 @@ public void Chercher_une_commande_absente_echoue_de_facon_deterministe() Outcome outcome = orders.Find(missingId); ErrorAssertion failure = outcome.ShouldFail() - .WithCode("ORDER.NOT_FOUND") + .WithCode("ORDER_NOT_FOUND") .WithContextEntry("OrderId", missingId); Assert.Equal(instant, failure.Subject.OccurredAt);