Skip to content

Commit 5d05481

Browse files
authored
feat(testing): add e2e test for release tag-and-release (#2222)
Adds an end-to-end test for the 'release tag-and-release' command. This test uses a mock GitHub server to verify that the command makes the correct API calls for creating tags and releases. Part of #1013. Note: improving coverage for new code in command.go requires tests with much setup. Given that the new code is a simple error check, and given the e2e test uses the code, codecov report is ignored.
1 parent 234f2a2 commit 5d05481

File tree

5 files changed

+152
-0
lines changed

5 files changed

+152
-0
lines changed

e2e_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
package librarian
1919

2020
import (
21+
"encoding/json"
2122
"fmt"
23+
"net/http"
24+
"net/http/httptest"
2225
"os"
2326
"os/exec"
2427
"path/filepath"
@@ -27,6 +30,7 @@ import (
2730

2831
"github.com/google/go-cmp/cmp"
2932
"github.com/google/go-cmp/cmp/cmpopts"
33+
"github.com/google/go-github/v69/github"
3034
"github.com/googleapis/librarian/internal/config"
3135
"gopkg.in/yaml.v3"
3236
)
@@ -522,6 +526,131 @@ END_COMMIT_OVERRIDE
522526
}
523527
}
524528

529+
func TestReleaseTagAndRelease(t *testing.T) {
530+
t.Parallel()
531+
for _, test := range []struct {
532+
name string
533+
prBody string
534+
push bool
535+
wantErr bool
536+
}{
537+
{
538+
name: "runs successfully",
539+
prBody: `<details><summary>go-google-cloud-pubsub-v1: v1.0.1</summary>
540+
### Features
541+
- feat: new feature
542+
</details>`,
543+
},
544+
} {
545+
t.Run(test.name, func(t *testing.T) {
546+
repo := t.TempDir()
547+
if err := initRepo(t, repo, "testdata/e2e/release/init/repo_init"); err != nil {
548+
t.Fatalf("prepare test error = %v", err)
549+
}
550+
runGit(t, repo, "tag", "go-google-cloud-pubsub-v1-1.0.0")
551+
newFilePath := filepath.Join(repo, "google-cloud-pubsub/v1", "new-file.txt")
552+
if err := os.WriteFile(newFilePath, []byte("new file"), 0644); err != nil {
553+
t.Fatal(err)
554+
}
555+
runGit(t, repo, "add", newFilePath)
556+
runGit(t, repo, "commit", "-m", "feat: new feature")
557+
headSHA, err := getHeadSHA(repo)
558+
if err != nil {
559+
t.Fatalf("failed to get head sha: %v", err)
560+
}
561+
562+
// Set up a mock GitHub API server using httptest.
563+
// This server will intercept HTTP requests made by the librarian command
564+
// and provide canned responses, avoiding any real calls to the GitHub API.
565+
// The handlers below simulate the endpoints that 'release tag-and-release' interacts with.
566+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
567+
// Verify that the GitHub token is being sent correctly.
568+
if r.Header.Get("Authorization") != "Bearer fake-token" {
569+
t.Errorf("missing or wrong authorization header: got %q", r.Header.Get("Authorization"))
570+
}
571+
572+
// Mock endpoint for GET /repos/{owner}/{repo}/pulls/{number}
573+
if r.Method == "GET" && strings.HasSuffix(r.URL.Path, "/pulls/123") {
574+
w.WriteHeader(http.StatusOK)
575+
// Return a minimal PR object with the body and merge commit SHA.
576+
fmt.Fprintf(w, `{"number": 123, "body": %q, "merge_commit_sha": %q}`, test.prBody, headSHA)
577+
return
578+
}
579+
580+
// Mock endpoint for POST /repos/{owner}/{repo}/git/refs (creating the release-please tag)
581+
if r.Method == "POST" && strings.HasSuffix(r.URL.Path, "/git/refs") {
582+
w.WriteHeader(http.StatusCreated)
583+
fmt.Fprint(w, `{"ref": "refs/tags/release-please-123"}`)
584+
return
585+
}
586+
587+
// Mock endpoint for POST /repos/{owner}/{repo}/releases (creating the GitHub Release)
588+
if r.Method == "POST" && strings.HasSuffix(r.URL.Path, "/releases") {
589+
var newRelease github.RepositoryRelease
590+
if err := json.NewDecoder(r.Body).Decode(&newRelease); err != nil {
591+
t.Fatalf("failed to decode request body: %v", err)
592+
}
593+
expectedTagName := "go-google-cloud-pubsub-v1-v1.0.1"
594+
if *newRelease.TagName != expectedTagName {
595+
t.Errorf("unexpected tag name: got %q, want %q", *newRelease.TagName, expectedTagName)
596+
}
597+
if *newRelease.TargetCommitish != headSHA {
598+
t.Errorf("unexpected commitish: got %q, want %q", *newRelease.TargetCommitish, headSHA)
599+
}
600+
w.WriteHeader(http.StatusCreated)
601+
fmt.Fprint(w, `{"name": "v1.0.1"}`)
602+
return
603+
}
604+
605+
// Mock endpoint for PUT /repos/{owner}/{repo}/issues/{number}/labels (updating labels)
606+
if r.Method == "PUT" && strings.HasSuffix(r.URL.Path, "/issues/123/labels") {
607+
w.WriteHeader(http.StatusOK)
608+
fmt.Fprint(w, `[]`)
609+
return
610+
}
611+
612+
// If any other request is made, fail the test.
613+
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
614+
}))
615+
defer server.Close()
616+
617+
cmdArgs := []string{
618+
"run",
619+
"github.com/googleapis/librarian/cmd/librarian",
620+
"release",
621+
"tag-and-release",
622+
fmt.Sprintf("--repo=%s", repo),
623+
fmt.Sprintf("--github-api-endpoint=%s/", server.URL),
624+
"--pr=https://github.com/googleapis/librarian/pull/123",
625+
}
626+
if test.push {
627+
cmdArgs = append(cmdArgs, "--push")
628+
}
629+
630+
cmd := exec.Command("go", cmdArgs...)
631+
cmd.Env = append(os.Environ(), "LIBRARIAN_GITHUB_TOKEN=fake-token")
632+
cmd.Stderr = os.Stderr
633+
cmd.Stdout = os.Stdout
634+
if err := cmd.Run(); err != nil {
635+
if !test.wantErr {
636+
t.Fatalf("Failed to run release tag-and-release: %v", err)
637+
}
638+
}
639+
})
640+
}
641+
}
642+
643+
// getHeadSHA is a helper to get the latest commit SHA from a repo.
644+
func getHeadSHA(dir string) (string, error) {
645+
cmd := exec.Command("git", "rev-parse", "HEAD")
646+
cmd.Dir = dir
647+
out, err := cmd.Output()
648+
if err != nil {
649+
return "", err
650+
}
651+
return strings.TrimSpace(string(out)), nil
652+
}
653+
525654
// initRepo initiates a git repo in the given directory, copy
526655
// files from source directory and create a commit.
527656
func initRepo(t *testing.T, dir, source string) error {

internal/config/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ type Config struct {
127127
// This flag is ignored if Push is set to true.
128128
Commit bool
129129

130+
// GitHubAPIEndpoint is the GitHub API endpoint to use for all GitHub API
131+
// operations.
132+
//
133+
// This is intended for testing and should not be used in production.
134+
GitHubAPIEndpoint string
135+
130136
// GitHubToken is the access token to use for all operations involving
131137
// GitHub.
132138
//

internal/librarian/command.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@ func newCommandRunner(cfg *config.Config) (*commandRunner, error) {
141141
}
142142
ghClient := github.NewClient(cfg.GitHubToken, gitRepo)
143143

144+
// If a custom GitHub API endpoint is provided (for testing),
145+
// parse it and set it as the BaseURL on the GitHub client.
146+
if cfg.GitHubAPIEndpoint != "" {
147+
endpoint, err := url.Parse(cfg.GitHubAPIEndpoint)
148+
if err != nil {
149+
return nil, fmt.Errorf("failed to parse github-api-endpoint: %w", err)
150+
}
151+
ghClient.BaseURL = endpoint
152+
}
153+
144154
container, err := docker.New(cfg.WorkRoot, image, cfg.UserUID, cfg.UserGID)
145155
if err != nil {
146156
return nil, err

internal/librarian/flags.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ func addFlagCommit(fs *flag.FlagSet, cfg *config.Config) {
4444
a pull request. This flag is ignored if push is set to true.`)
4545
}
4646

47+
func addFlagGitHubAPIEndpoint(fs *flag.FlagSet, cfg *config.Config) {
48+
fs.StringVar(&cfg.GitHubAPIEndpoint, "github-api-endpoint", "",
49+
`The GitHub API endpoint to use for all GitHub API operations.
50+
This is intended for testing and should not be used in production.`)
51+
}
52+
4753
func addFlagHostMount(fs *flag.FlagSet, cfg *config.Config) {
4854
defaultValue := ""
4955
fs.StringVar(&cfg.HostMount, "host-mount", defaultValue,

internal/librarian/librarian.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ func newCmdTagAndRelease() *cli.Command {
119119
cmdTagAndRelease.Init()
120120
addFlagRepo(cmdTagAndRelease.Flags, cmdTagAndRelease.Config)
121121
addFlagPR(cmdTagAndRelease.Flags, cmdTagAndRelease.Config)
122+
addFlagGitHubAPIEndpoint(cmdTagAndRelease.Flags, cmdTagAndRelease.Config)
122123
return cmdTagAndRelease
123124
}
124125

0 commit comments

Comments
 (0)