f3226d6171
--- type: change message: |- Refactor combined commits a bit This commit makes the combine method not take in revisions, which simplifies some things as well as makes it more consistent with other methods. Additionally this commit fixes authorship for combined commits so that they only show the authors of the change commits involved, not all commits with credentials. change_hash: APQFD5UML2F+ulO3awYJ3BHqvn05EZSFqmCg2i7vTfgA credentials: - type: pgp_signature pub_key_id: 95C46FA6A41148AC body: iQIzBAABAgAdFiEEJ6tQKp6olvZKJ0lwlcRvpqQRSKwFAl55PNYACgkQlcRvpqQRSKz51Q//anLA1v9Qqtn0dey24oGngaJnV80fi53CZf57AGulbTj6WWQIlZrH126B/YJgSAFg6EzRZZj8vWW7JTXLXhExVM3vGvGWJWyslW9vadBLXRbK9Uw4RuPxN9hDgnXPYs4TNbIm11q5m8jxrCPAehs4aMebsRB4k//m0ul/17kjWlIOHTJupE/gyg47hjoyD//q2vNi9izkEEtRYDVykxXzy++UtEp7CCt9MO9Tp6H8FRxDH1h5jwWaGbedL/AakLDBAN0DhEZ1tvStpeSwycRiGlbIO3eRpDoLoBgX5yLwwrCqF6acEToJZR+amerKlTa5dNf/cqynS8CzMs35u+ZZx0QKTp7eqn/TEP3LNlAbbzILyrktFVBml47JNjLwS2LVZknJvV3mWI6K9r6yE01m/S+2zbkZz2EoADn0PgA99tKfJoozjFbqRQaTJn/B6/4L38GyVKSW3DXF3BYjz9DrFiEZR95449q9gHmJCVZ7S7wX/KfYvI5K7QMpBhiGUZSsWKc4DX6de2TP1HgXQX/08WJGb5463J6TQoB2LoQbDzOUBli4VblQwpWaREDp0DlPz+BUaFVCAwIXsaYGGrUkKeqFN0MH1OR7Ir5t5uVg8C97RbywAYvdThyOdQLyKQaK4CHwtyHU0BPrKSU13rE6tl66NTqY46MgyBSPEJ4ukvA= account: mediocregopher
200 lines
5.4 KiB
Go
200 lines
5.4 KiB
Go
package dehub
|
|
|
|
import (
|
|
"dehub/sigcred"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
yaml "gopkg.in/yaml.v2"
|
|
)
|
|
|
|
func TestChangeCommitVerify(t *testing.T) {
|
|
type step struct {
|
|
msg string
|
|
msgHead string // defaults to msg
|
|
tree map[string]string
|
|
}
|
|
testCases := []struct {
|
|
descr string
|
|
steps []step
|
|
}{
|
|
{
|
|
descr: "single commit",
|
|
steps: []step{
|
|
{
|
|
msg: "first commit",
|
|
tree: map[string]string{"a": "0", "b": "1"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
descr: "multiple commits",
|
|
steps: []step{
|
|
{
|
|
msg: "first commit",
|
|
tree: map[string]string{"a": "0", "b": "1"},
|
|
},
|
|
{
|
|
msg: "second commit, changing a",
|
|
tree: map[string]string{"a": "1"},
|
|
},
|
|
{
|
|
msg: "third commit, empty",
|
|
},
|
|
{
|
|
msg: "fourth commit, adding c, removing b",
|
|
tree: map[string]string{"b": "", "c": "2"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
descr: "big body commits",
|
|
steps: []step{
|
|
{
|
|
msg: "first commit, single line but with newline\n",
|
|
},
|
|
{
|
|
msg: "second commit, single line but with two newlines\n\n",
|
|
msgHead: "second commit, single line but with two newlines\n\n",
|
|
},
|
|
{
|
|
msg: "third commit, multi-line with one newline\nanother line!",
|
|
msgHead: "third commit, multi-line with one newline\n\n",
|
|
},
|
|
{
|
|
msg: "fourth commit, multi-line with two newlines\n\nanother line!",
|
|
msgHead: "fourth commit, multi-line with two newlines\n\n",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range testCases {
|
|
t.Run(test.descr, func(t *testing.T) {
|
|
h := newHarness(t)
|
|
for _, step := range test.steps {
|
|
h.stage(step.tree)
|
|
account := h.cfg.Accounts[0]
|
|
|
|
gitCommit := h.changeCommit(step.msg, account.ID, h.sig)
|
|
if step.msgHead == "" {
|
|
step.msgHead = strings.TrimSpace(step.msg) + "\n\n"
|
|
}
|
|
|
|
if !strings.HasPrefix(gitCommit.GitCommit.Message, step.msgHead) {
|
|
t.Fatalf("commit message %q does not start with expected head %q",
|
|
gitCommit.GitCommit.Message, step.msgHead)
|
|
}
|
|
|
|
var actualCommit Commit
|
|
if err := actualCommit.UnmarshalText([]byte(gitCommit.GitCommit.Message)); err != nil {
|
|
t.Fatalf("error unmarshaling commit body: %v", err)
|
|
} else if !reflect.DeepEqual(actualCommit, gitCommit.Commit) {
|
|
t.Fatalf("returned change commit:\n%s\ndoes not match actual one:\n%s",
|
|
spew.Sdump(gitCommit.Commit), spew.Sdump(actualCommit))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCombineCommitChanges(t *testing.T) {
|
|
h := newHarness(t)
|
|
|
|
// commit initial config, so the root user can modify it in the next commit
|
|
h.changeCommit("initial commit", h.cfg.Accounts[0].ID, h.sig)
|
|
|
|
// add a toot user and modify the access controls such that both accounts
|
|
// are required for the main branch
|
|
tootSig, tootPubKeyBody := sigcred.SignifierPGPTmp("toot", h.rand)
|
|
h.cfg.Accounts = append(h.cfg.Accounts, Account{
|
|
ID: "toot",
|
|
Signifiers: []sigcred.Signifier{{PGPPublicKey: &sigcred.SignifierPGP{
|
|
Body: string(tootPubKeyBody),
|
|
}}},
|
|
})
|
|
err := yaml.Unmarshal([]byte(`
|
|
- action: allow
|
|
filters:
|
|
- type: branch
|
|
pattern: main
|
|
- type: commit_type
|
|
commit_type: change
|
|
- type: signature
|
|
any_account: true
|
|
count: 2
|
|
|
|
- action: allow
|
|
filters:
|
|
- type: not
|
|
filter:
|
|
type: branch
|
|
pattern: main
|
|
- type: signature
|
|
any_account: true
|
|
count: 1
|
|
|
|
- action: deny
|
|
`), &h.cfg.AccessControls)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
h.stageCfg()
|
|
tootCommit := h.changeCommit("add toot", h.cfg.Accounts[0].ID, h.sig)
|
|
|
|
// make a single change commit in another branch using root. Then add a
|
|
// credential using toot, and combine them onto main.
|
|
otherBranch := plumbing.NewBranchReferenceName("other")
|
|
h.checkout(otherBranch)
|
|
h.stage(map[string]string{"foo": "bar"})
|
|
fooCommit := h.changeCommit("add foo file", h.cfg.Accounts[0].ID, h.sig)
|
|
|
|
// now adding a credential commit from toot should work
|
|
credCommitObj, err := h.repo.NewCommitCredential(fooCommit.Interface.GetHash())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
credCommit := h.tryCommit(true, credCommitObj, h.cfg.Accounts[1].ID, tootSig)
|
|
|
|
allCommits, err := h.repo.GetGitCommitRange(
|
|
tootCommit.GitCommit.Hash,
|
|
credCommit.GitCommit.Hash,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("error getting commits: %v", err)
|
|
}
|
|
|
|
combinedCommit, err := h.repo.CombineCommitChanges(allCommits, MainRefName)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// that new commit should have both credentials
|
|
creds := combinedCommit.Commit.Common.Credentials
|
|
if len(creds) != 2 {
|
|
t.Fatalf("combined commit has %d credentials, not 2", len(creds))
|
|
} else if creds[0].AccountID != "root" {
|
|
t.Fatalf("combined commit first credential should be from root, is from %q", creds[0].AccountID)
|
|
} else if creds[1].AccountID != "toot" {
|
|
t.Fatalf("combined commit second credential should be from toot, is from %q", creds[1].AccountID)
|
|
}
|
|
|
|
// double check that the HEAD commit of main got properly set
|
|
h.checkout(MainRefName)
|
|
mainHead, err := h.repo.GetGitHead()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if mainHead.GitCommit.Hash != combinedCommit.GitCommit.Hash {
|
|
t.Fatalf("mainHead's should be pointed at %s but is pointed at %s",
|
|
combinedCommit.GitCommit.Hash, mainHead.GitCommit.Hash)
|
|
} else if err = h.repo.VerifyCommits(MainRefName, []GitCommit{combinedCommit}); err != nil {
|
|
t.Fatalf("unable to verify combined commit: %v", err)
|
|
} else if author := combinedCommit.GitCommit.Author.Name; author != "root" {
|
|
t.Fatalf("unexpected author value %q", author)
|
|
}
|
|
}
|