gitforge/models/actions/run_job_test.go
Andreas Ahlenstorf 72b35c5a73 feat: display detailed action run diagnostics (#9966)
Forgejo Actions allows variables in `jobs.<job_id>.runs-on`. However, the action list [skips checking whether a suitable runner](c3412d0579/routers/web/repo/actions/actions.go (L114-L148)) is available if an expression contains variables. That hampers a user's ability to figure out whether an expression was evaluated correctly and why a job might not be picked up by an available runner.

This PR adds the ability to surface more complex and additional diagnostic information on the action view screen. Previously, only a job's status (waiting, running, ...) was displayed. Now, extended messages like "Waiting for a runner with the following labels: docker, trixie" are displayed with the possibility to show multiple messages simultaneously.

How it looked before:

![old](/attachments/019e4e83-d44e-4143-8df0-7fceb611a3bd)

How it looks after updating Forgejo without reloading the window:

![old-after-update](/attachments/e909af81-cf9e-4f44-a011-75585a2d1950)

How it looks afterwards with a single label:

![new-single-label](/attachments/72f4a862-e23d-4ab5-9f96-09545954f982)

How it looks afterwards with multiple labels:

![new-multiple-labels](/attachments/53036d7b-3589-4eeb-bad1-4da4cd5ff4b5)

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [x] in `web_src/js/*.test.js` if it can be unit tested.
  - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [ ] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/9966): <!--number 9966 --><!--line 0 --><!--description ZGlzcGxheSBkZXRhaWxlZCBhY3Rpb24gcnVuIGRpYWdub3N0aWNz-->display detailed action run diagnostics<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9966
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
Co-committed-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
2025-11-11 04:39:02 +01:00

150 lines
4.5 KiB
Go

// SPDX-License-Identifier: MIT
package actions
import (
"fmt"
"html/template"
"testing"
"forgejo.org/models/db"
"forgejo.org/models/unittest"
"forgejo.org/modules/translation"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestActionRunJob_ItRunsOn(t *testing.T) {
actionJob := ActionRunJob{RunsOn: []string{"ubuntu"}}
agentLabels := []string{"ubuntu", "node-20"}
assert.True(t, actionJob.ItRunsOn(agentLabels))
assert.False(t, actionJob.ItRunsOn([]string{}))
actionJob.RunsOn = append(actionJob.RunsOn, "node-20")
assert.True(t, actionJob.ItRunsOn(agentLabels))
agentLabels = []string{"ubuntu"}
assert.False(t, actionJob.ItRunsOn(agentLabels))
actionJob.RunsOn = []string{}
assert.False(t, actionJob.ItRunsOn(agentLabels))
}
func TestActionRunJob_HTMLURL(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
tests := []struct {
id int64
expected string
}{
{
id: 192,
expected: "https://try.gitea.io/user5/repo4/actions/runs/187/jobs/0/attempt/1",
},
{
id: 393,
expected: "https://try.gitea.io/user2/repo1/actions/runs/187/jobs/1/attempt/1",
},
{
id: 394,
expected: "https://try.gitea.io/user2/repo1/actions/runs/187/jobs/2/attempt/2",
},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("id=%d", tt.id), func(t *testing.T) {
var job ActionRunJob
has, err := db.GetEngine(t.Context()).Where("id=?", tt.id).Get(&job)
require.NoError(t, err)
require.True(t, has, "load ActionRunJob from fixture")
err = job.LoadAttributes(t.Context())
require.NoError(t, err)
url, err := job.HTMLURL(t.Context())
require.NoError(t, err)
assert.Equal(t, tt.expected, url)
})
}
}
func TestActionRunJob_StatusDiagnostics(t *testing.T) {
translation.InitLocales(t.Context())
english := translation.NewLocale("en-US")
tests := []struct {
name string
job ActionRunJob
expected []template.HTML
}{
{
name: "Unknown status",
job: ActionRunJob{RunsOn: []string{"windows"}, Status: StatusUnknown, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Unknown"},
},
{
name: "Waiting without labels",
job: ActionRunJob{RunsOn: []string{}, Status: StatusWaiting, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Waiting for a runner with the following labels: "},
},
{
name: "Waiting with one label",
job: ActionRunJob{RunsOn: []string{"freebsd"}, Status: StatusWaiting, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Waiting for a runner with the following label: freebsd"},
},
{
name: "Waiting with labels, no approval",
job: ActionRunJob{RunsOn: []string{"docker", "ubuntu"}, Status: StatusWaiting, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Waiting for a runner with the following labels: docker, ubuntu"},
},
{
name: "Waiting with labels, approval",
job: ActionRunJob{RunsOn: []string{"docker", "ubuntu"}, Status: StatusWaiting, Run: &ActionRun{NeedApproval: true}},
expected: []template.HTML{
"Waiting for a runner with the following labels: docker, ubuntu",
"Need approval to run workflows for fork pull request.",
},
},
{
name: "Running",
job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusRunning, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Running"},
},
{
name: "Success",
job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusSuccess, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Success"},
},
{
name: "Failure",
job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusFailure, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Failure"},
},
{
name: "Cancelled",
job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusCancelled, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Canceled"},
},
{
name: "Skipped",
job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusSkipped, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Skipped"},
},
{
name: "Blocked",
job: ActionRunJob{RunsOn: []string{"debian"}, Status: StatusBlocked, Run: &ActionRun{NeedApproval: false}},
expected: []template.HTML{"Blocked"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, tt.job.StatusDiagnostics(english))
})
}
}