0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-07-21 03:48:30 +02:00

improve tests and fix bugs

This commit is contained in:
Christopher Homberger 2025-05-03 17:44:23 +02:00
parent 27c9c279d8
commit 48379a2bb5
11 changed files with 548 additions and 28 deletions

View File

@ -80,19 +80,19 @@ type FindRunJobOptions struct {
func (opts FindRunJobOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RunID > 0 {
cond = cond.And(builder.Eq{"run_id": opts.RunID})
cond = cond.And(builder.Eq{"`action_run_job`.run_id": opts.RunID})
}
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
cond = cond.And(builder.Eq{"`action_run_job`.repo_id": opts.RepoID})
}
if opts.CommitSHA != "" {
cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA})
cond = cond.And(builder.Eq{"`action_run_job`.commit_sha": opts.CommitSHA})
}
if len(opts.Statuses) > 0 {
cond = cond.And(builder.In("status", opts.Statuses))
cond = cond.And(builder.In("`action_run_job`.status", opts.Statuses))
}
if opts.UpdatedBefore > 0 {
cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore})
cond = cond.And(builder.Lt{"`action_run_job`.updated": opts.UpdatedBefore})
}
return cond
}

View File

@ -77,25 +77,25 @@ type FindRunOptions struct {
func (opts FindRunOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
cond = cond.And(builder.Eq{"`action_run`.repo_id": opts.RepoID})
}
if opts.WorkflowID != "" {
cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowID})
cond = cond.And(builder.Eq{"`action_run`.workflow_id": opts.WorkflowID})
}
if opts.TriggerUserID > 0 {
cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID})
cond = cond.And(builder.Eq{"`action_run`.trigger_user_id": opts.TriggerUserID})
}
if opts.Approved {
cond = cond.And(builder.Gt{"approved_by": 0})
cond = cond.And(builder.Gt{"`action_run`.approved_by": 0})
}
if len(opts.Status) > 0 {
cond = cond.And(builder.In("status", opts.Status))
cond = cond.And(builder.In("`action_run`.status", opts.Status))
}
if opts.Ref != "" {
cond = cond.And(builder.Eq{"ref": opts.Ref})
cond = cond.And(builder.Eq{"`action_run`.ref": opts.Ref})
}
if opts.TriggerEvent != "" {
cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent})
cond = cond.And(builder.Eq{"`action_run`.trigger_event": opts.TriggerEvent})
}
return cond
}

View File

@ -9,6 +9,7 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@ -28,6 +29,7 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@ -47,6 +49,7 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@ -66,6 +69,7 @@
ref: "refs/heads/test"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@ -85,6 +89,7 @@
ref: "refs/heads/test"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@ -99,11 +104,12 @@
repo_id: 2
owner_id: 0
workflow_id: "test.yaml"
index: 191
index: 192
trigger_user_id: 1
ref: "refs/heads/test"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528

View File

@ -110,6 +110,20 @@ func ListWorkflowJobs(ctx *context.APIContext) {
// summary: Lists all jobs
// produces:
// - application/json
// parameters:
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/WorkflowJobsList"
@ -128,6 +142,35 @@ func ListWorkflowRuns(ctx *context.APIContext) {
// summary: Lists all runs
// produces:
// - application/json
// parameters:
// - name: event
// in: query
// description: workflow event name
// type: string
// required: false
// - name: branch
// in: query
// description: workflow branch
// type: string
// required: false
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: actor
// in: query
// description: triggered by user
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/WorkflowRunsList"

View File

@ -582,6 +582,19 @@ func (Action) ListWorkflowJobs(ctx *context.APIContext) {
// description: name of the organization
// type: string
// required: true
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/WorkflowJobsList"
@ -604,6 +617,34 @@ func (Action) ListWorkflowRuns(ctx *context.APIContext) {
// description: name of the organization
// type: string
// required: true
// - name: event
// in: query
// description: workflow event name
// type: string
// required: false
// - name: branch
// in: query
// description: workflow branch
// type: string
// required: false
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: actor
// in: query
// description: triggered by user
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/WorkflowRunsList"

View File

@ -668,6 +668,19 @@ func (Action) ListWorkflowJobs(ctx *context.APIContext) {
// description: name of the repository
// type: string
// required: true
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/WorkflowJobsList"
@ -714,6 +727,19 @@ func (Action) ListWorkflowRuns(ctx *context.APIContext) {
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: actor
// in: query
// description: triggered by user
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/ArtifactsList"
@ -1138,6 +1164,19 @@ func ListWorkflowRunJobs(ctx *context.APIContext) {
// description: runid of the workflow run
// type: integer
// required: true
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/WorkflowJobsList"

View File

@ -5,6 +5,7 @@ package shared
import (
"errors"
"fmt"
"net/http"
actions_model "code.gitea.io/gitea/models/actions"
@ -132,12 +133,24 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64) {
if ownerID != 0 && repoID != 0 {
setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
}
jobs, total, err := db.FindAndCount[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{
opts := actions_model.FindRunJobOptions{
OwnerID: ownerID,
RepoID: repoID,
RunID: runID,
ListOptions: utils.GetListOptions(ctx),
})
}
if statuses, ok := ctx.Req.URL.Query()["status"]; ok {
for _, status := range statuses {
values, err := convertToInternal(status)
if err != nil {
ctx.APIError(http.StatusBadRequest, fmt.Errorf("Invalid status %s", status))
return
}
opts.Statuses = append(opts.Statuses, values...)
}
}
jobs, total, err := db.FindAndCount[actions_model.ActionRunJob](ctx, opts)
if err != nil {
ctx.APIErrorInternal(err)
return
@ -172,22 +185,31 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64) {
ctx.JSON(http.StatusOK, &res)
}
func convertToInternal(s string) actions_model.Status {
func convertToInternal(s string) ([]actions_model.Status, error) {
switch s {
case "pending":
return actions_model.StatusBlocked
case "pending", "waiting", "requested", "action_required":
return []actions_model.Status{actions_model.StatusBlocked}, nil
case "queued":
return actions_model.StatusWaiting
return []actions_model.Status{actions_model.StatusWaiting}, nil
case "in_progress":
return actions_model.StatusRunning
return []actions_model.Status{actions_model.StatusRunning}, nil
case "completed":
return []actions_model.Status{
actions_model.StatusSuccess,
actions_model.StatusFailure,
actions_model.StatusSkipped,
actions_model.StatusCancelled,
}, nil
case "failure":
return actions_model.StatusFailure
return []actions_model.Status{actions_model.StatusFailure}, nil
case "success":
return actions_model.StatusSuccess
case "skipped":
return actions_model.StatusSkipped
return []actions_model.Status{actions_model.StatusSuccess}, nil
case "skipped", "neutral":
return []actions_model.Status{actions_model.StatusSkipped}, nil
case "cancelled", "timed_out":
return []actions_model.Status{actions_model.StatusCancelled}, nil
default:
return actions_model.StatusUnknown
return nil, fmt.Errorf("invalid status %s", s)
}
}
@ -213,8 +235,15 @@ func ListRuns(ctx *context.APIContext, ownerID, repoID int64) {
if branch := ctx.Req.URL.Query().Get("branch"); branch != "" {
opts.Ref = string(git.RefNameFromBranch(branch))
}
if status := ctx.Req.URL.Query().Get("status"); status != "" {
opts.Status = []actions_model.Status{convertToInternal(status)}
if statuses, ok := ctx.Req.URL.Query()["status"]; ok {
for _, status := range statuses {
values, err := convertToInternal(status)
if err != nil {
ctx.APIError(http.StatusBadRequest, fmt.Errorf("Invalid status %s", status))
return
}
opts.Status = append(opts.Status, values...)
}
}
if actor := ctx.Req.URL.Query().Get("actor"); actor != "" {
user, err := user_model.GetUserByName(ctx, actor)

View File

@ -108,11 +108,44 @@ func ListWorkflowRuns(ctx *context.APIContext) {
// swagger:operation GET /user/actions/runs user getUserWorkflowRuns
// ---
// summary: Get workflow runs
// parameters:
// - name: event
// in: query
// description: workflow event name
// type: string
// required: false
// - name: branch
// in: query
// description: workflow branch
// type: string
// required: false
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: actor
// in: query
// description: triggered by user
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// produces:
// - application/json
// responses:
// "200":
// "$ref": "#/responses/WorkflowRunsList"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.ListRuns(ctx, ctx.Doer.ID, 0)
}
@ -121,10 +154,29 @@ func ListWorkflowJobs(ctx *context.APIContext) {
// swagger:operation GET /user/actions/jobs user getUserWorkflowJobs
// ---
// summary: Get workflow jobs
// parameters:
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// produces:
// - application/json
// responses:
// "200":
// "$ref": "#/responses/WorkflowJobsList"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.ListJobs(ctx, ctx.Doer.ID, 0, 0)
}

View File

@ -306,6 +306,8 @@ func ToActionsStatus(status actions_model.Status) (string, string) {
conclusion = "cancelled"
case actions_model.StatusFailure:
conclusion = "failure"
case actions_model.StatusSkipped:
conclusion = "skipped"
}
}
return action, conclusion

View File

@ -85,6 +85,26 @@
],
"summary": "Lists all jobs",
"operationId": "listAdminWorkflowJobs",
"parameters": [
{
"type": "string",
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/WorkflowJobsList"
@ -210,6 +230,44 @@
],
"summary": "Lists all runs",
"operationId": "listAdminWorkflowRuns",
"parameters": [
{
"type": "string",
"description": "workflow event name",
"name": "event",
"in": "query"
},
{
"type": "string",
"description": "workflow branch",
"name": "branch",
"in": "query"
},
{
"type": "string",
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "triggered by user",
"name": "actor",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/WorkflowRunsList"
@ -1862,6 +1920,24 @@
"name": "org",
"in": "path",
"required": true
},
{
"type": "string",
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
@ -2052,6 +2128,42 @@
"name": "org",
"in": "path",
"required": true
},
{
"type": "string",
"description": "workflow event name",
"name": "event",
"in": "query"
},
{
"type": "string",
"description": "workflow branch",
"name": "branch",
"in": "query"
},
{
"type": "string",
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "triggered by user",
"name": "actor",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
@ -4653,6 +4765,24 @@
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
@ -4995,6 +5125,24 @@
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "triggered by user",
"name": "actor",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
@ -5139,6 +5287,24 @@
"name": "run",
"in": "path",
"required": true
},
{
"type": "string",
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
@ -17821,9 +17987,35 @@
],
"summary": "Get workflow jobs",
"operationId": "getUserWorkflowJobs",
"parameters": [
{
"type": "string",
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/WorkflowJobsList"
},
"400": {
"$ref": "#/responses/error"
},
"404": {
"$ref": "#/responses/notFound"
}
}
}
@ -17955,9 +18147,53 @@
],
"summary": "Get workflow runs",
"operationId": "getUserWorkflowRuns",
"parameters": [
{
"type": "string",
"description": "workflow event name",
"name": "event",
"in": "query"
},
{
"type": "string",
"description": "workflow branch",
"name": "branch",
"in": "query"
},
{
"type": "string",
"description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "triggered by user",
"name": "actor",
"in": "query"
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/WorkflowRunsList"
},
"400": {
"$ref": "#/responses/error"
},
"404": {
"$ref": "#/responses/notFound"
}
}
}

View File

@ -6,6 +6,7 @@ package integration
import (
"fmt"
"net/http"
"net/url"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
@ -44,6 +45,11 @@ func testAPIWorkflowRunBasic(t *testing.T, runAPIURL string, itemCount int, user
foundRun := false
for _, run := range runnerList.Entries {
verifyWorkflowRunCanbeFoundWithStatusFilter(t, runAPIURL, token, run.ID, "", run.Status, "", "")
verifyWorkflowRunCanbeFoundWithStatusFilter(t, runAPIURL, token, run.ID, run.Conclusion, "", "", "")
verifyWorkflowRunCanbeFoundWithStatusFilter(t, runAPIURL, token, run.ID, "", "", "", run.HeadBranch)
verifyWorkflowRunCanbeFoundWithStatusFilter(t, runAPIURL, token, run.ID, "", "", run.Event, "")
req := NewRequest(t, "GET", fmt.Sprintf("%s/%s", run.URL, "jobs")).AddTokenAuth(token)
jobsResp := MakeRequest(t, req, http.StatusOK)
jobList := api.ActionWorkflowJobsResponse{}
@ -53,6 +59,9 @@ func testAPIWorkflowRunBasic(t *testing.T, runAPIURL string, itemCount int, user
foundRun = true
assert.Len(t, jobList.Entries, 1)
for _, job := range jobList.Entries {
verifyWorkflowJobCanbeFoundWithStatusFilter(t, fmt.Sprintf("%s/%s", run.URL, "jobs"), token, job.ID, "", job.Status)
verifyWorkflowJobCanbeFoundWithStatusFilter(t, fmt.Sprintf("%s/%s", run.URL, "jobs"), token, job.ID, job.Conclusion, "")
req := NewRequest(t, "GET", job.URL).AddTokenAuth(token)
jobsResp := MakeRequest(t, req, http.StatusOK)
apiJob := api.ActionWorkflowJob{}
@ -64,5 +73,68 @@ func testAPIWorkflowRunBasic(t *testing.T, runAPIURL string, itemCount int, user
}
}
}
assert.True(t, foundRun, "Expected to find run with ID 802")
assert.True(t, foundRun, "Expected to find run with ID %d", runID)
}
func verifyWorkflowRunCanbeFoundWithStatusFilter(t *testing.T, runAPIURL, token string, id int64, conclusion, status, event, branch string) {
filter := url.Values{}
if conclusion != "" {
filter.Add("status", conclusion)
}
if status != "" {
filter.Add("status", status)
}
if event != "" {
filter.Set("event", event)
}
if branch != "" {
filter.Set("branch", branch)
}
req := NewRequest(t, "GET", runAPIURL+"?"+filter.Encode()).AddTokenAuth(token)
runResp := MakeRequest(t, req, http.StatusOK)
runList := api.ActionWorkflowRunsResponse{}
DecodeJSON(t, runResp, &runList)
found := false
for _, run := range runList.Entries {
if conclusion != "" {
assert.Equal(t, conclusion, run.Conclusion)
}
if status != "" {
assert.Equal(t, status, run.Status)
}
if event != "" {
assert.Equal(t, event, run.Event)
}
if branch != "" {
assert.Equal(t, branch, run.HeadBranch)
}
found = found || run.ID == id
}
assert.True(t, found, "Expected to find run with ID %d", id)
}
func verifyWorkflowJobCanbeFoundWithStatusFilter(t *testing.T, runAPIURL, token string, id int64, conclusion, status string) {
filter := conclusion
if filter == "" {
filter = status
}
if filter == "" {
return
}
req := NewRequest(t, "GET", runAPIURL+"?status="+filter).AddTokenAuth(token)
jobListResp := MakeRequest(t, req, http.StatusOK)
jobList := api.ActionWorkflowJobsResponse{}
DecodeJSON(t, jobListResp, &jobList)
found := false
for _, job := range jobList.Entries {
if conclusion != "" {
assert.Equal(t, conclusion, job.Conclusion)
} else {
assert.Equal(t, status, job.Status)
}
found = found || job.ID == id
}
assert.True(t, found, "Expected to find job with ID %d", id)
}