diff --git a/api/apis/azureClient/azBlobClient.go b/api/apis/azureClient/azBlobClient.go new file mode 100644 index 0000000..1a76d45 --- /dev/null +++ b/api/apis/azureClient/azBlobClient.go @@ -0,0 +1,116 @@ +package azureClient + +import ( + "context" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service" + "io" + "os" +) + +// azblob.client has no interface +// but we need one for mocking in the tests. +// So we have to define our own interface + +type IAzBlobClient interface { + CreateContainer(ctx context.Context, containerName string, o *azblob.CreateContainerOptions) (azblob.CreateContainerResponse, error) + DeleteContainer(ctx context.Context, containerName string, o *azblob.DeleteContainerOptions) (azblob.DeleteContainerResponse, error) + DeleteBlob(ctx context.Context, containerName string, blobName string, o *azblob.DeleteBlobOptions) (azblob.DeleteBlobResponse, error) + NewListBlobsFlatPager(containerName string, o *azblob.ListBlobsFlatOptions) *runtime.Pager[azblob.ListBlobsFlatResponse] + NewListContainersPager(o *azblob.ListContainersOptions) *runtime.Pager[azblob.ListContainersResponse] + UploadBuffer(ctx context.Context, containerName string, blobName string, buffer []byte, o *azblob.UploadBufferOptions) (azblob.UploadBufferResponse, error) + UploadFile(ctx context.Context, containerName string, blobName string, file *os.File, o *azblob.UploadFileOptions) (azblob.UploadFileResponse, error) + UploadStream(ctx context.Context, containerName string, blobName string, body io.Reader, o *azblob.UploadStreamOptions) (azblob.UploadStreamResponse, error) + DownloadBuffer(ctx context.Context, containerName string, blobName string, buffer []byte, o *azblob.DownloadBufferOptions) (int64, error) + DownloadFile(ctx context.Context, containerName string, blobName string, file *os.File, o *azblob.DownloadFileOptions) (int64, error) + DownloadStream(ctx context.Context, containerName string, blobName string, o *azblob.DownloadStreamOptions) (azblob.DownloadStreamResponse, error) +} + +type azBlobClient struct { + client *azblob.Client +} + +func AzBlobClient(azure *azblob.Client) IAzBlobClient { + return &azBlobClient{ + client: azure, + } +} + +// URL returns the URL endpoint used by the BlobClient object. +func (c *azBlobClient) URL() string { + return c.URL() +} + +// ServiceClient returns the embedded service client for this client. +func (c *azBlobClient) ServiceClient() *service.Client { + return c.client.ServiceClient() +} + +// CreateContainer is a lifecycle method to creates a new container under the specified account. +// If the container with the same name already exists, a ResourceExistsError will be raised. +// This method returns a client with which to interact with the newly created container. +func (c *azBlobClient) CreateContainer(ctx context.Context, containerName string, o *azblob.CreateContainerOptions) (azblob.CreateContainerResponse, error) { + return c.client.CreateContainer(ctx, containerName, o) +} + +// DeleteContainer is a lifecycle method that marks the specified container for deletion. +// The container and any blobs contained within it are later deleted during garbage collection. +// If the container is not found, a ResourceNotFoundError will be raised. +func (c *azBlobClient) DeleteContainer(ctx context.Context, containerName string, o *azblob.DeleteContainerOptions) (azblob.DeleteContainerResponse, error) { + return c.client.DeleteContainer(ctx, containerName, o) +} + +// DeleteBlob marks the specified blob or snapshot for deletion. The blob is later deleted during garbage collection. +// Note that deleting a blob also deletes all its snapshots. +// For more information, see https://docs.microsoft.com/rest/api/storageservices/delete-blob. +func (c *azBlobClient) DeleteBlob(ctx context.Context, containerName string, blobName string, o *azblob.DeleteBlobOptions) (azblob.DeleteBlobResponse, error) { + return c.client.DeleteBlob(ctx, containerName, blobName, o) +} + +// NewListBlobsFlatPager returns a pager for blobs starting from the specified Marker. Use an empty +// Marker to start enumeration from the beginning. Blob names are returned in lexicographic order. +// For more information, see https://docs.microsoft.com/rest/api/storageservices/list-blobs. +func (c *azBlobClient) NewListBlobsFlatPager(containerName string, o *azblob.ListBlobsFlatOptions) *runtime.Pager[azblob.ListBlobsFlatResponse] { + return c.client.NewListBlobsFlatPager(containerName, o) +} + +// NewListContainersPager operation returns a pager of the containers under the specified account. +// Use an empty Marker to start enumeration from the beginning. Container names are returned in lexicographic order. +// For more information, see https://docs.microsoft.com/rest/api/storageservices/list-containers2. +func (c *azBlobClient) NewListContainersPager(o *azblob.ListContainersOptions) *runtime.Pager[azblob.ListContainersResponse] { + return c.client.NewListContainersPager(o) +} + +// UploadBuffer uploads a buffer in blocks to a block blob. +func (c *azBlobClient) UploadBuffer(ctx context.Context, containerName string, blobName string, buffer []byte, o *azblob.UploadBufferOptions) (azblob.UploadBufferResponse, error) { + return c.client.UploadBuffer(ctx, containerName, blobName, buffer, o) +} + +// UploadFile uploads a file in blocks to a block blob. +func (c *azBlobClient) UploadFile(ctx context.Context, containerName string, blobName string, file *os.File, o *azblob.UploadFileOptions) (azblob.UploadFileResponse, error) { + return c.client.UploadFile(ctx, containerName, blobName, file, o) +} + +// UploadStream copies the file held in io.Reader to the Blob at blockBlobClient. +// A Context deadline or cancellation will cause this to error. +func (c *azBlobClient) UploadStream(ctx context.Context, containerName string, blobName string, body io.Reader, o *azblob.UploadStreamOptions) (azblob.UploadStreamResponse, error) { + return c.client.UploadStream(ctx, containerName, blobName, body, o) +} + +// DownloadBuffer downloads an Azure blob to a buffer with parallel. +func (c *azBlobClient) DownloadBuffer(ctx context.Context, containerName string, blobName string, buffer []byte, o *azblob.DownloadBufferOptions) (int64, error) { + return c.client.DownloadBuffer(ctx, containerName, blobName, buffer, o) +} + +// DownloadFile downloads an Azure blob to a local file. +// The file would be truncated if the size doesn't match. +func (c *azBlobClient) DownloadFile(ctx context.Context, containerName string, blobName string, file *os.File, o *azblob.DownloadFileOptions) (int64, error) { + return c.client.DownloadFile(ctx, containerName, blobName, file, o) +} + +// DownloadStream reads a range of bytes from a blob. The response also includes the blob's properties and metadata. +// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-blob. +func (c *azBlobClient) DownloadStream(ctx context.Context, containerName string, blobName string, o *azblob.DownloadStreamOptions) (azblob.DownloadStreamResponse, error) { + return c.client.DownloadStream(ctx, containerName, blobName, o) +} diff --git a/api/go.mod b/api/go.mod index fa97623..2fb9931 100644 --- a/api/go.mod +++ b/api/go.mod @@ -7,6 +7,7 @@ toolchain go1.22.4 replace indiegamestream.com/indiegamestream => github.com/AustrianDataLAB/IndieGameStream/operator v0.0.0-20240618115824-3e17bbf6fde1 require ( + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 @@ -16,6 +17,7 @@ require ( github.com/go-sql-driver/mysql v1.8.1 github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 + github.com/stretchr/testify v1.9.0 google.golang.org/api v0.183.0 indiegamestream.com/indiegamestream v0.0.0-00010101000000-000000000000 k8s.io/apimachinery v0.30.1 @@ -28,7 +30,6 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/bytedance/sonic v1.11.6 // indirect @@ -72,7 +73,9 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/api/go.sum b/api/go.sum index 4d3197f..0807e4d 100644 --- a/api/go.sum +++ b/api/go.sum @@ -191,6 +191,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/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.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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/api/services/gameService.go b/api/services/gameService.go index b24db50..e1e5126 100644 --- a/api/services/gameService.go +++ b/api/services/gameService.go @@ -40,7 +40,9 @@ func (g gameService) FindByID(id uuid.UUID) (*models.Game, error) { if err != nil { return nil, err } else { - g.updateGameUrl(game) + if game.Url == "" { + g.updateGameUrl(game) + } return game, nil } } @@ -133,10 +135,6 @@ func (g gameService) Delete(id uuid.UUID) error { } func (g gameService) updateGameUrl(game *models.Game) { - if game == nil { - return - } - url, err := g.k8s.ReadGameUrl(game.ID) if err != nil { log.Println(fmt.Sprintf("Error reading game url: %s", err)) diff --git a/api/tests/gameController_test.go b/api/tests/gameController_test.go new file mode 100644 index 0000000..26a1aeb --- /dev/null +++ b/api/tests/gameController_test.go @@ -0,0 +1,244 @@ +package tests + +import ( + "api/apis" + "api/controllers" + "api/dtos" + "api/models" + "api/repositories" + "api/services" + "api/shared" + "api/tests/mocks" + "database/sql" + "encoding/json" + "fmt" + "github.com/DATA-DOG/go-sqlmock" + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/stretchr/testify/mock" + v1 "indiegamestream.com/indiegamestream/api/stream/v1" + "io/ioutil" + "log" + "net/http/httptest" + "regexp" + "testing" +) + +func Test_Read_By_Id_And_Refresh_Should_Succeed(t *testing.T) { + //======================= PREPARE PREPARE PREPARE PREPARE ======================= + owner := "MockOwner" + url := "fsdfsf-91f3975e-cdd5-4b9b-8f00-a86bb44d4b82.possum-climb.ts.net" + // Create Models + game := mocks.GameMock("A") + game.ID, _ = uuid.Parse("66c887ca-1f56-426e-ac0c-bc92fff8b798") + game.Owner = owner + game.Url = "" + game.Status = shared.Status_Installing + // Create fake k8s client + fakek8s := mocks.K8sMock(&mock.Mock{}) + k8sApi := apis.K8sService(fakek8s) + //Mock the calls to k8s + fakek8s.Mock(). + On("Get", + mock.AnythingOfType("context.backgroundCtx"), + mock.AnythingOfType("types.NamespacedName"), + mock.AnythingOfType("*v1.Game")). + Return(nil). + Run(func(args mock.Arguments) { + arg := args.Get(2).(*v1.Game) + arg.Status.URL = url + }) + + // Create database mock + db, dbMock := databaseMock() + defer db.Close() + // Define queries + dbMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE ID = ?")). + WithArgs(game.ID).WillReturnRows( + sqlmock.NewRows([]string{"Id", "Title", "StorageLocation", "Status", "Url", "Owner", "FileName"}). + AddRow(game.ID, game.Title, game.StorageLocation, game.Status, game.Url, game.Owner, game.FileName), + ) + + dbMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE ID = ?")). + WithArgs(game.ID).WillReturnRows( + sqlmock.NewRows([]string{"Id", "Title", "StorageLocation", "Status", "Url", "Owner", "FileName"}). + AddRow(game.ID, game.Title, game.StorageLocation, game.Status, game.Url, game.Owner, game.FileName), + ) + + dbMock.ExpectPrepare(regexp. + QuoteMeta("UPDATE games SET Title=?, StorageLocation=?, Status=?, Url=?, FileName=? WHERE ID = ?")) + + dbMock.ExpectExec(regexp. + QuoteMeta("UPDATE games SET Title=?, StorageLocation=?, Status=?, Url=?, FileName=? WHERE ID = ?")). + WithArgs(game.Title, game.StorageLocation, shared.Status_Installed, url, game.FileName, game.ID). + WillReturnResult(sqlmock.NewResult(0, 1)) + + // Finally, create gameController + gameController := gameController(db, k8sApi, nil) + + // Prepare Gin + gin.SetMode(gin.TestMode) + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("subject", owner) + + //======================= EXECUTE EXECUTE EXECUTE EXECUTE ======================= + c.Params = gin.Params{gin.Param{Key: "id", Value: game.ID.String()}} + gameController.GetGameById(c) + + //======================= VERIFY VERIFY VERIFY VERIFY ======================= + //Check HTTP response + if w.Code != 200 { + b, _ := ioutil.ReadAll(w.Body) + t.Error(w.Code, string(b)) + } + + //Check response body + var responseBody dtos.GetAllGamesResponseBody + err := json.Unmarshal(w.Body.Bytes(), &responseBody) + if err != nil { + t.Error(err) + } + if &responseBody == nil { + t.Error("response body is empty") + } + + //Verify games + game.Url = url // Game url should be set correctly + game.Status = shared.Status_Installed //Game Status should be set correctly + verifyDto(t, &responseBody, game) + +} + +func Test_Read_By_Id_Should_Succeed(t *testing.T) { + //======================= PREPARE PREPARE PREPARE PREPARE ======================= + owner := "MockOwner" + // Create database mock + db, dbMock := databaseMock() + defer db.Close() + // Create Models + game := mocks.GameMock("A") + game.Owner = owner + // Define queries + dbMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE ID = ?")). + WithArgs(game.ID).WillReturnRows( + sqlmock.NewRows([]string{"Id", "Title", "StorageLocation", "Status", "Url", "Owner", "FileName"}). + AddRow(game.ID, game.Title, game.StorageLocation, game.Status, game.Url, game.Owner, game.FileName), + ) + + // Finally, create gameController + gameController := gameController(db, nil, nil) + // Prepare Gin + gin.SetMode(gin.TestMode) + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("subject", owner) + + //======================= EXECUTE EXECUTE EXECUTE EXECUTE ======================= + c.Params = gin.Params{gin.Param{Key: "id", Value: game.ID.String()}} + gameController.GetGameById(c) + + //======================= VERIFY VERIFY VERIFY VERIFY ======================= + //Check HTTP response + if w.Code != 200 { + b, _ := ioutil.ReadAll(w.Body) + t.Error(w.Code, string(b)) + } + + //Check response body + var responseBody dtos.GetAllGamesResponseBody + err := json.Unmarshal(w.Body.Bytes(), &responseBody) + if err != nil { + t.Error(err) + } + if &responseBody == nil { + t.Error("response body is empty") + } + + //Verify games + verifyDto(t, &responseBody, game) + +} + +func Test_Read_All_Should_Succeed(t *testing.T) { + //======================= PREPARE PREPARE PREPARE PREPARE ======================= + owner := "MockOwner" + // Create database mock + db, dbMock := databaseMock() + defer db.Close() + // Create Models + gameA := mocks.GameMock("A") + gameA.Owner = owner + gameB := mocks.GameMock("B") + gameB.Owner = owner + // Define queries + dbMock.ExpectPrepare(regexp.QuoteMeta("SELECT * FROM games WHERE owner = ?")) + dbMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE owner = ?")). + WithArgs(owner). + WillReturnRows( + sqlmock.NewRows([]string{"ID", "Title", "StorageLocation", "Status", "Url", "Owner", "FileName"}). + AddRow(gameA.ID, gameA.Title, gameA.StorageLocation, gameA.Status, gameA.Url, gameA.Owner, gameA.FileName). + AddRow(gameB.ID, gameB.Title, gameB.StorageLocation, gameB.Status, gameB.Url, gameB.Owner, gameB.FileName), + ) + // Finally, create gameController + gameController := gameController(db, nil, nil) + // Prepare Gin + gin.SetMode(gin.TestMode) + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("subject", owner) + + //======================= EXECUTE EXECUTE EXECUTE EXECUTE ======================= + gameController.GetAllGames(c) + + //======================= VERIFY VERIFY VERIFY VERIFY ======================= + //Check HTTP response + if w.Code != 200 { + b, _ := ioutil.ReadAll(w.Body) + t.Error(w.Code, string(b)) + } + + //Check response body + var responseBody []dtos.GetAllGamesResponseBody + err := json.Unmarshal(w.Body.Bytes(), &responseBody) + if err != nil { + t.Error(err) + } + if len(responseBody) != 2 { + t.Error(fmt.Sprint("Expected 2 games, got ", len(responseBody))) + } + + //Verify games + verifyDto(t, &responseBody[0], gameA) + verifyDto(t, &responseBody[1], gameB) + +} + +func verifyDto(t *testing.T, dto *dtos.GetAllGamesResponseBody, game *models.Game) { + if dto.Title != game.Title { + t.Error(fmt.Sprintf("Expected title %s, got %s", game.Title, dto.Title)) + } + if dto.Url != game.Url { + t.Error(fmt.Sprintf("Expected url %s, got %s", game.Url, dto.Url)) + } + if dto.ID != game.ID { + t.Error(fmt.Sprintf("Expected id %v, got %v", game.ID, dto.ID)) + } + if dto.Status != game.Status { + t.Error(fmt.Sprintf("Expected status %v, got %v", game.Status, dto.Status)) + } +} + +func databaseMock() (*sql.DB, sqlmock.Sqlmock) { + db, mock, err := sqlmock.New() + if err != nil { + log.Fatalf(err.Error()) + } + return db, mock +} + +func gameController(db *sql.DB, k8s apis.IK8sApi, azure apis.IAzureApi) controllers.IGameController { + gamesRepository := repositories.GameRepository(db) + gamesService := services.GameService(gamesRepository, k8s, azure) + return controllers.GameController(gamesService) +} diff --git a/api/tests/mocks/azureMock.go b/api/tests/mocks/azureMock.go new file mode 100644 index 0000000..df2daa5 --- /dev/null +++ b/api/tests/mocks/azureMock.go @@ -0,0 +1,66 @@ +package mocks + +import ( + "context" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "io" + "os" +) + +type AzureBlobClientMock struct{} + +func (AzureBlobClientMock) CreateContainer(ctx context.Context, containerName string, o *azblob.CreateContainerOptions) (azblob.CreateContainerResponse, error) { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) DeleteContainer(ctx context.Context, containerName string, o *azblob.DeleteContainerOptions) (azblob.DeleteContainerResponse, error) { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) DeleteBlob(ctx context.Context, containerName string, blobName string, o *azblob.DeleteBlobOptions) (azblob.DeleteBlobResponse, error) { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) NewListBlobsFlatPager(containerName string, o *azblob.ListBlobsFlatOptions) *runtime.Pager[azblob.ListBlobsFlatResponse] { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) NewListContainersPager(o *azblob.ListContainersOptions) *runtime.Pager[azblob.ListContainersResponse] { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) UploadBuffer(ctx context.Context, containerName string, blobName string, buffer []byte, o *azblob.UploadBufferOptions) (azblob.UploadBufferResponse, error) { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) UploadFile(ctx context.Context, containerName string, blobName string, file *os.File, o *azblob.UploadFileOptions) (azblob.UploadFileResponse, error) { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) UploadStream(ctx context.Context, containerName string, blobName string, body io.Reader, o *azblob.UploadStreamOptions) (azblob.UploadStreamResponse, error) { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) DownloadBuffer(ctx context.Context, containerName string, blobName string, buffer []byte, o *azblob.DownloadBufferOptions) (int64, error) { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) DownloadFile(ctx context.Context, containerName string, blobName string, file *os.File, o *azblob.DownloadFileOptions) (int64, error) { + //TODO implement me + panic("implement me") +} + +func (AzureBlobClientMock) DownloadStream(ctx context.Context, containerName string, blobName string, o *azblob.DownloadStreamOptions) (azblob.DownloadStreamResponse, error) { + //TODO implement me + panic("implement me") +} diff --git a/api/tests/mocks/k8sMock.go b/api/tests/mocks/k8sMock.go new file mode 100644 index 0000000..d417080 --- /dev/null +++ b/api/tests/mocks/k8sMock.go @@ -0,0 +1,87 @@ +package mocks + +import ( + "context" + "github.com/stretchr/testify/mock" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type k8sMock struct { + mock *mock.Mock +} + +func K8sMock(mock *mock.Mock) *k8sMock { + return &k8sMock{mock: mock} +} + +func (s k8sMock) Mock() *mock.Mock { + return s.mock +} + +func (s k8sMock) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + s.mock.Called(ctx, key, obj) + return nil +} + +func (s k8sMock) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + s.mock.Called(ctx, list) + return nil +} + +func (s k8sMock) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + s.mock.Called(ctx, obj) + return nil +} + +func (s k8sMock) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { + s.mock.Called(ctx, obj) + return nil +} + +func (s k8sMock) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + s.mock.Called(ctx, obj) + return nil +} + +func (s k8sMock) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + s.mock.Called(ctx, obj, patch) + return nil +} + +func (s k8sMock) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { + s.mock.Called(ctx, obj) + return nil +} + +func (s k8sMock) Status() client.SubResourceWriter { + s.mock.Called() + return nil +} + +func (s k8sMock) SubResource(subResource string) client.SubResourceClient { + s.mock.Called(subResource) + return nil +} + +func (s k8sMock) Scheme() *runtime.Scheme { + s.mock.Called() + return nil +} + +func (s k8sMock) RESTMapper() meta.RESTMapper { + s.mock.Called() + return nil +} + +func (s k8sMock) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { + s.mock.Called(obj) + return schema.GroupVersionKind{}, nil +} + +func (s k8sMock) IsObjectNamespaced(obj runtime.Object) (bool, error) { + s.mock.Called(obj) + return true, nil +} diff --git a/api/tests/mocks/modelMock.go b/api/tests/mocks/modelMock.go new file mode 100644 index 0000000..86f9b3c --- /dev/null +++ b/api/tests/mocks/modelMock.go @@ -0,0 +1,19 @@ +package mocks + +import ( + "api/models" + "api/shared" + "github.com/google/uuid" +) + +func GameMock(identifier string) *models.Game { + return &models.Game{ + ID: uuid.New(), + Title: "Title_" + identifier, + StorageLocation: "Storage_Location_" + identifier, + Status: shared.Status_New, + Url: "Url_" + identifier, + Owner: "Owner_" + identifier, + FileName: "File_" + identifier, + } +}