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
7 changes: 6 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ dotnet test Linq2GraphQL.CI.slnf --no-build
# Refresh them after a template or test-schema change
./scripts/regenerate-test-clients.ps1

# After a release: compare against it from now on (compatibility tests and package validation)
./scripts/update-compat-baseline.ps1 -Version <released version>

# Single test / class
dotnet test test/Linq2GraphQL.Tests --filter "FullyQualifiedName~QueryTests.Hello_WithNoName_HelloWorld"
dotnet test test/Linq2GraphQL.Tests --filter "FullyQualifiedName~ExpressionParserTests"
Expand All @@ -39,7 +42,7 @@ dotnet run --project test/Linq2GraphQL.TestServer
dotnet run --project test/Linq2GraphQL.Console
```

`Linq2GraphQL.Release.slnf` is the pack/publish subset; versioning is Nerdbank.GitVersioning (`version.json`) — do not hand-edit version numbers.
`Linq2GraphQL.Release.slnf` is the pack/publish subset; versioning is Nerdbank.GitVersioning (`version.json`) — do not hand-edit version numbers. `Linq2GraphQLBaselineVersion` in `Directory.Build.props` names the last published release: `dotnet pack` fails on a public API break against it (package validation, `src/Directory.Build.targets`) and the compatibility tests below run against it. Move it with `update-compat-baseline.ps1`, not by hand.

## Runtime architecture (src/Linq2GraphQL.Client)

Expand Down Expand Up @@ -77,4 +80,6 @@ Key details that bite:

`Linq2GraphQL.TestClient` / `TestClientNullable` / `TestClientStandalone` hold **checked-in generated output**. They are not regenerated by the build — run `./scripts/regenerate-test-clients.ps1` (it boots the test servers over plain HTTP, regenerates the clients with the flags that produced the committed output, and writes them back) and commit the diff. The `generated-clients` CI job runs the same script with `-Check` and fails if the committed output no longer matches the generator, so a template or schema change that you forget to regenerate is caught in CI rather than passing against stale output. `TestClientStandalone` contains a copy of the runtime, so **any change under `src/Linq2GraphQL.Client`, `.Subscriptions` or `.AI` also needs a regenerate**; `-Check` ignores only the version line in its `RuntimeInfo.cs`.

`test/Linq2GraphQL.Compat` guards apps that update the runtime packages and keep their generated client. `FrozenClients/` holds both test clients as the baseline release generated them; `Scenarios/` is a list of things an app does with them (queries, errors, paging, hooks, DI, SSE subscriptions, canned HTTP responses). The same sources compile twice: `Baseline/` against the published baseline packages (run as its own process, since both runtimes are `Linq2GraphQL.Client.dll`) and `Tests/` against `src/`. Each scenario records the requests it sent on the wire and what it returned or threw, and `CompatibilityTests` requires the two records to be identical. A deliberate behavior change goes into `Tests/accepted-differences.json` with the reason; the test fails if a listed scenario matches again. Scenarios must compile against the baseline's API, so tests of new API belong in `Linq2GraphQL.Tests`. Both records are written to `compat-baseline.json`/`compat-current.json` in the test output directory.

`docs/` (the Blazor documentation site) and `StartGG/` (a sample client) are outside the CI solution filter and can drift.
6 changes: 6 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<Project>
<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<!--
The last published release. Package validation (src/Directory.Build.targets) checks the public API against it
and test/Linq2GraphQL.Compat runs a client generated by it against it and against this code. Move it with
scripts/update-compat-baseline.ps1, never by hand.
-->
<Linq2GraphQLBaselineVersion>1.0.67-prerelease</Linq2GraphQLBaselineVersion>
</PropertyGroup>
</Project>
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<PackageVersion Include="HotChocolate.Data" Version="16.6.4" />
<PackageVersion Include="HotChocolate.Types.Scalars" Version="16.6.4" />
<PackageVersion Include="HotChocolate.Types.Analyzers" Version="16.6.4" />
<!-- Only the compatibility baseline runner (test/Linq2GraphQL.Compat) uses the published packages. -->
<PackageVersion Include="Linq2GraphQL.Client" Version="$(Linq2GraphQLBaselineVersion)" />
<PackageVersion Include="Linq2GraphQL.Client.Subscriptions" Version="$(Linq2GraphQLBaselineVersion)" />
<PackageVersion Include="Macross.Json.Extensions" Version="3.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.11" />
<PackageVersion Include="Azure.Identity" Version="1.21.0" />
Expand Down
2 changes: 2 additions & 0 deletions Linq2GraphQL.CI.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"src\\Linq2GraphQL.Generator\\Linq2GraphQL.Generator.csproj",
"test\\Linq2GraphQL.Console\\Linq2GraphQL.Console.csproj",
"test\\Linq2GraphQL.Agent.Sample\\Linq2GraphQL.Agent.Sample.csproj",
"test\\Linq2GraphQL.Compat\\Baseline\\Linq2GraphQL.Compat.Baseline.csproj",
"test\\Linq2GraphQL.Compat\\Tests\\Linq2GraphQL.Compat.Tests.csproj",
"test\\Linq2GraphQL.TestClient\\Linq2GraphQL.TestClient.csproj",
"test\\Linq2GraphQL.TestClientNullable\\Linq2GraphQL.TestClientNullable.csproj",
"test\\Linq2GraphQL.TestClientStandalone\\Linq2GraphQL.TestClientStandalone.csproj",
Expand Down
17 changes: 17 additions & 0 deletions Linq2GraphQL.sln
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,26 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nullable", "nullable", "{3D
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nonnullable", "nonnullable", "{1F4F9593-3C19-4C2E-AF8C-C269659AA12A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "compat", "compat", "{5A3C9E21-7B4D-4E8F-A1C2-3D5E6F708192}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Linq2GraphQL.Compat.Baseline", "test\Linq2GraphQL.Compat\Baseline\Linq2GraphQL.Compat.Baseline.csproj", "{71546B4D-AD64-4204-B806-F580FB20CBF7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Linq2GraphQL.Compat.Tests", "test\Linq2GraphQL.Compat\Tests\Linq2GraphQL.Compat.Tests.csproj", "{2ECBBDFD-961C-4686-9D40-0F35D83D196D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{71546B4D-AD64-4204-B806-F580FB20CBF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71546B4D-AD64-4204-B806-F580FB20CBF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71546B4D-AD64-4204-B806-F580FB20CBF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71546B4D-AD64-4204-B806-F580FB20CBF7}.Release|Any CPU.Build.0 = Release|Any CPU
{2ECBBDFD-961C-4686-9D40-0F35D83D196D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2ECBBDFD-961C-4686-9D40-0F35D83D196D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2ECBBDFD-961C-4686-9D40-0F35D83D196D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2ECBBDFD-961C-4686-9D40-0F35D83D196D}.Release|Any CPU.Build.0 = Release|Any CPU
{AD8291B6-5979-4C43-B785-EAD4FF93C564}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD8291B6-5979-4C43-B785-EAD4FF93C564}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD8291B6-5979-4C43-B785-EAD4FF93C564}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -141,6 +155,9 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{71546B4D-AD64-4204-B806-F580FB20CBF7} = {5A3C9E21-7B4D-4E8F-A1C2-3D5E6F708192}
{2ECBBDFD-961C-4686-9D40-0F35D83D196D} = {5A3C9E21-7B4D-4E8F-A1C2-3D5E6F708192}
{5A3C9E21-7B4D-4E8F-A1C2-3D5E6F708192} = {5BE1963B-E57D-4CF5-B03E-644D8F39B4D2}
{AD8291B6-5979-4C43-B785-EAD4FF93C564} = {1F4F9593-3C19-4C2E-AF8C-C269659AA12A}
{F412B4C3-F872-483C-A55D-B49782ACAAFE} = {1F4F9593-3C19-4C2E-AF8C-C269659AA12A}
{F9EAFD31-9AF1-47DF-9C78-003C7AC67C5D} = {5BE1963B-E57D-4CF5-B03E-644D8F39B4D2}
Expand Down
78 changes: 78 additions & 0 deletions scripts/update-compat-baseline.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Moves the compatibility baseline to another published release.

.DESCRIPTION
test/Linq2GraphQL.Compat checks that an app which updates the runtime packages and keeps its generated client
behaves as before. It holds a frozen copy of the test clients as the baseline release generated them, and runs
them against that release's packages and against this code.

After a release, run this with the new version. It copies the test clients from the release's git tag into
test/Linq2GraphQL.Compat/FrozenClients, sets Linq2GraphQLBaselineVersion in Directory.Build.props (package
validation uses it too), clears the accepted differences, which the new baseline contains, and refreshes the
baseline runner's lock file. Review and commit the result.

.PARAMETER Version
The published version to compare against; its git tag must exist.

.EXAMPLE
./scripts/update-compat-baseline.ps1 -Version 1.0.67-prerelease
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Version
)

$ErrorActionPreference = 'Stop'
$repoRoot = Split-Path -Parent $PSScriptRoot
$compatRoot = Join-Path $repoRoot 'test/Linq2GraphQL.Compat'
$frozenClients = Join-Path $compatRoot 'FrozenClients'
$clients = @('Linq2GraphQL.TestClient', 'Linq2GraphQL.TestClientNullable')

git -C $repoRoot rev-parse --verify --quiet "refs/tags/$Version" | Out-Null
if ($LASTEXITCODE -ne 0) { throw "There is no git tag $Version. Run git fetch --tags first." }

$extracted = Join-Path ([System.IO.Path]::GetTempPath()) "linq2graphql-baseline-$([guid]::NewGuid())"
New-Item -ItemType Directory $extracted | Out-Null
try {
Write-Host "==> Copying the test clients of $Version" -ForegroundColor Cyan
$archive = Join-Path $extracted 'clients.tar'
git -C $repoRoot archive --format=tar -o $archive $Version -- @($clients | ForEach-Object { "test/$_" })
if ($LASTEXITCODE -ne 0) { throw 'git archive failed.' }
tar -xf $archive -C $extracted
if ($LASTEXITCODE -ne 0) { throw 'Extracting the test clients failed.' }

Remove-Item $frozenClients -Recurse -Force -ErrorAction Ignore
foreach ($client in $clients) {
$source = Join-Path $extracted "test/$client"
Get-ChildItem $source -Recurse -File -Filter '*.cs' | ForEach-Object {
$target = Join-Path $frozenClients $client ([System.IO.Path]::GetRelativePath($source, $_.FullName))
New-Item -ItemType Directory -Force (Split-Path $target) | Out-Null
Copy-Item $_.FullName $target
}
}
}
finally {
Remove-Item $extracted -Recurse -Force -ErrorAction Ignore
}

Write-Host "==> Setting Linq2GraphQLBaselineVersion to $Version" -ForegroundColor Cyan
$props = Join-Path $repoRoot 'Directory.Build.props'
$content = [System.IO.File]::ReadAllText($props)
$updated = [regex]::Replace($content, '<Linq2GraphQLBaselineVersion>[^<]*</Linq2GraphQLBaselineVersion>',
"<Linq2GraphQLBaselineVersion>$Version</Linq2GraphQLBaselineVersion>")
if ($updated -eq $content -and $content -notmatch [regex]::Escape(">$Version<")) {
throw "Linq2GraphQLBaselineVersion was not found in $props."
}
[System.IO.File]::WriteAllText($props, $updated)

Write-Host '==> Clearing the accepted differences' -ForegroundColor Cyan
[System.IO.File]::WriteAllText((Join-Path $compatRoot 'Tests/accepted-differences.json'), "{}`n")

Write-Host '==> Refreshing the baseline runner lock file' -ForegroundColor Cyan
dotnet restore (Join-Path $compatRoot 'Baseline/Linq2GraphQL.Compat.Baseline.csproj') --nologo
if ($LASTEXITCODE -ne 0) { throw 'Restore failed.' }

Write-Host '==> Done. Review and commit the result.' -ForegroundColor Green
4 changes: 2 additions & 2 deletions src/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<Project>
<!--
dotnet pack fails when a runtime package breaks the public API of this published release, so apps that update
the package without regenerating or recompiling their client keep working. Raise the baseline after a release.
the package without regenerating or recompiling their client keep working. The baseline is set in Directory.Build.props.
An intended break: pack with -p:ApiCompatGenerateSuppressionFile=true and commit CompatibilitySuppressions.xml.
-->
<PropertyGroup Condition="'$(PackAsTool)' != 'true'">
<EnablePackageValidation>true</EnablePackageValidation>
<PackageValidationBaselineVersion>1.0.67-prerelease</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>$(Linq2GraphQLBaselineVersion)</PackageValidationBaselineVersion>
</PropertyGroup>
</Project>
19 changes: 19 additions & 0 deletions test/Linq2GraphQL.Compat/Baseline/BaselineRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Text.Json;

namespace Linq2GraphQL.Compat;

internal static class BaselineRunner
{
public static async Task<int> Main(string[] args)
{
if (args.Length != 1)
{
await Console.Error.WriteLineAsync("Usage: Linq2GraphQL.Compat.Baseline <output file>");
return 2;
}

var records = await CompatRunner.RunAllAsync();
await File.WriteAllTextAsync(args[0], JsonSerializer.Serialize(records));
return 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<!--
Runs the compatibility scenarios against the published packages of the baseline release (Linq2GraphQLBaselineVersion)
and writes the records to the file named by its argument. Linq2GraphQL.Compat.Tests compares them with this code's.
-->
<Import Project="../Compat.props" />

<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<!-- Private, so the old packages do not flow into the tests project, which references this one for build order. -->
<PackageReference Include="Linq2GraphQL.Client" PrivateAssets="all" />
<PackageReference Include="Linq2GraphQL.Client.Subscriptions" PrivateAssets="all" />
</ItemGroup>
</Project>
Loading
Loading