forgejo_backup/tests/integration/ssh_key_test.go
Earl Warren d6e4342353 fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks (#8326)
- fix: API must use headGitRepo instead of ctx.Repo.GitRepo for comparing
- fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks
- add test coverage for both fixes and the underlying function `parseCompareInfo`
- refactor and improve part of the helpers from `tests/integration/api_helper_for_declarative_test.go`
- remove a few wrong or misleading comments

Refs forgejo/forgejo#7978

## Note on the focus of the PR

It was initially created to address a regression introduced in v12. But the tests that verify it is fixed discovered a v11.0 bug. They cannot conveniently be separated because they both relate to the same area of code that was previously not covered by any test.

## Note on v11.0 backport

It must be manually done by cherry-picking all commits up to and not including `fix: API must use headGitRepo instead of ctx.Repo.GitRepo for comparing` because it is v12 specific.

## Checklist

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

### Tests

- I added test coverage for Go changes...
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.

### Documentation

- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] I do not want this change to show in the release notes.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8326
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: Earl Warren <contact@earl-warren.org>
Co-committed-by: Earl Warren <contact@earl-warren.org>
2025-06-28 23:28:12 +02:00

214 lines
7 KiB
Go

// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"testing"
"time"
auth_model "forgejo.org/models/auth"
"forgejo.org/modules/git"
api "forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func doCheckRepositoryEmptyStatus(ctx APITestContext, isEmpty bool) func(*testing.T) {
return doAPIGetRepository(ctx, func(t *testing.T, repository api.Repository) {
assert.Equal(t, isEmpty, repository.Empty)
})
}
func doAddChangesToCheckout(dstPath, filename string) func(*testing.T) {
return func(t *testing.T) {
require.NoError(t, os.WriteFile(filepath.Join(dstPath, filename), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s at time: %v", dstPath, time.Now())), 0o644))
require.NoError(t, git.AddChanges(dstPath, true))
signature := git.Signature{
Email: "test@example.com",
Name: "test",
When: time.Now(),
}
require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
Committer: &signature,
Author: &signature,
Message: "Initial Commit",
}))
}
}
func TestPushDeployKeyOnEmptyRepo(t *testing.T) {
onGiteaRun(t, testPushDeployKeyOnEmptyRepo)
}
func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) {
forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) {
// OK login
ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-"+objectFormat.Name(), auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
keyname := fmt.Sprintf("%s-push", ctx.Reponame)
u.Path = ctx.GitPath()
opts := &api.CreateRepoOption{
Description: "Temporary repo",
Name: ctx.Reponame,
Private: true,
Template: true,
}
t.Run("CreateEmptyRepository", doAPICreateRepository(ctx, opts, objectFormat))
t.Run("CheckIsEmpty", doCheckRepositoryEmptyStatus(ctx, true))
withKeyFile(t, keyname, func(keyFile string) {
t.Run("CreatePushDeployKey", doAPICreateDeployKey(ctx, keyname, keyFile, false))
// Setup the testing repository
dstPath := t.TempDir()
t.Run("InitTestRepository", doGitInitTestRepository(dstPath, objectFormat))
// Setup remote link
sshURL := createSSHUrl(ctx.GitPath(), u)
t.Run("AddRemote", doGitAddRemote(dstPath, "origin", sshURL))
t.Run("SSHPushTestRepository", doGitPushTestRepository(dstPath, "origin", "master"))
t.Run("CheckIsNotEmpty", doCheckRepositoryEmptyStatus(ctx, false))
t.Run("DeleteRepository", doAPIDeleteRepository(ctx))
})
})
}
func TestKeyOnlyOneType(t *testing.T) {
onGiteaRun(t, testKeyOnlyOneType)
}
func testKeyOnlyOneType(t *testing.T, u *url.URL) {
// Once a key is a user key we cannot use it as a deploy key
// If we delete it from the user we should be able to use it as a deploy key
reponame := "ssh-key-test-repo"
username := "user2"
u.Path = fmt.Sprintf("%s/%s.git", username, reponame)
keyname := fmt.Sprintf("%s-push", reponame)
// OK login
ctx := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
ctxWithDeleteRepo := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
otherCtx := ctx
otherCtx.Reponame = "ssh-key-test-repo-2"
otherCtxWithDeleteRepo := ctxWithDeleteRepo
otherCtxWithDeleteRepo.Reponame = otherCtx.Reponame
failCtx := ctx
failCtx.ExpectedCode = http.StatusUnprocessableEntity
t.Run("CreateRepository", doAPICreateRepository(ctx, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat
t.Run("CreateOtherRepository", doAPICreateRepository(otherCtx, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat
withKeyFile(t, keyname, func(keyFile string) {
var userKeyPublicKeyID int64
t.Run("KeyCanOnlyBeUser", func(t *testing.T) {
dstPath := t.TempDir()
sshURL := createSSHUrl(ctx.GitPath(), u)
t.Run("FailToClone", doGitCloneFail(sshURL))
t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) {
userKeyPublicKeyID = publicKey.ID
}))
t.Run("FailToAddReadOnlyDeployKey", doAPICreateDeployKey(failCtx, keyname, keyFile, true))
t.Run("FailToAddDeployKey", doAPICreateDeployKey(failCtx, keyname, keyFile, false))
t.Run("Clone", doGitClone(dstPath, sshURL))
t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES1.md"))
t.Run("Push", doGitPushTestRepository(dstPath, "origin", "master"))
t.Run("DeleteUserKey", doAPIDeleteUserKey(ctx, userKeyPublicKeyID))
})
t.Run("KeyCanBeAnyDeployButNotUserAswell", func(t *testing.T) {
dstPath := t.TempDir()
sshURL := createSSHUrl(ctx.GitPath(), u)
t.Run("FailToClone", doGitCloneFail(sshURL))
// Should now be able to add...
t.Run("AddReadOnlyDeployKey", doAPICreateDeployKey(ctx, keyname, keyFile, true))
t.Run("Clone", doGitClone(dstPath, sshURL))
t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES2.md"))
t.Run("FailToPush", doGitPushTestRepositoryFail(dstPath, "origin", "master"))
otherSSHURL := createSSHUrl(otherCtx.GitPath(), u)
dstOtherPath := t.TempDir()
t.Run("AddWriterDeployKeyToOther", doAPICreateDeployKey(otherCtx, keyname, keyFile, false))
t.Run("CloneOther", doGitClone(dstOtherPath, otherSSHURL))
t.Run("AddChangesToOther", doAddChangesToCheckout(dstOtherPath, "CHANGES3.md"))
t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master"))
t.Run("FailToCreateUserKey", doAPICreateUserKey(failCtx, keyname, keyFile))
})
t.Run("DeleteRepositoryShouldReleaseKey", func(t *testing.T) {
otherSSHURL := createSSHUrl(otherCtx.GitPath(), u)
dstOtherPath := t.TempDir()
t.Run("DeleteRepository", doAPIDeleteRepository(ctxWithDeleteRepo))
t.Run("FailToCreateUserKeyAsStillDeploy", doAPICreateUserKey(failCtx, keyname, keyFile))
t.Run("MakeSureCloneOtherStillWorks", doGitClone(dstOtherPath, otherSSHURL))
t.Run("AddChangesToOther", doAddChangesToCheckout(dstOtherPath, "CHANGES3.md"))
t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master"))
t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtxWithDeleteRepo))
t.Run("RecreateRepository", doAPICreateRepository(ctxWithDeleteRepo, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat
t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) {
userKeyPublicKeyID = publicKey.ID
}))
dstPath := t.TempDir()
sshURL := createSSHUrl(ctx.GitPath(), u)
t.Run("Clone", doGitClone(dstPath, sshURL))
t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES1.md"))
t.Run("Push", doGitPushTestRepository(dstPath, "origin", "master"))
})
t.Run("DeleteUserKeyShouldRemoveAbilityToClone", func(t *testing.T) {
sshURL := createSSHUrl(ctx.GitPath(), u)
t.Run("DeleteUserKey", doAPIDeleteUserKey(ctx, userKeyPublicKeyID))
t.Run("FailToClone", doGitCloneFail(sshURL))
})
})
}