test(ui): extend redirect guard meta-test to Tier-2 pattern templates (#204 review)
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 6s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m41s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m33s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m11s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 6m48s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m42s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 6s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m41s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m33s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m11s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 6m48s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m42s
Fork adversarial review nit: Map_Keys_Should_Not_Begin_With_Forbidden_Prefix covered only Tier-1 Map keys, not the Tier-2 PatternRule templates. That guard invariant is the load-bearing protection for the un-prefix-guarded /api|/artwork| /docs|/openapi surface, so make it self-enforcing over ALL rules — a future prefix-violating template now fails the test instead of slipping through. Exposes internal LegacyUiRedirects.PatternTemplates (InternalsVisibleTo already set for ErsatzTV.Tests); stores the raw template on PatternRule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #260.
This commit is contained in:
@@ -178,6 +178,18 @@ public class LegacyUiRedirectsTests
|
|||||||
.ShouldBeFalse($"Map key '{key}' must not begin with forbidden prefix '{prefix}'");
|
.ShouldBeFalse($"Map key '{key}' must not begin with forbidden prefix '{prefix}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The guard invariant is load-bearing for the un-prefix-guarded /api|/artwork|
|
||||||
|
// /docs|/openapi surface, so it must cover the Tier-2 pattern templates too —
|
||||||
|
// not only the Tier-1 Map keys — or a future prefix-violating rule would slip in.
|
||||||
|
foreach (string template in LegacyUiRedirects.PatternTemplates)
|
||||||
|
{
|
||||||
|
foreach (string prefix in forbidden)
|
||||||
|
{
|
||||||
|
template.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
|
||||||
|
.ShouldBeFalse($"Pattern template '{template}' must not begin with forbidden prefix '{prefix}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -280,14 +280,23 @@ public static class LegacyUiRedirects
|
|||||||
private static bool IsPositiveInteger(string segment) =>
|
private static bool IsPositiveInteger(string segment) =>
|
||||||
int.TryParse(segment, NumberStyles.None, CultureInfo.InvariantCulture, out int id) && id > 0;
|
int.TryParse(segment, NumberStyles.None, CultureInfo.InvariantCulture, out int id) && id > 0;
|
||||||
|
|
||||||
|
// Exposed for the guard-invariant meta-test (InternalsVisibleTo ErsatzTV.Tests):
|
||||||
|
// the source-side template of every Tier-2 rule, so the test can assert no
|
||||||
|
// template — not just no Tier-1 Map key — begins with a forbidden prefix.
|
||||||
|
internal static IEnumerable<string> PatternTemplates => Patterns.Select(rule => rule.Template);
|
||||||
|
|
||||||
private sealed class PatternRule
|
private sealed class PatternRule
|
||||||
{
|
{
|
||||||
public PatternRule(string template, string target)
|
public PatternRule(string template, string target)
|
||||||
{
|
{
|
||||||
|
Template = template;
|
||||||
Segments = template.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
Segments = template.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
Target = target;
|
Target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The raw legacy route template, e.g. "/channels/{id}" (source side).
|
||||||
|
public string Template { get; }
|
||||||
|
|
||||||
// e.g. ["channels", "{id}"]
|
// e.g. ["channels", "{id}"]
|
||||||
public string[] Segments { get; }
|
public string[] Segments { get; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user