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>
This commit is contained in:
Earl Warren 2025-06-28 23:28:12 +02:00 committed by Earl Warren
commit d6e4342353
14 changed files with 311 additions and 138 deletions

View file

@ -109,13 +109,14 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
defer tests.PrintCurrentTest(t)()
testCtx := NewAPITestContext(t, username, "initial-unsigned"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CheckMasterBranchUnsigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
})
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
@ -191,25 +192,27 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
defer tests.PrintCurrentTest(t)()
testCtx := NewAPITestContext(t, username, "initial-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CheckMasterBranchSigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
require.NotNil(t, branch.Commit)
require.NotNil(t, branch.Commit.Verification)
assert.True(t, branch.Commit.Verification.Verified)
assert.Equal(t, "fox@example.com", branch.Commit.Verification.Signer.Email)
}))
})
})
t.Run("No publickey", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
testCtx := NewAPITestContext(t, "user4", "initial-no-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CheckMasterBranchSigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
require.NotNil(t, branch.Commit)
require.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
}))
})
})
})
@ -226,25 +229,27 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
testCtx := NewAPITestContext(t, username, "initial-2fa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
unittest.AssertSuccessfulInsert(t, &auth_model.WebAuthnCredential{UserID: user.ID})
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CheckMasterBranchSigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
require.NotNil(t, branch.Commit)
require.NotNil(t, branch.Commit.Verification)
assert.True(t, branch.Commit.Verification.Verified)
assert.Equal(t, "fox@example.com", branch.Commit.Verification.Signer.Email)
}))
})
})
t.Run("No 2fa", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
testCtx := NewAPITestContext(t, "user4", "initial-no-2fa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CheckMasterBranchSigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
require.NotNil(t, branch.Commit)
require.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
}))
})
})
})
@ -253,13 +258,14 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
setting.Repository.Signing.InitialCommit = []string{"always"}
testCtx := NewAPITestContext(t, username, "initial-always"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CheckMasterBranchSigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
require.NotNil(t, branch.Commit)
require.NotNil(t, branch.Commit.Verification)
assert.True(t, branch.Commit.Verification.Verified)
assert.Equal(t, "fox@example.com", branch.Commit.Verification.Signer.Email)
}))
})
})
t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) {
@ -267,7 +273,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
setting.Repository.Signing.CRUDActions = []string{"never"}
testCtx := NewAPITestContext(t, username, "initial-always-never"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
@ -279,7 +285,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
testCtx := NewAPITestContext(t, username, "initial-always-parent"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
@ -294,7 +300,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
defer tests.PrintCurrentTest(t)()
testCtx := NewAPITestContext(t, username, "initial-always-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile(
t, testCtx, user, "master", "pubkey", "signed-pubkey.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
@ -306,7 +312,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
defer tests.PrintCurrentTest(t)()
testCtx := NewAPITestContext(t, "user4", "initial-always-no-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile(
t, testCtx, user, "master", "pubkey", "unsigned-pubkey.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
@ -326,7 +332,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
testCtx := NewAPITestContext(t, username, "initial-always-twofa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
unittest.AssertSuccessfulInsert(t, &auth_model.WebAuthnCredential{UserID: user.ID})
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CreateCRUDFile-Twofa", crudActionCreateFile(
t, testCtx, user, "master", "twofa", "signed-twofa.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
@ -338,7 +344,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
defer tests.PrintCurrentTest(t)()
testCtx := NewAPITestContext(t, "user4", "initial-always-no-twofa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile(
t, testCtx, user, "master", "twofa", "unsigned-twofa.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
@ -351,7 +357,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
setting.Repository.Signing.CRUDActions = []string{"always"}
testCtx := NewAPITestContext(t, username, "initial-always-always"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat))
t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat))
t.Run("CreateCRUDFile-Always", crudActionCreateFile(
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
@ -369,12 +375,13 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
require.NoError(t, err)
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
})
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CheckMasterBranchUnsigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
require.NotNil(t, branch.Commit)
require.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
})
})
t.Run("BaseSignedMerging", func(t *testing.T) {
@ -387,12 +394,13 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
require.NoError(t, err)
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
})
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CheckMasterBranchUnsigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
require.NotNil(t, branch.Commit)
require.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
})
})
t.Run("CommitsSignedMerging", func(t *testing.T) {
@ -405,11 +413,12 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O
require.NoError(t, err)
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
})
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
t.Run("CheckMasterBranchUnsigned", func(t *testing.T) {
branch := doAPIGetBranch(testCtx, "master")(t)
require.NotNil(t, branch.Commit)
require.NotNil(t, branch.Commit.Verification)
assert.True(t, branch.Commit.Verification.Verified)
}))
})
})
}