Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -260,36 +260,42 @@ private static IEnumerable<string> TopLevelSplit(string text, char separator)
}
}

// These read the pinned upstream client and test constants, never untrusted input, in time linear in
// it. A wall-clock match timeout would only fail them on a stalled CI runner.
[GeneratedRegex(
@"\(input\??: (?<input>\w+)Input, requestOptions\?: RequestOptions\)(?:: [^=]*?)? =>\s*\w+<[^\n]*?>\(\s*\{\s*method: ""(?<method>\w+)"",\s*path: `(?<path>[^`]*)`,(?:\s*query: \{(?<query>[^}]*)\},)?",
RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex RouteCall();

[GeneratedRegex(@"(?<key>\w+|""[^""]+""): input\??\.?\[""(?<field>\w+)""\]", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(
@"(?<key>\w+|""[^""]+""): input\??\.?\[""(?<field>\w+)""\]",
RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex QueryEntry();

[GeneratedRegex(@"\$\{encodeURIComponent\(input(?:\.(?<name>\w+)|\[""(?<name>\w+)""\])\)\}", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(
@"\$\{encodeURIComponent\(input(?:\.(?<name>\w+)|\[""(?<name>\w+)""\])\)\}",
RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex PathValue();

[GeneratedRegex(@"\$\{encodePath\([^)]*\)\}", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(@"\$\{encodePath\([^)]*\)\}", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex WildcardTail();

[GeneratedRegex(@"(?:export )?type (?<name>\w+)Input = \{", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(@"(?:export )?type (?<name>\w+)Input = \{", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex InputType();

[GeneratedRegex(@"^readonly (?<name>\w+)\??:\s*(?<type>[\s\S]*)$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(@"^readonly (?<name>\w+)\??:\s*(?<type>[\s\S]*)$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex Field();

[GeneratedRegex(@"^\{(?<shape>[\s\S]*)\}\[""(?<field>\w+)""\]$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(@"^\{(?<shape>[\s\S]*)\}\[""(?<field>\w+)""\]$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex IndexedAccess();

[GeneratedRegex(@"^""(?:[^""\\]|\\.)*""$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(@"^""(?:[^""\\]|\\.)*""$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex StringLiteral();

[GeneratedRegex(@"^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(@"^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*$", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex NamedType();

[GeneratedRegex(@"\s+", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(@"\s+", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex Whitespace();

private sealed record UpstreamRoute(string Input, Dictionary<string, string> Query);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Globalization;

namespace OpenCode.Sdk.Tools.Tests.Generator.Binding.Support;

/// <summary>
Expand Down Expand Up @@ -104,6 +106,27 @@ public async Task Parse_Should_Keep_A_Literal_With_Escaped_Quotes_And_Braces_Who
await Assert.That(type).IsEqualTo("\"a \\\" quote, a { brace and readonly text\"").Because(problem);
}

/// <summary>
/// A client forty times the pinned client's size still reads its first and last route: matching
/// stays linear in the source, which is why the parse carries no wall-clock timeout.
/// </summary>
[Test]
public async Task Parse_Should_Read_The_First_And_Last_Route_Of_A_Client_Forty_Times_The_Pinned_Size()
{
const int copies = 3000;
var client = string.Concat(Enumerable.Range(0, copies)
.Select(static copy => Client.Replace("/api/vcs/diff", RoutePath(copy), StringComparison.Ordinal)));

var contract = UpstreamClientContract.Parse(client, Api);

_ = contract.TryGetQueryType("GET", RoutePath(0), "mode", out var first, out var firstProblem);
_ = contract.TryGetQueryType("GET", RoutePath(copies - 1), "mode", out var last, out var lastProblem);
await Assert.That(first).IsEqualTo("Vcs.Mode").Because(firstProblem);
await Assert.That(last).IsEqualTo("Vcs.Mode").Because(lastProblem);

static string RoutePath(int copy) => "/api/vcs/diff" + copy.ToString(CultureInfo.InvariantCulture);
}

[Test]
[Arguments("GET", "/api/vcs/other", "mode", "has no route for GET /api/vcs/other")]
[Arguments("GET", "/api/vcs/diff", "missing", "carries no field for the query key 'missing'")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ await Assert.That(doors.Select(static door => door.TypeName).Distinct(StringComp
await Assert.That(unmarked).IsEmpty();
}

[GeneratedRegex(@"Operation <c>(?<id>[^<]+)</c>: <c>(?<method>[A-Z]+) (?<route>[^<]+)</c>\.", RegexOptions.CultureInvariant, matchTimeoutMilliseconds: 1000)]
// Reads the repository's own hand-written door remarks, never untrusted input; a wall-clock match
// timeout would only fail it on a stalled CI runner.
[GeneratedRegex(
@"Operation <c>(?<id>[^<]+)</c>: <c>(?<method>[A-Z]+) (?<route>[^<]+)</c>\.",
RegexOptions.CultureInvariant, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex RemarkPattern();

private static async Task<List<HandWrittenMethod>> LoadHandWrittenMethodsAsync()
Expand Down
Loading