Files
forge/tests/integration/api_repo_file_delete_test.go
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
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]>
2026-09-10 17:50:35 -07:00

166 lines
6.7 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"fmt"
"net/http"
"net/url"
"testing"
auth_model "github.com/hanzoai/git/models/auth"
repo_model "github.com/hanzoai/git/models/repo"
"github.com/hanzoai/git/models/unittest"
user_model "github.com/hanzoai/git/models/user"
api "github.com/hanzoai/git/modules/structs"
"github.com/stretchr/testify/assert"
)
func getDeleteFileOptions() *api.DeleteFileOptions {
return &api.DeleteFileOptions{
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
FileOptions: api.FileOptions{
BranchName: "master",
NewBranchName: "master",
Message: "Removing the file new/file.txt",
Author: api.Identity{
Name: "John Doe",
Email: "[email protected]",
},
Committer: api.Identity{
Name: "Jane Doe",
Email: "[email protected]",
},
},
}
}
func TestAPIDeleteFile(t *testing.T) {
onGitRun(t, func(t *testing.T, u *url.URL) {
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo1 & repo16
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the repo3, is an org
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // owner of neither repos
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo
repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo
fileID := 0
// Get user2's token
session := loginUser(t, user2.Name)
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
// Get user4's token
session = loginUser(t, user4.Name)
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
// Test deleting a file in repo1 which user2 owns, try both with branch and empty branch
for _, branch := range [...]string{
"master", // Branch
"", // Empty branch
} {
fileID++
treePath := fmt.Sprintf("delete/file%d.txt", fileID)
createFile(user2, repo1, treePath)
deleteFileOptions := getDeleteFileOptions()
deleteFileOptions.BranchName = branch
req := NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &deleteFileOptions).
AddTokenAuth(token2)
resp := MakeRequest(t, req, http.StatusOK)
fileResponse := DecodeJSON(t, resp, &api.FileResponse{})
assert.NotNil(t, fileResponse)
assert.Nil(t, fileResponse.Content)
}
// Test deleting file and making the delete in a new branch
fileID++
treePath := fmt.Sprintf("delete/file%d.txt", fileID)
createFile(user2, repo1, treePath)
deleteFileOptions := getDeleteFileOptions()
deleteFileOptions.BranchName = repo1.DefaultBranch
deleteFileOptions.NewBranchName = "new_branch"
req := NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &deleteFileOptions).
AddTokenAuth(token2)
resp := MakeRequest(t, req, http.StatusOK)
fileResponse := DecodeJSON(t, resp, &api.FileResponse{})
assert.NotNil(t, fileResponse)
assert.Nil(t, fileResponse.Content)
assert.Equal(t, deleteFileOptions.Message+"\n", fileResponse.Commit.Message)
// Test deleting file without a message
fileID++
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
createFile(user2, repo1, treePath)
deleteFileOptions = getDeleteFileOptions()
deleteFileOptions.Message = ""
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &deleteFileOptions).
AddTokenAuth(token2)
resp = MakeRequest(t, req, http.StatusOK)
fileResponse = DecodeJSON(t, resp, &api.FileResponse{})
expectedMessage := "Delete " + treePath + "\n"
assert.Equal(t, expectedMessage, fileResponse.Commit.Message)
// Test deleting a file with the wrong SHA
fileID++
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
createFile(user2, repo1, treePath)
deleteFileOptions = getDeleteFileOptions()
deleteFileOptions.SHA = "badsha"
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &deleteFileOptions).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusUnprocessableEntity)
// Test creating a file in repo16 by user4 who does not have write access
fileID++
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
createFile(user2, repo16, treePath)
deleteFileOptions = getDeleteFileOptions()
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath), &deleteFileOptions).
AddTokenAuth(token4)
MakeRequest(t, req, http.StatusNotFound)
// Tests a repo with no token given so will fail
fileID++
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
createFile(user2, repo16, treePath)
deleteFileOptions = getDeleteFileOptions()
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath), &deleteFileOptions)
MakeRequest(t, req, http.StatusNotFound)
// Test using access token for a private repo that the user of the token owns
fileID++
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
createFile(user2, repo16, treePath)
deleteFileOptions = getDeleteFileOptions()
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath), &deleteFileOptions).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusOK)
// Test using org repo "org3/repo3" where user2 is a collaborator
fileID++
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
createFile(org3, repo3, treePath)
deleteFileOptions = getDeleteFileOptions()
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", org3.Name, repo3.Name, treePath), &deleteFileOptions).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusOK)
// Test using org repo "org3/repo3" with no user token
fileID++
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
createFile(org3, repo3, treePath)
deleteFileOptions = getDeleteFileOptions()
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", org3.Name, repo3.Name, treePath), &deleteFileOptions)
MakeRequest(t, req, http.StatusNotFound)
// Test using repo "user2/repo1" where user4 is a NOT collaborator
fileID++
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
createFile(user2, repo1, treePath)
deleteFileOptions = getDeleteFileOptions()
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &deleteFileOptions).
AddTokenAuth(token4)
MakeRequest(t, req, http.StatusForbidden)
})
}