diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 919e80d..7952132 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -41,6 +41,35 @@ jobs: uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5 with: token: ${{ secrets.CODECOV_TOKEN }} + flags: unit + + test-e2e-forgejo: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version-file: go.mod + + # We can not use "jobs..services". + # We want to mount the config file, which is only available after "Checkout". + - name: Start Forgejo + working-directory: test/e2e/forgejo + run: docker compose up --wait + + - name: Run tests + run: go test -tags e2e_forgejo -v -race -coverpkg=./... -coverprofile=coverage.txt ./test/e2e/forgejo + + + - name: Upload results to Codecov + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: e2e go-mod-tidy: runs-on: ubuntu-latest diff --git a/cmd/rp/cmd/run.go b/cmd/rp/cmd/run.go index 2c65af9..eef84e6 100644 --- a/cmd/rp/cmd/run.go +++ b/cmd/rp/cmd/run.go @@ -5,14 +5,15 @@ import ( "log/slog" "strings" - "github.com/apricote/releaser-pleaser/internal/forge/forgejo" "github.com/spf13/cobra" rp "github.com/apricote/releaser-pleaser" "github.com/apricote/releaser-pleaser/internal/commitparser/conventionalcommits" "github.com/apricote/releaser-pleaser/internal/forge" + "github.com/apricote/releaser-pleaser/internal/forge/forgejo" "github.com/apricote/releaser-pleaser/internal/forge/github" "github.com/apricote/releaser-pleaser/internal/forge/gitlab" + "github.com/apricote/releaser-pleaser/internal/log" "github.com/apricote/releaser-pleaser/internal/updater" "github.com/apricote/releaser-pleaser/internal/versioning" ) @@ -34,10 +35,11 @@ func newRunCommand() *cobra.Command { Use: "run", RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() + logger := log.GetLogger(cmd.ErrOrStderr()) var err error - slog.DebugContext(ctx, "run called", + logger.DebugContext(ctx, "run called", "forge", flagForge, "branch", flagBranch, "owner", flagOwner, @@ -53,8 +55,8 @@ func newRunCommand() *cobra.Command { switch flagForge { case "gitlab": - slog.DebugContext(ctx, "using forge GitLab") - f, err = gitlab.New(slog.Default(), &gitlab.Options{ + logger.DebugContext(ctx, "using forge GitLab") + f, err = gitlab.New(logger, &gitlab.Options{ Options: forgeOptions, Path: fmt.Sprintf("%s/%s", flagOwner, flagRepo), }) @@ -63,15 +65,15 @@ func newRunCommand() *cobra.Command { return fmt.Errorf("failed to create gitlab client: %w", err) } case "github": - slog.DebugContext(ctx, "using forge GitHub") - f = github.New(slog.Default(), &github.Options{ + logger.DebugContext(ctx, "using forge GitHub") + f = github.New(logger, &github.Options{ Options: forgeOptions, Owner: flagOwner, Repo: flagRepo, }) case "forgejo": - slog.DebugContext(ctx, "using forge Forgejo") - f, err = forgejo.New(slog.Default(), &forgejo.Options{ + logger.DebugContext(ctx, "using forge Forgejo") + f, err = forgejo.New(logger, &forgejo.Options{ Options: forgeOptions, Owner: flagOwner, Repo: flagRepo, @@ -81,7 +83,7 @@ func newRunCommand() *cobra.Command { Username: flagUsername, }) if err != nil { - slog.ErrorContext(ctx, "failed to create client", "err", err) + logger.ErrorContext(ctx, "failed to create client", "err", err) return fmt.Errorf("failed to create forgejo client: %w", err) } default: @@ -92,9 +94,9 @@ func newRunCommand() *cobra.Command { releaserPleaser := rp.New( f, - slog.Default(), + logger, flagBranch, - conventionalcommits.NewParser(slog.Default()), + conventionalcommits.NewParser(logger), versioning.SemVer, extraFiles, []updater.NewUpdater{updater.Generic}, diff --git a/codecov.yaml b/codecov.yaml new file mode 100644 index 0000000..b52d0c4 --- /dev/null +++ b/codecov.yaml @@ -0,0 +1,2 @@ +ignore: + - "test" diff --git a/go.mod b/go.mod index 32b0ad0..b29348e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/apricote/releaser-pleaser -go 1.23.2 +go 1.24 toolchain go1.24.4 @@ -55,4 +55,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 => ../../../codeberg.org/mvdkleijn/forgejo-sdk/forgejo +replace codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 => codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561 diff --git a/go.sum b/go.sum index c536164..fd2be2f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.1.0 h1:BsxZzd3nPPMyv8r6W964OL7XgTpsXvxO8f0vBTWpPok= -codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.1.0/go.mod h1:2i9GsyawlJtVMO5pTS/Om5uo2O3JN/eCjGWy5v15NGg= +codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561 h1:ZFGmrGQ7cd2mbSLrfjrj3COwPKFfKM6sDO/IsrGDW7w= +codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561/go.mod h1:2i9GsyawlJtVMO5pTS/Om5uo2O3JN/eCjGWy5v15NGg= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= diff --git a/internal/forge/forgejo/forgejo.go b/internal/forge/forgejo/forgejo.go index a3d9209..9b0c548 100644 --- a/internal/forge/forgejo/forgejo.go +++ b/internal/forge/forgejo/forgejo.go @@ -8,13 +8,14 @@ import ( "strings" "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2" + "github.com/blang/semver/v4" + "github.com/go-git/go-git/v5/plumbing/transport" + "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/apricote/releaser-pleaser/internal/forge" "github.com/apricote/releaser-pleaser/internal/git" "github.com/apricote/releaser-pleaser/internal/pointer" "github.com/apricote/releaser-pleaser/internal/releasepr" - "github.com/blang/semver/v4" - "github.com/go-git/go-git/v5/plumbing/transport" - "github.com/go-git/go-git/v5/plumbing/transport/http" ) const () @@ -146,7 +147,7 @@ func (f *Forgejo) CommitsSince(ctx context.Context, tag *git.Tag) ([]git.Commit, return commits, nil } -func (f *Forgejo) commitsSinceTag(ctx context.Context, tag *git.Tag) ([]*forgejo.Commit, error) { +func (f *Forgejo) commitsSinceTag(_ context.Context, tag *git.Tag) ([]*forgejo.Commit, error) { head := f.options.BaseBranch log := f.log.With("base", tag.Hash, "head", head) log.Debug("comparing commits") @@ -161,7 +162,7 @@ func (f *Forgejo) commitsSinceTag(ctx context.Context, tag *git.Tag) ([]*forgejo return compare.Commits, nil } -func (f *Forgejo) commitsSinceInit(ctx context.Context) ([]*forgejo.Commit, error) { +func (f *Forgejo) commitsSinceInit(_ context.Context) ([]*forgejo.Commit, error) { head := f.options.BaseBranch log := f.log.With("head", head) log.Debug("listing all commits") @@ -182,7 +183,7 @@ func (f *Forgejo) commitsSinceInit(ctx context.Context) ([]*forgejo.Commit, erro return repositoryCommits, nil } -func (f *Forgejo) prForCommit(ctx context.Context, commit git.Commit) (*git.PullRequest, error) { +func (f *Forgejo) prForCommit(_ context.Context, commit git.Commit) (*git.PullRequest, error) { // We naively look up the associated PR for each commit through the "List pull requests associated with a commit" // endpoint. This requires len(commits) requests. // Using the "List pull requests" endpoint might be faster, as it allows us to fetch 100 arbitrary PRs per request, @@ -195,13 +196,17 @@ func (f *Forgejo) prForCommit(ctx context.Context, commit git.Commit) (*git.Pull commit.Hash, ) if err != nil { + if strings.HasPrefix(err.Error(), "pull request does not exist") { + return nil, nil + } + return nil, err } return forgejoPRToPullRequest(pullRequest), nil } -func (f *Forgejo) EnsureLabelsExist(ctx context.Context, labels []releasepr.Label) error { +func (f *Forgejo) EnsureLabelsExist(_ context.Context, labels []releasepr.Label) error { f.log.Debug("fetching labels on repo") fLabels, err := all(func(listOptions forgejo.ListOptions) ([]*forgejo.Label, *forgejo.Response, error) { return f.client.ListRepoLabels( @@ -234,7 +239,7 @@ func (f *Forgejo) EnsureLabelsExist(ctx context.Context, labels []releasepr.Labe return nil } -func (f *Forgejo) PullRequestForBranch(ctx context.Context, branch string) (*releasepr.ReleasePullRequest, error) { +func (f *Forgejo) PullRequestForBranch(_ context.Context, branch string) (*releasepr.ReleasePullRequest, error) { prs, err := all( func(listOptions forgejo.ListOptions) ([]*forgejo.PullRequest, *forgejo.Response, error) { return f.client.ListRepoPullRequests( @@ -284,7 +289,7 @@ func (f *Forgejo) CreatePullRequest(ctx context.Context, pr *releasepr.ReleasePu return nil } -func (f *Forgejo) UpdatePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error { +func (f *Forgejo) UpdatePullRequest(_ context.Context, pr *releasepr.ReleasePullRequest) error { _, _, err := f.client.EditPullRequest( f.options.Owner, f.options.Repo, int64(pr.ID), forgejo.EditPullRequestOption{ @@ -299,12 +304,15 @@ func (f *Forgejo) UpdatePullRequest(ctx context.Context, pr *releasepr.ReleasePu return nil } -func (f *Forgejo) SetPullRequestLabels(ctx context.Context, pr *releasepr.ReleasePullRequest, remove, add []releasepr.Label) error { +func (f *Forgejo) SetPullRequestLabels(_ context.Context, pr *releasepr.ReleasePullRequest, remove, add []releasepr.Label) error { allLabels, err := all( func(listOptions forgejo.ListOptions) ([]*forgejo.Label, *forgejo.Response, error) { return f.client.ListRepoLabels(f.options.Owner, f.options.Repo, forgejo.ListLabelsOptions{ListOptions: listOptions}) }, ) + if err != nil { + return err + } findLabel := func(labelName string) *forgejo.Label { for _, fLabel := range allLabels { @@ -352,7 +360,7 @@ func (f *Forgejo) SetPullRequestLabels(ctx context.Context, pr *releasepr.Releas return nil } -func (f *Forgejo) ClosePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error { +func (f *Forgejo) ClosePullRequest(_ context.Context, pr *releasepr.ReleasePullRequest) error { _, _, err := f.client.EditPullRequest( f.options.Owner, f.options.Repo, int64(pr.ID), forgejo.EditPullRequestOption{ @@ -366,7 +374,7 @@ func (f *Forgejo) ClosePullRequest(ctx context.Context, pr *releasepr.ReleasePul return nil } -func (f *Forgejo) PendingReleases(ctx context.Context, pendingLabel releasepr.Label) ([]*releasepr.ReleasePullRequest, error) { +func (f *Forgejo) PendingReleases(_ context.Context, pendingLabel releasepr.Label) ([]*releasepr.ReleasePullRequest, error) { fPRs, err := all(func(listOptions forgejo.ListOptions) ([]*forgejo.PullRequest, *forgejo.Response, error) { return f.client.ListRepoPullRequests( f.options.Owner, f.options.Repo, @@ -377,6 +385,7 @@ func (f *Forgejo) PendingReleases(ctx context.Context, pendingLabel releasepr.La }) }) if err != nil { + // "The target couldn't be found." means that the repo does not have pull requests activated. return nil, err } @@ -402,7 +411,7 @@ func (f *Forgejo) PendingReleases(ctx context.Context, pendingLabel releasepr.La return prs, nil } -func (f *Forgejo) CreateRelease(ctx context.Context, commit git.Commit, title, changelog string, preRelease, latest bool) error { +func (f *Forgejo) CreateRelease(_ context.Context, commit git.Commit, title, changelog string, preRelease, latest bool) error { // latest can not be set through the API _, _, err := f.client.CreateRelease( diff --git a/internal/log/log.go b/internal/log/log.go index 88e72c1..32219fd 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -1,6 +1,7 @@ package log import ( + "io" "log/slog" "os" "time" @@ -8,11 +9,15 @@ import ( "github.com/lmittmann/tint" ) -func init() { - slog.SetDefault(slog.New( - tint.NewHandler(os.Stderr, &tint.Options{ +func GetLogger(w io.Writer) *slog.Logger { + return slog.New( + tint.NewHandler(w, &tint.Options{ Level: slog.LevelDebug, TimeFormat: time.RFC3339, }), - )) + ) +} + +func init() { + slog.SetDefault(GetLogger(os.Stderr)) } diff --git a/test/e2e/forge.go b/test/e2e/forge.go new file mode 100644 index 0000000..ea55545 --- /dev/null +++ b/test/e2e/forge.go @@ -0,0 +1,19 @@ +package e2e + +import ( + "context" + "testing" +) + +type TestForge interface { + Init(ctx context.Context, runID string) error + CreateRepo(t *testing.T, opts CreateRepoOpts) (*Repository, error) + + RunArguments() []string +} + +type CreateRepoOpts struct { + Name string + Description string + DefaultBranch string +} diff --git a/test/e2e/forgejo/compose.yaml b/test/e2e/forgejo/compose.yaml index a6d9d12..3f20c3a 100644 --- a/test/e2e/forgejo/compose.yaml +++ b/test/e2e/forgejo/compose.yaml @@ -8,5 +8,9 @@ services: - data:/data/gitea - ./app.ini:/data/gitea/conf/app.ini:ro + healthcheck: + test: ["CMD", "curl", "localhost:3000/api/healthz"] + + volumes: data: diff --git a/test/e2e/forgejo/forge.go b/test/e2e/forgejo/forge.go new file mode 100644 index 0000000..692c0cd --- /dev/null +++ b/test/e2e/forgejo/forge.go @@ -0,0 +1,111 @@ +package forgejo + +import ( + "context" + "fmt" + "log/slog" + "os/exec" + "strings" + "testing" + + "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2" + + "github.com/apricote/releaser-pleaser/test/e2e" +) + +const ( + TestAPIURL = "http://localhost:3000" + + TestUserNameTemplate = "rp-%s" + TestUserPassword = "releaser-pleaser" + TestUserEmailTemplate = "releaser-pleaser-%s@example.com" + TestTokenName = "rp" + TestTokenScopes = "write:user,write:issue,write:repository" +) + +type TestForge struct { + username string + token string + client *forgejo.Client +} + +func (f *TestForge) Init(ctx context.Context, runID string) error { + if err := f.initUser(ctx, runID); err != nil { + return err + } + if err := f.initClient(ctx); err != nil { + return err + } + + return nil +} + +func (f *TestForge) initUser(ctx context.Context, runID string) error { + f.username = fmt.Sprintf(TestUserNameTemplate, runID) + + if output, err := exec.CommandContext(ctx, + "docker", "compose", "exec", "--user=1000", "forgejo", + "forgejo", "admin", "user", "create", + "--username", f.username, + "--password", TestUserPassword, + "--email", fmt.Sprintf(TestUserEmailTemplate, runID), + "--must-change-password=false", + ).CombinedOutput(); err != nil { + slog.Debug("create forgejo user output", "output", output) + return fmt.Errorf("failed to create forgejo user: %w", err) + } + + token, err := exec.CommandContext(ctx, + "docker", "compose", "exec", "--user=1000", "forgejo", + "forgejo", "admin", "user", "generate-access-token", + "--username", f.username, + "--token-name", TestTokenName, + "--scopes", TestTokenScopes, + "--raw", + ).Output() + if err != nil { + return fmt.Errorf("failed to create forgejo token: %w", err) + } + + f.token = strings.TrimSpace(string(token)) + + return nil +} + +func (f *TestForge) initClient(ctx context.Context) (err error) { + f.client, err = forgejo.NewClient(TestAPIURL, + forgejo.SetToken(f.token), + forgejo.SetUserAgent("releaser-pleaser-e2e-tests"), + forgejo.SetContext(ctx), + // forgejo.SetDebugMode(), + ) + return err +} + +func (f *TestForge) CreateRepo(t *testing.T, opts e2e.CreateRepoOpts) (*e2e.Repository, error) { + t.Helper() + + repo, _, err := f.client.CreateRepo(forgejo.CreateRepoOption{ + Name: opts.Name, + Description: opts.Description, + DefaultBranch: opts.DefaultBranch, + Readme: "Default", + AutoInit: true, + }) + if err != nil { + return nil, err + } + + return &e2e.Repository{ + Name: repo.Name, + }, nil +} + +func (f *TestForge) RunArguments() []string { + return []string{"--forge=forgejo", + fmt.Sprintf("--owner=%s", f.username), + fmt.Sprintf("--api-url=%s", TestAPIURL), + fmt.Sprintf("--api-token=%s", f.token), + fmt.Sprintf("--username=%s", f.username), + } +} diff --git a/test/e2e/forgejo/forgejo_test.go b/test/e2e/forgejo/forgejo_test.go new file mode 100644 index 0000000..b52504f --- /dev/null +++ b/test/e2e/forgejo/forgejo_test.go @@ -0,0 +1,39 @@ +//go:build e2e_forgejo + +package forgejo + +import ( + "context" + "log/slog" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/apricote/releaser-pleaser/test/e2e" +) + +var ( + f *e2e.Framework +) + +func TestMain(m *testing.M) { + ctx := context.Background() + + var err error + f, err = e2e.NewFramework(ctx, &TestForge{}) + if err != nil { + slog.Error("failed to set up test framework", "err", err) + } + + os.Exit(m.Run()) +} + +func TestCreateRepository(t *testing.T) { + _ = f.NewRepository(t, t.Name()) +} + +func TestRun(t *testing.T) { + repo := f.NewRepository(t, t.Name()) + require.NoError(t, f.Run(t, repo, []string{})) +} diff --git a/test/e2e/forgejo/framework.go b/test/e2e/forgejo/framework.go deleted file mode 100644 index 4a51da4..0000000 --- a/test/e2e/forgejo/framework.go +++ /dev/null @@ -1,147 +0,0 @@ -//go:build e2e_forgejo - -package forgejo - -import ( - "bytes" - "context" - "crypto/rand" - "encoding/hex" - "fmt" - "log/slog" - "os/exec" - "strings" - "testing" - - "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2" - "github.com/apricote/releaser-pleaser/cmd/rp/cmd" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const ( - TestAPIURL = "http://localhost:3000" - - TestUserNameTemplate = "rp-%s" - TestUserPassword = "releaser-pleaser" - TestUserEmailTemplate = "releaser-pleaser-%s@example.com" - TestTokenName = "rp" - TestTokenScopes = "write:user,write:issue,write:repository" - - TestDefaultBranch = "main" -) - -var ( - TestToken string - TestUserName string - TestClient *forgejo.Client -) - -func randomSuffix() string { - bytes := make([]byte, 4) - if _, err := rand.Read(bytes); err != nil { - panic(err) - } - return hex.EncodeToString(bytes) -} - -func setupTestUser(ctx context.Context, suffix string) string { - TestUserName = fmt.Sprintf(TestUserNameTemplate, suffix) - - if output, err := exec.CommandContext(ctx, - "docker", "compose", "exec", "--user=1000", "forgejo", - "forgejo", "admin", "user", "create", - "--username", TestUserName, - "--password", TestUserPassword, - "--email", fmt.Sprintf(TestUserEmailTemplate, suffix), - "--must-change-password=false", - ).CombinedOutput(); err != nil { - slog.ErrorContext(ctx, "failed to create forgejo user", "err", err, "output", output) - panic(err) - } - - token, err := exec.CommandContext(ctx, - "docker", "compose", "exec", "--user=1000", "forgejo", - "forgejo", "admin", "user", "generate-access-token", - "--username", TestUserName, - "--token-name", TestTokenName, - "--scopes", TestTokenScopes, - "--raw", - ).Output() - if err != nil { - slog.ErrorContext(ctx, "failed to create forgejo token", "err", err) - panic(err) - } - - return strings.TrimSpace(string(token)) -} - -func setupTestClient(ctx context.Context, token string) *forgejo.Client { - client, err := forgejo.NewClient(TestAPIURL, - forgejo.SetToken(token), - forgejo.SetUserAgent("releaser-pleaser-e2e-tests"), - forgejo.SetContext(ctx), - // forgejo.SetDebugMode(), - ) - if err != nil { - panic(err) - } - - return client -} - -type Repository struct { - Name string -} - -func NewRepository(t *testing.T, name string) *Repository { - t.Helper() - - r := &Repository{ - Name: fmt.Sprintf("%s-%s", name, randomSuffix())} - - repo, _, err := TestClient.CreateRepo(forgejo.CreateRepoOption{ - Name: r.Name, - Description: name, - DefaultBranch: TestDefaultBranch, - }) - require.NoError(t, err) - require.NotNil(t, repo) - - return r -} - -func Run(t *testing.T, r *Repository, extraFiles []string) (stdout bytes.Buffer, stderr bytes.Buffer, err error) { - t.Helper() - - ctx := t.Context() - - rootCmd := cmd.NewRootCmd() - rootCmd.SetArgs([]string{ - "run", "--forge=forgejo", - fmt.Sprintf("--branch=%s", TestDefaultBranch), - fmt.Sprintf("--owner=%s", TestUserName), - fmt.Sprintf("--repo=%s", r.Name), - fmt.Sprintf("--extra-files=%q", strings.Join(extraFiles, "\n")), - fmt.Sprintf("--api-url=%s", TestAPIURL), - fmt.Sprintf("--api-token=%s", TestToken), - fmt.Sprintf("--username=%s", TestUserName), - }) - - rootCmd.SetOut(&stdout) - rootCmd.SetErr(&stderr) - - err = rootCmd.ExecuteContext(ctx) - - return stdout, stderr, err -} - -func MustRun(t *testing.T, r *Repository, extraFiles []string) { - t.Helper() - - stdout, stderr, err := Run(t, r, extraFiles) - if !assert.NoError(t, err) { - t.Log(stdout) - t.Log(stderr) - } -} diff --git a/test/e2e/forgejo/framework_test.go b/test/e2e/forgejo/framework_test.go deleted file mode 100644 index 4d99743..0000000 --- a/test/e2e/forgejo/framework_test.go +++ /dev/null @@ -1,39 +0,0 @@ -//go:build e2e_forgejo - -package forgejo - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestMain(m *testing.M) { - ctx := context.Background() - - suffix := randomSuffix() - - TestToken = setupTestUser(ctx, suffix) - - TestClient = setupTestClient(ctx, TestToken) - - os.Exit(m.Run()) -} - -func TestAPIAccess(t *testing.T) { - user, _, err := TestClient.GetMyUserInfo() - require.NoError(t, err) - require.NotNil(t, user) -} - -func TestCreateRepository(t *testing.T) { - _ = NewRepository(t, t.Name()) -} - -func TestRun(t *testing.T) { - repo := NewRepository(t, t.Name()) - MustRun(t, repo, []string{}) - -} diff --git a/test/e2e/framework.go b/test/e2e/framework.go new file mode 100644 index 0000000..7352f14 --- /dev/null +++ b/test/e2e/framework.go @@ -0,0 +1,96 @@ +package e2e + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/hex" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/apricote/releaser-pleaser/cmd/rp/cmd" +) + +const ( + TestDefaultBranch = "main" +) + +func randomString() string { + randomBytes := make([]byte, 4) + if _, err := rand.Read(randomBytes); err != nil { + panic(err) + } + return hex.EncodeToString(randomBytes) +} + +type Framework struct { + runID string + forge TestForge +} + +func NewFramework(ctx context.Context, forge TestForge) (*Framework, error) { + f := &Framework{ + runID: randomString(), + forge: forge, + } + + err := forge.Init(ctx, f.runID) + if err != nil { + return nil, err + } + + return f, nil +} + +type Repository struct { + Name string +} + +func (f *Framework) NewRepository(t *testing.T, name string) *Repository { + t.Helper() + + r := &Repository{ + Name: fmt.Sprintf("%s-%s-%s", name, f.runID, randomString()), + } + + repo, err := f.forge.CreateRepo(t, CreateRepoOpts{ + Name: r.Name, + Description: name, + DefaultBranch: TestDefaultBranch, + }) + require.NoError(t, err) + require.NotNil(t, repo) + + return r +} + +func (f *Framework) Run(t *testing.T, r *Repository, extraFiles []string) error { + t.Helper() + + ctx := t.Context() + + rootCmd := cmd.NewRootCmd() + rootCmd.SetArgs(append([]string{ + "run", + fmt.Sprintf("--repo=%s", r.Name), + fmt.Sprintf("--extra-files=%q", strings.Join(extraFiles, "\n")), + }, f.forge.RunArguments()...)) + + var stdout, stderr bytes.Buffer + + rootCmd.SetOut(&stdout) + rootCmd.SetErr(&stderr) + + err := rootCmd.ExecuteContext(ctx) + + stdoutString := stdout.String() + stderrString := stderr.String() + + t.Log(stdoutString) + t.Log(stderrString) + + return err +}