* Create an APIError that should only be used for api returned errors. It'll wrap an error and can have different Kinds and optional code and message. * The http handlers will use the first APIError available in the error chain and generate a json response body containing the code and the user message. The wrapped error is internal and is not sent in the response. If no api error is available in the chain a generic internal server error will be returned. * Add a RemoteError type that will be created from remote services calls (runservice, configstore). It's similar to the APIError but a different type to not propagate to the caller response and it'll not contain any wrapped error. * Gateway: when we call a remote service, by default, we'll create a APIError using the RemoteError Kind (omitting the code and the message that usually must not be propagated). This is done for all the remote service calls as a starting point, in future, if this default behavior is not the right one for a specific remote service call, a new api error with a different kind and/or augmented with the calling service error codes and user messages could be created. * datamanager: Use a dedicated ErrNotExist (and converting objectstorage ErrNotExist).
2003 lines
53 KiB
Go
2003 lines
53 KiB
Go
// Copyright 2019 Sorint.lab
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package tests
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"agola.io/agola/internal/services/config"
|
|
"agola.io/agola/internal/services/configstore"
|
|
"agola.io/agola/internal/services/executor"
|
|
"agola.io/agola/internal/services/gateway"
|
|
"agola.io/agola/internal/services/gitserver"
|
|
"agola.io/agola/internal/services/notification"
|
|
rsscheduler "agola.io/agola/internal/services/runservice"
|
|
"agola.io/agola/internal/services/scheduler"
|
|
"agola.io/agola/internal/testutil"
|
|
"agola.io/agola/internal/util"
|
|
gwapitypes "agola.io/agola/services/gateway/api/types"
|
|
gwclient "agola.io/agola/services/gateway/client"
|
|
rstypes "agola.io/agola/services/runservice/types"
|
|
|
|
"code.gitea.io/sdk/gitea"
|
|
"github.com/google/go-cmp/cmp"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zaptest"
|
|
errors "golang.org/x/xerrors"
|
|
"gopkg.in/src-d/go-billy.v4/memfs"
|
|
"gopkg.in/src-d/go-billy.v4/osfs"
|
|
"gopkg.in/src-d/go-git.v4"
|
|
gitconfig "gopkg.in/src-d/go-git.v4/config"
|
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
"gopkg.in/src-d/go-git.v4/plumbing/cache"
|
|
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
|
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
|
"gopkg.in/src-d/go-git.v4/storage/filesystem"
|
|
"gopkg.in/src-d/go-git.v4/storage/memory"
|
|
)
|
|
|
|
const (
|
|
giteaUser01 = "user01"
|
|
giteaUser02 = "user02"
|
|
|
|
agolaUser01 = "user01"
|
|
)
|
|
|
|
type ConfigFormat string
|
|
|
|
const (
|
|
// ConfigFormatJSON handles both json or yaml format (since json is a subset of yaml)
|
|
ConfigFormatJSON ConfigFormat = "json"
|
|
ConfigFormatJsonnet ConfigFormat = "jsonnet"
|
|
ConfigFormatStarlark ConfigFormat = "starlark"
|
|
)
|
|
|
|
func setupEtcd(t *testing.T, logger *zap.Logger, dir string) *testutil.TestEmbeddedEtcd {
|
|
tetcd, err := testutil.NewTestEmbeddedEtcd(t, logger, dir)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if err := tetcd.Start(); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if err := tetcd.WaitUp(30 * time.Second); err != nil {
|
|
t.Fatalf("error waiting on etcd up: %v", err)
|
|
}
|
|
return tetcd
|
|
}
|
|
|
|
func shutdownEtcd(tetcd *testutil.TestEmbeddedEtcd) {
|
|
if tetcd.Etcd != nil {
|
|
_ = tetcd.Kill()
|
|
}
|
|
}
|
|
|
|
func setupGitea(t *testing.T, dir, dockerBridgeAddress string) *testutil.TestGitea {
|
|
tgitea, err := testutil.NewTestGitea(t, dir, dockerBridgeAddress)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if err := tgitea.Start(); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
// wait for gitea ready
|
|
err = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
cmd := exec.Command(tgitea.GiteaPath, "admin", "user", "create", "--name", giteaUser01, "--email", giteaUser01+"@example.com", "--password", "password", "--admin", "--config", tgitea.ConfigPath)
|
|
// just retry until no error
|
|
if err := cmd.Run(); err != nil {
|
|
return false, nil
|
|
}
|
|
return true, nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
|
|
giteaClient := gitea.NewClient(giteaAPIURL, "")
|
|
|
|
// Wait for gitea api to be ready
|
|
err = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
giteaClient.SetBasicAuth(giteaUser01, "password")
|
|
if _, err := giteaClient.ListAccessTokens(gitea.ListAccessTokensOptions{}); err != nil {
|
|
return false, nil
|
|
}
|
|
return true, nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
return tgitea
|
|
}
|
|
|
|
func shutdownGitea(tgitea *testutil.TestGitea) {
|
|
tgitea.Kill()
|
|
}
|
|
|
|
func startAgola(ctx context.Context, t *testing.T, logger *zap.Logger, dir string, c *config.Config) (<-chan error, error) {
|
|
rs, err := rsscheduler.NewRunservice(ctx, logger, &c.Runservice)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to start run service scheduler: %w", err)
|
|
}
|
|
|
|
ex, err := executor.NewExecutor(ctx, logger, &c.Executor)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to start run service executor: %w", err)
|
|
}
|
|
|
|
cs, err := configstore.NewConfigstore(ctx, logger, &c.Configstore)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to start config store: %w", err)
|
|
}
|
|
|
|
sched, err := scheduler.NewScheduler(ctx, logger, &c.Scheduler)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to start scheduler: %w", err)
|
|
}
|
|
|
|
ns, err := notification.NewNotificationService(ctx, logger, c)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to start notification service: %w", err)
|
|
}
|
|
|
|
gw, err := gateway.NewGateway(ctx, logger, c)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to start gateway: %w", err)
|
|
}
|
|
|
|
gs, err := gitserver.NewGitserver(ctx, logger, &c.Gitserver)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to start git server: %w", err)
|
|
}
|
|
|
|
errCh := make(chan error)
|
|
|
|
go func() { errCh <- rs.Run(ctx) }()
|
|
go func() { errCh <- ex.Run(ctx) }()
|
|
go func() { errCh <- cs.Run(ctx) }()
|
|
go func() { errCh <- sched.Run(ctx) }()
|
|
go func() { errCh <- ns.Run(ctx) }()
|
|
go func() { errCh <- gw.Run(ctx) }()
|
|
go func() { errCh <- gs.Run(ctx) }()
|
|
|
|
// TODO(sgotti) find a better way to test that all is ready instead of sleeping
|
|
time.Sleep(5 * time.Second)
|
|
|
|
return errCh, nil
|
|
}
|
|
|
|
func setup(ctx context.Context, t *testing.T, dir string) (*testutil.TestEmbeddedEtcd, *testutil.TestGitea, *config.Config) {
|
|
logger := zaptest.NewLogger(t, zaptest.Level(zap.InfoLevel))
|
|
|
|
dockerBridgeAddress := os.Getenv("DOCKER_BRIDGE_ADDRESS")
|
|
if dockerBridgeAddress == "" {
|
|
dockerBridgeAddress = "172.17.0.1"
|
|
}
|
|
agolaBinDir := os.Getenv("AGOLA_BIN_DIR")
|
|
if agolaBinDir == "" {
|
|
t.Fatalf("env var AGOLA_BIN_DIR is undefined")
|
|
}
|
|
|
|
c := &config.Config{
|
|
ID: "agola",
|
|
Gateway: config.Gateway{
|
|
Debug: false,
|
|
APIExposedURL: "",
|
|
WebExposedURL: "",
|
|
RunserviceURL: "",
|
|
ConfigstoreURL: "",
|
|
GitserverURL: "",
|
|
Web: config.Web{
|
|
ListenAddress: "",
|
|
TLS: false,
|
|
},
|
|
TokenSigning: config.TokenSigning{
|
|
Duration: 12 * time.Hour,
|
|
Method: "hmac",
|
|
Key: "supersecretsigningkey",
|
|
},
|
|
AdminToken: "admintoken",
|
|
},
|
|
Scheduler: config.Scheduler{
|
|
Debug: false,
|
|
RunserviceURL: "",
|
|
},
|
|
Notification: config.Notification{
|
|
Debug: false,
|
|
WebExposedURL: "",
|
|
RunserviceURL: "",
|
|
ConfigstoreURL: "",
|
|
Etcd: config.Etcd{
|
|
Endpoints: "",
|
|
},
|
|
},
|
|
Runservice: config.Runservice{
|
|
Debug: false,
|
|
DataDir: filepath.Join(dir, "runservice"),
|
|
Web: config.Web{
|
|
ListenAddress: ":4000",
|
|
TLS: false,
|
|
},
|
|
Etcd: config.Etcd{
|
|
Endpoints: "",
|
|
},
|
|
ObjectStorage: config.ObjectStorage{
|
|
Type: "posix",
|
|
Path: filepath.Join(dir, "runservice/ost"),
|
|
},
|
|
RunCacheExpireInterval: 604800000000000,
|
|
},
|
|
Executor: config.Executor{
|
|
Debug: false,
|
|
DataDir: filepath.Join(dir, "executor"),
|
|
RunserviceURL: "",
|
|
ToolboxPath: agolaBinDir,
|
|
Web: config.Web{
|
|
ListenAddress: ":4001",
|
|
TLS: false,
|
|
},
|
|
Driver: config.Driver{
|
|
Type: "docker",
|
|
},
|
|
Labels: map[string]string{},
|
|
ActiveTasksLimit: 2,
|
|
InitImage: config.InitImage{
|
|
Image: "busybox:stable",
|
|
},
|
|
},
|
|
Configstore: config.Configstore{
|
|
Debug: false,
|
|
DataDir: filepath.Join(dir, "configstore"),
|
|
Web: config.Web{
|
|
ListenAddress: ":4002",
|
|
TLS: false,
|
|
},
|
|
Etcd: config.Etcd{
|
|
Endpoints: "",
|
|
},
|
|
ObjectStorage: config.ObjectStorage{
|
|
Type: "posix",
|
|
Path: filepath.Join(dir, "configstore/ost"),
|
|
},
|
|
},
|
|
Gitserver: config.Gitserver{
|
|
Debug: false,
|
|
DataDir: filepath.Join(dir, "gitserver"),
|
|
Web: config.Web{
|
|
ListenAddress: ":4003",
|
|
TLS: false,
|
|
},
|
|
Etcd: config.Etcd{
|
|
Endpoints: "",
|
|
},
|
|
RepositoryCleanupInterval: 24 * time.Hour,
|
|
},
|
|
}
|
|
|
|
tgitea := setupGitea(t, dir, dockerBridgeAddress)
|
|
|
|
etcdDir := filepath.Join(dir, "etcd")
|
|
tetcd := setupEtcd(t, logger, etcdDir)
|
|
|
|
c.Runservice.Etcd.Endpoints = tetcd.Endpoint
|
|
c.Configstore.Etcd.Endpoints = tetcd.Endpoint
|
|
|
|
_, gwPort, err := testutil.GetFreePort(true, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
_, csPort, err := testutil.GetFreePort(true, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
_, rsPort, err := testutil.GetFreePort(true, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
_, exPort, err := testutil.GetFreePort(true, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
listenAddress, gitServerPort, err := testutil.GetFreePort(true, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
gwURL := fmt.Sprintf("http://%s:%s", dockerBridgeAddress, gwPort)
|
|
csURL := fmt.Sprintf("http://%s:%s", listenAddress, csPort)
|
|
rsURL := fmt.Sprintf("http://%s:%s", listenAddress, rsPort)
|
|
gitServerURL := fmt.Sprintf("http://%s:%s", dockerBridgeAddress, gitServerPort)
|
|
|
|
c.Gateway.Web.ListenAddress = fmt.Sprintf("%s:%s", dockerBridgeAddress, gwPort)
|
|
c.Configstore.Web.ListenAddress = fmt.Sprintf("%s:%s", listenAddress, csPort)
|
|
c.Runservice.Web.ListenAddress = fmt.Sprintf("%s:%s", listenAddress, rsPort)
|
|
c.Executor.Web.ListenAddress = fmt.Sprintf("%s:%s", listenAddress, exPort)
|
|
c.Gitserver.Web.ListenAddress = fmt.Sprintf("%s:%s", dockerBridgeAddress, gitServerPort)
|
|
|
|
c.Gateway.APIExposedURL = gwURL
|
|
c.Gateway.WebExposedURL = gwURL
|
|
c.Gateway.RunserviceURL = rsURL
|
|
c.Gateway.ConfigstoreURL = csURL
|
|
c.Gateway.GitserverURL = gitServerURL
|
|
|
|
c.Scheduler.RunserviceURL = rsURL
|
|
|
|
c.Notification.WebExposedURL = gwURL
|
|
c.Notification.RunserviceURL = rsURL
|
|
c.Notification.ConfigstoreURL = csURL
|
|
|
|
c.Executor.RunserviceURL = rsURL
|
|
|
|
errCh, err := startAgola(ctx, t, logger, dir, c)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
go func() {
|
|
err := <-errCh
|
|
if err != nil {
|
|
panic(fmt.Errorf("agola component returned error: %w", err))
|
|
}
|
|
}()
|
|
|
|
return tetcd, tgitea, c
|
|
}
|
|
|
|
func TestCreateLinkedAccount(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
createLinkedAccount(ctx, t, tgitea, c)
|
|
}
|
|
|
|
func createAgolaUserToken(ctx context.Context, t *testing.T, c *config.Config) string {
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken")
|
|
token, _, err := gwClient.CreateUserToken(ctx, agolaUser01, &gwapitypes.CreateUserTokenRequest{TokenName: "token01"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created agola user token: %s", token.Token)
|
|
|
|
return token.Token
|
|
}
|
|
|
|
func createLinkedAccount(ctx context.Context, t *testing.T, tgitea *testutil.TestGitea, c *config.Config) (string, string) {
|
|
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
|
|
giteaClient := gitea.NewClient(giteaAPIURL, "")
|
|
|
|
giteaClient.SetBasicAuth(giteaUser01, "password")
|
|
giteaToken, err := giteaClient.CreateAccessToken(gitea.CreateAccessTokenOption{Name: "token01"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created gitea user token: %s", giteaToken.Token)
|
|
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken")
|
|
user, _, err := gwClient.CreateUser(ctx, &gwapitypes.CreateUserRequest{UserName: agolaUser01})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created agola user: %s", user.UserName)
|
|
|
|
token := createAgolaUserToken(ctx, t, c)
|
|
|
|
rs, _, err := gwClient.CreateRemoteSource(ctx, &gwapitypes.CreateRemoteSourceRequest{
|
|
Name: "gitea",
|
|
APIURL: giteaAPIURL,
|
|
Type: "gitea",
|
|
AuthType: "password",
|
|
SkipSSHHostKeyCheck: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created agola remote source: %s", rs.Name)
|
|
|
|
// From now use the user token
|
|
gwClient = gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
la, _, err := gwClient.CreateUserLA(ctx, agolaUser01, &gwapitypes.CreateUserLARequest{
|
|
RemoteSourceName: "gitea",
|
|
RemoteSourceLoginName: giteaUser01,
|
|
RemoteSourceLoginPassword: "password",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created user linked account: %s", util.Dump(la))
|
|
|
|
return giteaToken.Token, token
|
|
}
|
|
|
|
func TestCreateProject(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
|
|
|
|
giteaToken, token := createLinkedAccount(ctx, t, tgitea, c)
|
|
|
|
giteaClient := gitea.NewClient(giteaAPIURL, giteaToken)
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
createProject(ctx, t, giteaClient, gwClient)
|
|
}
|
|
|
|
func TestUpdateProject(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
passVarsToForkedPR bool
|
|
expected_pre bool
|
|
expected_post bool
|
|
}{
|
|
{
|
|
name: "test project update with pass-vars-to-forked-pr true",
|
|
passVarsToForkedPR: true,
|
|
expected_pre: false,
|
|
expected_post: true,
|
|
},
|
|
{
|
|
name: "test project update with pass-vars-to-forked-pr false",
|
|
passVarsToForkedPR: false,
|
|
expected_pre: false,
|
|
expected_post: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
|
|
|
|
giteaToken, token := createLinkedAccount(ctx, t, tgitea, c)
|
|
|
|
giteaClient := gitea.NewClient(giteaAPIURL, giteaToken)
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
_, project := createProject(ctx, t, giteaClient, gwClient)
|
|
if project.PassVarsToForkedPR != tt.expected_pre {
|
|
t.Fatalf("expected PassVarsToForkedPR %v, got %v (pre-update)", tt.expected_pre, project.PassVarsToForkedPR)
|
|
}
|
|
project = updateProject(ctx, t, giteaClient, gwClient, project.ID, tt.passVarsToForkedPR)
|
|
if project.PassVarsToForkedPR != tt.expected_post {
|
|
t.Fatalf("expected PassVarsToForkedPR %v, got %v (port-update)", tt.expected_post, project.PassVarsToForkedPR)
|
|
}
|
|
}
|
|
}
|
|
|
|
func createProject(ctx context.Context, t *testing.T, giteaClient *gitea.Client, gwClient *gwclient.Client) (*gitea.Repository, *gwapitypes.ProjectResponse) {
|
|
giteaRepo, err := giteaClient.CreateRepo(gitea.CreateRepoOption{
|
|
Name: "repo01",
|
|
Private: false,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created gitea repo: %s", giteaRepo.Name)
|
|
|
|
project, _, err := gwClient.CreateProject(ctx, &gwapitypes.CreateProjectRequest{
|
|
Name: "project01",
|
|
ParentRef: path.Join("user", agolaUser01),
|
|
RemoteSourceName: "gitea",
|
|
RepoPath: path.Join(giteaUser01, "repo01"),
|
|
Visibility: gwapitypes.VisibilityPublic,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
return giteaRepo, project
|
|
}
|
|
|
|
func updateProject(ctx context.Context, t *testing.T, giteaClient *gitea.Client, gwClient *gwclient.Client, projectRef string, passVarsToForkedPR bool) *gwapitypes.ProjectResponse {
|
|
project, _, err := gwClient.UpdateProject(ctx, projectRef, &gwapitypes.UpdateProjectRequest{
|
|
PassVarsToForkedPR: util.BoolP(passVarsToForkedPR),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
return project
|
|
}
|
|
|
|
func push(t *testing.T, config, cloneURL, remoteToken, message string, pushNewBranch bool) {
|
|
gitfs := memfs.New()
|
|
f, err := gitfs.Create(".agola/config.jsonnet")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if _, err = f.Write([]byte(config)); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
r, err := git.Init(memory.NewStorage(), gitfs)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
if _, err := r.CreateRemote(&gitconfig.RemoteConfig{
|
|
Name: "origin",
|
|
URLs: []string{cloneURL},
|
|
}); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
wt, err := r.Worktree()
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if _, err := wt.Add(".agola/config.jsonnet"); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
_, err = wt.Commit(message, &git.CommitOptions{
|
|
Author: &object.Signature{
|
|
Name: "user01",
|
|
Email: "user01@example.com",
|
|
When: time.Now(),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
t.Logf("sshurl: %s", cloneURL)
|
|
if err := r.Push(&git.PushOptions{
|
|
RemoteName: "origin",
|
|
Auth: &http.BasicAuth{
|
|
Username: giteaUser01,
|
|
Password: remoteToken,
|
|
},
|
|
}); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
if pushNewBranch {
|
|
// change worktree and push to a new branch
|
|
headRef, err := r.Head()
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
ref := plumbing.NewHashReference("refs/heads/new-branch", headRef.Hash())
|
|
err = r.Storer.SetReference(ref)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
f, err = gitfs.Create("file1")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if _, err = f.Write([]byte("my file content")); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if _, err := wt.Add("file1"); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
_, err = wt.Commit("add file1", &git.CommitOptions{
|
|
Author: &object.Signature{
|
|
Name: "user01",
|
|
Email: "user01@example.com",
|
|
When: time.Now(),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
if err := r.Push(&git.PushOptions{
|
|
RemoteName: "origin",
|
|
RefSpecs: []gitconfig.RefSpec{
|
|
gitconfig.RefSpec("refs/heads/new-branch:refs/heads/new-branch"),
|
|
},
|
|
Auth: &http.BasicAuth{
|
|
Username: giteaUser01,
|
|
Password: remoteToken,
|
|
},
|
|
}); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPush(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config string
|
|
num int
|
|
annotations map[string]string
|
|
message string
|
|
}{
|
|
{
|
|
name: "test push",
|
|
config: `
|
|
{
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'env' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
`,
|
|
num: 1,
|
|
annotations: map[string]string{
|
|
"branch": "master",
|
|
"ref": "refs/heads/master",
|
|
"ref_type": "branch",
|
|
},
|
|
message: "commit",
|
|
},
|
|
{
|
|
name: "test push with unmatched branch",
|
|
config: `
|
|
{
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'env' },
|
|
],
|
|
},
|
|
],
|
|
when: {
|
|
branch: 'notmaster',
|
|
},
|
|
},
|
|
],
|
|
}
|
|
`,
|
|
num: 0,
|
|
message: "commit",
|
|
},
|
|
{
|
|
name: "test push with [ci skip] in subject",
|
|
config: `
|
|
{
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'env' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
`,
|
|
num: 0,
|
|
message: "[ci skip] commit",
|
|
},
|
|
{
|
|
name: "test push with [ci skip] in body",
|
|
config: `
|
|
{
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'env' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
`,
|
|
num: 0,
|
|
message: "commit\n\n[ci skip] body",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
|
|
|
|
giteaToken, token := createLinkedAccount(ctx, t, tgitea, c)
|
|
|
|
giteaClient := gitea.NewClient(giteaAPIURL, giteaToken)
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
giteaRepo, project := createProject(ctx, t, giteaClient, gwClient)
|
|
|
|
push(t, tt.config, giteaRepo.CloneURL, giteaToken, tt.message, false)
|
|
|
|
_ = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/project", project.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
if len(runs) == 0 {
|
|
return false, nil
|
|
}
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
})
|
|
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/project", project.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
t.Logf("runs: %s", util.Dump(runs))
|
|
|
|
if len(runs) != tt.num {
|
|
t.Fatalf("expected %d run got: %d", tt.num, len(runs))
|
|
}
|
|
|
|
if len(runs) > 0 {
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
t.Fatalf("expected run phase %q, got %q", rstypes.RunPhaseFinished, run.Phase)
|
|
}
|
|
if run.Result != rstypes.RunResultSuccess {
|
|
t.Fatalf("expected run result %q, got %q", rstypes.RunResultSuccess, run.Result)
|
|
}
|
|
for k, v := range tt.annotations {
|
|
if run.Annotations[k] != v {
|
|
t.Fatalf("expected run annotation %q value %q, got %q", k, v, run.Annotations[k])
|
|
}
|
|
}
|
|
}
|
|
|
|
})
|
|
}
|
|
}
|
|
|
|
func directRun(t *testing.T, dir, config string, configFormat ConfigFormat, gatewayURL, token string, args ...string) {
|
|
agolaBinDir := os.Getenv("AGOLA_BIN_DIR")
|
|
if agolaBinDir == "" {
|
|
t.Fatalf("env var AGOLA_BIN_DIR is undefined")
|
|
}
|
|
agolaBinDir, err := filepath.Abs(agolaBinDir)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
repoDir, err := ioutil.TempDir(dir, "repo")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
gitfs := osfs.New(repoDir)
|
|
dot, _ := gitfs.Chroot(".git")
|
|
|
|
var configPath string
|
|
switch configFormat {
|
|
case ConfigFormatJsonnet:
|
|
configPath = ".agola/config.jsonnet"
|
|
case ConfigFormatStarlark:
|
|
configPath = ".agola/config.star"
|
|
}
|
|
|
|
f, err := gitfs.Create(configPath)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if _, err = f.Write([]byte(config)); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
_, err = git.Init(filesystem.NewStorage(dot, cache.NewObjectLRUDefault()), gitfs)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
args = append([]string{"--gateway-url", gatewayURL, "--token", token, "directrun", "start", "--untracked", "false"}, args...)
|
|
cmd := exec.Command(filepath.Join(agolaBinDir, "agola"), args...)
|
|
cmd.Dir = repoDir
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v, out: %s", err, out)
|
|
}
|
|
t.Logf("directrun start out: %s", out)
|
|
}
|
|
|
|
func TestDirectRun(t *testing.T) {
|
|
config := `
|
|
{
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'env' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
`
|
|
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
annotations map[string]string
|
|
}{
|
|
{
|
|
name: "test direct run",
|
|
annotations: map[string]string{
|
|
"branch": "master",
|
|
"ref": "refs/heads/master",
|
|
"ref_type": "branch",
|
|
},
|
|
},
|
|
{
|
|
name: "test direct run with destination branch",
|
|
args: []string{"--branch", "develop"},
|
|
annotations: map[string]string{
|
|
"branch": "develop",
|
|
"ref": "refs/heads/develop",
|
|
"ref_type": "branch",
|
|
},
|
|
},
|
|
{
|
|
name: "test direct run with destination tag",
|
|
args: []string{"--tag", "v0.1.0"},
|
|
annotations: map[string]string{
|
|
"tag": "v0.1.0",
|
|
"ref": "refs/tags/v0.1.0",
|
|
"ref_type": "tag",
|
|
},
|
|
},
|
|
{
|
|
name: "test direct run with destination ref as a pr",
|
|
args: []string{"--ref", "refs/pull/1/head"},
|
|
annotations: map[string]string{
|
|
"pull_request_id": "1",
|
|
"ref": "refs/pull/1/head",
|
|
"ref_type": "pull_request",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken")
|
|
user, _, err := gwClient.CreateUser(ctx, &gwapitypes.CreateUserRequest{UserName: agolaUser01})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created agola user: %s", user.UserName)
|
|
|
|
token := createAgolaUserToken(ctx, t, c)
|
|
|
|
// From now use the user token
|
|
gwClient = gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
directRun(t, dir, config, ConfigFormatJsonnet, c.Gateway.APIExposedURL, token, tt.args...)
|
|
|
|
_ = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
if len(runs) != 1 {
|
|
return false, nil
|
|
}
|
|
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
})
|
|
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
t.Logf("runs: %s", util.Dump(runs))
|
|
|
|
if len(runs) != 1 {
|
|
t.Fatalf("expected 1 run got: %d", len(runs))
|
|
}
|
|
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
t.Fatalf("expected run phase %q, got %q", rstypes.RunPhaseFinished, run.Phase)
|
|
}
|
|
if run.Result != rstypes.RunResultSuccess {
|
|
t.Fatalf("expected run result %q, got %q", rstypes.RunResultSuccess, run.Result)
|
|
}
|
|
for k, v := range tt.annotations {
|
|
if run.Annotations[k] != v {
|
|
t.Fatalf("expected run annotation %q value %q, got %q", k, v, run.Annotations[k])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDirectRunVariables(t *testing.T) {
|
|
config := `
|
|
{
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
environment: {
|
|
ENV01: { from_variable: 'variable01' },
|
|
ENV02: { from_variable: 'variable02' },
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'env' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
`
|
|
|
|
varfile01 := `
|
|
variable01: "variable value 01"
|
|
variable02: variable value 02
|
|
`
|
|
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
env map[string]string
|
|
}{
|
|
{
|
|
name: "test direct run without variables",
|
|
args: []string{},
|
|
env: map[string]string{
|
|
"ENV01": "",
|
|
"ENV02": "",
|
|
},
|
|
},
|
|
{
|
|
name: "test direct run with two variables",
|
|
args: []string{"--var", "variable01=VARIABLEVALUE01", "--var", "variable02=VARIABLEVALUE02"},
|
|
env: map[string]string{
|
|
"ENV01": "VARIABLEVALUE01",
|
|
"ENV02": "VARIABLEVALUE02",
|
|
},
|
|
},
|
|
{
|
|
name: "test direct run with a var file",
|
|
args: []string{"--var-file", "../varfile01.yml"},
|
|
env: map[string]string{
|
|
"ENV01": "variable value 01",
|
|
"ENV02": "variable value 02",
|
|
},
|
|
},
|
|
{
|
|
name: "test direct run with a var file and a var that overrides",
|
|
args: []string{"--var-file", "../varfile01.yml", "--var", "variable02=VARIABLEVALUE02"},
|
|
env: map[string]string{
|
|
"ENV01": "variable value 01",
|
|
"ENV02": "VARIABLEVALUE02",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
if err := ioutil.WriteFile(filepath.Join(dir, "varfile01.yml"), []byte(varfile01), 0644); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken")
|
|
user, _, err := gwClient.CreateUser(ctx, &gwapitypes.CreateUserRequest{UserName: agolaUser01})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created agola user: %s", user.UserName)
|
|
|
|
token := createAgolaUserToken(ctx, t, c)
|
|
|
|
// From now use the user token
|
|
gwClient = gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
directRun(t, dir, config, ConfigFormatJsonnet, c.Gateway.APIExposedURL, token, tt.args...)
|
|
|
|
// TODO(sgotti) add an util to wait for a run phase
|
|
_ = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
if len(runs) != 1 {
|
|
return false, nil
|
|
}
|
|
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
})
|
|
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
t.Logf("runs: %s", util.Dump(runs))
|
|
|
|
if len(runs) != 1 {
|
|
t.Fatalf("expected 1 run got: %d", len(runs))
|
|
}
|
|
|
|
run, _, err := gwClient.GetRun(ctx, runs[0].ID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
t.Fatalf("expected run phase %q, got %q", rstypes.RunPhaseFinished, run.Phase)
|
|
}
|
|
if run.Result != rstypes.RunResultSuccess {
|
|
t.Fatalf("expected run result %q, got %q", rstypes.RunResultSuccess, run.Result)
|
|
}
|
|
|
|
var task *gwapitypes.RunResponseTask
|
|
for _, t := range run.Tasks {
|
|
if t.Name == "task01" {
|
|
task = t
|
|
break
|
|
}
|
|
}
|
|
|
|
resp, err := gwClient.GetLogs(ctx, run.ID, task.ID, false, 1, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
logs, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
curEnv, err := testutil.ParseEnvs(bytes.NewReader(logs))
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
for n, e := range tt.env {
|
|
if ce, ok := curEnv[n]; !ok {
|
|
t.Fatalf("missing env var %s", n)
|
|
} else {
|
|
if ce != e {
|
|
t.Fatalf("different env var %s value, want: %q, got %q", n, e, ce)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDirectRunLogs(t *testing.T) {
|
|
config := `
|
|
{
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'echo STEPLOG' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
`
|
|
|
|
tests := []struct {
|
|
name string
|
|
setup bool
|
|
step int
|
|
delete bool
|
|
err error
|
|
}{
|
|
{
|
|
name: "test get log step 1",
|
|
step: 1,
|
|
},
|
|
{
|
|
name: "test get log setup",
|
|
setup: true,
|
|
},
|
|
{
|
|
name: "test get log with unexisting step",
|
|
step: 99,
|
|
err: util.NewRemoteError(util.ErrNotExist, "", ""),
|
|
},
|
|
{
|
|
name: "test delete log step 1",
|
|
step: 1,
|
|
delete: true,
|
|
},
|
|
{
|
|
name: "test delete log setup",
|
|
setup: true,
|
|
delete: true,
|
|
},
|
|
{
|
|
name: "test delete log with unexisting step",
|
|
step: 99,
|
|
delete: true,
|
|
err: util.NewRemoteError(util.ErrNotExist, "", ""),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken")
|
|
user, _, err := gwClient.CreateUser(ctx, &gwapitypes.CreateUserRequest{UserName: agolaUser01})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created agola user: %s", user.UserName)
|
|
|
|
token := createAgolaUserToken(ctx, t, c)
|
|
|
|
// From now use the user token
|
|
gwClient = gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
directRun(t, dir, config, ConfigFormatJsonnet, c.Gateway.APIExposedURL, token)
|
|
|
|
_ = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
if len(runs) != 1 {
|
|
return false, nil
|
|
}
|
|
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
})
|
|
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
t.Logf("runs: %s", util.Dump(runs))
|
|
|
|
if len(runs) != 1 {
|
|
t.Fatalf("expected 1 run got: %d", len(runs))
|
|
}
|
|
|
|
run, _, err := gwClient.GetRun(ctx, runs[0].ID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
t.Fatalf("expected run phase %q, got %q", rstypes.RunPhaseFinished, run.Phase)
|
|
}
|
|
if run.Result != rstypes.RunResultSuccess {
|
|
t.Fatalf("expected run result %q, got %q", rstypes.RunResultSuccess, run.Result)
|
|
}
|
|
|
|
var task *gwapitypes.RunResponseTask
|
|
for _, t := range run.Tasks {
|
|
if t.Name == "task01" {
|
|
task = t
|
|
break
|
|
}
|
|
}
|
|
|
|
_ = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
t, _, err := gwClient.GetRunTask(ctx, runs[0].ID, task.ID)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
if tt.step >= len(t.Steps) {
|
|
return true, nil
|
|
}
|
|
if !t.Steps[tt.step].LogArchived {
|
|
return false, nil
|
|
}
|
|
return true, nil
|
|
})
|
|
|
|
if tt.delete {
|
|
_, err = gwClient.DeleteLogs(ctx, run.ID, task.ID, tt.setup, tt.step)
|
|
} else {
|
|
_, err = gwClient.GetLogs(ctx, run.ID, task.ID, tt.setup, tt.step, false)
|
|
}
|
|
|
|
if err != nil {
|
|
if tt.err == nil {
|
|
t.Fatalf("got error: %v, expected no error", err)
|
|
}
|
|
if !strings.HasPrefix(err.Error(), tt.err.Error()) {
|
|
t.Fatalf("got error: %v, want error: %v", err, tt.err)
|
|
}
|
|
} else {
|
|
if tt.err != nil {
|
|
t.Fatalf("got nil error, want error: %v", tt.err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPullRequest(t *testing.T) {
|
|
config := `
|
|
{
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
environment: {
|
|
MYPASSWORD: { from_variable: 'mypassword' },
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'echo -n $MYPASSWORD' },
|
|
],
|
|
},
|
|
],
|
|
when: {
|
|
ref: '#refs/pull/\\d+/head#',
|
|
},
|
|
},
|
|
],
|
|
}
|
|
`
|
|
|
|
tests := []struct {
|
|
name string
|
|
passVarsToForkedPR bool
|
|
prFromSameRepo bool
|
|
expected string
|
|
}{
|
|
{
|
|
name: "test PR from same repowith PassVarsToForkedPR set to false",
|
|
passVarsToForkedPR: false,
|
|
prFromSameRepo: true,
|
|
expected: "mysupersecretpassword",
|
|
},
|
|
{
|
|
name: "test PR from same repo with PassVarsToForkedPR set to true",
|
|
passVarsToForkedPR: true,
|
|
prFromSameRepo: true,
|
|
expected: "mysupersecretpassword",
|
|
},
|
|
{
|
|
name: "test PR from forked repo with PassVarsToForkedPR set to false",
|
|
passVarsToForkedPR: false,
|
|
prFromSameRepo: false,
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "test PR from forked repo with PassVarsToForkedPR set to true",
|
|
passVarsToForkedPR: true,
|
|
prFromSameRepo: false,
|
|
expected: "mysupersecretpassword",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
|
|
|
|
giteaToken, token := createLinkedAccount(ctx, t, tgitea, c)
|
|
|
|
giteaClient := gitea.NewClient(giteaAPIURL, giteaToken)
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
giteaRepo, project := createProject(ctx, t, giteaClient, gwClient)
|
|
project = updateProject(ctx, t, giteaClient, gwClient, project.ID, tt.passVarsToForkedPR)
|
|
|
|
//create project secret
|
|
secretData := map[string]string{"mypassword": "mysupersecretpassword"}
|
|
sreq := &gwapitypes.CreateSecretRequest{
|
|
Name: "mysecret",
|
|
Type: gwapitypes.SecretTypeInternal,
|
|
Data: secretData,
|
|
}
|
|
|
|
secret, _, err := gwClient.CreateProjectSecret(context.TODO(), project.ID, sreq)
|
|
if err != nil {
|
|
t.Fatal("failed to create project secret: %w", err)
|
|
}
|
|
|
|
// create project variable
|
|
rvalues := []gwapitypes.VariableValueRequest{}
|
|
rvalues = append(rvalues, gwapitypes.VariableValueRequest{
|
|
SecretName: secret.Name,
|
|
SecretVar: "mypassword",
|
|
})
|
|
|
|
vreq := &gwapitypes.CreateVariableRequest{
|
|
Name: "mypassword",
|
|
Values: rvalues,
|
|
}
|
|
|
|
_, _, err = gwClient.CreateProjectVariable(context.TODO(), project.ID, vreq)
|
|
if err != nil {
|
|
t.Fatal("failed to create project variable: %w", err)
|
|
}
|
|
|
|
if tt.prFromSameRepo {
|
|
// create PR from branch on same repo
|
|
push(t, config, giteaRepo.CloneURL, giteaToken, "commit", true)
|
|
|
|
prOpts := gitea.CreatePullRequestOption{
|
|
Head: "new-branch",
|
|
Base: "master",
|
|
Title: "add file1 from new-branch on same repo",
|
|
}
|
|
_, err = giteaClient.CreatePullRequest(giteaUser01, "repo01", prOpts)
|
|
if err != nil {
|
|
t.Fatal("failed to create pull request: %w", err)
|
|
}
|
|
} else {
|
|
// create PR from forked repo
|
|
push(t, config, giteaRepo.CloneURL, giteaToken, "commit", false)
|
|
|
|
userOpts := gitea.CreateUserOption{
|
|
Username: giteaUser02,
|
|
Password: "password",
|
|
Email: "user02@example.com",
|
|
MustChangePassword: util.BoolP(false),
|
|
}
|
|
_, err := giteaClient.AdminCreateUser(userOpts)
|
|
if err != nil {
|
|
t.Fatal("failed to create user02: %w", err)
|
|
}
|
|
|
|
giteaClient.SetBasicAuth(giteaUser02, "password")
|
|
giteaUser02Token, err := giteaClient.CreateAccessToken(gitea.CreateAccessTokenOption{Name: "token01"})
|
|
if err != nil {
|
|
t.Fatalf("failed to create token for user02: %v", err)
|
|
}
|
|
|
|
giteaUser02Client := gitea.NewClient(giteaAPIURL, giteaUser02Token.Token)
|
|
giteaForkedRepo, err := giteaUser02Client.CreateFork(giteaUser01, "repo01", gitea.CreateForkOption{})
|
|
if err != nil {
|
|
t.Fatal("failed to fork repo01: %w", err)
|
|
}
|
|
|
|
gitfs := memfs.New()
|
|
r, err := git.Clone(memory.NewStorage(), gitfs, &git.CloneOptions{
|
|
Auth: &http.BasicAuth{
|
|
Username: giteaUser02,
|
|
Password: giteaUser02Token.Token,
|
|
},
|
|
URL: giteaForkedRepo.CloneURL,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to clone forked repo: %v", err)
|
|
}
|
|
|
|
wt, err := r.Worktree()
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
f, err := gitfs.Create("file2")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if _, err = f.Write([]byte("file2 content")); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if _, err := wt.Add("file2"); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
_, err = wt.Commit("commit from user02", &git.CommitOptions{
|
|
Author: &object.Signature{
|
|
Name: giteaUser02,
|
|
Email: "user02@example.com",
|
|
When: time.Now(),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
if err := r.Push(&git.PushOptions{
|
|
RemoteName: "origin",
|
|
RefSpecs: []gitconfig.RefSpec{
|
|
gitconfig.RefSpec("refs/heads/master:refs/heads/master"),
|
|
},
|
|
Auth: &http.BasicAuth{
|
|
Username: giteaUser02,
|
|
Password: giteaUser02Token.Token,
|
|
},
|
|
}); err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
prOpts := gitea.CreatePullRequestOption{
|
|
Head: "user02:master",
|
|
Base: "master",
|
|
Title: "add file1 from master on forked repo",
|
|
}
|
|
_, err = giteaUser02Client.CreatePullRequest(giteaUser01, "repo01", prOpts)
|
|
if err != nil {
|
|
t.Fatal("failed to create pull request: %w", err)
|
|
}
|
|
}
|
|
_ = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/project", project.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
if len(runs) == 0 {
|
|
return false, nil
|
|
}
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
})
|
|
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/project", project.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
t.Logf("runs: %s", util.Dump(runs))
|
|
|
|
run, _, err := gwClient.GetRun(ctx, runs[0].ID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
var task *gwapitypes.RunResponseTask
|
|
for _, t := range run.Tasks {
|
|
if t.Name == "task01" {
|
|
task = t
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(runs) > 0 {
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
t.Fatalf("expected run phase %q, got %q", rstypes.RunPhaseFinished, run.Phase)
|
|
}
|
|
if run.Result != rstypes.RunResultSuccess {
|
|
t.Fatalf("expected run result %q, got %q", rstypes.RunResultSuccess, run.Result)
|
|
}
|
|
resp, err := gwClient.GetLogs(ctx, run.ID, task.ID, false, 1, false)
|
|
if err != nil {
|
|
t.Fatalf("failed to get log: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
mypassword, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
t.Fatalf("failed to read log: %v", err)
|
|
}
|
|
if tt.expected != string(mypassword) {
|
|
t.Fatalf("expected mypassword %q, got %q", tt.expected, string(mypassword))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigContext(t *testing.T) {
|
|
jsonnetConfig := `
|
|
function(ctx) {
|
|
runs: [
|
|
{
|
|
name: 'run01',
|
|
tasks: [
|
|
{
|
|
name: 'task01',
|
|
runtime: {
|
|
containers: [
|
|
{
|
|
image: 'alpine/git',
|
|
},
|
|
],
|
|
},
|
|
environment: {
|
|
REF_TYPE: ctx.ref_type,
|
|
REF: ctx.ref,
|
|
BRANCH: ctx.branch,
|
|
TAG: ctx.tag,
|
|
PULL_REQUEST_ID: ctx.pull_request_id,
|
|
COMMIT_SHA: ctx.commit_sha,
|
|
},
|
|
steps: [
|
|
{ type: 'clone' },
|
|
{ type: 'run', command: 'env' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
`
|
|
|
|
starlarkConfig := `
|
|
def main(ctx):
|
|
return {
|
|
"runs": [
|
|
{
|
|
"name": 'run01',
|
|
"tasks": [
|
|
{
|
|
"name": 'task01',
|
|
"runtime": {
|
|
"containers": [
|
|
{
|
|
"image": 'alpine/git',
|
|
}
|
|
]
|
|
},
|
|
"environment": {
|
|
"REF_TYPE": ctx["ref_type"],
|
|
"REF": ctx["ref"],
|
|
"BRANCH": ctx["branch"],
|
|
"TAG": ctx["tag"],
|
|
"PULL_REQUEST_ID": ctx["pull_request_id"],
|
|
"COMMIT_SHA": ctx["commit_sha"]
|
|
},
|
|
"steps": [
|
|
{ "type": 'clone' },
|
|
{ "type": 'run', "command": 'env' }
|
|
],
|
|
},
|
|
],
|
|
},
|
|
]
|
|
}
|
|
`
|
|
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
env map[string]string
|
|
}{
|
|
{
|
|
name: "test direct run branch",
|
|
env: map[string]string{
|
|
"REF_TYPE": "branch",
|
|
"REF": "refs/heads/master",
|
|
"BRANCH": "master",
|
|
"TAG": "",
|
|
"PULL_REQUEST_ID": "",
|
|
"COMMIT_SHA": "",
|
|
},
|
|
},
|
|
{
|
|
name: "test direct run tag",
|
|
args: []string{"--tag", "v0.1.0"},
|
|
env: map[string]string{
|
|
"REF_TYPE": "tag",
|
|
"REF": "refs/tags/v0.1.0",
|
|
"BRANCH": "",
|
|
"TAG": "v0.1.0",
|
|
"PULL_REQUEST_ID": "",
|
|
"COMMIT_SHA": "",
|
|
},
|
|
},
|
|
{
|
|
name: "test direct run with pr",
|
|
args: []string{"--ref", "refs/pull/1/head"},
|
|
env: map[string]string{
|
|
"REF_TYPE": "pull_request",
|
|
"REF": "refs/pull/1/head",
|
|
"BRANCH": "",
|
|
"TAG": "",
|
|
"PULL_REQUEST_ID": "1",
|
|
"COMMIT_SHA": "",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, configFormat := range []ConfigFormat{ConfigFormatJsonnet, ConfigFormatStarlark} {
|
|
for _, tt := range tests {
|
|
t.Run(fmt.Sprintf("%s with %s config", tt.name, configFormat), func(t *testing.T) {
|
|
var config string
|
|
switch configFormat {
|
|
case ConfigFormatJsonnet:
|
|
config = jsonnetConfig
|
|
case ConfigFormatStarlark:
|
|
config = starlarkConfig
|
|
}
|
|
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken")
|
|
user, _, err := gwClient.CreateUser(ctx, &gwapitypes.CreateUserRequest{UserName: agolaUser01})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
t.Logf("created agola user: %s", user.UserName)
|
|
|
|
token := createAgolaUserToken(ctx, t, c)
|
|
|
|
// From now use the user token
|
|
gwClient = gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
directRun(t, dir, config, configFormat, c.Gateway.APIExposedURL, token, tt.args...)
|
|
|
|
// TODO(sgotti) add an util to wait for a run phase
|
|
_ = testutil.Wait(30*time.Second, func() (bool, error) {
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
if len(runs) != 1 {
|
|
return false, nil
|
|
}
|
|
|
|
run := runs[0]
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
})
|
|
|
|
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
t.Logf("runs: %s", util.Dump(runs))
|
|
|
|
if len(runs) != 1 {
|
|
t.Fatalf("expected 1 run got: %d", len(runs))
|
|
}
|
|
|
|
run, _, err := gwClient.GetRun(ctx, runs[0].ID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
if run.Phase != rstypes.RunPhaseFinished {
|
|
t.Fatalf("expected run phase %q, got %q", rstypes.RunPhaseFinished, run.Phase)
|
|
}
|
|
if run.Result != rstypes.RunResultSuccess {
|
|
t.Fatalf("expected run result %q, got %q", rstypes.RunResultSuccess, run.Result)
|
|
}
|
|
|
|
var task *gwapitypes.RunResponseTask
|
|
for _, t := range run.Tasks {
|
|
if t.Name == "task01" {
|
|
task = t
|
|
break
|
|
}
|
|
}
|
|
|
|
resp, err := gwClient.GetLogs(ctx, run.ID, task.ID, false, 1, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
logs, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
curEnv, err := testutil.ParseEnvs(bytes.NewReader(logs))
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
// update commit sha from annotations since it will change at every test
|
|
tt.env["COMMIT_SHA"] = run.Annotations["commit_sha"]
|
|
|
|
for n, e := range tt.env {
|
|
if ce, ok := curEnv[n]; !ok {
|
|
t.Fatalf("missing env var %s", n)
|
|
} else {
|
|
if ce != e {
|
|
t.Fatalf("different env var %s value, want: %q, got %q", n, e, ce)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUserOrgs(t *testing.T) {
|
|
dir, err := ioutil.TempDir("", "agola")
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
tetcd, tgitea, c := setup(ctx, t, dir)
|
|
defer shutdownGitea(tgitea)
|
|
defer shutdownEtcd(tetcd)
|
|
|
|
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken")
|
|
|
|
org01, _, err := gwClient.CreateOrg(ctx, &gwapitypes.CreateOrgRequest{Name: "org01", Visibility: gwapitypes.VisibilityPublic})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
org02, _, err := gwClient.CreateOrg(ctx, &gwapitypes.CreateOrgRequest{Name: "org02", Visibility: gwapitypes.VisibilityPrivate})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
_, _, err = gwClient.CreateOrg(ctx, &gwapitypes.CreateOrgRequest{Name: "org03", Visibility: gwapitypes.VisibilityPublic})
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
_, token := createLinkedAccount(ctx, t, tgitea, c)
|
|
|
|
_, _, err = gwClient.AddOrgMember(ctx, "org01", giteaUser01, gwapitypes.MemberRoleMember)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
_, _, err = gwClient.AddOrgMember(ctx, "org02", giteaUser01, gwapitypes.MemberRoleOwner)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
gwClientNew := gwclient.NewClient(c.Gateway.APIExposedURL, token)
|
|
|
|
orgs, _, err := gwClientNew.GetUserOrgs(ctx)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
|
|
expectedOrgs := []*gwapitypes.UserOrgsResponse{
|
|
{
|
|
Organization: &gwapitypes.OrgResponse{ID: org01.ID, Name: "org01", Visibility: gwapitypes.VisibilityPublic},
|
|
Role: gwapitypes.MemberRoleMember,
|
|
},
|
|
|
|
{
|
|
Organization: &gwapitypes.OrgResponse{ID: org02.ID, Name: "org02", Visibility: gwapitypes.VisibilityPrivate},
|
|
Role: gwapitypes.MemberRoleOwner,
|
|
},
|
|
}
|
|
|
|
if diff := cmp.Diff(expectedOrgs, orgs); diff != "" {
|
|
t.Fatalf("user orgs mismatch (-want +got):\n%s", diff)
|
|
}
|
|
}
|