Skip to content

Commit d8aba70

Browse files
Copilotxperiandri
andauthored
Publish CI test results into PR discussions, harden artifact download, and add PR preview publishing (#570)
Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Andrii Chebukin <XperiAndri@Outlook.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent 50b4569 commit d8aba70

14 files changed

Lines changed: 348 additions & 422 deletions
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: Publish Test Results
2+
3+
on:
4+
workflow_run:
5+
workflows: ["Build and Test"]
6+
types:
7+
- completed
8+
9+
permissions: {}
10+
11+
jobs:
12+
test-results:
13+
if: github.event.workflow_run.conclusion != 'skipped' && github.event.workflow_run.event == 'pull_request'
14+
name: Test Results
15+
runs-on: ubuntu-latest
16+
17+
permissions:
18+
actions: read
19+
checks: write
20+
pull-requests: write
21+
22+
steps:
23+
- name: Download event file
24+
uses: dawidd6/action-download-artifact@v6
25+
with:
26+
run_id: ${{ github.event.workflow_run.id }}
27+
path: artifacts
28+
name: EventFile
29+
30+
- name: Download test results
31+
uses: dawidd6/action-download-artifact@v6
32+
with:
33+
run_id: ${{ github.event.workflow_run.id }}
34+
path: test-results
35+
name: ^test-results-.*
36+
name_is_regexp: true
37+
if_no_artifact_found: warn
38+
39+
- name: Resolve event file path
40+
id: event_file
41+
shell: pwsh
42+
run: |
43+
$eventFilePath =
44+
Get-ChildItem -Path artifacts -Filter event.json -Recurse |
45+
Select-Object -First 1 -ExpandProperty FullName
46+
47+
if ($null -eq $eventFilePath) {
48+
throw "event.json file was not found in downloaded artifacts"
49+
}
50+
51+
"path=$eventFilePath" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
52+
53+
- name: Publish test results
54+
if: ${{ hashFiles('test-results/**/*.trx') != '' }}
55+
uses: EnricoMi/publish-unit-test-result-action@v2
56+
with:
57+
commit: ${{ github.event.workflow_run.head_sha }}
58+
comment_mode: always
59+
event_file: ${{ steps.event_file.outputs.path }}
60+
event_name: ${{ github.event.workflow_run.event }}
61+
files: "test-results/**/*.trx"

.github/workflows/pull-request.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,25 @@ env:
1414
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
1515
DOTNET_NOLOGO: true
1616

17+
permissions: {}
18+
1719
jobs:
20+
event_file:
21+
if: github.event_name == 'pull_request'
22+
name: Publish event file
23+
runs-on: ubuntu-latest
24+
permissions:
25+
contents: read
26+
steps:
27+
- name: Upload event file
28+
uses: actions/upload-artifact@v4
29+
with:
30+
name: EventFile
31+
path: ${{ github.event_path }}
32+
1833
build:
34+
permissions:
35+
contents: read
1936

2037
strategy:
2138
fail-fast: false
@@ -50,3 +67,10 @@ jobs:
5067

5168
- name: Build and run integration tests
5269
run: dotnet run --project build/Build.fsproj --launch-profile BuildAndTest
70+
71+
- name: Upload test results
72+
if: always()
73+
uses: actions/upload-artifact@v4
74+
with:
75+
name: test-results-${{ matrix.os }}
76+
path: test-results

build/Program.fs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,17 @@ let startGraphQLServer (project : string) port (streamRef : DataRef<Stream>) =
9898

9999
System.Threading.Thread.Sleep (2000)
100100

101-
let runTests (project : string) (args : string) =
101+
let runTests (project : string) =
102+
let projectName = Path.GetFileNameWithoutExtension project
103+
let resultsFileName = $"{projectName}.trx"
104+
102105
DotNet.test
103106
(fun options ->
104107
{
105108
options with
106109
NoBuild = true
110+
Logger = Some $"trx;LogFileName={resultsFileName}"
111+
ResultsDirectory = Some "test-results"
107112
Framework = Some DotNetMoniker
108113
Configuration = configuration
109114
MSBuildParams = {
@@ -164,18 +169,36 @@ let integrationTestsProjectPath =
164169

165170
let [<Literal>] UpdateIntrospectionFileTarget = "UpdateIntrospectionFile"
166171
Target.create UpdateIntrospectionFileTarget <| fun _ ->
167-
integrationTestsProjectPath
168-
|> DotNet.test (fun options -> {
169-
options with
170-
Framework = Some DotNetMoniker
171-
Configuration = configuration
172-
Common = { DotNetCli.setVersion options.Common with CustomParams = Some "--filter FullyQualifiedName~IntrospectionUpdateTests" }
173-
MSBuildParams = {
174-
options.MSBuildParams with
175-
DisableInternalBinLog = true
176-
Verbosity = Some Normal
172+
let projectName = Path.GetFileNameWithoutExtension integrationTestsProjectPath
173+
let resultsFileName = $"{projectName}.trx"
174+
175+
DotNet.test
176+
(fun options ->
177+
{
178+
options with
179+
NoBuild = true
180+
Logger = Some $"trx;LogFileName={resultsFileName}"
181+
ResultsDirectory = Some "test-results"
182+
Framework = Some DotNetMoniker
183+
Configuration = configuration
184+
Common = {
185+
options.Common with
186+
CustomParams = Some "--filter FullyQualifiedName~IntrospectionUpdateTests"
187+
}
188+
MSBuildParams = {
189+
options.MSBuildParams with
190+
DisableInternalBinLog = true
191+
Verbosity = Some Normal
192+
Properties = [
193+
if embedAll then
194+
("DebugType", "embedded")
195+
("EmbedAllSources", "true")
196+
]
197+
}
177198
}
178-
})
199+
|> _.WithRedirectOutput(true)
200+
|> _.WithCommon(DotNetCli.setVersion))
201+
integrationTestsProjectPath
179202

180203
let unitTestsProjectPath =
181204
"tests"
@@ -184,7 +207,7 @@ let unitTestsProjectPath =
184207

185208
let [<Literal>] RunUnitTestsTarget = "RunUnitTests"
186209
Target.create RunUnitTestsTarget <| fun _ ->
187-
runTests unitTestsProjectPath ""
210+
runTests unitTestsProjectPath
188211

189212
let prepareDocGen () =
190213
Shell.rm "docs/release-notes.md"

tests/FSharp.Data.GraphQL.IntegrationTests/IntrospectionUpdateTests.fs

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,66 +20,66 @@ let normalizeJsonDocument options (document : JsonDocument) =
2020
JsonDocument.Parse buffer
2121

2222
let parseAndNormalizeJsonAsync ct options stream = task {
23-
let! document = JsonDocument.ParseAsync (stream, cancellationToken = ct)
24-
return normalizeJsonDocument options document
25-
}
23+
let! document = JsonDocument.ParseAsync (stream, cancellationToken = ct)
24+
return normalizeJsonDocument options document
25+
}
2626

2727
let areSchemasEqual (document1 : JsonDocument) (document2 : JsonDocument) =
2828
let schema1 = document1.RootElement.GetProperty("data").GetProperty ("__schema")
2929
let schema2 = document2.RootElement.GetProperty("data").GetProperty ("__schema")
3030
schema1.GetRawText () = schema2.GetRawText ()
3131

3232
let readDestinationDocumentAsync ct (stream : FileStream) = task {
33-
try
34-
let! document = JsonDocument.ParseAsync (stream, cancellationToken = ct)
35-
return ValueSome document
36-
with :? JsonException ->
37-
return ValueNone
38-
}
33+
try
34+
let! document = JsonDocument.ParseAsync (stream, cancellationToken = ct)
35+
return ValueSome document
36+
with :? JsonException ->
37+
return ValueNone
38+
}
3939

4040
let updateIntrospectionFileAsync ct sourceStream = task {
41-
use destinationStream =
42-
new FileStream (introspectionFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read)
41+
use destinationStream =
42+
new FileStream (introspectionFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read)
4343

4444
let options = JsonWriterOptions (Indented = true)
45-
let! sourceDocument = parseAndNormalizeJsonAsync ct options sourceStream
46-
destinationStream.Seek (0L, SeekOrigin.Begin) |> ignore
47-
let! destinationDocument = readDestinationDocumentAsync ct destinationStream
45+
let! sourceDocument = parseAndNormalizeJsonAsync ct options sourceStream
46+
destinationStream.Seek (0L, SeekOrigin.Begin) |> ignore
47+
let! destinationDocument = readDestinationDocumentAsync ct destinationStream
4848

49-
let shouldUpdate =
50-
match destinationDocument with
51-
| ValueNone -> true
52-
| ValueSome document -> not (areSchemasEqual document sourceDocument)
49+
let shouldUpdate =
50+
match destinationDocument with
51+
| ValueNone -> true
52+
| ValueSome document -> not (areSchemasEqual document sourceDocument)
5353

54-
if shouldUpdate then
55-
destinationStream.Seek (0L, SeekOrigin.Begin) |> ignore
56-
destinationStream.SetLength 0
57-
use writer = new Utf8JsonWriter (destinationStream, options)
58-
sourceDocument.WriteTo writer
59-
writer.Flush ()
54+
if shouldUpdate then
55+
destinationStream.Seek (0L, SeekOrigin.Begin) |> ignore
56+
destinationStream.SetLength 0
57+
use writer = new Utf8JsonWriter (destinationStream, options)
58+
sourceDocument.WriteTo writer
59+
writer.Flush ()
6060

61-
return shouldUpdate
62-
}
61+
return shouldUpdate
62+
}
6363

6464
[<Fact>]
6565
let ``Get GraphQL introspection response returns schema`` () = task {
66-
use httpClient = TestHosts.createIntegrationHttpClient ()
66+
use httpClient = TestHosts.createIntegrationHttpClient ()
6767
let! response = httpClient.GetFromJsonAsync<JsonElement> ("/", CancellationToken.None)
6868
let schema = response.GetProperty("data").GetProperty ("__schema")
6969
Assert.NotEqual (Unchecked.defaultof<JsonElement>, schema)
70-
let hasErrors, _ = response.TryGetProperty "errors"
71-
Assert.False hasErrors
72-
}
70+
let hasErrors, _ = response.TryGetProperty "errors"
71+
Assert.False hasErrors
72+
}
7373

7474
[<Fact>]
7575
let ``Update integration introspection file when schema changes`` () = task {
76-
use httpClient = TestHosts.createIntegrationHttpClient ()
76+
use httpClient = TestHosts.createIntegrationHttpClient ()
7777
let! sourceStream = httpClient.GetStreamAsync ("/")
78-
let! wasUpdated = updateIntrospectionFileAsync CancellationToken.None sourceStream
78+
let! wasUpdated = updateIntrospectionFileAsync CancellationToken.None sourceStream
7979
Assert.True (File.Exists introspectionFilePath)
80-
if wasUpdated then
80+
if wasUpdated then
8181
let! sourceStreamSecondRun = httpClient.GetStreamAsync ("/")
82-
use sourceStreamForVerification = sourceStreamSecondRun
83-
let! wasUpdatedSecondRun = updateIntrospectionFileAsync CancellationToken.None sourceStreamForVerification
84-
Assert.False wasUpdatedSecondRun
85-
}
82+
use sourceStreamForVerification = sourceStreamSecondRun
83+
let! wasUpdatedSecondRun = updateIntrospectionFileAsync CancellationToken.None sourceStreamForVerification
84+
Assert.False wasUpdatedSecondRun
85+
}

0 commit comments

Comments
 (0)