blob: db783f875ecd1aba44907ea7d6378f59a945f284 [file] [log] [blame]
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"os"
"runtime"
"strings"
"testing"
)
func TestSubmitErrors(t *testing.T) {
gt := newGitTest(t)
defer gt.done()
srv := newGerritServer(t)
defer srv.done()
t.Logf("> no commit")
testMainDied(t, "submit")
testPrintedStderr(t, "cannot submit: no changes pending")
write(t, gt.client+"/file1", "")
trun(t, gt.client, "git", "add", "file1")
trun(t, gt.client, "git", "commit", "-m", "msg\n\nDO NOT SUBMIT\n\nChange-Id: I123456789\n")
// Gerrit checks this too, but add a local check.
t.Logf("> do not submit")
testMainDied(t, "submit")
testPrintedStderr(t, "DO NOT SUBMIT")
trun(t, gt.client, "git", "commit", "--amend", "-m", "msg\n\nChange-Id: I123456789\n")
t.Logf("> staged changes")
write(t, gt.client+"/file1", "asdf")
trun(t, gt.client, "git", "add", "file1")
testMainDied(t, "submit")
testPrintedStderr(t, "cannot submit: staged changes exist",
"git status", "!git stash", "!git add", "git-codereview change")
testNoStdout(t)
t.Logf("> unstaged changes")
write(t, gt.client+"/file1", "actual content")
testMainDied(t, "submit")
testPrintedStderr(t, "cannot submit: unstaged changes exist",
"git status", "git stash", "git add", "git-codereview change")
testNoStdout(t)
testRan(t)
trun(t, gt.client, "git", "add", "file1")
trun(t, gt.client, "git", "commit", "--amend", "--no-edit")
t.Logf("> not found")
testMainDied(t, "submit")
testPrintedStderr(t, "change not found on Gerrit server")
const id = "I123456789"
t.Logf("> malformed json")
srv.setJSON(id, "XXX")
testMainDied(t, "submit")
testRan(t) // nothing
testPrintedStderr(t, "malformed json response")
t.Logf("> unexpected change status")
srv.setJSON(id, `{"status": "UNEXPECTED"}`)
testMainDied(t, "submit")
testRan(t) // nothing
testPrintedStderr(t, "cannot submit: unexpected Gerrit change status \"UNEXPECTED\"")
t.Logf("> already merged")
srv.setJSON(id, `{"status": "MERGED"}`)
testMainDied(t, "submit")
testRan(t) // nothing
testPrintedStderr(t, "cannot submit: change already submitted, run 'git sync'")
t.Logf("> abandoned")
srv.setJSON(id, `{"status": "ABANDONED"}`)
testMainDied(t, "submit")
testRan(t) // nothing
testPrintedStderr(t, "cannot submit: change abandoned")
t.Logf("> missing approval")
srv.setJSON(id, `{"status": "NEW", "labels": {"Code-Review": {}}}`)
testMainDied(t, "submit")
testRan(t) // nothing
testPrintedStderr(t, "cannot submit: change missing Code-Review approval")
t.Logf("> rejection")
srv.setJSON(id, `{"status": "NEW", "labels": {"Code-Review": {"rejected": {}}}}`)
testMainDied(t, "submit")
testRan(t) // nothing
testPrintedStderr(t, "cannot submit: change has Code-Review rejection")
t.Logf("> unmergeable")
srv.setJSON(id, `{"status": "NEW", "mergeable": false, "labels": {"Code-Review": {"approved": {}}}}`)
testMainDied(t, "submit")
testRan(t, "git push -q origin HEAD:refs/for/master")
testPrintedStderr(t, "cannot submit: conflicting changes submitted, run 'git sync'")
t.Logf("> submit with unexpected status")
const newJSON = `{"status": "NEW", "mergeable": true, "labels": {"Code-Review": {"approved": {}}}}`
srv.setJSON(id, newJSON)
srv.setReply("/a/changes/proj~master~I123456789/submit", gerritReply{body: ")]}'\n" + newJSON})
testMainDied(t, "submit")
testRan(t, "git push -q origin HEAD:refs/for/master")
testPrintedStderr(t, "submit error: unexpected post-submit Gerrit change status \"NEW\"")
}
func TestSubmitTimeout(t *testing.T) {
gt := newGitTest(t)
defer gt.done()
srv := newGerritServer(t)
defer srv.done()
gt.work(t)
setJSON := func(json string) {
srv.setReply("/a/changes/proj~master~I123456789", gerritReply{body: ")]}'\n" + json})
}
t.Log("> submit with timeout")
const submittedJSON = `{"status": "SUBMITTED", "mergeable": true, "labels": {"Code-Review": {"approved": {}}}}`
setJSON(submittedJSON)
srv.setReply("/a/changes/proj~master~I123456789/submit", gerritReply{body: ")]}'\n" + submittedJSON})
testMainDied(t, "submit")
testRan(t, "git push -q origin HEAD:refs/for/master")
testPrintedStderr(t, "cannot submit: timed out waiting for change to be submitted by Gerrit")
}
func TestSubmit(t *testing.T) {
gt := newGitTest(t)
defer gt.done()
srv := newGerritServer(t)
defer srv.done()
gt.work(t)
trun(t, gt.client, "git", "tag", "-f", "work.mailed")
clientHead := strings.TrimSpace(trun(t, gt.client, "git", "log", "-n", "1", "--format=format:%H"))
write(t, gt.server+"/file", "another change")
trun(t, gt.server, "git", "add", "file")
trun(t, gt.server, "git", "commit", "-m", "conflict")
serverHead := strings.TrimSpace(trun(t, gt.server, "git", "log", "-n", "1", "--format=format:%H"))
t.Log("> submit")
var (
newJSON = `{"status": "NEW", "mergeable": true, "current_revision": "` + clientHead + `", "labels": {"Code-Review": {"approved": {}}}}`
submittedJSON = `{"status": "SUBMITTED", "mergeable": true, "current_revision": "` + clientHead + `", "labels": {"Code-Review": {"approved": {}}}}`
mergedJSON = `{"status": "MERGED", "mergeable": true, "current_revision": "` + serverHead + `", "labels": {"Code-Review": {"approved": {}}}}`
)
submitted := false
npoll := 0
srv.setReply("/a/changes/proj~master~I123456789", gerritReply{f: func() gerritReply {
if !submitted {
return gerritReply{body: ")]}'\n" + newJSON}
}
if npoll++; npoll <= 2 {
return gerritReply{body: ")]}'\n" + submittedJSON}
}
return gerritReply{body: ")]}'\n" + mergedJSON}
}})
srv.setReply("/a/changes/proj~master~I123456789/submit", gerritReply{f: func() gerritReply {
if submitted {
return gerritReply{status: 409}
}
submitted = true
return gerritReply{body: ")]}'\n" + submittedJSON}
}})
testMain(t, "submit")
testRan(t,
"git fetch -q",
"git checkout -q -B work "+serverHead+" --")
}
func TestSubmitMultiple(t *testing.T) {
gt := newGitTest(t)
defer gt.done()
srv := newGerritServer(t)
defer srv.done()
cl1, cl2 := testSubmitMultiple(t, gt, srv)
testMain(t, "submit", cl1.CurrentRevision, cl2.CurrentRevision)
}
func TestSubmitMultipleNamed(t *testing.T) {
gt := newGitTest(t)
defer gt.done()
srv := newGerritServer(t)
defer srv.done()
_, _ = testSubmitMultiple(t, gt, srv)
testMain(t, "submit", "HEAD^", "HEAD")
}
func TestSubmitInteractive(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("see golang.org/issue/13406")
}
gt := newGitTest(t)
defer gt.done()
srv := newGerritServer(t)
defer srv.done()
cl1, cl2 := testSubmitMultiple(t, gt, srv)
os.Setenv("GIT_EDITOR", "echo "+cl1.CurrentRevision+" > ")
testMain(t, "submit", "-i")
if cl1.Status != "MERGED" {
t.Fatalf("want cl1.Status == MERGED; got %v", cl1.Status)
}
if cl2.Status != "NEW" {
t.Fatalf("want cl2.Status == NEW; got %v", cl1.Status)
}
}
func testSubmitMultiple(t *testing.T, gt *gitTest, srv *gerritServer) (*GerritChange, *GerritChange) {
write(t, gt.client+"/file1", "")
trun(t, gt.client, "git", "add", "file1")
trun(t, gt.client, "git", "commit", "-m", "msg\n\nChange-Id: I0000001\n")
hash1 := strings.TrimSpace(trun(t, gt.client, "git", "log", "-n", "1", "--format=format:%H"))
write(t, gt.client+"/file2", "")
trun(t, gt.client, "git", "add", "file2")
trun(t, gt.client, "git", "commit", "-m", "msg\n\nChange-Id: I0000002\n")
hash2 := strings.TrimSpace(trun(t, gt.client, "git", "log", "-n", "1", "--format=format:%H"))
testMainDied(t, "submit")
testPrintedStderr(t, "cannot submit: multiple changes pending")
cl1 := GerritChange{
Status: "NEW",
Mergeable: true,
CurrentRevision: hash1,
Labels: map[string]*GerritLabel{"Code-Review": &GerritLabel{Approved: new(GerritAccount)}},
}
cl2 := GerritChange{
Status: "NEW",
Mergeable: false,
CurrentRevision: hash2,
Labels: map[string]*GerritLabel{"Code-Review": &GerritLabel{Approved: new(GerritAccount)}},
}
srv.setReply("/a/changes/proj~master~I0000001", gerritReply{f: func() gerritReply {
return gerritReply{json: cl1}
}})
srv.setReply("/a/changes/proj~master~I0000001/submit", gerritReply{f: func() gerritReply {
if cl1.Status != "NEW" {
return gerritReply{status: 409}
}
cl1.Status = "MERGED"
cl2.Mergeable = true
return gerritReply{json: cl1}
}})
srv.setReply("/a/changes/proj~master~I0000002", gerritReply{f: func() gerritReply {
return gerritReply{json: cl2}
}})
srv.setReply("/a/changes/proj~master~I0000002/submit", gerritReply{f: func() gerritReply {
if cl2.Status != "NEW" || !cl2.Mergeable {
return gerritReply{status: 409}
}
cl2.Status = "MERGED"
return gerritReply{json: cl2}
}})
return &cl1, &cl2
}