1818package librarian
1919
2020import (
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.
527656func initRepo (t * testing.T , dir , source string ) error {
0 commit comments