From 3fa19cbeadfbf35abd2c94adef056736eb8c595a Mon Sep 17 00:00:00 2001 From: Toma Puljak Date: Wed, 3 Apr 2024 14:42:38 +0000 Subject: [PATCH] refactor: add agent test Signed-off-by: Toma Puljak --- .golangci.yaml | 5 +- .vscode/settings.json | 9 ++ go.mod | 5 +- go.sum | 8 +- internal/testing/agent/mocks/apiserver.go | 60 +++++++++++++ internal/testing/agent/mocks/gitservice.go | 43 +++++++++ internal/testing/agent/mocks/sshserver.go | 24 +++++ .../testing/agent/mocks/tailscaleserver.go | 24 +++++ .../apiclient/server/conversion/project.go | 34 ++++++++ pkg/agent/agent_test.go | 87 +++++++++++++++++++ 10 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 internal/testing/agent/mocks/apiserver.go create mode 100644 internal/testing/agent/mocks/gitservice.go create mode 100644 internal/testing/agent/mocks/sshserver.go create mode 100644 internal/testing/agent/mocks/tailscaleserver.go create mode 100644 internal/util/apiclient/server/conversion/project.go create mode 100644 pkg/agent/agent_test.go diff --git a/.golangci.yaml b/.golangci.yaml index ed3037a855..9b6377d426 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -5,4 +5,7 @@ linters-settings: - io.Copy - syscall.Syscall - (github.com/gliderlabs/ssh.Session).Exit - - (io.Writer).Write \ No newline at end of file + - (io.Writer).Write +run: + build-tags: + - testing \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..68f2924604 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "gopls": { + "build.buildFlags": [ + "-tags", + "testing" + ] + }, + "go.testTags": "testing" +} \ No newline at end of file diff --git a/go.mod b/go.mod index 1c96ba0b10..b3bad9b75e 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 + github.com/stretchr/testify v1.9.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.3 @@ -77,6 +78,7 @@ require ( github.com/coreos/go-oidc/v3 v3.8.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dblohm7/wingoes v0.0.0-20231025182615-65d8b4b5428f // indirect github.com/deckarep/golang-set/v2 v2.4.0 // indirect @@ -171,6 +173,7 @@ require ( github.com/pion/transport/v2 v2.2.1 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect @@ -269,7 +272,7 @@ require ( github.com/rivo/uniseg v0.4.4 // indirect github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.1 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/xanzy/go-gitlab v0.97.0 github.com/yuin/goldmark v1.6.0 // indirect github.com/yuin/goldmark-emoji v1.0.2 // indirect diff --git a/go.sum b/go.sum index bbba56b246..5d9e98adca 100644 --- a/go.sum +++ b/go.sum @@ -1334,8 +1334,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= -github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1346,10 +1346,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= diff --git a/internal/testing/agent/mocks/apiserver.go b/internal/testing/agent/mocks/apiserver.go new file mode 100644 index 0000000000..c52830b4ce --- /dev/null +++ b/internal/testing/agent/mocks/apiserver.go @@ -0,0 +1,60 @@ +//go:build testing + +// Copyright 2024 Daytona Platforms Inc. +// SPDX-License-Identifier: Apache-2.0 + +package mocks + +import ( + "fmt" + "net/http" + "testing" + + "github.com/daytonaio/daytona/pkg/server" + "github.com/daytonaio/daytona/pkg/workspace" + "github.com/gin-gonic/gin" +) + +func NewMockRestServer(t *testing.T, workspace *workspace.Workspace) *http.Server { + router := gin.Default() + serverController := router.Group("/server") + { + serverController.GET("/config", func(ctx *gin.Context) { + ctx.JSON(200, &server.Config{ + ProvidersDir: "", + RegistryUrl: "", + Id: "", + ServerDownloadUrl: "", + ApiPort: 3000, + HeadscalePort: 4000, + BinariesPath: "", + LogFilePath: "", + }) + }) + serverController.POST("/network-key", func(ctx *gin.Context) { + ctx.JSON(200, &server.NetworkKey{Key: "test-key"}) + }) + } + + workspaceController := router.Group("/workspace") + { + workspaceController.GET("/:workspaceId", func(ctx *gin.Context) { + ctx.JSON(http.StatusOK, workspace) + }) + } + + gitproviderController := router.Group("/gitprovider") + { + gitproviderController.GET("/for-url/:url", func(ctx *gin.Context) { + // This simulates a non-configured git provider + ctx.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to get git provider for url")) + }) + } + + server := &http.Server{ + Addr: ":3000", + Handler: router, + } + + return server +} diff --git a/internal/testing/agent/mocks/gitservice.go b/internal/testing/agent/mocks/gitservice.go new file mode 100644 index 0000000000..a8afc36212 --- /dev/null +++ b/internal/testing/agent/mocks/gitservice.go @@ -0,0 +1,43 @@ +//go:build testing + +// Copyright 2024 Daytona Platforms Inc. +// SPDX-License-Identifier: Apache-2.0 + +package mocks + +import ( + "github.com/daytonaio/daytona/internal/util/apiclient/server/conversion" + "github.com/daytonaio/daytona/pkg/serverapiclient" + "github.com/daytonaio/daytona/pkg/workspace" + "github.com/stretchr/testify/mock" +) + +type mockGitService struct { + mock.Mock +} + +func (m *mockGitService) CloneRepository(project *serverapiclient.Project, authToken *string) error { + args := m.Called(project, authToken) + return args.Error(0) +} + +func (m *mockGitService) RepositoryExists(project *serverapiclient.Project) (bool, error) { + args := m.Called(project) + return args.Bool(0), args.Error(1) +} + +func (m *mockGitService) SetGitConfig(userData *serverapiclient.GitUser) error { + args := m.Called(userData) + return args.Error(0) +} + +func NewMockGitService(repositoryShouldExist bool, project *workspace.Project) *mockGitService { + gitService := new(mockGitService) + gitService.On("RepositoryExists", conversion.ToProjectDTO(project)).Return(repositoryShouldExist, nil) + if !repositoryShouldExist { + gitService.On("CloneRepository", project, mock.Anything).Return(nil) + } + gitService.On("SetGitConfig", mock.Anything).Return(nil) + + return gitService +} diff --git a/internal/testing/agent/mocks/sshserver.go b/internal/testing/agent/mocks/sshserver.go new file mode 100644 index 0000000000..9903197724 --- /dev/null +++ b/internal/testing/agent/mocks/sshserver.go @@ -0,0 +1,24 @@ +//go:build testing + +// Copyright 2024 Daytona Platforms Inc. +// SPDX-License-Identifier: Apache-2.0 + +package mocks + +import "github.com/stretchr/testify/mock" + +type mockSshServer struct { + mock.Mock +} + +func (m *mockSshServer) Start() error { + args := m.Called() + return args.Error(0) +} + +func NewMockSshServer() *mockSshServer { + mockSshServer := new(mockSshServer) + mockSshServer.On("Start").Return(nil) + + return mockSshServer +} diff --git a/internal/testing/agent/mocks/tailscaleserver.go b/internal/testing/agent/mocks/tailscaleserver.go new file mode 100644 index 0000000000..fbb3142ba5 --- /dev/null +++ b/internal/testing/agent/mocks/tailscaleserver.go @@ -0,0 +1,24 @@ +//go:build testing + +// Copyright 2024 Daytona Platforms Inc. +// SPDX-License-Identifier: Apache-2.0 + +package mocks + +import "github.com/stretchr/testify/mock" + +type mockTailscaleServer struct { + mock.Mock +} + +func (m *mockTailscaleServer) Start() error { + args := m.Called() + return args.Error(0) +} + +func NewMockTailscaleServer() *mockTailscaleServer { + mockTailscaleServer := new(mockTailscaleServer) + mockTailscaleServer.On("Start").Return(nil) + + return mockTailscaleServer +} diff --git a/internal/util/apiclient/server/conversion/project.go b/internal/util/apiclient/server/conversion/project.go new file mode 100644 index 0000000000..06e008df5f --- /dev/null +++ b/internal/util/apiclient/server/conversion/project.go @@ -0,0 +1,34 @@ +// Copyright 2024 Daytona Platforms Inc. +// SPDX-License-Identifier: Apache-2.0 + +package conversion + +import ( + "github.com/daytonaio/daytona/pkg/serverapiclient" + "github.com/daytonaio/daytona/pkg/workspace" +) + +func ToProjectDTO(project *workspace.Project) *serverapiclient.Project { + projectDto := &serverapiclient.Project{ + Name: &project.Name, + Target: &project.Target, + WorkspaceId: &project.WorkspaceId, + Repository: &serverapiclient.GitRepository{ + Id: &project.Repository.Id, + Name: &project.Repository.Name, + Branch: project.Repository.Branch, + Owner: &project.Repository.Owner, + Path: project.Repository.Path, + Sha: &project.Repository.Sha, + Source: &project.Repository.Source, + Url: &project.Repository.Url, + }, + } + + if project.Repository.PrNumber != nil { + prNumber := int32(*project.Repository.PrNumber) + projectDto.Repository.PrNumber = &prNumber + } + + return projectDto +} diff --git a/pkg/agent/agent_test.go b/pkg/agent/agent_test.go new file mode 100644 index 0000000000..6414168a2f --- /dev/null +++ b/pkg/agent/agent_test.go @@ -0,0 +1,87 @@ +// Copyright 2024 Daytona Platforms Inc. +// SPDX-License-Identifier: Apache-2.0 + +package agent_test + +import ( + "bytes" + "net/http" + "testing" + + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + + "github.com/daytonaio/daytona/internal/testing/agent/mocks" + "github.com/daytonaio/daytona/pkg/agent" + "github.com/daytonaio/daytona/pkg/agent/config" + "github.com/daytonaio/daytona/pkg/gitprovider" + "github.com/daytonaio/daytona/pkg/workspace" +) + +var project1 = &workspace.Project{ + Name: "test", + Repository: &gitprovider.GitRepository{ + Id: "123", + Url: "https://github.com/daytonaio/daytona", + Name: "daytona", + }, + WorkspaceId: "123", + Target: "local", +} + +var workspace1 = &workspace.Workspace{ + Id: "123", + Name: "test", + Target: "local", + Projects: []*workspace.Project{ + project1, + }, +} + +var mockConfig = &config.Config{ + WorkspaceId: workspace1.Id, + ProjectName: project1.Name, + Server: config.DaytonaServerConfig{ + Url: "http://localhost:3000", + ApiKey: "test-api-key", + }, +} + +func TestAgent(t *testing.T) { + buf := bytes.Buffer{} + log.SetOutput(&buf) + + apiServer := mocks.NewMockRestServer(t, workspace1) + defer apiServer.Close() + go func() { + if err := apiServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("ListenAndServe(): %s", err) + } + }() + + mockGitService := mocks.NewMockGitService(true, project1) + mockSshServer := mocks.NewMockSshServer() + mockTailscaleServer := mocks.NewMockTailscaleServer() + + mockConfig.ProjectDir = t.TempDir() + + // Create a new Agent instance + a := &agent.Agent{ + Config: mockConfig, + Git: mockGitService, + Ssh: mockSshServer, + Tailscale: mockTailscaleServer, + } + + t.Run("Start agent", func(t *testing.T) { + err := a.Start() + + require.Nil(t, err) + }) + + t.Cleanup(func() { + mockGitService.AssertExpectations(t) + mockSshServer.AssertExpectations(t) + mockTailscaleServer.AssertExpectations(t) + }) +}