Skip to content

Commit

Permalink
env create: add a create environment command
Browse files Browse the repository at this point in the history
Implemented a create environment command. Add unit tests
  • Loading branch information
gibiw committed Jun 10, 2024
1 parent 5559596 commit d808942
Show file tree
Hide file tree
Showing 8 changed files with 429 additions and 0 deletions.
88 changes: 88 additions & 0 deletions cmd/testops/env/create/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package create

import (
"fmt"
"github.com/qase-tms/qasectl/cmd/flags"
"github.com/qase-tms/qasectl/internal/client"
"github.com/qase-tms/qasectl/internal/service/env"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"log/slog"
"os"
"path"
"strings"
)

const (
titleFlag = "title"
descriptionFlag = "description"
slugFlag = "slug"
hostFlag = "host"
outputFlag = "output"
)

// Command returns a new cobra command for create environments
func Command() *cobra.Command {
var (
title string
description string
slug string
host string
output string
)

cmd := &cobra.Command{
Use: "create",
Short: "Create a new environment",
Example: "qli testops env create --title 'New environment' --slug local --description 'This is a environment' --host app.server.com --project 'PRJ' --token 'TOKEN'",
RunE: func(cmd *cobra.Command, args []string) error {
if strings.Contains(slug, " ") {
return fmt.Errorf("slug can't contain spaces")
}

token := viper.GetString(flags.TokenFlag)
project := viper.GetString(flags.ProjectFlag)

c := client.NewClientV1(token)
s := env.NewService(c)

e, err := s.CreateEnvironment(cmd.Context(), project, title, description, slug, host)
if err != nil {
return fmt.Errorf("failed to create environment: %w", err)
}

if output == "" {
dir, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current directory: %w", err)
}
output = path.Join(dir, "qase.env")
}

err = os.WriteFile(output, []byte(fmt.Sprintf("QASE_ENVIRONMENT=%d", e.ID)), 0644)
if err != nil {
return fmt.Errorf("failed to write environament ID to file: %w", err)
}

slog.Info(fmt.Sprintf("Environment created with ID: %d", e.ID))

return nil
},
}

cmd.Flags().StringVar(&title, titleFlag, "", "title of the environment")
err := cmd.MarkFlagRequired(titleFlag)
if err != nil {
fmt.Println(err)
}
cmd.Flags().StringVarP(&description, descriptionFlag, "d", "", "description of the environment")
cmd.Flags().StringVarP(&slug, slugFlag, "s", "", "slug of the environment, (string without spaces)")
err = cmd.MarkFlagRequired(slugFlag)
if err != nil {
fmt.Println(err)
}
cmd.Flags().StringVar(&host, hostFlag, "", "host of the environment")
cmd.Flags().StringVarP(&output, outputFlag, "o", "", "output path for the environment ID")

return cmd
}
19 changes: 19 additions & 0 deletions cmd/testops/env/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package env

import (
"github.com/qase-tms/qasectl/cmd/testops/env/create"
"github.com/spf13/cobra"
)

// Command returns a new cobra command for envs
func Command() *cobra.Command {

cmd := &cobra.Command{
Use: "env",
Short: "Manage environments",
}

cmd.AddCommand(create.Command())

return cmd
}
2 changes: 2 additions & 0 deletions cmd/testops/testops.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package testops
import (
"fmt"
"github.com/qase-tms/qasectl/cmd/flags"
"github.com/qase-tms/qasectl/cmd/testops/env"
"github.com/qase-tms/qasectl/cmd/testops/result"
"github.com/qase-tms/qasectl/cmd/testops/run"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -44,6 +45,7 @@ func Command() *cobra.Command {

cmd.AddCommand(run.Command())
cmd.AddCommand(result.Command())
cmd.AddCommand(env.Command())

return cmd
}
43 changes: 43 additions & 0 deletions internal/client/clientv1.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,49 @@ func NewClientV1(token string) *ClientV1 {
}
}

// CreateEnvironment creates a new environment
func (c *ClientV1) CreateEnvironment(ctx context.Context, pc, n, d, s, h string) (run.Environment, error) {
const op = "client.clientv1.createenvironment"
logger := slog.With("op", op)

logger.Debug("creating environment", "projectCode", pc, "name", n, "description", d, "slug", s, "host", h)

ctx, client := c.getApiV1Client(ctx)

m := apiV1Client.EnvironmentCreate{
Title: n,
Slug: s,
}

if d != "" {
m.SetDescription(d)
}

if h != "" {
m.SetHost(h)
}

resp, r, err := client.EnvironmentsAPI.
CreateEnvironment(ctx, pc).
EnvironmentCreate(m).
Execute()

if err != nil {
logger.Debug("failed to create environment", "response", r)
return run.Environment{}, fmt.Errorf("failed to create environment: %w", err)
}

env := run.Environment{
Title: n,
Slug: s,
ID: resp.Result.GetId(),
}

logger.Info("created environment", "environment", env)

return env, nil
}

// GetEnvironments returns environments
func (c *ClientV1) GetEnvironments(ctx context.Context, projectCode string) ([]run.Environment, error) {
const op = "client.clientv1.getenvironments"
Expand Down
43 changes: 43 additions & 0 deletions internal/service/env/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package env

import (
"context"
"fmt"
"github.com/qase-tms/qasectl/internal/models/run"
"log/slog"
)

// client is a client for env
//
//go:generate mockgen -source=$GOFILE -destination=$PWD/mocks/${GOFILE} -package=mocks
type client interface {
CreateEnvironment(ctx context.Context, pc, n, d, s, h string) (run.Environment, error)
GetEnvironments(ctx context.Context, projectCode string) ([]run.Environment, error)
}

// Service is a Service for env
type Service struct {
client client
}

// NewService creates a new env Service
func NewService(client client) *Service {
return &Service{client: client}
}

// CreateEnvironment creates a new environment
func (srv *Service) CreateEnvironment(ctx context.Context, pc, n, d, s, h string) (run.Environment, error) {
envs, err := srv.client.GetEnvironments(ctx, pc)
if err != nil {
return run.Environment{}, fmt.Errorf("failed to get environments: %w", err)
}

for _, env := range envs {
if env.Slug == s {
slog.Info("environment already exists")
return env, nil
}
}

return srv.client.CreateEnvironment(ctx, pc, n, d, s, h)
}
144 changes: 144 additions & 0 deletions internal/service/env/env_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package env

import (
"context"
"errors"
"github.com/magiconair/properties/assert"
"github.com/qase-tms/qasectl/internal/models/run"
"go.uber.org/mock/gomock"
"reflect"
"testing"
)

func TestService_CreateEnvironment(t *testing.T) {
type args struct {
pc string
n string
d string
s string
h string
}
tests := []struct {
name string
args args
want run.Environment
envs []run.Environment
wantErr bool
errGet error
createUse bool
errCreate error
errMessage string
}{
{
name: "success create environment",
args: args{
pc: "projectCode",
n: "name",
d: "description",
s: "slug",
h: "host",
},
want: run.Environment{
ID: 1,
Title: "name",
Slug: "slug",
},
envs: []run.Environment{},
wantErr: false,
errCreate: nil,
createUse: true,
errGet: nil,
errMessage: "",
},
{
name: "success get environment",
args: args{
pc: "projectCode",
n: "name",
d: "description",
s: "slug",
h: "host",
},
want: run.Environment{
ID: 1,
Title: "name",
Slug: "slug",
},
envs: []run.Environment{
{
ID: 1,
Title: "name",
Slug: "slug",
},
{
ID: 2,
Title: "name2",
Slug: "slug2",
},
},
wantErr: false,
errCreate: nil,
createUse: false,
errGet: nil,
errMessage: "",
},
{
name: "failed get environment",
args: args{
pc: "projectCode",
n: "name",
d: "description",
s: "slug",
h: "host",
},
want: run.Environment{},
envs: []run.Environment{},
wantErr: true,
errCreate: nil,
createUse: false,
errGet: errors.New("error"),
errMessage: "error",
},
{
name: "failed create environment",
args: args{
pc: "projectCode",
n: "name",
d: "description",
s: "slug",
h: "host",
},
want: run.Environment{},
envs: []run.Environment{},
wantErr: true,
errCreate: errors.New("error"),
createUse: true,
errGet: nil,
errMessage: "error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := newFixture(t)

f.client.EXPECT().GetEnvironments(gomock.Any(), tt.args.pc).Return(tt.envs, tt.errGet)
if tt.createUse {
f.client.EXPECT().CreateEnvironment(gomock.Any(), tt.args.pc, tt.args.n, tt.args.d, tt.args.s, tt.args.h).Return(tt.want, tt.errCreate)
}

srv := NewService(f.client)
got, err := srv.CreateEnvironment(context.Background(), tt.args.pc, tt.args.n, tt.args.d, tt.args.s, tt.args.h)
if (err != nil) != tt.wantErr {
if !tt.wantErr {
t.Errorf("CreateEnvironment() error = %v, wantErr %v", err, tt.wantErr)
return
}

assert.Equal(t, tt.errMessage, err.Error())
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("CreateEnvironment() got = %v, want %v", got, tt.want)
}
})
}
}
19 changes: 19 additions & 0 deletions internal/service/env/fixture_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package env

import (
"github.com/qase-tms/qasectl/internal/service/env/mocks"
"go.uber.org/mock/gomock"
"testing"
)

type fixture struct {
client *mocks.Mockclient
}

func newFixture(t *testing.T) *fixture {
ctrl := gomock.NewController(t)

return &fixture{
client: mocks.NewMockclient(ctrl),
}
}
Loading

0 comments on commit d808942

Please sign in to comment.