mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-23 12:23:43 +02:00
Merge branch 'main' into main
Signed-off-by: Karthik Bhandary <34509856+karthikbhandary2@users.noreply.github.com>
This commit is contained in:
125 files changed
+3959
-3309
No files matched your search
@@ -11,23 +11,28 @@ import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers/api/actions"
|
||||
actions_service "code.gitea.io/gitea/services/actions"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
@@ -48,15 +53,18 @@ func TestActionsArtifactV4UploadSingleFile(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
version int32
|
||||
blockID bool
|
||||
noLength bool
|
||||
append int
|
||||
name string
|
||||
version int32
|
||||
contentType string
|
||||
blockID bool
|
||||
noLength bool
|
||||
append int
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "artifact",
|
||||
version: 4,
|
||||
path: "artifact.zip",
|
||||
},
|
||||
{
|
||||
name: "artifact2",
|
||||
@@ -98,6 +106,23 @@ func TestActionsArtifactV4UploadSingleFile(t *testing.T) {
|
||||
append: 4,
|
||||
blockID: true,
|
||||
},
|
||||
{
|
||||
name: "artifact9.json",
|
||||
version: 7,
|
||||
contentType: "application/json",
|
||||
},
|
||||
{
|
||||
name: "artifact10",
|
||||
version: 7,
|
||||
contentType: "application/zip",
|
||||
path: "artifact10.zip",
|
||||
},
|
||||
{
|
||||
name: "artifact11.zip",
|
||||
version: 7,
|
||||
contentType: "application/zip",
|
||||
path: "artifact11.zip",
|
||||
},
|
||||
}
|
||||
|
||||
for _, entry := range table {
|
||||
@@ -108,6 +133,7 @@ func TestActionsArtifactV4UploadSingleFile(t *testing.T) {
|
||||
Name: entry.name,
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
MimeType: util.Iif(entry.contentType != "", wrapperspb.String(entry.contentType), nil),
|
||||
})).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var uploadResp actions.CreateArtifactResponse
|
||||
@@ -120,9 +146,8 @@ func TestActionsArtifactV4UploadSingleFile(t *testing.T) {
|
||||
blocks := make([]string, 0, util.Iif(entry.blockID, entry.append+1, 0))
|
||||
|
||||
// get upload url
|
||||
idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
|
||||
for i := range entry.append + 1 {
|
||||
url := uploadResp.SignedUploadUrl[idx:]
|
||||
url := uploadResp.SignedUploadUrl
|
||||
// See https://learn.microsoft.com/en-us/rest/api/storageservices/append-block
|
||||
// See https://learn.microsoft.com/en-us/rest/api/storageservices/put-block
|
||||
if entry.blockID {
|
||||
@@ -146,7 +171,7 @@ func TestActionsArtifactV4UploadSingleFile(t *testing.T) {
|
||||
|
||||
if entry.blockID && entry.append > 0 {
|
||||
// https://learn.microsoft.com/en-us/rest/api/storageservices/put-block-list
|
||||
blockListURL := uploadResp.SignedUploadUrl[idx:] + "&comp=blocklist"
|
||||
blockListURL := uploadResp.SignedUploadUrl + "&comp=blocklist"
|
||||
// upload artifact blockList
|
||||
blockList := &actions.BlockList{
|
||||
Latest: blocks,
|
||||
@@ -174,6 +199,19 @@ func TestActionsArtifactV4UploadSingleFile(t *testing.T) {
|
||||
var finalizeResp actions.FinalizeArtifactResponse
|
||||
protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
|
||||
assert.True(t, finalizeResp.Ok)
|
||||
|
||||
artifact := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionArtifact{ID: finalizeResp.ArtifactId})
|
||||
if entry.contentType != "" {
|
||||
assert.Equal(t, entry.contentType, artifact.ContentEncodingOrType)
|
||||
} else {
|
||||
assert.Equal(t, "application/zip", artifact.ContentEncodingOrType)
|
||||
}
|
||||
if entry.path != "" {
|
||||
assert.Equal(t, entry.path, artifact.ArtifactPath)
|
||||
}
|
||||
assert.Equal(t, actions_model.ArtifactStatusUploadConfirmed, artifact.Status)
|
||||
assert.Equal(t, int64(entry.append+1)*1024, artifact.FileSize)
|
||||
assert.Equal(t, int64(entry.append+1)*1024, artifact.FileCompressedSize)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -198,8 +236,7 @@ func TestActionsArtifactV4UploadSingleFileWrongChecksum(t *testing.T) {
|
||||
assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
|
||||
|
||||
// get upload url
|
||||
idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
|
||||
url := uploadResp.SignedUploadUrl[idx:] + "&comp=block"
|
||||
url := uploadResp.SignedUploadUrl + "&comp=block"
|
||||
|
||||
// upload artifact chunk
|
||||
body := strings.Repeat("B", 1024)
|
||||
@@ -243,8 +280,7 @@ func TestActionsArtifactV4UploadSingleFileWithRetentionDays(t *testing.T) {
|
||||
assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
|
||||
|
||||
// get upload url
|
||||
idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
|
||||
url := uploadResp.SignedUploadUrl[idx:] + "&comp=block"
|
||||
url := uploadResp.SignedUploadUrl + "&comp=block"
|
||||
|
||||
// upload artifact chunk
|
||||
body := strings.Repeat("A", 1024)
|
||||
@@ -290,9 +326,8 @@ func TestActionsArtifactV4UploadSingleFileWithPotentialHarmfulBlockID(t *testing
|
||||
assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
|
||||
|
||||
// get upload urls
|
||||
idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
|
||||
url := uploadResp.SignedUploadUrl[idx:] + "&comp=block&blockid=%2f..%2fmyfile"
|
||||
blockListURL := uploadResp.SignedUploadUrl[idx:] + "&comp=blocklist"
|
||||
url := uploadResp.SignedUploadUrl + "&comp=block&blockid=%2f..%2fmyfile"
|
||||
blockListURL := uploadResp.SignedUploadUrl + "&comp=blocklist"
|
||||
|
||||
// upload artifact chunk
|
||||
body := strings.Repeat("A", 1024)
|
||||
@@ -339,63 +374,126 @@ func TestActionsArtifactV4UploadSingleFileWithChunksOutOfOrder(t *testing.T) {
|
||||
token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// acquire artifact upload url
|
||||
req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact", toProtoJSON(&actions.CreateArtifactRequest{
|
||||
Version: 4,
|
||||
Name: "artifactWithChunksOutOfOrder",
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
})).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var uploadResp actions.CreateArtifactResponse
|
||||
protojson.Unmarshal(resp.Body.Bytes(), &uploadResp)
|
||||
assert.True(t, uploadResp.Ok)
|
||||
assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
|
||||
|
||||
// get upload urls
|
||||
idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
|
||||
block1URL := uploadResp.SignedUploadUrl[idx:] + "&comp=block&blockid=block1"
|
||||
block2URL := uploadResp.SignedUploadUrl[idx:] + "&comp=block&blockid=block2"
|
||||
blockListURL := uploadResp.SignedUploadUrl[idx:] + "&comp=blocklist"
|
||||
|
||||
// upload artifact chunks
|
||||
bodyb := strings.Repeat("B", 1024)
|
||||
req = NewRequestWithBody(t, "PUT", block2URL, strings.NewReader(bodyb))
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
bodya := strings.Repeat("A", 1024)
|
||||
req = NewRequestWithBody(t, "PUT", block1URL, strings.NewReader(bodya))
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
// upload artifact blockList
|
||||
blockList := &actions.BlockList{
|
||||
Latest: []string{
|
||||
"block1",
|
||||
"block2",
|
||||
},
|
||||
table := []struct {
|
||||
name string
|
||||
artifactName string
|
||||
serveDirect bool
|
||||
contentType string
|
||||
}{
|
||||
{name: "Upload-Zip", artifactName: "artifact-v4-upload", contentType: ""},
|
||||
{name: "Upload-Pdf", artifactName: "report-upload.pdf", contentType: "application/pdf"},
|
||||
{name: "Upload-Html", artifactName: "report-upload.html", contentType: "application/html"},
|
||||
{name: "ServeDirect-Zip", artifactName: "artifact-v4-upload-serve-direct", contentType: "", serveDirect: true},
|
||||
{name: "ServeDirect-Pdf", artifactName: "report-upload-serve-direct.pdf", contentType: "application/pdf", serveDirect: true},
|
||||
{name: "ServeDirect-Html", artifactName: "report-upload-serve-direct.html", contentType: "application/html", serveDirect: true},
|
||||
}
|
||||
rawBlockList, err := xml.Marshal(blockList)
|
||||
assert.NoError(t, err)
|
||||
req = NewRequestWithBody(t, "PUT", blockListURL, bytes.NewReader(rawBlockList))
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
t.Logf("Create artifact confirm")
|
||||
for _, entry := range table {
|
||||
t.Run(entry.name, func(t *testing.T) {
|
||||
// Only AzureBlobStorageType supports ServeDirect Uploads
|
||||
switch setting.Actions.ArtifactStorage.Type {
|
||||
case setting.AzureBlobStorageType:
|
||||
defer test.MockVariableValue(&setting.Actions.ArtifactStorage.AzureBlobConfig.ServeDirect, entry.serveDirect)()
|
||||
default:
|
||||
if entry.serveDirect {
|
||||
t.Skip()
|
||||
}
|
||||
}
|
||||
// acquire artifact upload url
|
||||
req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact", toProtoJSON(&actions.CreateArtifactRequest{
|
||||
Version: util.Iif[int32](entry.contentType != "", 7, 4),
|
||||
Name: entry.artifactName,
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
MimeType: util.Iif(entry.contentType != "", wrapperspb.String(entry.contentType), nil),
|
||||
})).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var uploadResp actions.CreateArtifactResponse
|
||||
protojson.Unmarshal(resp.Body.Bytes(), &uploadResp)
|
||||
assert.True(t, uploadResp.Ok)
|
||||
if !entry.serveDirect {
|
||||
assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
|
||||
}
|
||||
|
||||
sha := sha256.Sum256([]byte(bodya + bodyb))
|
||||
// get upload urls
|
||||
block1URL := uploadResp.SignedUploadUrl + "&comp=block&blockid=" + base64.RawURLEncoding.EncodeToString([]byte("block1"))
|
||||
block2URL := uploadResp.SignedUploadUrl + "&comp=block&blockid=" + base64.RawURLEncoding.EncodeToString([]byte("block2"))
|
||||
blockListURL := uploadResp.SignedUploadUrl + "&comp=blocklist"
|
||||
|
||||
// confirm artifact upload
|
||||
req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact", toProtoJSON(&actions.FinalizeArtifactRequest{
|
||||
Name: "artifactWithChunksOutOfOrder",
|
||||
Size: 2048,
|
||||
Hash: wrapperspb.String("sha256:" + hex.EncodeToString(sha[:])),
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
})).
|
||||
AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
var finalizeResp actions.FinalizeArtifactResponse
|
||||
protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
|
||||
assert.True(t, finalizeResp.Ok)
|
||||
// upload artifact chunks
|
||||
bodyb := strings.Repeat("B", 1024)
|
||||
req = NewRequestWithBody(t, "PUT", block2URL, strings.NewReader(bodyb))
|
||||
if entry.serveDirect {
|
||||
req.Request.RequestURI = ""
|
||||
nresp, err := http.DefaultClient.Do(req.Request)
|
||||
require.NoError(t, err)
|
||||
nresp.Body.Close()
|
||||
require.Equal(t, http.StatusCreated, nresp.StatusCode)
|
||||
} else {
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
}
|
||||
|
||||
bodya := strings.Repeat("A", 1024)
|
||||
req = NewRequestWithBody(t, "PUT", block1URL, strings.NewReader(bodya))
|
||||
if entry.serveDirect {
|
||||
req.Request.RequestURI = ""
|
||||
nresp, err := http.DefaultClient.Do(req.Request)
|
||||
require.NoError(t, err)
|
||||
nresp.Body.Close()
|
||||
require.Equal(t, http.StatusCreated, nresp.StatusCode)
|
||||
} else {
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
}
|
||||
|
||||
// upload artifact blockList
|
||||
blockList := &actions.BlockList{
|
||||
Latest: []string{
|
||||
base64.RawURLEncoding.EncodeToString([]byte("block1")),
|
||||
base64.RawURLEncoding.EncodeToString([]byte("block2")),
|
||||
},
|
||||
}
|
||||
rawBlockList, err := xml.Marshal(blockList)
|
||||
assert.NoError(t, err)
|
||||
req = NewRequestWithBody(t, "PUT", blockListURL, bytes.NewReader(rawBlockList))
|
||||
if entry.serveDirect {
|
||||
req.Request.RequestURI = ""
|
||||
nresp, err := http.DefaultClient.Do(req.Request)
|
||||
require.NoError(t, err)
|
||||
nresp.Body.Close()
|
||||
require.Equal(t, http.StatusCreated, nresp.StatusCode)
|
||||
} else {
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
}
|
||||
|
||||
t.Logf("Create artifact confirm")
|
||||
|
||||
sha := sha256.Sum256([]byte(bodya + bodyb))
|
||||
|
||||
// confirm artifact upload
|
||||
req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact", toProtoJSON(&actions.FinalizeArtifactRequest{
|
||||
Name: entry.artifactName,
|
||||
Size: 2048,
|
||||
Hash: wrapperspb.String("sha256:" + hex.EncodeToString(sha[:])),
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
})).
|
||||
AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
var finalizeResp actions.FinalizeArtifactResponse
|
||||
protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
|
||||
assert.True(t, finalizeResp.Ok)
|
||||
|
||||
artifact := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionArtifact{ID: finalizeResp.ArtifactId})
|
||||
if entry.contentType != "" {
|
||||
assert.Equal(t, entry.contentType, artifact.ContentEncodingOrType)
|
||||
} else {
|
||||
assert.Equal(t, "application/zip", artifact.ContentEncodingOrType)
|
||||
}
|
||||
assert.Equal(t, actions_model.ArtifactStatusUploadConfirmed, artifact.Status)
|
||||
assert.Equal(t, int64(2048), artifact.FileSize)
|
||||
assert.Equal(t, int64(2048), artifact.FileCompressedSize)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionsArtifactV4DownloadSingle(t *testing.T) {
|
||||
@@ -404,33 +502,97 @@ func TestActionsArtifactV4DownloadSingle(t *testing.T) {
|
||||
token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// list artifacts by name
|
||||
req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts", toProtoJSON(&actions.ListArtifactsRequest{
|
||||
NameFilter: wrapperspb.String("artifact-v4-download"),
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
})).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var listResp actions.ListArtifactsResponse
|
||||
protojson.Unmarshal(resp.Body.Bytes(), &listResp)
|
||||
assert.Len(t, listResp.Artifacts, 1)
|
||||
table := []struct {
|
||||
Name string
|
||||
ArtifactName string
|
||||
FileName string
|
||||
ServeDirect bool
|
||||
ContentType string
|
||||
ContentDisposition string
|
||||
}{
|
||||
{Name: "Download-Zip", ArtifactName: "artifact-v4-download", FileName: "artifact-v4-download.zip", ContentType: "application/zip"},
|
||||
{Name: "Download-Pdf", ArtifactName: "report.pdf", FileName: "report.pdf", ContentType: "application/pdf"},
|
||||
{Name: "Download-Html", ArtifactName: "report.html", FileName: "report.html", ContentType: "application/html"},
|
||||
{Name: "ServeDirect-Zip", ArtifactName: "artifact-v4-download", FileName: "artifact-v4-download.zip", ContentType: "application/zip", ServeDirect: true},
|
||||
{Name: "ServeDirect-Pdf", ArtifactName: "report.pdf", FileName: "report.pdf", ContentType: "application/pdf", ServeDirect: true},
|
||||
{Name: "ServeDirect-Html", ArtifactName: "report.html", FileName: "report.html", ContentType: "application/html", ServeDirect: true},
|
||||
}
|
||||
|
||||
// acquire artifact download url
|
||||
req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/GetSignedArtifactURL", toProtoJSON(&actions.GetSignedArtifactURLRequest{
|
||||
Name: "artifact-v4-download",
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
})).
|
||||
AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
var finalizeResp actions.GetSignedArtifactURLResponse
|
||||
protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
|
||||
assert.NotEmpty(t, finalizeResp.SignedUrl)
|
||||
for _, entry := range table {
|
||||
t.Run(entry.Name, func(t *testing.T) {
|
||||
switch setting.Actions.ArtifactStorage.Type {
|
||||
case setting.AzureBlobStorageType:
|
||||
defer test.MockVariableValue(&setting.Actions.ArtifactStorage.AzureBlobConfig.ServeDirect, entry.ServeDirect)()
|
||||
case setting.MinioStorageType:
|
||||
defer test.MockVariableValue(&setting.Actions.ArtifactStorage.MinioConfig.ServeDirect, entry.ServeDirect)()
|
||||
default:
|
||||
if entry.ServeDirect {
|
||||
t.Skip()
|
||||
}
|
||||
}
|
||||
|
||||
req = NewRequest(t, "GET", finalizeResp.SignedUrl)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
body := strings.Repeat("D", 1024)
|
||||
assert.Equal(t, body, resp.Body.String())
|
||||
// list artifacts by name
|
||||
req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts", toProtoJSON(&actions.ListArtifactsRequest{
|
||||
NameFilter: wrapperspb.String(entry.ArtifactName),
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
})).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var listResp actions.ListArtifactsResponse
|
||||
require.NoError(t, protojson.Unmarshal(resp.Body.Bytes(), &listResp))
|
||||
require.Len(t, listResp.Artifacts, 1)
|
||||
|
||||
// list artifacts by id
|
||||
req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts", toProtoJSON(&actions.ListArtifactsRequest{
|
||||
IdFilter: wrapperspb.Int64(listResp.Artifacts[0].DatabaseId),
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
})).AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
require.NoError(t, protojson.Unmarshal(resp.Body.Bytes(), &listResp))
|
||||
assert.Len(t, listResp.Artifacts, 1)
|
||||
|
||||
// acquire artifact download url
|
||||
req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/GetSignedArtifactURL", toProtoJSON(&actions.GetSignedArtifactURLRequest{
|
||||
Name: entry.ArtifactName,
|
||||
WorkflowRunBackendId: "792",
|
||||
WorkflowJobRunBackendId: "193",
|
||||
})).
|
||||
AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
var finalizeResp actions.GetSignedArtifactURLResponse
|
||||
require.NoError(t, protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp))
|
||||
assert.NotEmpty(t, finalizeResp.SignedUrl)
|
||||
|
||||
body := strings.Repeat("D", 1024)
|
||||
var contentDisposition string
|
||||
if entry.ServeDirect {
|
||||
externalReq, err := http.NewRequestWithContext(t.Context(), http.MethodGet, finalizeResp.SignedUrl, nil)
|
||||
require.NoError(t, err)
|
||||
externalResp, err := http.DefaultClient.Do(externalReq)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, externalResp.StatusCode)
|
||||
assert.Equal(t, entry.ContentType, externalResp.Header.Get("Content-Type"))
|
||||
contentDisposition = externalResp.Header.Get("Content-Disposition")
|
||||
buf := make([]byte, 1024)
|
||||
n, err := io.ReadAtLeast(externalResp.Body, buf, len(buf))
|
||||
externalResp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(buf), n)
|
||||
assert.Equal(t, body, string(buf))
|
||||
} else {
|
||||
req = NewRequest(t, "GET", finalizeResp.SignedUrl)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, entry.ContentType, resp.Header().Get("Content-Type"))
|
||||
contentDisposition = resp.Header().Get("Content-Disposition")
|
||||
assert.Equal(t, body, resp.Body.String())
|
||||
}
|
||||
disposition, param, err := mime.ParseMediaType(contentDisposition)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "inline", disposition)
|
||||
assert.Equal(t, entry.FileName, param["filename"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionsArtifactV4RunDownloadSinglePublicApi(t *testing.T) {
|
||||
@@ -561,7 +723,7 @@ func TestActionsArtifactV4ListAndGetPublicApi(t *testing.T) {
|
||||
for _, artifact := range listResp.Entries {
|
||||
assert.Contains(t, artifact.URL, fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), artifact.ID))
|
||||
assert.Contains(t, artifact.ArchiveDownloadURL, fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip", repo.FullName(), artifact.ID))
|
||||
req = NewRequestWithBody(t, "GET", listResp.Entries[0].URL, nil).
|
||||
req = NewRequestWithBody(t, "GET", artifact.URL, nil).
|
||||
AddTokenAuth(token)
|
||||
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
@@ -141,34 +141,45 @@ func TestAPIOrgGeneral(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("OrgEdit", func(t *testing.T) {
|
||||
org := api.EditOrgOption{
|
||||
FullName: "Org3 organization new full name",
|
||||
Description: "A new description",
|
||||
Website: "https://try.gitea.io/new",
|
||||
Location: "Beijing",
|
||||
Visibility: "private",
|
||||
}
|
||||
req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).AddTokenAuth(user1Token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"})
|
||||
assert.NotEqual(t, api.VisibleTypeLimited, org3.Visibility)
|
||||
|
||||
var apiOrg api.Organization
|
||||
DecodeJSON(t, resp, &apiOrg)
|
||||
org3Edit := api.EditOrgOption{
|
||||
FullName: new("new full name"),
|
||||
Description: new("new description"),
|
||||
Website: new("https://org3-new-website.example.com"),
|
||||
Location: new("new location"),
|
||||
Visibility: new("limited"),
|
||||
Email: new("org3-new-email@example.com"),
|
||||
}
|
||||
req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org3Edit).AddTokenAuth(user1Token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
apiOrg := DecodeJSON(t, resp, &api.Organization{})
|
||||
|
||||
assert.Equal(t, "org3", apiOrg.Name)
|
||||
assert.Equal(t, org.FullName, apiOrg.FullName)
|
||||
assert.Equal(t, org.Description, apiOrg.Description)
|
||||
assert.Equal(t, org.Website, apiOrg.Website)
|
||||
assert.Equal(t, org.Location, apiOrg.Location)
|
||||
assert.Equal(t, org.Visibility, apiOrg.Visibility)
|
||||
assert.Equal(t, *org3Edit.FullName, apiOrg.FullName)
|
||||
assert.Equal(t, *org3Edit.Description, apiOrg.Description)
|
||||
assert.Equal(t, *org3Edit.Website, apiOrg.Website)
|
||||
assert.Equal(t, *org3Edit.Location, apiOrg.Location)
|
||||
assert.Equal(t, *org3Edit.Visibility, apiOrg.Visibility)
|
||||
assert.Equal(t, *org3Edit.Email, apiOrg.Email)
|
||||
org3 = unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"})
|
||||
assert.Equal(t, api.VisibleTypeLimited, org3.Visibility)
|
||||
|
||||
// empty email can clear the email, nil fields won't change the settings
|
||||
req = NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &api.EditOrgOption{
|
||||
Email: new(""),
|
||||
}).AddTokenAuth(user1Token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
apiOrg = DecodeJSON(t, resp, &api.Organization{})
|
||||
assert.Equal(t, *org3Edit.FullName, apiOrg.FullName)
|
||||
assert.Equal(t, *org3Edit.Visibility, apiOrg.Visibility)
|
||||
assert.Empty(t, apiOrg.Email)
|
||||
})
|
||||
|
||||
t.Run("OrgEditBadVisibility", func(t *testing.T) {
|
||||
t.Run("OrgEditInvalidVisibility", func(t *testing.T) {
|
||||
org := api.EditOrgOption{
|
||||
FullName: "Org3 organization new full name",
|
||||
Description: "A new description",
|
||||
Website: "https://try.gitea.io/new",
|
||||
Location: "Beijing",
|
||||
Visibility: "badvisibility",
|
||||
Visibility: new("invalid-visibility"),
|
||||
}
|
||||
req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).AddTokenAuth(user1Token)
|
||||
MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/cmd"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -38,13 +37,14 @@ func Test_CmdKeys(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// FIXME: this test is not quite right. Each "command run" always re-initializes settings
|
||||
defer test.MockVariableValue(&cmd.CmdKeys.Before, nil)() // don't re-initialize logger during the test
|
||||
keysCmd := cmd.NewKeysCommand()
|
||||
keysCmd.Before = nil // don't re-initialize logger during the test
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
app := &cli.Command{
|
||||
Writer: &stdout,
|
||||
ErrWriter: &stderr,
|
||||
Commands: []*cli.Command{cmd.CmdKeys},
|
||||
Commands: []*cli.Command{keysCmd},
|
||||
}
|
||||
err := app.Run(t.Context(), append([]string{"prog"}, tt.args...))
|
||||
if tt.wantErr {
|
||||
|
||||
@@ -8,86 +8,66 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDownloadByID(t *testing.T) {
|
||||
func TestDownloadRepoContent(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Request raw blob
|
||||
req := NewRequest(t, "GET", "/user2/repo1/raw/blob/4b4851ad51df6a7d9f25c979345979eaeb5b349f")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
t.Run("RawBlob", func(t *testing.T) {
|
||||
req := NewRequest(t, "GET", "/user2/repo1/raw/blob/4b4851ad51df6a7d9f25c979345979eaeb5b349f")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, "# repo1\n\nDescription for repo1", resp.Body.String())
|
||||
})
|
||||
|
||||
assert.Equal(t, "# repo1\n\nDescription for repo1", resp.Body.String())
|
||||
}
|
||||
|
||||
func TestDownloadByIDForSVGUsesSecureHeaders(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Request raw blob
|
||||
req := NewRequest(t, "GET", "/user2/repo2/raw/blob/6395b68e1feebb1e4c657b4f9f6ba2676a283c0b")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
assert.Equal(t, "default-src 'none'; style-src 'unsafe-inline'; sandbox", resp.Header().Get("Content-Security-Policy"))
|
||||
assert.Equal(t, "image/svg+xml", resp.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "nosniff", resp.Header().Get("X-Content-Type-Options"))
|
||||
}
|
||||
|
||||
func TestDownloadByIDMedia(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Request raw blob
|
||||
req := NewRequest(t, "GET", "/user2/repo1/media/blob/4b4851ad51df6a7d9f25c979345979eaeb5b349f")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
assert.Equal(t, "# repo1\n\nDescription for repo1", resp.Body.String())
|
||||
}
|
||||
|
||||
func TestDownloadByIDMediaForSVGUsesSecureHeaders(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Request raw blob
|
||||
req := NewRequest(t, "GET", "/user2/repo2/media/blob/6395b68e1feebb1e4c657b4f9f6ba2676a283c0b")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
assert.Equal(t, "default-src 'none'; style-src 'unsafe-inline'; sandbox", resp.Header().Get("Content-Security-Policy"))
|
||||
assert.Equal(t, "image/svg+xml", resp.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "nosniff", resp.Header().Get("X-Content-Type-Options"))
|
||||
}
|
||||
|
||||
func TestDownloadRawTextFileWithoutMimeTypeMapping(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
req := NewRequest(t, "GET", "/user2/repo2/raw/branch/master/test.xml")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
assert.Equal(t, "text/plain; charset=utf-8", resp.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestDownloadRawTextFileWithMimeTypeMapping(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
setting.MimeTypeMap.Map[".xml"] = "text/xml"
|
||||
setting.MimeTypeMap.Enabled = true
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
req := NewRequest(t, "GET", "/user2/repo2/raw/branch/master/test.xml")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
assert.Equal(t, "text/xml; charset=utf-8", resp.Header().Get("Content-Type"))
|
||||
|
||||
delete(setting.MimeTypeMap.Map, ".xml")
|
||||
setting.MimeTypeMap.Enabled = false
|
||||
t.Run("SVGUsesSecureHeaders", func(t *testing.T) {
|
||||
req := NewRequest(t, "GET", "/user2/repo2/raw/blob/6395b68e1feebb1e4c657b4f9f6ba2676a283c0b")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, "default-src 'none'; style-src 'unsafe-inline'; sandbox", resp.Header().Get("Content-Security-Policy"))
|
||||
assert.Equal(t, "image/svg+xml", resp.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "nosniff", resp.Header().Get("X-Content-Type-Options"))
|
||||
})
|
||||
|
||||
t.Run("MediaBlob", func(t *testing.T) {
|
||||
req := NewRequest(t, "GET", "/user2/repo1/media/blob/4b4851ad51df6a7d9f25c979345979eaeb5b349f")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, "# repo1\n\nDescription for repo1", resp.Body.String())
|
||||
})
|
||||
|
||||
t.Run("MediaSVGUsesSecureHeaders", func(t *testing.T) {
|
||||
req := NewRequest(t, "GET", "/user2/repo2/media/blob/6395b68e1feebb1e4c657b4f9f6ba2676a283c0b")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, "default-src 'none'; style-src 'unsafe-inline'; sandbox", resp.Header().Get("Content-Security-Policy"))
|
||||
assert.Equal(t, "image/svg+xml", resp.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "nosniff", resp.Header().Get("X-Content-Type-Options"))
|
||||
})
|
||||
|
||||
t.Run("MimeTypeMap", func(t *testing.T) {
|
||||
req := NewRequest(t, "GET", "/user2/repo2/raw/branch/master/test.xml")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
// although the file is a valid XML file, it is served as "text/plain" to avoid site content spamming (the same to "text/html" files)
|
||||
assert.Equal(t, "text/plain; charset=utf-8", resp.Header().Get("Content-Type"))
|
||||
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
defer test.MockVariableValue(&setting.MimeTypeMap)()
|
||||
setting.MimeTypeMap.Enabled = true
|
||||
|
||||
setting.MimeTypeMap.Map[".xml"] = "text/xml"
|
||||
req = NewRequest(t, "GET", "/user2/repo2/raw/branch/master/test.xml")
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
// respect the mime mapping, and "text/plain" protection isn't used anymore
|
||||
assert.Equal(t, "text/xml; charset=utf-8", resp.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "inline; filename=test.xml", resp.Header().Get("Content-Disposition"))
|
||||
|
||||
setting.MimeTypeMap.Map[".xml"] = "application/xml"
|
||||
req = NewRequest(t, "GET", "/user2/repo2/raw/branch/master/test.xml")
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
// non-text file don't have "charset"
|
||||
assert.Equal(t, "application/xml", resp.Header().Get("Content-Type"))
|
||||
})
|
||||
}
|
||||
@@ -410,12 +410,13 @@ func logUnexpectedResponse(t testing.TB, recorder *httptest.ResponseRecorder) {
|
||||
}
|
||||
}
|
||||
|
||||
func DecodeJSON(t testing.TB, resp *httptest.ResponseRecorder, v any) {
|
||||
func DecodeJSON[T any](t testing.TB, resp *httptest.ResponseRecorder, v T) (ret T) {
|
||||
t.Helper()
|
||||
|
||||
// FIXME: JSON-KEY-CASE: for testing purpose only, because many structs don't provide `json` tags, they just use capitalized field names
|
||||
decoder := json.NewDecoderCaseInsensitive(resp.Body)
|
||||
require.NoError(t, decoder.Decode(v))
|
||||
return v
|
||||
}
|
||||
|
||||
func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile string) {
|
||||
|
||||
@@ -23,9 +23,18 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOrgRepos(t *testing.T) {
|
||||
func TestOrg(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
t.Run("OrgRepos", testOrgRepos)
|
||||
t.Run("PrivateOrg", testPrivateOrg)
|
||||
t.Run("LimitedOrg", testLimitedOrg)
|
||||
t.Run("OrgMembers", testOrgMembers)
|
||||
t.Run("OrgRestrictedUser", testOrgRestrictedUser)
|
||||
t.Run("TeamSearch", testTeamSearch)
|
||||
t.Run("OrgSettings", testOrgSettings)
|
||||
}
|
||||
|
||||
func testOrgRepos(t *testing.T) {
|
||||
var (
|
||||
users = []string{"user1", "user2"}
|
||||
cases = map[string][]string{
|
||||
@@ -53,10 +62,8 @@ func TestOrgRepos(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLimitedOrg(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
// not logged in user
|
||||
func testLimitedOrg(t *testing.T) {
|
||||
// not logged-in user
|
||||
req := NewRequest(t, "GET", "/limited_org")
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
req = NewRequest(t, "GET", "/limited_org/public_repo_on_limited_org")
|
||||
@@ -83,10 +90,8 @@ func TestLimitedOrg(t *testing.T) {
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestPrivateOrg(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
// not logged in user
|
||||
func testPrivateOrg(t *testing.T) {
|
||||
// not logged-in user
|
||||
req := NewRequest(t, "GET", "/privated_org")
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
req = NewRequest(t, "GET", "/privated_org/public_repo_on_private_org")
|
||||
@@ -122,10 +127,8 @@ func TestPrivateOrg(t *testing.T) {
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestOrgMembers(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
// not logged in user
|
||||
func testOrgMembers(t *testing.T) {
|
||||
// not logged-in user
|
||||
req := NewRequest(t, "GET", "/org/org25/members")
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
@@ -140,9 +143,7 @@ func TestOrgMembers(t *testing.T) {
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestOrgRestrictedUser(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
func testOrgRestrictedUser(t *testing.T) {
|
||||
// privated_org is a private org who has id 23
|
||||
orgName := "privated_org"
|
||||
|
||||
@@ -200,9 +201,7 @@ func TestOrgRestrictedUser(t *testing.T) {
|
||||
restrictedSession.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestTeamSearch(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
func testTeamSearch(t *testing.T) {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
|
||||
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17})
|
||||
|
||||
@@ -251,3 +250,24 @@ func TestTeamSearch(t *testing.T) {
|
||||
assert.Len(t, teams, 1) // team permission is "write", so can write "code"
|
||||
})
|
||||
}
|
||||
|
||||
func testOrgSettings(t *testing.T) {
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
req := NewRequestWithValues(t, "POST", "/org/org3/settings", map[string]string{
|
||||
"full_name": "org3 new full name",
|
||||
"email": "org3-new-email@example.com",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
assert.Equal(t, "org3 new full name", org.FullName)
|
||||
assert.Equal(t, "org3-new-email@example.com", org.Email)
|
||||
|
||||
req = NewRequestWithValues(t, "POST", "/org/org3/settings", map[string]string{
|
||||
"email": "", // empty email means "clear email"
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
assert.Equal(t, "org3 new full name", org.FullName)
|
||||
assert.Empty(t, org.Email)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepositoryVisibilityChange(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
t.Run("MakePrivateRequiresCorrectName", func(t *testing.T) {
|
||||
// Wrong name should be rejected with a JSON error
|
||||
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||
"action": "visibility",
|
||||
"private": "true",
|
||||
"confirm_repo_name": "wrong-name",
|
||||
})
|
||||
resp := session.MakeRequest(t, req, http.StatusBadRequest)
|
||||
assert.NotEmpty(t, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
assert.False(t, repo1.IsPrivate)
|
||||
|
||||
// Correct full name (owner/repo) should succeed with a JSON redirect
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||
"action": "visibility",
|
||||
"private": "true",
|
||||
"confirm_repo_name": "user2/repo1",
|
||||
})
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEmpty(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect)
|
||||
|
||||
repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
assert.True(t, repo1.IsPrivate)
|
||||
})
|
||||
|
||||
t.Run("MakePublicDoesNotRequireName", func(t *testing.T) {
|
||||
req := NewRequestWithValues(t, "POST", "/user2/repo2/settings", map[string]string{
|
||||
"action": "visibility",
|
||||
"private": "false",
|
||||
})
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEmpty(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect)
|
||||
|
||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
assert.False(t, repo2.IsPrivate)
|
||||
})
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/repo"
|
||||
@@ -1146,6 +1147,10 @@ func Test_WebhookWorkflowRun(t *testing.T) {
|
||||
testWorkflowRunEventsOnCancellingAbandonedRun(t, webhookData, false)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WorkflowRunOnStoppingEndlessTasksForMultipleRuns",
|
||||
testFunc: testWorkflowRunOnStoppingEndlessTasksForMultipleRuns,
|
||||
},
|
||||
}
|
||||
for _, obj := range testCases {
|
||||
t.Run(obj.name, func(t *testing.T) {
|
||||
@@ -1576,6 +1581,84 @@ jobs:
|
||||
assert.Equal(t, "user2/"+repoName, webhookData.payloads[1].Repo.FullName)
|
||||
}
|
||||
|
||||
func testWorkflowRunOnStoppingEndlessTasksForMultipleRuns(t *testing.T, webhookData *workflowRunWebhook) {
|
||||
defer test.MockVariableValue(&setting.Actions.EndlessTaskTimeout, time.Second)()
|
||||
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
session := loginUser(t, "user2")
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||
|
||||
repoName := "test-workflow-run-stop-endless-tasks"
|
||||
testRepo := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: createActionsTestRepo(t, token, repoName, false).ID})
|
||||
|
||||
testAPICreateWebhookForRepo(t, session, "user2", repoName, webhookData.URL, "workflow_run")
|
||||
|
||||
runners := make([]*mockRunner, 2)
|
||||
for i := range runners {
|
||||
runners[i] = newMockRunner()
|
||||
runners[i].registerAsRepoRunner(t, "user2", repoName, fmt.Sprintf("mock-runner-%d", i), []string{"ubuntu-latest"}, false)
|
||||
}
|
||||
|
||||
workflowPath1 := ".gitea/workflows/endless-1.yml"
|
||||
workflowPath2 := ".gitea/workflows/endless-2.yml"
|
||||
workflowContent1 := `name: endless-1
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.gitea/workflows/endless-1.yml'
|
||||
jobs:
|
||||
job-1:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo 'job-1'
|
||||
`
|
||||
workflowContent2 := `name: endless-2
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.gitea/workflows/endless-2.yml'
|
||||
jobs:
|
||||
job-2:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo 'job-2'
|
||||
`
|
||||
|
||||
opts1 := getWorkflowCreateFileOptions(user2, testRepo.DefaultBranch, "create "+workflowPath1, workflowContent1)
|
||||
createWorkflowFile(t, token, "user2", repoName, workflowPath1, opts1)
|
||||
opts2 := getWorkflowCreateFileOptions(user2, testRepo.DefaultBranch, "create "+workflowPath2, workflowContent2)
|
||||
createWorkflowFile(t, token, "user2", repoName, workflowPath2, opts2)
|
||||
|
||||
task1 := runners[0].fetchTask(t)
|
||||
task2 := runners[1].fetchTask(t)
|
||||
_, job1, _ := getTaskAndJobAndRunByTaskID(t, task1.Id)
|
||||
_, job2, _ := getTaskAndJobAndRunByTaskID(t, task2.Id)
|
||||
require.NotEqual(t, job1.RunID, job2.RunID)
|
||||
|
||||
initialRunEventsLen := len(webhookData.payloads)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
require.NoError(t, actions.StopEndlessTasks(t.Context()))
|
||||
|
||||
require.Len(t, webhookData.payloads, initialRunEventsLen+2)
|
||||
|
||||
var completedRunIDs []int64
|
||||
for _, payload := range webhookData.payloads[initialRunEventsLen:] {
|
||||
assert.Equal(t, "completed", payload.Action)
|
||||
assert.Equal(t, "completed", payload.WorkflowRun.Status)
|
||||
completedRunIDs = append(completedRunIDs, payload.WorkflowRun.ID)
|
||||
}
|
||||
assert.Len(t, completedRunIDs, 2)
|
||||
assert.Contains(t, completedRunIDs, job1.RunID)
|
||||
assert.Contains(t, completedRunIDs, job2.RunID)
|
||||
|
||||
run1 := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: job1.RunID})
|
||||
run2 := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: job2.RunID})
|
||||
assert.Equal(t, actions_model.StatusFailure, run1.Status)
|
||||
assert.Equal(t, actions_model.StatusFailure, run2.Status)
|
||||
}
|
||||
|
||||
func testWebhookWorkflowRun(t *testing.T, webhookData *workflowRunWebhook) {
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
Reference in new issue
Block a user