mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
693 lines
24 KiB
Go
693 lines
24 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
repo_model "github.com/hanzoai/git/models/repo"
|
|
"github.com/hanzoai/git/modules/git"
|
|
"github.com/hanzoai/git/modules/gitrepo"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
api "github.com/hanzoai/git/modules/structs"
|
|
"github.com/hanzoai/git/services/contexttest"
|
|
files_service "github.com/hanzoai/git/services/repository/files"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func getCreateRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
|
|
return &files_service.ChangeRepoFilesOptions{
|
|
Files: []*files_service.ChangeRepoFile{
|
|
{
|
|
Operation: "create",
|
|
TreePath: "new/file.txt",
|
|
ContentReader: strings.NewReader("This is a NEW file"),
|
|
},
|
|
},
|
|
OldBranch: repo.DefaultBranch,
|
|
NewBranch: repo.DefaultBranch,
|
|
Message: "Creates new/file.txt",
|
|
Author: nil,
|
|
Committer: nil,
|
|
}
|
|
}
|
|
|
|
func getUpdateRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
|
|
return &files_service.ChangeRepoFilesOptions{
|
|
Files: []*files_service.ChangeRepoFile{
|
|
{
|
|
Operation: "update",
|
|
TreePath: "README.md",
|
|
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
|
ContentReader: strings.NewReader("This is UPDATED content for the README file"),
|
|
},
|
|
},
|
|
OldBranch: repo.DefaultBranch,
|
|
NewBranch: repo.DefaultBranch,
|
|
Message: "Updates README.md",
|
|
Author: nil,
|
|
Committer: nil,
|
|
}
|
|
}
|
|
|
|
func getUpdateRepoFilesRenameOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
|
|
return &files_service.ChangeRepoFilesOptions{
|
|
Files: []*files_service.ChangeRepoFile{
|
|
// move normally
|
|
{
|
|
Operation: "rename",
|
|
FromTreePath: "README.md",
|
|
TreePath: "README.txt",
|
|
},
|
|
// move from in lfs
|
|
{
|
|
Operation: "rename",
|
|
FromTreePath: "crypt.bin",
|
|
TreePath: "crypt1.bin",
|
|
},
|
|
// move from lfs to normal
|
|
{
|
|
Operation: "rename",
|
|
FromTreePath: "jpeg.jpg",
|
|
TreePath: "jpeg.jpeg",
|
|
},
|
|
// move from normal to lfs
|
|
{
|
|
Operation: "rename",
|
|
FromTreePath: "CONTRIBUTING.md",
|
|
TreePath: "CONTRIBUTING.md.bin",
|
|
},
|
|
},
|
|
OldBranch: repo.DefaultBranch,
|
|
NewBranch: repo.DefaultBranch,
|
|
Message: "Rename files",
|
|
}
|
|
}
|
|
|
|
func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git.Commit) *api.FileResponse {
|
|
treePath := "new/file.txt"
|
|
encoding := "base64"
|
|
content := "VGhpcyBpcyBhIE5FVyBmaWxl"
|
|
selfURL := setting.AppURL + "v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
|
|
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
|
|
gitURL := setting.AppURL + "v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885"
|
|
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
|
|
return &api.FileResponse{
|
|
Content: &api.ContentsResponse{
|
|
Name: path.Base(treePath),
|
|
Path: treePath,
|
|
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
|
LastCommitSHA: new(lastCommit.ID.String()),
|
|
LastCommitterDate: new(lastCommit.Committer.When),
|
|
LastAuthorDate: new(lastCommit.Author.When),
|
|
Type: "file",
|
|
Size: 18,
|
|
Encoding: &encoding,
|
|
Content: &content,
|
|
URL: &selfURL,
|
|
HTMLURL: &htmlURL,
|
|
GitURL: &gitURL,
|
|
DownloadURL: &downloadURL,
|
|
Links: &api.FileLinksResponse{
|
|
Self: &selfURL,
|
|
GitURL: &gitURL,
|
|
HTMLURL: &htmlURL,
|
|
},
|
|
},
|
|
Commit: &api.FileCommitResponse{
|
|
CommitMeta: api.CommitMeta{
|
|
URL: setting.AppURL + "v1/repos/user2/repo1/git/commits/" + commitID,
|
|
SHA: commitID,
|
|
},
|
|
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
|
Author: &api.CommitUser{
|
|
Identity: api.Identity{
|
|
Name: "User Two",
|
|
Email: "[email protected]",
|
|
},
|
|
Date: time.Now().UTC().Format(time.RFC3339),
|
|
},
|
|
Committer: &api.CommitUser{
|
|
Identity: api.Identity{
|
|
Name: "User Two",
|
|
Email: "[email protected]",
|
|
},
|
|
Date: time.Now().UTC().Format(time.RFC3339),
|
|
},
|
|
Parents: []*api.CommitMeta{
|
|
{
|
|
URL: setting.AppURL + "v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
},
|
|
},
|
|
Message: "Creates new/file.txt\n",
|
|
Tree: &api.CommitMeta{
|
|
URL: setting.AppURL + "v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
|
SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc",
|
|
},
|
|
},
|
|
Verification: &api.PayloadCommitVerification{
|
|
Verified: false,
|
|
Reason: "gpg.error.not_signed_commit",
|
|
Signature: "",
|
|
Payload: "",
|
|
},
|
|
}
|
|
}
|
|
|
|
func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA string, lastCommitterWhen, lastAuthorWhen time.Time) *api.FileResponse {
|
|
encoding := "base64"
|
|
content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ=="
|
|
selfURL := setting.AppURL + "v1/repos/user2/repo1/contents/" + filename + "?ref=master"
|
|
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + filename
|
|
gitURL := setting.AppURL + "v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647"
|
|
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename
|
|
return &api.FileResponse{
|
|
Content: &api.ContentsResponse{
|
|
Name: filename,
|
|
Path: filename,
|
|
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
|
|
LastCommitSHA: new(lastCommitSHA),
|
|
LastCommitterDate: new(lastCommitterWhen),
|
|
LastAuthorDate: new(lastAuthorWhen),
|
|
Type: "file",
|
|
Size: 43,
|
|
Encoding: &encoding,
|
|
Content: &content,
|
|
URL: &selfURL,
|
|
HTMLURL: &htmlURL,
|
|
GitURL: &gitURL,
|
|
DownloadURL: &downloadURL,
|
|
Links: &api.FileLinksResponse{
|
|
Self: &selfURL,
|
|
GitURL: &gitURL,
|
|
HTMLURL: &htmlURL,
|
|
},
|
|
},
|
|
Commit: &api.FileCommitResponse{
|
|
CommitMeta: api.CommitMeta{
|
|
URL: setting.AppURL + "v1/repos/user2/repo1/git/commits/" + commitID,
|
|
SHA: commitID,
|
|
},
|
|
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
|
Author: &api.CommitUser{
|
|
Identity: api.Identity{
|
|
Name: "User Two",
|
|
Email: "[email protected]",
|
|
},
|
|
Date: time.Now().UTC().Format(time.RFC3339),
|
|
},
|
|
Committer: &api.CommitUser{
|
|
Identity: api.Identity{
|
|
Name: "User Two",
|
|
Email: "[email protected]",
|
|
},
|
|
Date: time.Now().UTC().Format(time.RFC3339),
|
|
},
|
|
Parents: []*api.CommitMeta{
|
|
{
|
|
URL: setting.AppURL + "v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
},
|
|
},
|
|
Message: "Updates README.md\n",
|
|
Tree: &api.CommitMeta{
|
|
URL: setting.AppURL + "v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
|
SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
|
},
|
|
},
|
|
Verification: &api.PayloadCommitVerification{
|
|
Verified: false,
|
|
Reason: "gpg.error.not_signed_commit",
|
|
Signature: "",
|
|
Payload: "",
|
|
},
|
|
}
|
|
}
|
|
|
|
func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA string) *api.FilesResponse {
|
|
details := []struct {
|
|
filename, sha, content string
|
|
size int64
|
|
lfsOid *string
|
|
lfsSize *int64
|
|
}{
|
|
{
|
|
filename: "README.txt",
|
|
sha: "8276d2a29779af982c0afa976bdb793b52d442a8",
|
|
size: 22,
|
|
content: "IyBBbiBMRlMtZW5hYmxlZCByZXBvCg==",
|
|
},
|
|
{
|
|
filename: "crypt1.bin",
|
|
sha: "d4a41a0d4db4949e129bd22f871171ea988103ef",
|
|
size: 129,
|
|
content: "dmVyc2lvbiBodHRwczovL2dpdC1sZnMuZ2l0aHViLmNvbS9zcGVjL3YxCm9pZCBzaGEyNTY6MmVjY2RiNDM4MjVkMmE0OWQ5OWQ1NDJkYWEyMDA3NWNmZjFkOTdkOWQyMzQ5YTg5NzdlZmU5YzAzNjYxNzM3YwpzaXplIDIwNDgK",
|
|
lfsOid: new("2eccdb43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c"),
|
|
lfsSize: new(int64(2048)),
|
|
},
|
|
{
|
|
filename: "jpeg.jpeg",
|
|
sha: "71911bf48766c7181518c1070911019fbb00b1fc",
|
|
size: 107,
|
|
content: "/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
|
|
},
|
|
{
|
|
filename: "CONTRIBUTING.md.bin",
|
|
sha: "2b6c6c4eaefa24b22f2092c3d54b263ff26feb58",
|
|
size: 127,
|
|
content: "dmVyc2lvbiBodHRwczovL2dpdC1sZnMuZ2l0aHViLmNvbS9zcGVjL3YxCm9pZCBzaGEyNTY6N2I2YjJjODhkYmE5Zjc2MGExYTU4NDY5YjY3ZmVlMmI2OThlZjdlOTM5OWM0Y2E0ZjM0YTE0Y2NiZTM5ZjYyMwpzaXplIDI3Cg==",
|
|
lfsOid: new("7b6b2c88dba9f760a1a58469b67fee2b698ef7e9399c4ca4f34a14ccbe39f623"),
|
|
lfsSize: new(int64(27)),
|
|
},
|
|
}
|
|
|
|
var responses []*api.ContentsResponse
|
|
for _, detail := range details {
|
|
selfURL := setting.AppURL + "v1/repos/user2/lfs/contents/" + detail.filename + "?ref=master"
|
|
htmlURL := setting.AppURL + "user2/lfs/src/branch/master/" + detail.filename
|
|
gitURL := setting.AppURL + "v1/repos/user2/lfs/git/blobs/" + detail.sha
|
|
downloadURL := setting.AppURL + "user2/lfs/raw/branch/master/" + detail.filename
|
|
// don't set time related fields because there might be different time in one operation
|
|
responses = append(responses, &api.ContentsResponse{
|
|
Name: detail.filename,
|
|
Path: detail.filename,
|
|
SHA: detail.sha,
|
|
LastCommitSHA: new(lastCommitSHA),
|
|
Type: "file",
|
|
Size: detail.size,
|
|
Encoding: new("base64"),
|
|
Content: &detail.content,
|
|
URL: &selfURL,
|
|
HTMLURL: &htmlURL,
|
|
GitURL: &gitURL,
|
|
DownloadURL: &downloadURL,
|
|
Links: &api.FileLinksResponse{
|
|
Self: &selfURL,
|
|
GitURL: &gitURL,
|
|
HTMLURL: &htmlURL,
|
|
},
|
|
LfsOid: detail.lfsOid,
|
|
LfsSize: detail.lfsSize,
|
|
})
|
|
}
|
|
|
|
return &api.FilesResponse{
|
|
Files: responses,
|
|
Commit: &api.FileCommitResponse{
|
|
CommitMeta: api.CommitMeta{
|
|
URL: setting.AppURL + "v1/repos/user2/lfs/git/commits/" + commitID,
|
|
SHA: commitID,
|
|
},
|
|
HTMLURL: setting.AppURL + "user2/lfs/commit/" + commitID,
|
|
Author: &api.CommitUser{
|
|
Identity: api.Identity{
|
|
Name: "User Two",
|
|
Email: "[email protected]",
|
|
},
|
|
},
|
|
Committer: &api.CommitUser{
|
|
Identity: api.Identity{
|
|
Name: "User Two",
|
|
Email: "[email protected]",
|
|
},
|
|
},
|
|
Parents: []*api.CommitMeta{
|
|
{
|
|
URL: setting.AppURL + "v1/repos/user2/lfs/git/commits/73cf03db6ece34e12bf91e8853dc58f678f2f82d",
|
|
SHA: "73cf03db6ece34e12bf91e8853dc58f678f2f82d",
|
|
},
|
|
},
|
|
Message: "Rename files\n",
|
|
Tree: &api.CommitMeta{
|
|
URL: setting.AppURL + "v1/repos/user2/lfs/git/trees/5307376dc3a5557dc1c403c29a8984668ca9ecb5",
|
|
SHA: "5307376dc3a5557dc1c403c29a8984668ca9ecb5",
|
|
},
|
|
},
|
|
Verification: &api.PayloadCommitVerification{
|
|
Verified: false,
|
|
Reason: "gpg.error.not_signed_commit",
|
|
Signature: "",
|
|
Payload: "",
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestChangeRepoFilesForCreate(t *testing.T) {
|
|
// setup
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
|
ctx.SetPathParam("id", "1")
|
|
contexttest.LoadRepo(t, ctx, 1)
|
|
contexttest.LoadRepoCommit(t, ctx)
|
|
contexttest.LoadUser(t, ctx, 2)
|
|
contexttest.LoadGitRepo(t, ctx)
|
|
defer ctx.Repo.GitRepo.Close()
|
|
|
|
repo := ctx.Repo.Repository
|
|
doer := ctx.Doer
|
|
opts := getCreateRepoFilesOptions(repo)
|
|
|
|
// test
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
|
|
// asserts
|
|
assert.NoError(t, err)
|
|
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
|
|
defer gitRepo.Close()
|
|
|
|
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
|
|
lastCommit, _ := gitRepo.GetCommitByPath("new/file.txt")
|
|
expectedFileResponse := getExpectedFileResponseForRepoFilesCreate(commitID, lastCommit)
|
|
assert.NotNil(t, expectedFileResponse)
|
|
if expectedFileResponse != nil {
|
|
assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
|
|
assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
|
|
assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
|
|
assert.Equal(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
|
|
assert.Equal(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestChangeRepoFilesForUpdate(t *testing.T) {
|
|
// setup
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
|
ctx.SetPathParam("id", "1")
|
|
contexttest.LoadRepo(t, ctx, 1)
|
|
contexttest.LoadRepoCommit(t, ctx)
|
|
contexttest.LoadUser(t, ctx, 2)
|
|
contexttest.LoadGitRepo(t, ctx)
|
|
defer ctx.Repo.GitRepo.Close()
|
|
|
|
repo := ctx.Repo.Repository
|
|
doer := ctx.Doer
|
|
opts := getUpdateRepoFilesOptions(repo)
|
|
|
|
// test
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
|
|
// asserts
|
|
assert.NoError(t, err)
|
|
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
|
|
defer gitRepo.Close()
|
|
|
|
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
|
|
lastCommit, _ := commit.GetCommitByPath(gitRepo, opts.Files[0].TreePath)
|
|
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
|
|
assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
|
|
assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
|
|
assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
|
|
assert.Equal(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
|
|
assert.Equal(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
|
|
})
|
|
}
|
|
|
|
func TestChangeRepoFilesForUpdateWithFileMove(t *testing.T) {
|
|
// setup
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
|
ctx.SetPathParam("id", "1")
|
|
contexttest.LoadRepo(t, ctx, 1)
|
|
contexttest.LoadRepoCommit(t, ctx)
|
|
contexttest.LoadUser(t, ctx, 2)
|
|
contexttest.LoadGitRepo(t, ctx)
|
|
defer ctx.Repo.GitRepo.Close()
|
|
|
|
repo := ctx.Repo.Repository
|
|
doer := ctx.Doer
|
|
opts := getUpdateRepoFilesOptions(repo)
|
|
opts.Files[0].FromTreePath = "README.md"
|
|
opts.Files[0].TreePath = "README_new.md" // new file name, README_new.md
|
|
|
|
// test
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
|
|
// asserts
|
|
assert.NoError(t, err)
|
|
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
|
|
defer gitRepo.Close()
|
|
|
|
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
|
|
lastCommit, _ := commit.GetCommitByPath(gitRepo, opts.Files[0].TreePath)
|
|
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
|
|
// assert that the old file no longer exists in the last commit of the branch
|
|
fromEntry, err := commit.GetTreeEntryByPath(ctx, gitRepo, opts.Files[0].FromTreePath)
|
|
switch err.(type) {
|
|
case git.ErrNotExist:
|
|
// correct, continue
|
|
default:
|
|
t.Fatalf("expected git.ErrNotExist, got:%v", err)
|
|
}
|
|
toEntry, err := commit.GetTreeEntryByPath(ctx, gitRepo, opts.Files[0].TreePath)
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, fromEntry) // Should no longer exist here
|
|
assert.NotNil(t, toEntry) // Should exist here
|
|
// assert SHA has remained the same but paths use the new file name
|
|
assert.Equal(t, expectedFileResponse.Content.SHA, filesResponse.Files[0].SHA)
|
|
assert.Equal(t, expectedFileResponse.Content.Name, filesResponse.Files[0].Name)
|
|
assert.Equal(t, expectedFileResponse.Content.Path, filesResponse.Files[0].Path)
|
|
assert.Equal(t, expectedFileResponse.Content.URL, filesResponse.Files[0].URL)
|
|
assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
|
|
assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
|
|
})
|
|
}
|
|
|
|
func TestChangeRepoFilesForUpdateWithFileRename(t *testing.T) {
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
ctx, _ := contexttest.MockContext(t, "user2/lfs")
|
|
ctx.SetPathParam("id", "54")
|
|
contexttest.LoadRepo(t, ctx, 54)
|
|
contexttest.LoadRepoCommit(t, ctx)
|
|
contexttest.LoadUser(t, ctx, 2)
|
|
contexttest.LoadGitRepo(t, ctx)
|
|
defer ctx.Repo.GitRepo.Close()
|
|
|
|
repo := ctx.Repo.Repository
|
|
opts := getUpdateRepoFilesRenameOptions(repo)
|
|
|
|
// test
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, ctx.Doer, opts)
|
|
|
|
// asserts
|
|
assert.NoError(t, err)
|
|
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
|
|
defer gitRepo.Close()
|
|
|
|
commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch)
|
|
lastCommit, _ := commit.GetCommitByPath(gitRepo, opts.Files[0].TreePath)
|
|
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdateRename(commit.ID.String(), lastCommit.ID.String())
|
|
for _, file := range filesResponse.Files {
|
|
file.LastCommitterDate, file.LastAuthorDate = nil, nil // there might be different time in one operation, so we ignore them
|
|
}
|
|
assert.Len(t, filesResponse.Files, 4)
|
|
assert.Equal(t, expectedFileResponse.Files, filesResponse.Files)
|
|
})
|
|
}
|
|
|
|
// Test opts with branch names removed, should get same results as above test
|
|
func TestChangeRepoFilesWithoutBranchNames(t *testing.T) {
|
|
// setup
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
|
ctx.SetPathParam("id", "1")
|
|
contexttest.LoadRepo(t, ctx, 1)
|
|
contexttest.LoadRepoCommit(t, ctx)
|
|
contexttest.LoadUser(t, ctx, 2)
|
|
contexttest.LoadGitRepo(t, ctx)
|
|
defer ctx.Repo.GitRepo.Close()
|
|
|
|
repo := ctx.Repo.Repository
|
|
doer := ctx.Doer
|
|
opts := getUpdateRepoFilesOptions(repo)
|
|
opts.OldBranch = ""
|
|
opts.NewBranch = ""
|
|
|
|
// test
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
|
|
// asserts
|
|
assert.NoError(t, err)
|
|
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
|
|
defer gitRepo.Close()
|
|
|
|
commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch)
|
|
lastCommit, _ := commit.GetCommitByPath(gitRepo, opts.Files[0].TreePath)
|
|
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
|
|
assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
|
|
})
|
|
}
|
|
|
|
func TestChangeRepoFilesForDelete(t *testing.T) {
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
|
ctx.SetPathParam("id", "1")
|
|
contexttest.LoadRepo(t, ctx, 1)
|
|
contexttest.LoadRepoCommit(t, ctx)
|
|
contexttest.LoadUser(t, ctx, 2)
|
|
contexttest.LoadGitRepo(t, ctx)
|
|
defer ctx.Repo.GitRepo.Close()
|
|
repo := ctx.Repo.Repository
|
|
doer := ctx.Doer
|
|
|
|
t.Run("Delete README.md by commit", func(t *testing.T) {
|
|
urlRaw := "/user2/repo1/raw/branch/branch2/README.md"
|
|
MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
|
|
opts := &files_service.ChangeRepoFilesOptions{
|
|
OldBranch: "branch2",
|
|
LastCommitID: "985f0301dba5e7b34be866819cd15ad3d8f508ee",
|
|
Files: []*files_service.ChangeRepoFile{
|
|
{
|
|
Operation: "delete",
|
|
TreePath: "README.md",
|
|
},
|
|
},
|
|
Message: "test message",
|
|
}
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, filesResponse)
|
|
assert.Nil(t, filesResponse.Files[0])
|
|
assert.Equal(t, "test message\n", filesResponse.Commit.Message)
|
|
MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
|
|
})
|
|
|
|
t.Run("Delete README.md with options", func(t *testing.T) {
|
|
urlRaw := "/user2/repo1/raw/branch/master/README.md"
|
|
MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
|
|
opts := &files_service.ChangeRepoFilesOptions{
|
|
Files: []*files_service.ChangeRepoFile{
|
|
{
|
|
Operation: "delete",
|
|
TreePath: "README.md",
|
|
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
|
},
|
|
},
|
|
OldBranch: repo.DefaultBranch,
|
|
NewBranch: repo.DefaultBranch,
|
|
Message: "Message for deleting README.md",
|
|
Author: &files_service.IdentityOptions{GitUserName: "Bob Smith", GitUserEmail: "[email protected]"},
|
|
}
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, filesResponse)
|
|
assert.Nil(t, filesResponse.Files[0])
|
|
assert.Equal(t, "Message for deleting README.md\n", filesResponse.Commit.Message)
|
|
assert.Equal(t, api.Identity{Name: "Bob Smith", Email: "[email protected]"}, filesResponse.Commit.Author.Identity)
|
|
assert.Equal(t, api.Identity{Name: "Bob Smith", Email: "[email protected]"}, filesResponse.Commit.Committer.Identity)
|
|
assert.Equal(t, &api.PayloadCommitVerification{Reason: "gpg.error.not_signed_commit"}, filesResponse.Verification)
|
|
MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
|
|
})
|
|
|
|
t.Run("Delete directory", func(t *testing.T) {
|
|
urlRaw := "/user2/repo1/raw/branch/sub-home-md-img-check/docs/README.md"
|
|
MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
|
|
opts := &files_service.ChangeRepoFilesOptions{
|
|
OldBranch: "sub-home-md-img-check",
|
|
LastCommitID: "4649299398e4d39a5c09eb4f534df6f1e1eb87cc",
|
|
Files: []*files_service.ChangeRepoFile{
|
|
{
|
|
Operation: "delete",
|
|
TreePath: "docs",
|
|
DeleteRecursively: true,
|
|
},
|
|
},
|
|
Message: "test message",
|
|
}
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, filesResponse)
|
|
assert.Nil(t, filesResponse.Files[0])
|
|
assert.Equal(t, "test message\n", filesResponse.Commit.Message)
|
|
MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestChangeRepoFilesErrors(t *testing.T) {
|
|
// setup
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
|
ctx.SetPathParam("id", "1")
|
|
contexttest.LoadRepo(t, ctx, 1)
|
|
contexttest.LoadRepoCommit(t, ctx)
|
|
contexttest.LoadUser(t, ctx, 2)
|
|
contexttest.LoadGitRepo(t, ctx)
|
|
defer ctx.Repo.GitRepo.Close()
|
|
|
|
repo := ctx.Repo.Repository
|
|
doer := ctx.Doer
|
|
|
|
t.Run("bad branch", func(t *testing.T) {
|
|
opts := getUpdateRepoFilesOptions(repo)
|
|
opts.OldBranch = "bad_branch"
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, filesResponse)
|
|
expectedError := fmt.Sprintf("branch does not exist [repo_id: %d name: %s]", repo.ID, opts.OldBranch)
|
|
assert.EqualError(t, err, expectedError)
|
|
})
|
|
|
|
t.Run("bad SHA", func(t *testing.T) {
|
|
opts := getUpdateRepoFilesOptions(repo)
|
|
origSHA := opts.Files[0].SHA
|
|
opts.Files[0].SHA = "bad_sha"
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
assert.Nil(t, filesResponse)
|
|
assert.Error(t, err)
|
|
expectedError := "sha does not match [given: " + opts.Files[0].SHA + ", expected: " + origSHA + "]"
|
|
assert.EqualError(t, err, expectedError)
|
|
})
|
|
|
|
t.Run("new branch already exists", func(t *testing.T) {
|
|
opts := getUpdateRepoFilesOptions(repo)
|
|
opts.NewBranch = "develop"
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
assert.Nil(t, filesResponse)
|
|
assert.Error(t, err)
|
|
expectedError := "branch already exists [name: " + opts.NewBranch + "]"
|
|
assert.EqualError(t, err, expectedError)
|
|
})
|
|
|
|
t.Run("treePath is empty:", func(t *testing.T) {
|
|
opts := getUpdateRepoFilesOptions(repo)
|
|
opts.Files[0].TreePath = ""
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
assert.Nil(t, filesResponse)
|
|
assert.Error(t, err)
|
|
expectedError := "path contains a malformed path component [path: ]"
|
|
assert.EqualError(t, err, expectedError)
|
|
})
|
|
|
|
t.Run("treePath is a git directory:", func(t *testing.T) {
|
|
opts := getUpdateRepoFilesOptions(repo)
|
|
opts.Files[0].TreePath = ".git"
|
|
filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
assert.Nil(t, filesResponse)
|
|
assert.Error(t, err)
|
|
expectedError := "path contains a malformed path component [path: " + opts.Files[0].TreePath + "]"
|
|
assert.EqualError(t, err, expectedError)
|
|
})
|
|
|
|
t.Run("create file that already exists", func(t *testing.T) {
|
|
opts := getCreateRepoFilesOptions(repo)
|
|
opts.Files[0].TreePath = "README.md" // already exists
|
|
fileResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
|
|
assert.Nil(t, fileResponse)
|
|
assert.Error(t, err)
|
|
expectedError := "repository file already exists [path: " + opts.Files[0].TreePath + "]"
|
|
assert.EqualError(t, err, expectedError)
|
|
})
|
|
})
|
|
}
|