From 177aa9cc574a810f4f48c124df2e3134437c8b14 Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Tue, 16 Jan 2024 19:40:19 +0100
Subject: [PATCH 01/19] refactor(shoppinglist-service): add auth middleware
generaly to the router
---
src/shoppinglist-service/api/router/router.go | 24 ++++++++++---------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/src/shoppinglist-service/api/router/router.go b/src/shoppinglist-service/api/router/router.go
index 1d8c0c98..c5b484ba 100644
--- a/src/shoppinglist-service/api/router/router.go
+++ b/src/shoppinglist-service/api/router/router.go
@@ -18,17 +18,19 @@ func New(
) *Router {
r := router.New()
- r.GET("/api/v1/shoppinglist/:userId", (*shoppingListController).GetLists, authMiddleware)
- r.GET("/api/v1/shoppinglist/:listId/:userId", (*shoppingListController).GetList, authMiddleware)
- r.PUT("/api/v1/shoppinglist/:listId/:userId", (*shoppingListController).PutList, authMiddleware)
- r.POST("/api/v1/shoppinglist/:userId", (*shoppingListController).PostList, authMiddleware)
- r.DELETE("/api/v1/shoppinglist/:listId", (*shoppingListController).DeleteList, authMiddleware)
-
- r.GET("/api/v1/shoppinglistentries/:listId", (*shoppingListEntryController).GetEntries, authMiddleware)
- r.GET("/api/v1/shoppinglistentries/:listId/:productId", (*shoppingListEntryController).GetEntry, authMiddleware)
- r.PUT("/api/v1/shoppinglistentries/:listId/:productId", (*shoppingListEntryController).PutEntry, authMiddleware)
- r.POST("/api/v1/shoppinglistentries/:listId/:productId", (*shoppingListEntryController).PostEntry, authMiddleware)
- r.DELETE("/api/v1/shoppinglistentries/:listId/:productId", (*shoppingListEntryController).DeleteEntry, authMiddleware)
+ r.RegisterMiddleware(authMiddleware)
+
+ r.GET("/api/v1/shoppinglist/:userId", (*shoppingListController).GetLists)
+ r.GET("/api/v1/shoppinglist/:listId/:userId", (*shoppingListController).GetList)
+ r.PUT("/api/v1/shoppinglist/:listId/:userId", (*shoppingListController).PutList)
+ r.POST("/api/v1/shoppinglist/:userId", (*shoppingListController).PostList)
+ r.DELETE("/api/v1/shoppinglist/:listId", (*shoppingListController).DeleteList)
+
+ r.GET("/api/v1/shoppinglistentries/:listId", (*shoppingListEntryController).GetEntries)
+ r.GET("/api/v1/shoppinglistentries/:listId/:productId", (*shoppingListEntryController).GetEntry)
+ r.PUT("/api/v1/shoppinglistentries/:listId/:productId", (*shoppingListEntryController).PutEntry)
+ r.POST("/api/v1/shoppinglistentries/:listId/:productId", (*shoppingListEntryController).PostEntry)
+ r.DELETE("/api/v1/shoppinglistentries/:listId/:productId", (*shoppingListEntryController).DeleteEntry)
return &Router{r}
}
From 74af40e5c18b7809a2610d12a78f6508e95f0e8c Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Tue, 16 Jan 2024 19:42:04 +0100
Subject: [PATCH 02/19] docs: Add info for kubernetes endpoints
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 870a63de..5278f37f 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,8 @@ The complete application can be deployed within a kubernetes cluster.
```shell
kubectl apply -R -f ./kubernetes/manifests
```
-
+5. The main page can be accessed via the public port of the `HTTP Proxy Service` container.
+Monitoring can be accessed via the Grafana public port. There is an example dashboard configuration file at [kubernetes/grafana](kubernetes/grafana/dashboard-config.json).
## Setup project in Docker
### Requirements
- Docker Compose version v2.23.3
From 1e80497c0427624298262af5fecd76fc2984eb12 Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 15:56:03 +0100
Subject: [PATCH 03/19] test(user-service): refactor tests for handler
---
.mockery.yaml | 10 +
.../api/http/handler/login_test.go | 348 ++++++++----------
.../api/http/handler/register_test.go | 290 ++++++++-------
.../auth/_mock/mock_TokenGenerator.go | 146 ++++++++
src/user-service/crypto/_mock/mock_Hasher.go | 137 +++++++
src/user-service/crypto/bcrypt_test.go | 9 -
6 files changed, 596 insertions(+), 344 deletions(-)
create mode 100644 src/user-service/auth/_mock/mock_TokenGenerator.go
create mode 100644 src/user-service/crypto/_mock/mock_Hasher.go
diff --git a/.mockery.yaml b/.mockery.yaml
index 666bab25..9c9f6591 100644
--- a/.mockery.yaml
+++ b/.mockery.yaml
@@ -30,3 +30,13 @@ packages:
interfaces:
Controller:
Repository:
+ hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/auth:
+ config:
+ dir: "./src/user-service/auth/_mock"
+ interfaces:
+ TokenGenerator:
+ hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/crypto:
+ config:
+ dir: "./src/user-service/crypto/_mock"
+ interfaces:
+ Hasher:
\ No newline at end of file
diff --git a/src/user-service/api/http/handler/login_test.go b/src/user-service/api/http/handler/login_test.go
index 02c89638..92a5252d 100644
--- a/src/user-service/api/http/handler/login_test.go
+++ b/src/user-service/api/http/handler/login_test.go
@@ -1,12 +1,14 @@
package handler
import (
- "encoding/json"
- "github.com/stretchr/testify/assert"
+ "errors"
+ "github.com/stretchr/testify/mock"
"hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/auth"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/auth/utils"
+ authMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/auth/_mock"
"hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/crypto"
+ cryptoMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/crypto/_mock"
"hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user"
+ userMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/_mock"
"hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/model"
"net/http"
"net/http/httptest"
@@ -15,213 +17,157 @@ import (
)
func TestLoginHandler(t *testing.T) {
- type fields struct {
- loginHandler *LoginHandler
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- expectedStatus int
- expectedResponse string
- }{
- {
- name: "Valid User (expect 200)",
- fields: fields{
- loginHandler: setupLoginHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/login",
- strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "123456"}`),
- ),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Invalid User Mail (expect 500)",
- fields: fields{
- loginHandler: setupLoginHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authenticationy/login",
- strings.NewReader(`{"email": "adaa.lovelace@gmail.com", "password": "123456"}`),
- ),
- },
- expectedStatus: http.StatusInternalServerError,
- expectedResponse: "",
- },
- {
- name: "Invalid Request - Empty Password (expect 400)",
- fields: fields{
- loginHandler: setupLoginHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/login",
- strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": ""}`),
- ),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- {
- name: "Wrong password (expect 401)",
- fields: fields{
- loginHandler: setupLoginHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/login",
- strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "98765"}`),
- ),
- },
- expectedStatus: http.StatusUnauthorized,
- expectedResponse: "",
- },
- {
- name: "Malformed JSON (expect 400)",
- fields: fields{
- loginHandler: setupLoginHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/login",
- strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "123456"`),
- ),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- {
- name: "Missing field (expect 400)",
- fields: fields{
- loginHandler: setupLoginHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/login",
- strings.NewReader(`{"email": "ada.lovelace@gmail.com"`),
- ),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- {
- name: "Invalid user data, incorrect Type for Email and Password (expected String) (expect 400)",
- fields: fields{
- loginHandler: setupLoginHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/login",
- strings.NewReader(`{"email": 120, "password": 120`),
- ),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
-
- tt.fields.loginHandler.Login(tt.args.writer, tt.args.request)
-
- // You can then assert the response status and content, and check against your expectations.
- if tt.args.writer.Code != tt.expectedStatus {
- t.Errorf("Expected status code %d, but got %d", tt.expectedStatus, tt.args.writer.Code)
- }
-
- if tt.expectedResponse != "" {
- actualResponse := tt.args.writer.Body.String()
- if actualResponse != tt.expectedResponse {
- t.Errorf("Expected response: %s, but got: %s", tt.expectedResponse, actualResponse)
- }
- }
- })
- }
-
- loginHandler := setupLoginHandler()
-
- writer := httptest.NewRecorder()
- request := httptest.NewRequest(
- "POST",
- "/api/v1/authentication/login",
- strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "123456"}`),
- )
-
- loginHandler.Login(writer, request)
-
- res := writer.Result()
- var response map[string]interface{}
- err := json.NewDecoder(res.Body).Decode(&response)
-
- assert.NoError(t, err)
- assert.Contains(t, response["access_token"], "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9")
- assert.Equal(t, "Bearer", response["token_type"])
- assert.Equal(t, float64(3600), response["expires_in"])
- assert.Equal(t, http.StatusOK, writer.Code)
-}
+ mockUsersRepository := userMock.MockRepository{}
+ var userRepository user.Repository = &mockUsersRepository
+ mockHasher := cryptoMock.NewMockHasher(t)
+ var hasher crypto.Hasher = mockHasher
+ mockTokenGenerator := authMock.MockTokenGenerator{}
+ var tokenGenerator auth.TokenGenerator = &mockTokenGenerator
-func setupLoginHandler() *LoginHandler {
- pemPrivateKey := utils.GenerateRandomECDSAPrivateKeyAsPEM()
+ loginHandler := NewLoginHandler(userRepository, hasher, tokenGenerator)
- var jwtToken, _ = auth.NewJwtTokenGenerator(
- auth.JwtConfig{PrivateKey: pemPrivateKey})
+ t.Run("Valid User (expect 200)", func(t *testing.T) {
+ expectedStatus := http.StatusOK
- return NewLoginHandler(setupMockRepository(),
- crypto.NewBcryptHasher(), jwtToken)
-}
+ mockUsersRepository.EXPECT().FindByEmail("ada.lovelace@gmail.com").Return(&model.User{
+ Id: 1,
+ Email: "ada.lovelace@gmail.com",
+ Password: []byte("123456"),
+ Name: "Ada Lovelace",
+ Role: model.Customer,
+ }, nil).Once()
-func setupMockRepository() user.Repository {
- repository := user.NewDemoRepository()
- userSlice := setupDemoUserSlice()
- for _, newUser := range userSlice {
- _, _ = repository.Create(newUser)
- }
+ mockTokenGenerator.EXPECT().CreateToken(mock.Anything).Return("valid-token", nil).Once()
- return repository
-}
+ mockHasher.EXPECT().Validate([]byte("123456"), []byte("123456")).Return(true).Once()
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/login",
+ strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "123456"}`),
+ )
+
+ loginHandler.Login(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("Invalid User Mail (expect 500)", func(t *testing.T) {
+ expectedStatus := http.StatusInternalServerError
+
+ mockUsersRepository.EXPECT().FindByEmail("ada.lovelace@gmail.com").Return(nil, errors.New(user.ErrorUserNotFound)).Once()
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/login",
+ strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "123456"}`),
+ )
+
+ loginHandler.Login(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("Invalid Request - Empty Password (expect 400)", func(t *testing.T) {
+ expectedStatus := http.StatusBadRequest
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/login",
+ strings.NewReader(`{"email": "ada.lovelace@gmail.com"}`),
+ )
+
+ loginHandler.Login(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
-func setupDemoUserSlice() []*model.User {
- bcryptHasher := crypto.NewBcryptHasher()
- hashedPassword, _ := bcryptHasher.Hash([]byte("123456"))
+ t.Run("Wrong password (expect 401)", func(t *testing.T) {
+ expectedStatus := http.StatusUnauthorized
- return []*model.User{
- {
+ mockUsersRepository.EXPECT().FindByEmail("ada.lovelace@gmail.com").Return(&model.User{
Id: 1,
Email: "ada.lovelace@gmail.com",
- Password: hashedPassword,
+ Password: []byte("894544"),
Name: "Ada Lovelace",
Role: model.Customer,
- },
- {
- Id: 2,
- Email: "alan.turin@gmail.com",
- Password: hashedPassword,
- Name: "Alan Turing",
- Role: model.Customer,
- },
- }
+ }, nil)
+
+ mockTokenGenerator.EXPECT().CreateToken(mock.Anything).Return("valid-token", nil).Once()
+
+ mockHasher.EXPECT().Validate([]byte("123456"), []byte("894544")).Return(false).Once()
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/login",
+ strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "123456"}`),
+ )
+
+ loginHandler.Login(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("Malformed JSON (expect 400)", func(t *testing.T) {
+ expectedStatus := http.StatusBadRequest
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/login",
+ strings.NewReader(`{"email: "ada.lovelace@gmail.com`),
+ )
+
+ loginHandler.Login(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("Missing field (expect 400)", func(t *testing.T) {
+ expectedStatus := http.StatusBadRequest
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/login",
+ strings.NewReader(`{"password": "12345"}`),
+ )
+
+ loginHandler.Login(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("Invalid user data, incorrect Type for Email and Password (expected String) (expect 400)", func(t *testing.T) {
+ expectedStatus := http.StatusBadRequest
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/login",
+ strings.NewReader(`{"email": 120 "password": 120}`),
+ )
+
+ loginHandler.Login(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
}
diff --git a/src/user-service/api/http/handler/register_test.go b/src/user-service/api/http/handler/register_test.go
index a363cbc1..95d631cf 100644
--- a/src/user-service/api/http/handler/register_test.go
+++ b/src/user-service/api/http/handler/register_test.go
@@ -1,7 +1,12 @@
package handler
import (
+ "errors"
"hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/crypto"
+ cryptoMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/crypto/_mock"
+ "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user"
+ userMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/_mock"
+ "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/model"
"net/http"
"net/http/httptest"
"strings"
@@ -9,139 +14,156 @@ import (
)
func TestRegisterHandler(t *testing.T) {
- type fields struct {
- registerHandler *RegisterHandler
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- expectedStatus int
- expectedResponse string
- }{
- {
- name: "Valid User (expect 200)",
- fields: fields{
- registerHandler: setUpRegisterHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/register",
- strings.NewReader(`{"email": "grace.hopper@gmail.com", "password": "123456", "name": "Grace Hopper", "role": 0}`),
- ),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Invalid Request - Empty Password (expect 400)",
- fields: fields{
- registerHandler: setUpRegisterHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/register",
- strings.NewReader(`{"email": "grace.hopper2@gmail.com", "password": "", "name": "Grace Hopper", "role": 0}`),
- ),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- {
- name: "User already exists (expect 409)",
- fields: fields{
- registerHandler: setUpRegisterHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/register",
- strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "123456", "name": "Ada Lovelace", "role": 0}`),
- ),
- },
- expectedStatus: http.StatusConflict,
- expectedResponse: "",
- },
- {
- name: "User should not be able to register as admin (expect 403)",
- fields: fields{
- registerHandler: setUpRegisterHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/register",
- strings.NewReader(`{"email": "ada.lovelace@gmail.com", "password": "123456", "name": "Ada Lovelace", "role": 2}`),
- ),
- },
- expectedStatus: http.StatusForbidden,
- expectedResponse: "",
- },
- {
- name: "Malformed JSON (expect 400)",
- fields: fields{
- registerHandler: setUpRegisterHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/register",
- strings.NewReader(`{"email": "grace.hopper@gmail.com", "password": "123456", "name": "Grace Hopper", "role": 0`),
- ),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- {
- name: "Invalid user data, incorrect Type for Email and Password (expected String) (expect 400)",
- fields: fields{
- registerHandler: setUpRegisterHandler(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/authentication/register",
- strings.NewReader(`{"email": "grace.hopper@gmail.com", "password": 123456, "name": "Grace Hopper", "role": false}`),
- ),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
-
- tt.fields.registerHandler.Register(tt.args.writer, tt.args.request)
-
- // You can then assert the response status and content, and check against your expectations.
- if tt.args.writer.Code != tt.expectedStatus {
- t.Errorf("Expected status code %d, but got %d", tt.expectedStatus, tt.args.writer.Code)
- }
-
- if tt.expectedResponse != "" {
- actualResponse := tt.args.writer.Body.String()
- if actualResponse != tt.expectedResponse {
- t.Errorf("Expected response: %s, but got: %s", tt.expectedResponse, actualResponse)
- }
- }
- })
- }
-}
+ mockUsersRepository := userMock.MockRepository{}
+ var userRepository user.Repository = &mockUsersRepository
+ mockHasher := cryptoMock.NewMockHasher(t)
+ var hasher crypto.Hasher = mockHasher
+
+ registerHandler := NewRegisterHandler(userRepository, hasher)
+
+ t.Run("Valid User (expect 200)", func(t *testing.T) {
+ expectedStatus := http.StatusOK
+ expectedResponse := `{"id":0,"name":"Grace Hopper","email":"grace.hopper@gmail.com","role":0}`
+
+ userToCreate := &model.User{
+ Email: "grace.hopper@gmail.com",
+ Password: []byte("123456"),
+ Name: "Grace Hopper",
+ Role: 0,
+ }
+
+ mockUsersRepository.EXPECT().FindByEmail("grace.hopper@gmail.com").Return(nil, errors.New(user.ErrorUserNotFound)).Once()
+ mockHasher.EXPECT().Hash([]byte("123456")).Return([]byte("123456"), nil).Once()
+ mockUsersRepository.EXPECT().Create(userToCreate).Return(userToCreate, nil).Once()
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/register",
+ strings.NewReader(`{"email": "grace.hopper@gmail.com", "password": "123456", "name": "Grace Hopper", "role": 0}`),
+ )
+
+ registerHandler.Register(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+
+ if strings.Compare(expectedResponse, writer.Body.String()) == 0 {
+ t.Errorf("Expected response %s, but got %s", expectedResponse, writer.Body.String())
+ }
+
+ })
+
+ t.Run("Invalid Request - Empty Password (expect 400)", func(t *testing.T) {
+ expectedStatus := http.StatusBadRequest
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/register",
+ strings.NewReader(`{"email": "grace.hopper@gmail.com", "name": "Grace Hopper", "role": 0}`),
+ )
+
+ registerHandler.Register(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("User already exists (expect 409)", func(t *testing.T) {
+ expectedStatus := http.StatusConflict
+
+ userToCreate := &model.User{
+ Email: "grace.hopper@gmail.com",
+ Password: []byte("123456"),
+ Name: "Grace Hopper",
+ Role: 0,
+ }
+
+ mockUsersRepository.EXPECT().FindByEmail("grace.hopper@gmail.com").Return(userToCreate, nil).Once()
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/register",
+ strings.NewReader(`{"email": "grace.hopper@gmail.com", "password": "123456", "name": "Grace Hopper", "role": 0}`),
+ )
+
+ registerHandler.Register(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("User should not be able to register as admin (expect 403)", func(t *testing.T) {
+ expectedStatus := http.StatusForbidden
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/register",
+ strings.NewReader(`{"email": "grace.hopper@gmail.com", "password": "123456", "name": "Grace Hopper", "role": 2}`),
+ )
+
+ registerHandler.Register(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+
+ })
+
+ t.Run("Invalid Request - Empty Password (expect 400)", func(t *testing.T) {
+ expectedStatus := http.StatusBadRequest
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/register",
+ strings.NewReader(`{"email": "grace.hopper@gmail.com", "password": "", "name": "Grace Hopper", "role": 0}`),
+ )
+
+ registerHandler.Register(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("Malformed JSON (expect 400)", func(t *testing.T) {
+ expectedStatus := http.StatusBadRequest
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/register",
+ strings.NewReader(`{"eil: "grace.hom", ame": "Grace Hopper", "role: 0`),
+ )
+
+ registerHandler.Register(writer, request)
+
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
+
+ t.Run("Invalid user data, incorrect Type for Email and Password (expected String) (expect 400)", func(t *testing.T) {
+ expectedStatus := http.StatusBadRequest
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "POST",
+ "/api/v1/authentication/register",
+ strings.NewReader(`{"email": 120, "password": 120, "name": "Grace Hopper", "role": 0}`),
+ )
+
+ registerHandler.Register(writer, request)
-func setUpRegisterHandler() *RegisterHandler {
- return NewRegisterHandler(setupMockRepository(),
- crypto.NewBcryptHasher())
+ if writer.Code != expectedStatus {
+ t.Errorf("Expected status code %d, but got %d", expectedStatus, writer.Code)
+ }
+ })
}
diff --git a/src/user-service/auth/_mock/mock_TokenGenerator.go b/src/user-service/auth/_mock/mock_TokenGenerator.go
new file mode 100644
index 00000000..2c0bf9ec
--- /dev/null
+++ b/src/user-service/auth/_mock/mock_TokenGenerator.go
@@ -0,0 +1,146 @@
+// Code generated by mockery v2.40.1. DO NOT EDIT.
+
+package auth
+
+import mock "github.com/stretchr/testify/mock"
+
+// MockTokenGenerator is an autogenerated mock type for the TokenGenerator type
+type MockTokenGenerator struct {
+ mock.Mock
+}
+
+type MockTokenGenerator_Expecter struct {
+ mock *mock.Mock
+}
+
+func (_m *MockTokenGenerator) EXPECT() *MockTokenGenerator_Expecter {
+ return &MockTokenGenerator_Expecter{mock: &_m.Mock}
+}
+
+// CreateToken provides a mock function with given fields: claims
+func (_m *MockTokenGenerator) CreateToken(claims map[string]interface{}) (string, error) {
+ ret := _m.Called(claims)
+
+ if len(ret) == 0 {
+ panic("no return value specified for CreateToken")
+ }
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(map[string]interface{}) (string, error)); ok {
+ return rf(claims)
+ }
+ if rf, ok := ret.Get(0).(func(map[string]interface{}) string); ok {
+ r0 = rf(claims)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(map[string]interface{}) error); ok {
+ r1 = rf(claims)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockTokenGenerator_CreateToken_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateToken'
+type MockTokenGenerator_CreateToken_Call struct {
+ *mock.Call
+}
+
+// CreateToken is a helper method to define mock.On call
+// - claims map[string]interface{}
+func (_e *MockTokenGenerator_Expecter) CreateToken(claims interface{}) *MockTokenGenerator_CreateToken_Call {
+ return &MockTokenGenerator_CreateToken_Call{Call: _e.mock.On("CreateToken", claims)}
+}
+
+func (_c *MockTokenGenerator_CreateToken_Call) Run(run func(claims map[string]interface{})) *MockTokenGenerator_CreateToken_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(map[string]interface{}))
+ })
+ return _c
+}
+
+func (_c *MockTokenGenerator_CreateToken_Call) Return(_a0 string, _a1 error) *MockTokenGenerator_CreateToken_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockTokenGenerator_CreateToken_Call) RunAndReturn(run func(map[string]interface{}) (string, error)) *MockTokenGenerator_CreateToken_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// VerifyToken provides a mock function with given fields: tokenString
+func (_m *MockTokenGenerator) VerifyToken(tokenString string) (map[string]interface{}, error) {
+ ret := _m.Called(tokenString)
+
+ if len(ret) == 0 {
+ panic("no return value specified for VerifyToken")
+ }
+
+ var r0 map[string]interface{}
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string) (map[string]interface{}, error)); ok {
+ return rf(tokenString)
+ }
+ if rf, ok := ret.Get(0).(func(string) map[string]interface{}); ok {
+ r0 = rf(tokenString)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(map[string]interface{})
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string) error); ok {
+ r1 = rf(tokenString)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockTokenGenerator_VerifyToken_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VerifyToken'
+type MockTokenGenerator_VerifyToken_Call struct {
+ *mock.Call
+}
+
+// VerifyToken is a helper method to define mock.On call
+// - tokenString string
+func (_e *MockTokenGenerator_Expecter) VerifyToken(tokenString interface{}) *MockTokenGenerator_VerifyToken_Call {
+ return &MockTokenGenerator_VerifyToken_Call{Call: _e.mock.On("VerifyToken", tokenString)}
+}
+
+func (_c *MockTokenGenerator_VerifyToken_Call) Run(run func(tokenString string)) *MockTokenGenerator_VerifyToken_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(string))
+ })
+ return _c
+}
+
+func (_c *MockTokenGenerator_VerifyToken_Call) Return(_a0 map[string]interface{}, _a1 error) *MockTokenGenerator_VerifyToken_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockTokenGenerator_VerifyToken_Call) RunAndReturn(run func(string) (map[string]interface{}, error)) *MockTokenGenerator_VerifyToken_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// NewMockTokenGenerator creates a new instance of MockTokenGenerator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMockTokenGenerator(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *MockTokenGenerator {
+ mock := &MockTokenGenerator{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/src/user-service/crypto/_mock/mock_Hasher.go b/src/user-service/crypto/_mock/mock_Hasher.go
new file mode 100644
index 00000000..d914ded9
--- /dev/null
+++ b/src/user-service/crypto/_mock/mock_Hasher.go
@@ -0,0 +1,137 @@
+// Code generated by mockery v2.40.1. DO NOT EDIT.
+
+package crypto
+
+import mock "github.com/stretchr/testify/mock"
+
+// MockHasher is an autogenerated mock type for the Hasher type
+type MockHasher struct {
+ mock.Mock
+}
+
+type MockHasher_Expecter struct {
+ mock *mock.Mock
+}
+
+func (_m *MockHasher) EXPECT() *MockHasher_Expecter {
+ return &MockHasher_Expecter{mock: &_m.Mock}
+}
+
+// Hash provides a mock function with given fields: _a0
+func (_m *MockHasher) Hash(_a0 []byte) ([]byte, error) {
+ ret := _m.Called(_a0)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Hash")
+ }
+
+ var r0 []byte
+ var r1 error
+ if rf, ok := ret.Get(0).(func([]byte) ([]byte, error)); ok {
+ return rf(_a0)
+ }
+ if rf, ok := ret.Get(0).(func([]byte) []byte); ok {
+ r0 = rf(_a0)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]byte)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func([]byte) error); ok {
+ r1 = rf(_a0)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockHasher_Hash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Hash'
+type MockHasher_Hash_Call struct {
+ *mock.Call
+}
+
+// Hash is a helper method to define mock.On call
+// - _a0 []byte
+func (_e *MockHasher_Expecter) Hash(_a0 interface{}) *MockHasher_Hash_Call {
+ return &MockHasher_Hash_Call{Call: _e.mock.On("Hash", _a0)}
+}
+
+func (_c *MockHasher_Hash_Call) Run(run func(_a0 []byte)) *MockHasher_Hash_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].([]byte))
+ })
+ return _c
+}
+
+func (_c *MockHasher_Hash_Call) Return(_a0 []byte, _a1 error) *MockHasher_Hash_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockHasher_Hash_Call) RunAndReturn(run func([]byte) ([]byte, error)) *MockHasher_Hash_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Validate provides a mock function with given fields: _a0, _a1
+func (_m *MockHasher) Validate(_a0 []byte, _a1 []byte) bool {
+ ret := _m.Called(_a0, _a1)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Validate")
+ }
+
+ var r0 bool
+ if rf, ok := ret.Get(0).(func([]byte, []byte) bool); ok {
+ r0 = rf(_a0, _a1)
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ return r0
+}
+
+// MockHasher_Validate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Validate'
+type MockHasher_Validate_Call struct {
+ *mock.Call
+}
+
+// Validate is a helper method to define mock.On call
+// - _a0 []byte
+// - _a1 []byte
+func (_e *MockHasher_Expecter) Validate(_a0 interface{}, _a1 interface{}) *MockHasher_Validate_Call {
+ return &MockHasher_Validate_Call{Call: _e.mock.On("Validate", _a0, _a1)}
+}
+
+func (_c *MockHasher_Validate_Call) Run(run func(_a0 []byte, _a1 []byte)) *MockHasher_Validate_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].([]byte), args[1].([]byte))
+ })
+ return _c
+}
+
+func (_c *MockHasher_Validate_Call) Return(_a0 bool) *MockHasher_Validate_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *MockHasher_Validate_Call) RunAndReturn(run func([]byte, []byte) bool) *MockHasher_Validate_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// NewMockHasher creates a new instance of MockHasher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMockHasher(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *MockHasher {
+ mock := &MockHasher{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/src/user-service/crypto/bcrypt_test.go b/src/user-service/crypto/bcrypt_test.go
index 8f7da906..a1b62d74 100644
--- a/src/user-service/crypto/bcrypt_test.go
+++ b/src/user-service/crypto/bcrypt_test.go
@@ -12,13 +12,10 @@ func TestBcryptHasher(t *testing.T) {
t.Run("Hash", func(t *testing.T) {
t.Run("should return hash with salt", func(t *testing.T) {
- // given
password := []byte("password")
- // when
hash, err := hasher.Hash(password)
- // then
assert.NoError(t, err)
assert.Len(t, hash, 60)
assert.Regexp(t, regexp.MustCompile(`\$2a\$10\$(.*)`), string(hash))
@@ -27,26 +24,20 @@ func TestBcryptHasher(t *testing.T) {
t.Run("Validate", func(t *testing.T) {
t.Run("should return true if password matches hash", func(t *testing.T) {
- // given
password := []byte("password")
hash := []byte("$2a$10$s3BvNfI4PZO0PhcyxK4vTeu0N3Hhxo4mMgd084ENY41q/DeXhstc6")
- // when
ok := hasher.Validate(password, hash)
- // then
assert.True(t, ok)
})
t.Run("should return false if password does not match hash", func(t *testing.T) {
- // given
password := []byte("password")
hash := []byte("$2a$10$s3BvNfI4PZO0PhcyxK4vTeu0N3Hhxo4mMgd084ENY41q/DeXhstc7")
- // when
ok := hasher.Validate(password, hash)
- // then
assert.False(t, ok)
})
})
From 612bd6c035e60910abbac9c44a19b030420ac8bd Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 15:56:16 +0100
Subject: [PATCH 04/19] test(user-service): add tests for grpc server
---
src/user-service/api/rpc/server_test.go | 81 +++++++++++++++++++++++++
1 file changed, 81 insertions(+)
create mode 100644 src/user-service/api/rpc/server_test.go
diff --git a/src/user-service/api/rpc/server_test.go b/src/user-service/api/rpc/server_test.go
new file mode 100644
index 00000000..94a4b027
--- /dev/null
+++ b/src/user-service/api/rpc/server_test.go
@@ -0,0 +1,81 @@
+package rpc
+
+import (
+ "context"
+ "errors"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ proto "hsfl.de/group6/hsfl-master-ai-cloud-engineering/lib/rpc/user"
+ "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/auth"
+ authMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/auth/_mock"
+ "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user"
+ userMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/_mock"
+ "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/model"
+ "testing"
+)
+
+func TestNewUserServiceServer(t *testing.T) {
+ mockUsersRepository := userMock.NewMockRepository(t)
+ var userRepository user.Repository = mockUsersRepository
+ mockTokenGenerator := authMock.NewMockTokenGenerator(t)
+ var tokenGenerator auth.TokenGenerator = mockTokenGenerator
+
+ server := NewUserServiceServer(&userRepository, tokenGenerator)
+
+ assert.NotNil(t, server)
+ assert.NotNil(t, server.userRepository, userRepository)
+ assert.NotNil(t, server.tokenGenerator, tokenGenerator)
+}
+
+func TestUserServiceServer_ValidateUserToken(t *testing.T) {
+ mockUserRepository := userMock.NewMockRepository(t)
+ var userRepository user.Repository = mockUserRepository
+ mockTokenGenerator := authMock.NewMockTokenGenerator(t)
+ var tokenGenerator auth.TokenGenerator = mockTokenGenerator
+
+ server := NewUserServiceServer(&userRepository, tokenGenerator)
+
+ t.Run("ValidToken", func(t *testing.T) {
+ expectedUserId := uint64(1)
+ expectedUser := &model.User{Id: expectedUserId, Email: "test@example.com", Name: "Test User", Role: 1}
+ mockUserRepository.EXPECT().FindById(expectedUserId).Return(expectedUser, nil).Once()
+
+ claims := map[string]interface{}{"id": float64(expectedUserId)}
+ mockTokenGenerator.EXPECT().VerifyToken(mock.Anything).Return(claims, nil).Once()
+
+ response, err := server.ValidateUserToken(context.Background(), &proto.ValidateUserTokenRequest{Token: "valid-token"})
+
+ assert.NoError(t, err)
+ assert.NotNil(t, response)
+ assert.Equal(t, expectedUser.Id, response.User.Id)
+ assert.Equal(t, expectedUser.Email, response.User.Email)
+ assert.Equal(t, expectedUser.Name, response.User.Name)
+ assert.Equal(t, int64(expectedUser.Role), response.User.Role)
+ })
+
+ t.Run("InvalidToken", func(t *testing.T) {
+ mockTokenGenerator.EXPECT().VerifyToken("invalid-token").Return(nil, errors.New("invalid token"))
+
+ _, err := server.ValidateUserToken(context.Background(), &proto.ValidateUserTokenRequest{Token: "invalid-token"})
+ assert.Error(t, err)
+ })
+
+ t.Run("InvalidClaims", func(t *testing.T) {
+ claims := make(map[string]interface{})
+ mockTokenGenerator.EXPECT().VerifyToken("token-without-id").Return(claims, nil).Once()
+
+ _, err := server.ValidateUserToken(context.Background(), &proto.ValidateUserTokenRequest{Token: "token-without-id"})
+ assert.Error(t, err)
+ })
+
+ t.Run("NonExistentUser", func(t *testing.T) {
+ nonExistingUserId := uint64(999)
+ claims := map[string]interface{}{"id": float64(nonExistingUserId)}
+ mockTokenGenerator.EXPECT().VerifyToken("valid-token").Return(claims, nil)
+ mockUserRepository.EXPECT().FindById(nonExistingUserId).Return(nil, errors.New("user not found")).Once()
+
+ _, err := server.ValidateUserToken(context.Background(), &proto.ValidateUserTokenRequest{Token: "valid-token"})
+ assert.Error(t, err)
+ })
+
+}
From b29797dd78d7e54a27e0066b4a57e0260e9cfda8 Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 15:56:32 +0100
Subject: [PATCH 05/19] refactor(user-service): remove demo repository
---
src/user-service/user/demo_repository.go | 125 --------
src/user-service/user/demo_repository_test.go | 277 ------------------
2 files changed, 402 deletions(-)
delete mode 100644 src/user-service/user/demo_repository.go
delete mode 100644 src/user-service/user/demo_repository_test.go
diff --git a/src/user-service/user/demo_repository.go b/src/user-service/user/demo_repository.go
deleted file mode 100644
index 4c286773..00000000
--- a/src/user-service/user/demo_repository.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package user
-
-import (
- "errors"
- "sort"
-
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/model"
-)
-
-type DemoRepository struct {
- users map[uint64]*model.User
-}
-
-func NewDemoRepository() *DemoRepository {
- return &DemoRepository{users: make(map[uint64]*model.User)}
-}
-
-func (repo *DemoRepository) Create(user *model.User) (*model.User, error) {
- var userId uint64
- if user.Id == 0 {
- userId = repo.findNextAvailableID()
- user.Id = userId
- } else {
- userId = user.Id
- }
-
- _, found := repo.users[userId]
- foundUser, err := repo.FindByEmail(user.Email)
- if found || foundUser != nil {
- return nil, errors.New(ErrorUserAlreadyExists)
- } else if err != nil && err.Error() != ErrorUserNotFound {
- return nil, err
- }
-
- repo.users[userId] = user
-
- return user, nil
-}
-
-func (repo *DemoRepository) Delete(user *model.User) error {
- _, found := repo.users[user.Id]
- if found {
- delete(repo.users, user.Id)
- return nil
- }
-
- return errors.New(ErrorUserDeletion)
-}
-
-func (repo *DemoRepository) FindAll() ([]*model.User, error) {
- if repo.users != nil {
- r := make([]*model.User, 0, len(repo.users))
- for _, v := range repo.users {
- r = append(r, v)
- }
-
- sort.Slice(r, func(i, j int) bool {
- return r[i].Name < r[j].Name
- })
- return r, nil
- }
-
- return nil, errors.New(ErrorUserList)
-}
-
-func (repo *DemoRepository) FindAllByRole(role model.Role) ([]*model.User, error) {
- if repo.users != nil {
- r := make([]*model.User, 0)
- for _, user := range repo.users {
- if user.Role == role {
- r = append(r, user)
- }
- }
-
- sort.Slice(r, func(i, j int) bool {
- return r[i].Name < r[j].Name
- })
- return r, nil
- }
-
- return nil, errors.New(ErrorUserList)
-}
-
-func (repo *DemoRepository) FindByEmail(email string) (*model.User, error) {
- for _, user := range repo.users {
- if user.Email == email {
- return user, nil
- }
- }
-
- return nil, errors.New(ErrorUserNotFound)
-}
-
-func (repo *DemoRepository) FindById(id uint64) (*model.User, error) {
- user, found := repo.users[id]
- if found {
- return user, nil
- }
-
- return nil, errors.New(ErrorUserNotFound)
-}
-
-func (repo *DemoRepository) Update(user *model.User) (*model.User, error) {
- existingUser, foundError := repo.FindById(user.Id)
-
- if foundError != nil {
- return nil, errors.New(ErrorUserUpdate)
- }
-
- existingUser.Name = user.Name
- existingUser.Email = user.Email
- existingUser.Role = user.Role
-
- return existingUser, nil
-}
-
-func (repo *DemoRepository) findNextAvailableID() uint64 {
- var maxID uint64
- for id := range repo.users {
- if id > maxID {
- maxID = id
- }
- }
- return maxID + 1
-}
diff --git a/src/user-service/user/demo_repository_test.go b/src/user-service/user/demo_repository_test.go
deleted file mode 100644
index 8caa3f24..00000000
--- a/src/user-service/user/demo_repository_test.go
+++ /dev/null
@@ -1,277 +0,0 @@
-package user
-
-import (
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/model"
- "reflect"
- "sort"
- "testing"
-)
-
-func TestDemoRepository_CreateUser(t *testing.T) {
- demoRepository := NewDemoRepository()
-
- user := model.User{
- Email: "ada.lovelace@gmail.com",
- Password: []byte("123456"),
- Name: "Ada Lovelace",
- Role: model.Customer,
- }
-
- // Create user with success
- _, err := demoRepository.Create(&user)
- if err != nil {
- t.Error(err)
- }
-
- // Check for doublet
- _, err = demoRepository.Create(&user)
- if err.Error() != ErrorUserAlreadyExists {
- t.Error(err)
- }
-}
-
-func TestDemoRepository_FindAll(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- users := []*model.User{
- {
- Id: 1,
- Email: "ada.lovelace@gmail.com",
- Password: []byte("123456"),
- Name: "Ada Lovelace",
- Role: model.Customer,
- },
- {
- Id: 2,
- Email: "alan.turin@gmail.com",
- Password: []byte("123456"),
- Name: "Alan Turing",
- Role: model.Customer,
- },
- }
-
- for _, user := range users {
- _, err := demoRepository.Create(user)
- if err != nil {
- t.Error("Failed to add prepared product for test")
- }
- }
-
- t.Run("Fetch all users", func(t *testing.T) {
- fetchedUsers, err := demoRepository.FindAll()
- if err != nil {
- t.Error("Can't fetch users")
- }
-
- if len(fetchedUsers) != len(users) {
- t.Errorf("Unexpected product count. Expected %d, got %d", len(users), len(fetchedUsers))
- }
- })
-
- userTests := []struct {
- name string
- want *model.User
- }{
- {
- name: "first",
- want: users[0],
- },
- {
- name: "second",
- want: users[1],
- },
- }
-
- for i, tt := range userTests {
- t.Run("Is fetched product matching with "+tt.name+" added product?", func(t *testing.T) {
- fetchedUsers, _ := demoRepository.FindAll()
- sort.Slice(fetchedUsers, func(i, j int) bool {
- return fetchedUsers[i].Id < fetchedUsers[j].Id
- })
- if !reflect.DeepEqual(tt.want, fetchedUsers[i]) {
- t.Error("Fetched product does not match original product")
- }
- })
- }
-}
-
-func TestDemoRepository_FindAllByRole(t *testing.T) {
- demoRepository := NewDemoRepository()
-
- users := []*model.User{
- {
- Id: 1,
- Email: "ada.lovelace@gmail.com",
- Password: []byte("123456"),
- Name: "Ada Lovelace",
- Role: model.Customer,
- },
- {
- Id: 2,
- Email: "alan.turin@gmail.com",
- Password: []byte("123456"),
- Name: "Alan Turing",
- Role: model.Merchant,
- },
- }
-
- for _, user := range users {
- _, err := demoRepository.Create(user)
- if err != nil {
- t.Error("Failed to add prepared user for test")
- }
- }
-
- t.Run("Fetch all users by merchant role", func(t *testing.T) {
- fetchedUsers, err := demoRepository.FindAllByRole(model.Merchant)
- if err != nil {
- t.Error("Can't fetch users")
- }
-
- if len(fetchedUsers) != 1 {
- t.Errorf("Unexpected user count. Expected 1, got %d", len(fetchedUsers))
- }
- })
-}
-
-func TestDemoRepository_FindById(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- user := model.User{
- Id: 1,
- Email: "ada.lovelace@gmail.com",
- Password: []byte("123456"),
- Name: "Ada Lovelace",
- Role: model.Customer,
- }
-
- _, err := demoRepository.Create(&user)
- if err != nil {
- t.Error("Failed to add prepare user for test")
- }
-
- // Fetch user with existing id
- fetchedUser, err := demoRepository.FindById(user.Id)
- if err != nil {
- t.Errorf("Can't find expected user with id %d", user.Id)
- }
-
- // Is fetched user matching with added user?
- if !reflect.DeepEqual(user, *fetchedUser) {
- t.Error("Fetched user does not match original user")
- }
-
- // Non-existing user test
- _, err = demoRepository.FindById(42)
- if err.Error() != "user could not be found" {
- t.Error(err)
- }
-}
-
-func TestDemoRepository_FindByEmail(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- user := model.User{
- Id: 1,
- Email: "ada.lovelace@gmail.com",
- Password: []byte("123456"),
- Name: "Ada Lovelace",
- Role: model.Customer,
- }
-
- _, err := demoRepository.Create(&user)
- if err != nil {
- t.Error("Failed to add prepare user for test")
- }
-
- // Fetch user with existing email
- fetchedUser, err := demoRepository.FindByEmail(user.Email)
- if err != nil {
- t.Errorf("Can't find expected user with email %s", user.Email)
- }
-
- // Is fetched user matching with added user?
- if !reflect.DeepEqual(user, *fetchedUser) {
- t.Error("Fetched user does not match original user")
- }
-
- // Non-existing user test
- _, err = demoRepository.FindByEmail("alan.turing@gmail.com")
- if err.Error() != "user could not be found" {
- t.Error(err)
- }
-}
-
-func TestDemoRepository_Update(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- user := model.User{
- Id: 1,
- Email: "ada.lovelace@gmail.com",
- Password: []byte("123456"),
- Name: "Ada Lovelace",
- Role: model.Customer,
- }
-
- fetchedUser, err := demoRepository.Create(&user)
- if err != nil {
- t.Error("Failed to add prepare user for test")
- }
-
- fetchedUser.Name = "Alan Turing"
- updatedUser, err := demoRepository.Update(fetchedUser)
-
- // Check if returned user has the updated name
- if fetchedUser.Name != updatedUser.Name {
- t.Error("Failed to update user")
- }
-}
-
-func TestDemoRepository_Delete(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- user := model.User{
- Id: 1,
- Email: "ada.lovelace@gmail.com",
- Password: []byte("123456"),
- Name: "Ada Lovelace",
- Role: model.Customer,
- }
-
- fetchedUser, err := demoRepository.Create(&user)
- if err != nil {
- t.Error("Failed to add prepare user for test")
- }
-
- // Test for deletion
- err = demoRepository.Delete(fetchedUser)
- if err != nil {
- t.Errorf("Failed to delete user with id %d", user.Id)
- }
-
- // Fetch user with existing id
- fetchedUser, err = demoRepository.FindById(user.Id)
- if err.Error() != "user could not be found" {
- t.Errorf("User with id %d was not deleted", user.Id)
- }
-
- // Try to delete non-existing user
- fakeUser := model.User{
- Id: 42,
- Email: "alan.turin@gmail.com",
- Password: []byte("123456"),
- Name: "Alan Turing",
- Role: model.Customer,
- }
-
- // Test for deletion
- err = demoRepository.Delete(&fakeUser)
- if err.Error() != "user could not be deleted" {
- t.Errorf("User with id %d was deleted", user.Id)
- }
-}
From c6656be23f3ab3c9ae9b26c9908dc8ee075bb30c Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 15:57:02 +0100
Subject: [PATCH 06/19] test(user-service): add grpc server tests
---
src/product-service/api/rpc/server_test.go | 441 +++++++++++++++++++--
1 file changed, 416 insertions(+), 25 deletions(-)
diff --git a/src/product-service/api/rpc/server_test.go b/src/product-service/api/rpc/server_test.go
index 80ef9cfc..1de95437 100644
--- a/src/product-service/api/rpc/server_test.go
+++ b/src/product-service/api/rpc/server_test.go
@@ -3,15 +3,24 @@ package rpc
import (
"context"
"github.com/stretchr/testify/assert"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ "gopkg.in/errgo.v2/errors"
proto "hsfl.de/group6/hsfl-master-ai-cloud-engineering/lib/rpc/product"
"hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices"
+ pricesMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices/_mock"
+ priceModel "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices/model"
"hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/products"
+ productsMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/products/_mock"
+ productModel "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/products/model"
"testing"
)
func TestNewProductServiceServer(t *testing.T) {
- var productRepo products.Repository = products.NewDemoRepository()
- var priceRepo prices.Repository = prices.NewDemoRepository()
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
server := NewProductServiceServer(&productRepo, &priceRepo)
@@ -21,8 +30,10 @@ func TestNewProductServiceServer(t *testing.T) {
}
func TestProductServiceServer_CreateProduct(t *testing.T) {
- productRepo := products.GenerateExampleDemoRepository()
- priceRepo := prices.GenerateExampleDemoRepository()
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
server := NewProductServiceServer(&productRepo, &priceRepo)
ctx := context.Background()
@@ -30,69 +41,449 @@ func TestProductServiceServer_CreateProduct(t *testing.T) {
Product: &proto.Product{
Id: 123,
Description: "Test",
- Ean: "123456789",
+ Ean: "12345670",
},
}
- resp, err := server.CreateProduct(ctx, req)
- if err != nil {
- t.Errorf("Error creating product: %v", err)
- }
+ t.Run("Successful create", func(t *testing.T) {
+ mockProductRepo.EXPECT().Create(
+ &productModel.Product{Id: 123, Description: "Test", Ean: "12345670"}).
+ Return(
+ &productModel.Product{Id: 123, Description: "Test", Ean: "12345670"}, nil).Once()
- if resp.Product.Id != 123 {
- t.Errorf("Expected product ID '123', got '%d'", resp.Product.Id)
- }
+ resp, err := server.CreateProduct(ctx, req)
+ if err != nil {
+ t.Errorf("Error creating product: %v", err)
+ }
+
+ if resp.Product.Id != 123 {
+ t.Errorf("Expected product ID '123', got '%d'", resp.Product.Id)
+ }
+ })
+
+ t.Run("Unsuccessful create", func(t *testing.T) {
+ mockProductRepo.EXPECT().Create(
+ &productModel.Product{Id: 123, Description: "Test", Ean: "12345670"}).
+ Return(nil, errors.New(products.ErrorProductAlreadyExists)).Once()
+
+ _, err := server.CreateProduct(ctx, req)
+ if e, ok := status.FromError(err); ok {
+ if e.Code() != codes.Internal {
+ t.Errorf("expected error: %v", err)
+ }
+ }
+ })
}
func TestProductServiceServer_GetProduct(t *testing.T) {
- productRepo := products.GenerateExampleDemoRepository()
- priceRepo := prices.GenerateExampleDemoRepository()
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
+
+ t.Run("Successful get product", func(t *testing.T) {
+ mockProductRepo.EXPECT().FindById(uint64(2)).Return(
+ &productModel.Product{Id: 2, Description: "Test", Ean: "12345670"}, nil).Once()
+
+ ctx := context.Background()
+ getReq := &proto.GetProductRequest{Id: 2}
+ resp, err := server.GetProduct(ctx, getReq)
+ if err != nil {
+ t.Errorf("Error getting product: %v", err)
+ }
+
+ if resp.Product.Id != 2 {
+ t.Errorf("Expected product ID '123', got '%d'", resp.Product.Id)
+ }
+ })
+
+ t.Run("Unsuccessful get product", func(t *testing.T) {
+ mockProductRepo.EXPECT().FindById(uint64(2)).Return(nil, errors.New(products.ErrorProductNotFound)).Once()
+
+ ctx := context.Background()
+ getReq := &proto.GetProductRequest{Id: 2}
+ _, err := server.GetProduct(ctx, getReq)
+ if e, ok := status.FromError(err); ok {
+ if e.Code() != codes.NotFound {
+ t.Errorf("expected error: %v", err)
+ }
+ }
+ })
+}
+
+func TestProductServiceServer_GetAllProducts(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
server := NewProductServiceServer(&productRepo, &priceRepo)
+ products := []*productModel.Product{
+ {Id: 1, Description: "Banane", Ean: "12345670"},
+ {Id: 2, Description: "Apfel", Ean: "13828523"},
+ }
+
+ mockProductRepo.EXPECT().FindAll().Return(products, nil)
+
ctx := context.Background()
- getReq := &proto.GetProductRequest{Id: 2}
- resp, err := server.GetProduct(ctx, getReq)
+ getReq := &proto.GetAllProductsRequest{}
+ resp, err := server.GetAllProducts(ctx, getReq)
if err != nil {
- t.Errorf("Error getting product: %v", err)
+ t.Errorf("Error getting all products: %v", err)
}
- if resp.Product.Id != 2 {
- t.Errorf("Expected product ID '123', got '%d'", resp.Product.Id)
+ for i, product := range products {
+ if product.Id != resp.Products[i].Id {
+ t.Errorf("Expected product ID '%d', got '%d'", product.Id, resp.Products[i].Id)
+ }
+ if product.Description != resp.Products[i].Description {
+ t.Errorf("Expected product Description '%s', got '%s'", product.Description, resp.Products[i].Description)
+ }
+ if product.Ean != resp.Products[i].Ean {
+ t.Errorf("Expected product Ean '%s', got '%s'", product.Ean, resp.Products[i].Ean)
+ }
}
}
-func TestProductServiceServer_CreatePrice(t *testing.T) {
+func TestProductServiceServer_UpdateProduct(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
-}
+ t.Run("Successful Update", func(t *testing.T) {
+ changedProduct := &productModel.Product{Id: 1, Description: "Erdbeere", Ean: "12345670"}
-func TestProductServiceServer_DeletePrice(t *testing.T) {
+ mockProductRepo.EXPECT().Update(changedProduct).Return(changedProduct, nil).Once()
+
+ ctx := context.Background()
+ updReq := &proto.UpdateProductRequest{Product: &proto.Product{
+ Id: 1,
+ Description: "Erdbeere",
+ Ean: "12345670",
+ }}
+ resp, err := server.UpdateProduct(ctx, updReq)
+ if err != nil {
+ t.Errorf("Error getting all products: %v", err)
+ }
+
+ if changedProduct.Id != resp.Product.Id {
+ t.Errorf("Expected product ID '%d', got '%d'", changedProduct.Id, resp.Product.Id)
+ }
+ if changedProduct.Description != resp.Product.Description {
+ t.Errorf("Expected product Description '%s', got '%s'", changedProduct.Description, resp.Product.Description)
+ }
+ if changedProduct.Ean != resp.Product.Ean {
+ t.Errorf("Expected product Ean '%s', got '%s'", changedProduct.Ean, resp.Product.Ean)
+ }
+ })
+ t.Run("Unsuccessful Update", func(t *testing.T) {
+ changedProduct := &productModel.Product{Id: 1, Description: "Erdbeere", Ean: "12345670"}
+ mockProductRepo.EXPECT().Update(changedProduct).Return(nil, errors.New(products.ErrorProductUpdate)).Once()
+
+ ctx := context.Background()
+ updReq := &proto.UpdateProductRequest{Product: &proto.Product{
+ Id: 1,
+ Description: "Erdbeere",
+ Ean: "12345670",
+ }}
+ _, err := server.UpdateProduct(ctx, updReq)
+ if e, ok := status.FromError(err); ok {
+ if e.Code() != codes.Internal {
+ t.Errorf("expected error: %v", err)
+ }
+ }
+ })
}
func TestProductServiceServer_DeleteProduct(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
+
+ t.Run("Successful Delete", func(t *testing.T) {
+ productToDelete := &productModel.Product{Id: 1}
+
+ mockProductRepo.EXPECT().Delete(productToDelete).Return(nil).Once()
+
+ ctx := context.Background()
+ delReq := &proto.DeleteProductRequest{Id: 1}
+ _, err := server.DeleteProduct(ctx, delReq)
+ if err != nil {
+ t.Errorf("Error getting deleting product: %v", err)
+ }
+ })
+
+ t.Run("Unsuccessful Delete", func(t *testing.T) {
+ productToDelete := &productModel.Product{Id: 1}
+
+ mockProductRepo.EXPECT().Delete(productToDelete).Return(errors.New(products.ErrorProductDeletion)).Once()
+
+ ctx := context.Background()
+ delReq := &proto.DeleteProductRequest{Id: 1}
+ _, err := server.DeleteProduct(ctx, delReq)
+ if e, ok := status.FromError(err); ok {
+ if e.Code() != codes.Internal {
+ t.Errorf("expected error: %v", err)
+ }
+ }
+ })
+}
+
+func TestProductServiceServer_CreatePrice(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
+
+ ctx := context.Background()
+ req := &proto.CreatePriceRequest{
+ Price: &proto.Price{
+ UserId: 1,
+ ProductId: 2,
+ Price: 2.99,
+ },
+ }
+
+ t.Run("Successful create", func(t *testing.T) {
+ mockPriceRepo.EXPECT().Create(
+ &priceModel.Price{UserId: 1, ProductId: 2, Price: 2.99}).
+ Return(
+ &priceModel.Price{UserId: 1, ProductId: 2, Price: 2.99}, nil).Once()
+
+ resp, err := server.CreatePrice(ctx, req)
+ if err != nil {
+ t.Errorf("Error creating product: %v", err)
+ }
+ if resp.Price.UserId != 1 {
+ t.Errorf("Expected user ID '1', got '%d'", resp.Price.UserId)
+ }
+
+ if resp.Price.ProductId != 2 {
+ t.Errorf("Expected product ID '2', got '%d'", resp.Price.ProductId)
+ }
+
+ if resp.Price.Price != 2.99 {
+ t.Errorf("Expected price '2.99', got '%f'", resp.Price.Price)
+ }
+ })
+
+ t.Run("Unsuccessful create", func(t *testing.T) {
+ mockPriceRepo.EXPECT().Create(
+ &priceModel.Price{UserId: 1, ProductId: 2, Price: 2.99}).
+ Return(nil, errors.New(prices.ErrorPriceAlreadyExists)).Once()
+
+ _, err := server.CreatePrice(ctx, req)
+ if e, ok := status.FromError(err); ok {
+ if e.Code() != codes.Internal {
+ t.Errorf("expected error: %v", err)
+ }
+ }
+ })
}
func TestProductServiceServer_FindAllPrices(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
+
+ availablePrices := []*priceModel.Price{
+ {UserId: 1, ProductId: 2, Price: 2.99},
+ {UserId: 1, ProductId: 1, Price: 3.99},
+ }
+
+ mockPriceRepo.EXPECT().FindAll().Return(availablePrices, nil)
+
+ ctx := context.Background()
+ getReq := &proto.FindAllPricesRequest{}
+ resp, err := server.FindAllPrices(ctx, getReq)
+ if err != nil {
+ t.Errorf("Error getting all products: %v", err)
+ }
+ for i, price := range availablePrices {
+ if price.UserId != resp.Price[i].UserId {
+ t.Errorf("Expected price UserID '%d', got '%d'", price.UserId, resp.Price[i].UserId)
+ }
+ if price.ProductId != resp.Price[i].ProductId {
+ t.Errorf("Expected price ProductID '%d', got '%d'", price.ProductId, resp.Price[i].ProductId)
+ }
+ if price.Price != resp.Price[i].Price {
+ t.Errorf("Expected price'%f', got '%f'", price.Price, resp.Price[i].Price)
+ }
+ }
}
func TestProductServiceServer_FindAllPricesFromUser(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
+
+ availablePrices := []*priceModel.Price{
+ {UserId: 1, ProductId: 2, Price: 2.99},
+ {UserId: 1, ProductId: 1, Price: 3.99},
+ }
+
+ mockPriceRepo.EXPECT().FindAllByUser(uint64(1)).Return(availablePrices, nil)
+ ctx := context.Background()
+ getReq := &proto.FindAllPricesFromUserRequest{UserId: 1}
+ resp, err := server.FindAllPricesFromUser(ctx, getReq)
+ if err != nil {
+ t.Errorf("Error getting all products: %v", err)
+ }
+
+ for i, price := range availablePrices {
+ if price.UserId != resp.Price[i].UserId {
+ t.Errorf("Expected price UserID '%d', got '%d'", price.UserId, resp.Price[i].UserId)
+ }
+ if price.ProductId != resp.Price[i].ProductId {
+ t.Errorf("Expected price ProductID '%d', got '%d'", price.ProductId, resp.Price[i].ProductId)
+ }
+ if price.Price != resp.Price[i].Price {
+ t.Errorf("Expected price'%f', got '%f'", price.Price, resp.Price[i].Price)
+ }
+ }
}
func TestProductServiceServer_FindPrice(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
-}
+ t.Run("Successful find Price", func(t *testing.T) {
+ mockPriceRepo.EXPECT().FindByIds(uint64(1), uint64(1)).Return(
+ &priceModel.Price{UserId: 1, ProductId: 1, Price: 3.99}, nil).Once()
-func TestProductServiceServer_GetAllProducts(t *testing.T) {
+ ctx := context.Background()
+ getReq := &proto.FindPriceRequest{UserId: 1, ProductId: 1}
+ resp, err := server.FindPrice(ctx, getReq)
+ if err != nil {
+ t.Errorf("Error getting product: %v", err)
+ }
+ if resp.Price.UserId != 1 {
+ t.Errorf("Expected User ID '1', got '%d'", resp.Price.UserId)
+ }
+ if resp.Price.ProductId != 1 {
+ t.Errorf("Expected Product ID '1', got '%d'", resp.Price.ProductId)
+ }
+ })
+
+ t.Run("Unsuccessful find Price", func(t *testing.T) {
+ mockPriceRepo.EXPECT().FindByIds(uint64(1), uint64(5)).Return(nil, errors.New(prices.ErrorPriceNotFound)).Once()
+
+ ctx := context.Background()
+ getReq := &proto.FindPriceRequest{UserId: 5, ProductId: 1}
+ _, err := server.FindPrice(ctx, getReq)
+ if e, ok := status.FromError(err); ok {
+ if e.Code() != codes.NotFound {
+ t.Errorf("expected error: %v", err)
+ }
+ }
+ })
}
func TestProductServiceServer_UpdatePrice(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
+
+ t.Run("Successful Update", func(t *testing.T) {
+ changedPrice := &priceModel.Price{UserId: 1, ProductId: 1, Price: 5.55}
+
+ mockPriceRepo.EXPECT().Update(changedPrice).Return(changedPrice, nil).Once()
+
+ ctx := context.Background()
+ updReq := &proto.UpdatePriceRequest{Price: &proto.Price{
+ UserId: 1,
+ ProductId: 1,
+ Price: 5.55,
+ }}
+ resp, err := server.UpdatePrice(ctx, updReq)
+ if err != nil {
+ t.Errorf("Error getting all products: %v", err)
+ }
+
+ if changedPrice.UserId != resp.Price.UserId {
+ t.Errorf("Expected price UserID '%d', got '%d'", changedPrice.UserId, resp.Price.UserId)
+ }
+ if changedPrice.ProductId != resp.Price.ProductId {
+ t.Errorf("Expected price ProductID '%d', got '%d'", changedPrice.ProductId, resp.Price.ProductId)
+ }
+ if changedPrice.Price != resp.Price.Price {
+ t.Errorf("Expected price '%f', got '%f'", changedPrice.Price, resp.Price.Price)
+ }
+ })
+
+ t.Run("Unsuccessful Update", func(t *testing.T) {
+ changedPrice := &priceModel.Price{UserId: 4, ProductId: 1, Price: 5.55}
+ mockPriceRepo.EXPECT().Update(changedPrice).Return(nil, errors.New(prices.ErrorPriceUpdate)).Once()
+
+ ctx := context.Background()
+ updReq := &proto.UpdatePriceRequest{Price: &proto.Price{
+ UserId: 4,
+ ProductId: 1,
+ Price: 5.55,
+ }}
+ _, err := server.UpdatePrice(ctx, updReq)
+ if e, ok := status.FromError(err); ok {
+ if e.Code() != codes.Internal {
+ t.Errorf("expected error: %v", err)
+ }
+ }
+ })
}
-func TestProductServiceServer_UpdateProduct(t *testing.T) {
+func TestProductServiceServer_DeletePrice(t *testing.T) {
+ mockProductRepo := productsMock.NewMockRepository(t)
+ mockPriceRepo := pricesMock.NewMockRepository(t)
+ var productRepo products.Repository = mockProductRepo
+ var priceRepo prices.Repository = mockPriceRepo
+ server := NewProductServiceServer(&productRepo, &priceRepo)
+
+ t.Run("Successful Delete", func(t *testing.T) {
+ priceToDelete := &priceModel.Price{UserId: 1, ProductId: 1}
+
+ mockPriceRepo.EXPECT().Delete(priceToDelete).Return(nil).Once()
+
+ ctx := context.Background()
+ delReq := &proto.DeletePriceRequest{UserId: 1, ProductId: 1}
+ _, err := server.DeletePrice(ctx, delReq)
+ if err != nil {
+ t.Errorf("Error getting deleting price: %v", err)
+ }
+ })
+
+ t.Run("Unsuccessful Delete", func(t *testing.T) {
+ priceToDelete := &priceModel.Price{UserId: 1, ProductId: 1}
+
+ mockPriceRepo.EXPECT().Delete(priceToDelete).Return(errors.New(products.ErrorProductDeletion)).Once()
+
+ ctx := context.Background()
+ delReq := &proto.DeletePriceRequest{UserId: 1, ProductId: 1}
+ _, err := server.DeletePrice(ctx, delReq)
+ if e, ok := status.FromError(err); ok {
+ if e.Code() != codes.Internal {
+ t.Errorf("expected error: %v", err)
+ }
+ }
+ })
}
From 84fa03e62e69459f64be093c396c5ad84c105c0a Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 15:57:43 +0100
Subject: [PATCH 07/19] refactor(product-service): remove unused repositories
and controller
---
.../prices/default_controller.go | 159 ------
.../prices/default_controller_test.go | 539 ------------------
src/product-service/prices/demo_repository.go | 122 ----
.../prices/demo_repository_example.go | 28 -
.../prices/demo_repository_test.go | 280 ---------
.../products/default_controller.go | 138 -----
.../products/default_controller_test.go | 520 -----------------
.../products/demo_repository.go | 101 ----
.../products/demo_repository_example.go | 28 -
.../products/demo_repository_test.go | 326 -----------
10 files changed, 2241 deletions(-)
delete mode 100644 src/product-service/prices/default_controller.go
delete mode 100644 src/product-service/prices/default_controller_test.go
delete mode 100644 src/product-service/prices/demo_repository.go
delete mode 100644 src/product-service/prices/demo_repository_example.go
delete mode 100644 src/product-service/prices/demo_repository_test.go
delete mode 100644 src/product-service/products/default_controller.go
delete mode 100644 src/product-service/products/default_controller_test.go
delete mode 100644 src/product-service/products/demo_repository.go
delete mode 100644 src/product-service/products/demo_repository_example.go
delete mode 100644 src/product-service/products/demo_repository_test.go
diff --git a/src/product-service/prices/default_controller.go b/src/product-service/prices/default_controller.go
deleted file mode 100644
index 14ca63c5..00000000
--- a/src/product-service/prices/default_controller.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package prices
-
-import (
- "encoding/json"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices/model"
- "net/http"
- "strconv"
-)
-
-type DefaultController struct {
- priceRepository Repository
-}
-
-func NewDefaultController(priceRepository Repository) *DefaultController {
- return &DefaultController{priceRepository}
-}
-
-func (controller *DefaultController) GetPrices(writer http.ResponseWriter, request *http.Request) {
- values, err := controller.priceRepository.FindAll()
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-
- writer.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(writer).Encode(values)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- return
- }
-}
-
-func (controller *DefaultController) GetPricesByUser(writer http.ResponseWriter, request *http.Request) {
- userId, err := strconv.ParseUint(request.Context().Value("userId").(string), 10, 64)
-
- if err != nil {
- http.Error(writer, "Invalid userId", http.StatusBadRequest)
- return
- }
- values, err := controller.priceRepository.FindAllByUser(userId)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-
- writer.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(writer).Encode(values)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- return
- }
-}
-
-func (controller *DefaultController) GetPricesByProduct(writer http.ResponseWriter, request *http.Request) {
- productId, err := strconv.ParseUint(request.Context().Value("productId").(string), 10, 64)
-
- if err != nil {
- http.Error(writer, "Invalid productId", http.StatusBadRequest)
- return
- }
- values, err := controller.priceRepository.FindAllByProduct(productId)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-
- writer.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(writer).Encode(values)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- return
- }
-}
-
-func (controller *DefaultController) PostPrice(writer http.ResponseWriter, request *http.Request) {
- productId, productIdErr := strconv.ParseUint(request.Context().Value("productId").(string), 10, 64)
- userId, userIdErr := strconv.ParseUint(request.Context().Value("userId").(string), 10, 64)
-
- if productIdErr != nil || userIdErr != nil {
- http.Error(writer, "Invalid listId or productId", http.StatusBadRequest)
- return
- }
-
- var requestData JsonFormatCreatePriceRequest
- if err := json.NewDecoder(request.Body).Decode(&requestData); err != nil {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
-
- if _, err := controller.priceRepository.Create(&model.Price{
- ProductId: productId,
- UserId: userId,
- Price: requestData.Price,
- }); err != nil {
- writer.WriteHeader(http.StatusInternalServerError)
- }
-}
-
-func (controller *DefaultController) GetPrice(writer http.ResponseWriter, request *http.Request) {
- userId, err := strconv.ParseUint(request.Context().Value("userId").(string), 10, 64)
- productId, err := strconv.ParseUint(request.Context().Value("productId").(string), 10, 64)
-
- if err != nil {
- http.Error(writer, err.Error(), http.StatusBadRequest)
- return
- }
-
- value, err := controller.priceRepository.FindByIds(productId, userId)
- if err != nil {
- if err.Error() == ErrorPriceNotFound {
- http.Error(writer, err.Error(), http.StatusNotFound)
- return
- }
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-
- writer.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(writer).Encode(value)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-}
-
-func (controller *DefaultController) PutPrice(writer http.ResponseWriter, request *http.Request) {
- userId, err := strconv.ParseUint(request.Context().Value("userId").(string), 10, 64)
- productId, err := strconv.ParseUint(request.Context().Value("productId").(string), 10, 64)
-
- if err != nil {
- http.Error(writer, err.Error(), http.StatusBadRequest)
- return
- }
-
- var requestData JsonFormatUpdatePriceRequest
- if err := json.NewDecoder(request.Body).Decode(&requestData); err != nil {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
-
- if _, err := controller.priceRepository.Update(&model.Price{
- UserId: userId,
- ProductId: productId,
- Price: requestData.Price,
- }); err != nil {
- writer.WriteHeader(http.StatusInternalServerError)
- return
- }
-}
-
-func (controller *DefaultController) DeletePrice(writer http.ResponseWriter, request *http.Request) {
- userId, err := strconv.ParseUint(request.Context().Value("userId").(string), 10, 64)
- productId, err := strconv.ParseUint(request.Context().Value("productId").(string), 10, 64)
-
- if err != nil {
- http.Error(writer, err.Error(), http.StatusBadRequest)
- return
- }
-
- if err := controller.priceRepository.Delete(&model.Price{ProductId: productId, UserId: userId}); err != nil {
- writer.WriteHeader(http.StatusInternalServerError)
- return
- }
-}
diff --git a/src/product-service/prices/default_controller_test.go b/src/product-service/prices/default_controller_test.go
deleted file mode 100644
index 72b9c9f4..00000000
--- a/src/product-service/prices/default_controller_test.go
+++ /dev/null
@@ -1,539 +0,0 @@
-package prices
-
-import (
- "context"
- "encoding/json"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices/model"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices/utils"
- "net/http"
- "net/http/httptest"
- "reflect"
- "strings"
- "testing"
-)
-
-func TestNewDefaultController(t *testing.T) {
- type args struct {
- priceRepository Repository
- }
- tests := []struct {
- name string
- args args
- want *DefaultController
- }{
- {
- name: "Test construction with DemoRepository",
- args: args{priceRepository: NewDemoRepository()},
- want: &DefaultController{priceRepository: NewDemoRepository()},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := NewDefaultController(tt.args.priceRepository); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("NewDefaultController() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestDefaultController_DeletePrice(t *testing.T) {
- type fields struct {
- priceRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- wantStatus int
- }{
- {
- name: "Successfully delete existing price (expect 200)",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest("DELETE", "/api/v1/price/1/1", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productId", "1"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "1"))
- return request
- }(),
- },
-
- wantStatus: http.StatusOK,
- },
- {
- name: "Bad non-numeric request (expect 400)",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: utils.CreatePriceRequestWithValues("DELETE", "/api/v1/price/abc/abc", "abc", "abc"),
- },
- wantStatus: http.StatusBadRequest,
- },
- {
- name: "Unknown product to delete (expect 500)",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: utils.CreatePriceRequestWithValues("DELETE", "/api/v1/price/42/42", "42", "42"),
- },
- wantStatus: http.StatusInternalServerError,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := DefaultController{
- priceRepository: tt.fields.priceRepository,
- }
- controller.DeletePrice(tt.args.writer, tt.args.request)
- if tt.args.writer.Code != tt.wantStatus {
- t.Errorf("Expected status code %d, got %d", tt.wantStatus, tt.args.writer.Code)
- }
- })
- }
-}
-
-func TestDefaultController_GetPrices(t *testing.T) {
- t.Run("should return all prices", func(t *testing.T) {
- controller := DefaultController{
- priceRepository: GenerateExampleDemoRepository(),
- }
-
- writer := httptest.NewRecorder()
- request := httptest.NewRequest("GET", "/api/v1/price", nil)
-
- // Test request
- controller.GetPrices(writer, request)
-
- res := writer.Result()
- var response []model.Price
- err := json.NewDecoder(res.Body).Decode(&response)
-
- if err != nil {
- t.Error(err)
- }
-
- if writer.Code != http.StatusOK {
- t.Errorf("Expected status code %d, got %d", http.StatusOK, writer.Code)
- }
-
- if writer.Header().Get("Content-Type") != "application/json" {
- t.Errorf("Expected content type %s, got %s",
- "application/json", writer.Header().Get("Content-Type"))
- }
-
- prices := GenerateExamplePriceSlice()
-
- if len(response) != len(prices) {
- t.Errorf("Expected count of prices is %d, got %d",
- 2, len(response))
- }
- })
-}
-
-func TestDefaultController_GetPricesByUser(t *testing.T) {
- type fields struct {
- priceRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- wantStatus int
- }{
- {
- name: "Bad non-numeric request (expect 400)",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest("GET", "/api/v1/price/user/abc", nil)
- request = request.WithContext(context.WithValue(request.Context(), "userId", "abc"))
- return request
- }(),
- },
- wantStatus: http.StatusBadRequest,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := DefaultController{
- priceRepository: tt.fields.priceRepository,
- }
- controller.GetPricesByUser(tt.args.writer, tt.args.request)
- if tt.args.writer.Code != tt.wantStatus {
- t.Errorf("Expected status code %d, got %d", tt.wantStatus, tt.args.writer.Code)
- }
- })
- }
-
- t.Run("Successfully get existing prices by user (expect 200 and prices)", func(t *testing.T) {
- writer := httptest.NewRecorder()
- request := httptest.NewRequest("GET", "/api/v1/price/user/1", nil)
- request = request.WithContext(context.WithValue(request.Context(), "userId", "1"))
-
- controller := DefaultController{
- priceRepository: GenerateExampleDemoRepository(),
- }
-
- // when
- controller.GetPricesByUser(writer, request)
-
- // then
- if writer.Code != http.StatusOK {
- t.Errorf("Expected status code %d, got %d", http.StatusOK, writer.Code)
- }
-
- if writer.Header().Get("Content-Type") != "application/json" {
- t.Errorf("Expected content type %s, got %s",
- "application/json", writer.Header().Get("Content-Type"))
- }
-
- result := writer.Result()
- var response []model.Price
- err := json.NewDecoder(result.Body).Decode(&response)
- if err != nil {
- t.Fatal(err.Error())
- }
-
- if len(response) != 1 {
- t.Errorf("Expected count of prices is %d, got %d",
- 1, len(response))
- }
-
- for i, price := range response {
- if price.UserId != 1 {
- t.Errorf("Expected role of user %d, got %d", 1, response[i].UserId)
- }
- }
- })
-}
-
-func TestDefaultController_GetPrice(t *testing.T) {
- type fields struct {
- priceRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- wantStatus int
- }{
- {
- name: "Bad non-numeric request (expect 400)",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: utils.CreatePriceRequestWithValues("GET", "/api/v1/price/abc/abc", "abc", "abc"),
- },
- wantStatus: http.StatusBadRequest,
- },
- {
- name: "Unknown price (expect 404)",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: utils.CreatePriceRequestWithValues("GET", "/api/v1/price/42/42", "42", "42"),
- },
- wantStatus: http.StatusNotFound,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := DefaultController{
- priceRepository: tt.fields.priceRepository,
- }
- controller.GetPrice(tt.args.writer, tt.args.request)
- if tt.args.writer.Code != tt.wantStatus {
- t.Errorf("Expected status code %d, got %d", tt.wantStatus, tt.args.writer.Code)
- }
- })
- }
-
- t.Run("Successfully get existing price (expect 200 and price)", func(t *testing.T) {
- writer := httptest.NewRecorder()
- request := httptest.NewRequest("GET", "/api/v1/price/1/1", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productId", "1"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "1"))
-
- controller := DefaultController{
- priceRepository: GenerateExampleDemoRepository(),
- }
-
- // when
- controller.GetPrice(writer, request)
-
- // then
- if writer.Code != http.StatusOK {
- t.Errorf("Expected status code %d, got %d", http.StatusOK, writer.Code)
- }
-
- if writer.Header().Get("Content-Type") != "application/json" {
- t.Errorf("Expected content type %s, got %s",
- "application/json", writer.Header().Get("Content-Type"))
- }
-
- result := writer.Result()
- var response model.Price
- err := json.NewDecoder(result.Body).Decode(&response)
- if err != nil {
- t.Fatal(err.Error())
- }
-
- if response.ProductId != 1 {
- t.Errorf("Expected product id of price %d, got %d", 1, response.ProductId)
- }
-
- if response.UserId != 1 {
- t.Errorf("Expected user id of product %d, got %d", 1, response.UserId)
- }
-
- if response.Price != 2.99 {
- t.Errorf("Expected ean of product %f, got %f", 2.99, response.Price)
- }
-
- })
-}
-
-func TestDefaultController_PostPrice(t *testing.T) {
- type fields struct {
- priceRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- expectedStatus int
- expectedResponse string
- }{
- {
- name: "Valid Price",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "POST",
- "/api/v1/price/4/4",
- strings.NewReader(`{"price": 0.99}`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "4"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "4"))
- return request
- }(),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Malformed JSON",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "POST",
- "/api/v1/price/5/5",
- strings.NewReader(`{"price": 0.99`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "5"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "5"))
- return request
- }(),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- {
- name: "Invalid price, incorrect Type for price (Non-numeric)",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "POST",
- "/api/v1/price/5/5",
- strings.NewReader(`{"price": "0.99"}`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "5"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "5"))
- return request
- }(),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := DefaultController{
- priceRepository: tt.fields.priceRepository,
- }
- controller.PostPrice(tt.args.writer, tt.args.request)
-
- // You can then assert the response status and content, and check against your expectations.
- if tt.args.writer.Code != tt.expectedStatus {
- t.Errorf("Expected status code %d, but got %d", tt.expectedStatus, tt.args.writer.Code)
- }
-
- if tt.expectedResponse != "" {
- actualResponse := tt.args.writer.Body.String()
- if actualResponse != tt.expectedResponse {
- t.Errorf("Expected response: %s, but got: %s", tt.expectedResponse, actualResponse)
- }
- }
- })
- }
-}
-
-func TestDefaultController_PutPrice(t *testing.T) {
- type fields struct {
- priceRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
-
- tests := []struct {
- name string
- fields fields
- args args
- expectedStatus int
- expectedResponse string // If you want to check the response content
- }{
- {
- name: "Valid Update",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "PUT",
- "/api/v1/price/1/1",
- strings.NewReader(`{"userId": 1, "productId": 1, "price": 10.99}`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "1"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "1"))
- return request
- }(),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Valid Update (Partly Fields)",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "PUT",
- "/api/v1/price/2/2",
- strings.NewReader(`{"price": 6.50}`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "2"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "2"))
- return request
- }(),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Malformed JSON",
- fields: fields{
- priceRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "PUT",
- "/api/v1/price/2/2",
- strings.NewReader(`{"price": 6.50`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "2"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "2"))
- return request
- }(),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- {
- name: "Incorrect Type for Price (Non-numeric)",
- fields: fields{
- // Set up your repository mock or test double here if needed
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "PUT",
- "/api/v1/price/2/2",
- strings.NewReader(`{"price": "Wrong Type"`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "2"))
- request = request.WithContext(context.WithValue(request.Context(), "userId", "2"))
- return request
- }(),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := DefaultController{
- priceRepository: tt.fields.priceRepository,
- }
- controller.PutPrice(tt.args.writer, tt.args.request)
-
- // You can then assert the response status and content, and check against your expectations.
- if tt.args.writer.Code != tt.expectedStatus {
- t.Errorf("Expected status code %d, but got %d", tt.expectedStatus, tt.args.writer.Code)
- }
-
- if tt.expectedResponse != "" {
- actualResponse := tt.args.writer.Body.String()
- if actualResponse != tt.expectedResponse {
- t.Errorf("Expected response: %s, but got: %s", tt.expectedResponse, actualResponse)
- }
- }
- })
- }
-}
diff --git a/src/product-service/prices/demo_repository.go b/src/product-service/prices/demo_repository.go
deleted file mode 100644
index ae77aff8..00000000
--- a/src/product-service/prices/demo_repository.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package prices
-
-import (
- "errors"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices/model"
- "reflect"
-)
-
-type priceEntryKey struct {
- UserId uint64
- ProductId uint64
-}
-
-type DemoRepository struct {
- prices map[priceEntryKey]*model.Price
-}
-
-func NewDemoRepository() *DemoRepository {
- return &DemoRepository{prices: make(map[priceEntryKey]*model.Price)}
-}
-
-func (repo *DemoRepository) Create(price *model.Price) (*model.Price, error) {
- for _, p := range repo.prices {
- if reflect.DeepEqual(p, price) {
- return nil, errors.New(ErrorPriceAlreadyExists)
- }
- }
-
- key := priceEntryKey{
- ProductId: price.ProductId,
- UserId: price.UserId,
- }
-
- repo.prices[key] = price
-
- return price, nil
-}
-
-func (repo *DemoRepository) FindAll() ([]*model.Price, error) {
- if repo.prices == nil {
- return nil, errors.New(ErrorPriceList)
- }
-
- prices := make([]*model.Price, 0, len(repo.prices))
- for _, price := range repo.prices {
- prices = append(prices, price)
- }
-
- return prices, nil
-}
-
-func (repo *DemoRepository) FindAllByUser(userId uint64) ([]*model.Price, error) {
- if repo.prices == nil {
- return nil, errors.New(ErrorPriceList)
- }
-
- var userPrices []*model.Price
- for _, price := range repo.prices {
- if price.UserId == userId {
- userPrices = append(userPrices, price)
- }
- }
-
- return userPrices, nil
-}
-
-func (repo *DemoRepository) FindAllByProduct(productId uint64) ([]*model.Price, error) {
- if repo.prices == nil {
- return nil, errors.New(ErrorPriceList)
- }
-
- var productPrices []*model.Price
- for _, price := range repo.prices {
- if price.ProductId == productId {
- productPrices = append(productPrices, price)
- }
- }
-
- return productPrices, nil
-}
-
-func (repo *DemoRepository) FindByIds(productId uint64, userId uint64) (*model.Price, error) {
- key := priceEntryKey{
- UserId: userId,
- ProductId: productId,
- }
-
- price, exists := repo.prices[key]
-
- if !exists {
- return nil, errors.New(ErrorPriceNotFound)
- }
-
- return price, nil
-}
-
-func (repo *DemoRepository) Update(price *model.Price) (*model.Price, error) {
- existingPrice, foundError := repo.FindByIds(price.ProductId, price.UserId)
-
- if foundError != nil {
- return nil, errors.New(ErrorPriceUpdate)
- }
-
- existingPrice.Price = price.Price
-
- return existingPrice, nil
-}
-
-func (repo *DemoRepository) Delete(price *model.Price) error {
- key := priceEntryKey{
- UserId: price.UserId,
- ProductId: price.ProductId,
- }
-
- _, exists := repo.prices[key]
- if !exists {
- return errors.New(ErrorPriceDeletion)
- }
-
- delete(repo.prices, key)
- return nil
-}
diff --git a/src/product-service/prices/demo_repository_example.go b/src/product-service/prices/demo_repository_example.go
deleted file mode 100644
index 23514ea7..00000000
--- a/src/product-service/prices/demo_repository_example.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package prices
-
-import "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices/model"
-
-func GenerateExampleDemoRepository() Repository {
- repository := NewDemoRepository()
- pricesSlice := GenerateExamplePriceSlice()
- for _, price := range pricesSlice {
- repository.Create(price)
- }
-
- return repository
-}
-
-func GenerateExamplePriceSlice() []*model.Price {
- return []*model.Price{
- {
- UserId: 1,
- ProductId: 1,
- Price: 2.99,
- },
- {
- UserId: 2,
- ProductId: 2,
- Price: 5.99,
- },
- }
-}
diff --git a/src/product-service/prices/demo_repository_test.go b/src/product-service/prices/demo_repository_test.go
deleted file mode 100644
index de59088f..00000000
--- a/src/product-service/prices/demo_repository_test.go
+++ /dev/null
@@ -1,280 +0,0 @@
-package prices
-
-import (
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/prices/model"
- "reflect"
- "testing"
-)
-
-func TestNewDemoRepository(t *testing.T) {
- t.Run("Demo repository correct initialized", func(t *testing.T) {
- got := NewDemoRepository()
- if len(got.prices) != 0 {
- t.Errorf("NewDemoRepository().prices has unexpected length, want 0")
- }
- })
-}
-
-func TestDemoRepository_Create(t *testing.T) {
- demoRepository := NewDemoRepository()
-
- price := model.Price{
- UserId: 1,
- ProductId: 1,
- Price: 2.99,
- }
-
- t.Run("Create price with success", func(t *testing.T) {
- _, err := demoRepository.Create(&price)
- if err != nil {
- t.Error(err)
- }
- })
-
- t.Run("Check for doublet", func(t *testing.T) {
- _, err := demoRepository.Create(&price)
- if err.Error() != ErrorPriceAlreadyExists {
- t.Error(err)
- }
- })
-}
-
-func TestDemoRepository_FindAll(t *testing.T) {
- demoRepository := NewDemoRepository()
-
- prices := []*model.Price{
- {
- UserId: 1,
- ProductId: 1,
- Price: 2.99,
- },
- {
- UserId: 2,
- ProductId: 3,
- Price: 0.55,
- },
- }
-
- for _, price := range prices {
- _, err := demoRepository.Create(price)
- if err != nil {
- t.Error("Failed to add prepared price for test")
- }
- }
-
- t.Run("Fetch all prices", func(t *testing.T) {
- fetchedPrices, err := demoRepository.FindAll()
- if err != nil {
- t.Error("Can't fetch prices")
- }
-
- if len(fetchedPrices) != len(prices) {
- t.Errorf("Unexpected price count. Expected %d, got %d", len(prices), len(fetchedPrices))
- }
- })
-
- priceTest := []struct {
- name string
- want *model.Price
- }{
- {
- name: "first",
- want: prices[0],
- },
- {
- name: "second",
- want: prices[1],
- },
- }
-
- for _, tt := range priceTest {
- t.Run("Is fetched price matching with "+tt.name+" added price?", func(t *testing.T) {
- fetchedPrices, _ := demoRepository.FindAll()
- found := false
-
- for _, fetchedPrice := range fetchedPrices {
- if reflect.DeepEqual(tt.want, fetchedPrice) {
- found = true
- break
- }
- }
-
- if !found {
- t.Error("Fetched price does not match original price")
- }
- })
- }
-}
-
-func TestDemoRepository_FindAllByUser(t *testing.T) {
- demoRepository := NewDemoRepository()
-
- prices := []*model.Price{
- {
- UserId: 1,
- ProductId: 1,
- Price: 2.99,
- },
- {
- UserId: 2,
- ProductId: 3,
- Price: 0.55,
- },
- }
-
- for _, price := range prices {
- _, err := demoRepository.Create(price)
- if err != nil {
- t.Fatalf("Failed to add prepared price for test: %v", err)
- }
- }
-
- t.Run("Fetch prices for a specific user", func(t *testing.T) {
- fetchedPrices, err := demoRepository.FindAllByUser(2)
- if err != nil {
- t.Fatalf("Error fetching prices: %v", err)
- }
-
- if len(fetchedPrices) != 1 {
- t.Errorf("Unexpected price count. Expected 1, got %d", len(fetchedPrices))
- }
-
- if !reflect.DeepEqual(prices[1], fetchedPrices[0]) {
- t.Error("Fetched price does not match the expected price")
- }
- })
-
- t.Run("Verify fetched prices match added prices", func(t *testing.T) {
- fetchedPrices, err := demoRepository.FindAll()
- if err != nil {
- t.Fatalf("Error fetching prices: %v", err)
- }
-
- for _, tt := range prices {
- found := false
-
- for _, fetchedPrice := range fetchedPrices {
- if reflect.DeepEqual(tt, fetchedPrice) {
- found = true
- break
- }
- }
-
- if !found {
- t.Errorf("Fetched price for user id %d does not match the original price", tt.UserId)
- }
- }
- })
-}
-
-func TestDemoRepository_FindByIds(t *testing.T) {
- demoRepository := NewDemoRepository()
-
- price := model.Price{
- UserId: 1,
- ProductId: 1,
- Price: 2.99,
- }
-
- _, err := demoRepository.Create(&price)
- if err != nil {
- t.Fatal("Failed to add prepare price for test")
- }
-
- t.Run("Fetch price with existing ids", func(t *testing.T) {
- _, err := demoRepository.FindByIds(price.ProductId, price.UserId)
- if err != nil {
- t.Errorf("Can't find expected price with product id %d and user id %d", price.ProductId, price.UserId)
- }
-
- t.Run("Is fetched price matching with added price?", func(t *testing.T) {
- fetchedPrice, _ := demoRepository.FindByIds(price.ProductId, price.UserId)
- if !reflect.DeepEqual(price, *fetchedPrice) {
- t.Error("Fetched price does not match original price")
- }
- })
- })
-
- t.Run("Non-existing price test", func(t *testing.T) {
- _, err = demoRepository.FindByIds(42, 42)
- if err.Error() != ErrorPriceNotFound {
- t.Error(err)
- }
- })
-}
-
-func TestDemoRepository_Update(t *testing.T) {
- demoRepository := NewDemoRepository()
-
- price := model.Price{
- UserId: 1,
- ProductId: 1,
- Price: 2.99,
- }
-
- fetchedPrice, err := demoRepository.Create(&price)
- if err != nil {
- t.Error("Failed to add prepare price for test")
- }
-
- t.Run("Check if updated price object has updated price", func(t *testing.T) {
- price := model.Price{
- UserId: 1,
- ProductId: 1,
- Price: 3.99,
- }
-
- updatedPrice, err := demoRepository.Update(&price)
- if err != nil {
- t.Error(err.Error())
- }
-
- if fetchedPrice.Price != updatedPrice.Price {
- t.Errorf("Failed to update price. Got %f, want %f.",
- fetchedPrice.Price, updatedPrice.Price)
- }
- })
-}
-
-func TestDemoRepository_Delete(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- price := model.Price{
- UserId: 1,
- ProductId: 1,
- Price: 2.99,
- }
-
- fetchedPrice, err := demoRepository.Create(&price)
- if err != nil {
- t.Error("Failed to add prepare price for test")
- }
-
- t.Run("Test for deletion", func(t *testing.T) {
- err = demoRepository.Delete(fetchedPrice)
- if err != nil {
- t.Errorf("Failed to delete price with product id %d and user id %d", price.ProductId, price.UserId)
- }
-
- t.Run("Try to fetch deleted price", func(t *testing.T) {
- fetchedPrice, err = demoRepository.FindByIds(price.ProductId, price.UserId)
- if err.Error() != ErrorPriceNotFound {
- t.Errorf("Price with with product id %d and user id %d was not deleted", price.ProductId, price.UserId)
- }
- })
- })
-
- t.Run("Try to delete non-existing price", func(t *testing.T) {
- fakePrice := model.Price{
- UserId: 2,
- ProductId: 2,
- Price: 5.99,
- }
-
- err = demoRepository.Delete(&fakePrice)
- if err.Error() != ErrorPriceDeletion {
- t.Errorf("Price with product id %d and user id %d was deleted", price.ProductId, price.UserId)
- }
- })
-}
diff --git a/src/product-service/products/default_controller.go b/src/product-service/products/default_controller.go
deleted file mode 100644
index 23ca8913..00000000
--- a/src/product-service/products/default_controller.go
+++ /dev/null
@@ -1,138 +0,0 @@
-package products
-
-import (
- "encoding/json"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/products/model"
- "net/http"
- "strconv"
-)
-
-type defaultController struct {
- productRepository Repository
-}
-
-func NewDefaultController(productRepository Repository) *defaultController {
- return &defaultController{productRepository}
-}
-
-func (controller *defaultController) GetProducts(writer http.ResponseWriter, request *http.Request) {
- values, err := controller.productRepository.FindAll()
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-
- writer.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(writer).Encode(values)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- return
- }
-}
-
-func (controller *defaultController) PostProduct(writer http.ResponseWriter, request *http.Request) {
- var requestData JsonFormatCreateProductRequest
- if err := json.NewDecoder(request.Body).Decode(&requestData); err != nil {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
-
- product, err := controller.productRepository.Create(&model.Product{
- Description: requestData.Description,
- Ean: requestData.Ean,
- })
-
- if err != nil {
- writer.WriteHeader(http.StatusInternalServerError)
- return
- }
-
- writer.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(writer).Encode(product)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- return
- }
-}
-
-func (controller *defaultController) GetProductById(writer http.ResponseWriter, request *http.Request) {
- productId, err := strconv.ParseUint(request.Context().Value("productId").(string), 10, 64)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusBadRequest)
- return
- }
-
- value, err := controller.productRepository.FindById(productId)
- if err != nil {
- if err.Error() == ErrorProductNotFound {
- http.Error(writer, err.Error(), http.StatusNotFound)
- return
- }
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-
- writer.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(writer).Encode(value)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-}
-
-func (controller *defaultController) GetProductByEan(writer http.ResponseWriter, request *http.Request) {
- productEan, exists := request.Context().Value("productEan").(string)
- if !exists {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
-
- values, err := controller.productRepository.FindByEan(productEan)
- if err != nil {
- if err.Error() == ErrorProductNotFound {
- http.Error(writer, err.Error(), http.StatusNotFound)
- return
- }
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- }
-
- writer.Header().Set("Content-Type", "application/json")
- err = json.NewEncoder(writer).Encode(values)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusInternalServerError)
- return
- }
-}
-
-func (controller *defaultController) PutProduct(writer http.ResponseWriter, request *http.Request) {
- productId, err := strconv.ParseUint(request.Context().Value("productId").(string), 10, 64)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusBadRequest)
- return
- }
-
- var requestData JsonFormatUpdateProductRequest
- if err := json.NewDecoder(request.Body).Decode(&requestData); err != nil {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
-
- if _, err := controller.productRepository.Update(&model.Product{
- Id: productId,
- Description: requestData.Description,
- Ean: requestData.Ean,
- }); err != nil {
- writer.WriteHeader(http.StatusInternalServerError)
- return
- }
-}
-
-func (controller *defaultController) DeleteProduct(writer http.ResponseWriter, request *http.Request) {
- productId, err := strconv.ParseUint(request.Context().Value("productId").(string), 10, 64)
- if err != nil {
- http.Error(writer, err.Error(), http.StatusBadRequest)
- return
- }
-
- if err := controller.productRepository.Delete(&model.Product{Id: productId}); err != nil {
- writer.WriteHeader(http.StatusInternalServerError)
- return
- }
-}
diff --git a/src/product-service/products/default_controller_test.go b/src/product-service/products/default_controller_test.go
deleted file mode 100644
index 4ce5f1a9..00000000
--- a/src/product-service/products/default_controller_test.go
+++ /dev/null
@@ -1,520 +0,0 @@
-package products
-
-import (
- "context"
- "encoding/json"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/products/model"
- "net/http"
- "net/http/httptest"
- "reflect"
- "sort"
- "strings"
- "testing"
-)
-
-func TestNewDefaultController(t *testing.T) {
- type args struct {
- productRepository Repository
- }
- tests := []struct {
- name string
- args args
- want *defaultController
- }{
- {
- name: "Test construction with DemoRepository",
- args: args{productRepository: NewDemoRepository()},
- want: &defaultController{productRepository: NewDemoRepository()},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := NewDefaultController(tt.args.productRepository); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("NewDefaultController() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestDefaultController_DeleteProduct(t *testing.T) {
- type fields struct {
- productRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- wantStatus int
- }{
- {
- name: "Successfully delete existing product (expect 200)",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest("DELETE", "/api/v1/product/1", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productId", "1"))
- return request
- }(),
- },
- wantStatus: http.StatusOK,
- },
- {
- name: "Bad non-numeric request (expect 400)",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest("DELETE", "/api/v1/product/abc", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productId", "abc"))
- return request
- }(),
- },
- wantStatus: http.StatusBadRequest,
- },
- {
- name: "Unknown product to delete (expect 500)",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest("DELETE", "/api/v1/product/5", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productId", "5"))
- return request
- }(),
- },
- wantStatus: http.StatusInternalServerError,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := defaultController{
- productRepository: tt.fields.productRepository,
- }
- controller.DeleteProduct(tt.args.writer, tt.args.request)
- if tt.args.writer.Code != tt.wantStatus {
- t.Errorf("Expected status code %d, got %d", tt.wantStatus, tt.args.writer.Code)
- }
- })
- }
-}
-
-func TestDefaultController_GetProductById(t *testing.T) {
- type fields struct {
- productRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- wantStatus int
- }{
- {
- name: "Bad non-numeric request (expect 400)",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest("GET", "/api/v1/product/abc", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productId", "abc"))
- return request
- }(),
- },
- wantStatus: http.StatusBadRequest,
- },
- {
- name: "Unknown product (expect 404)",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest("GET", "/api/v1/product/4", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productId", "4"))
- return request
- }(),
- },
- wantStatus: http.StatusNotFound,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := defaultController{
- productRepository: tt.fields.productRepository,
- }
- controller.GetProductById(tt.args.writer, tt.args.request)
- if tt.args.writer.Code != tt.wantStatus {
- t.Errorf("Expected status code %d, got %d", tt.wantStatus, tt.args.writer.Code)
- }
- })
- }
-
- t.Run("Successfully get existing product (expect 200 and product)", func(t *testing.T) {
- writer := httptest.NewRecorder()
- request := httptest.NewRequest("GET", "/api/v1/product/1", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productId", "1"))
-
- controller := defaultController{
- productRepository: GenerateExampleDemoRepository(),
- }
-
- // when
- controller.GetProductById(writer, request)
-
- // then
- if writer.Code != http.StatusOK {
- t.Errorf("Expected status code %d, got %d", http.StatusOK, writer.Code)
- }
-
- if writer.Header().Get("Content-Type") != "application/json" {
- t.Errorf("Expected content type %s, got %s",
- "application/json", writer.Header().Get("Content-Type"))
- }
-
- result := writer.Result()
- var response model.Product
- err := json.NewDecoder(result.Body).Decode(&response)
- if err != nil {
- t.Fatal(err.Error())
- }
-
- if response.Id != 1 {
- t.Errorf("Expected id of product %d, got %d", 1, response.Id)
- }
-
- if response.Description != "Strauchtomaten" {
- t.Errorf("Expected description of product %s, got %s", "Strauchtomaten", response.Description)
- }
-
- if response.Ean != "4014819040771" {
- t.Errorf("Expected ean of product %s, got %s", "4014819040771", response.Ean)
- }
-
- })
-}
-
-func TestDefaultController_GetProductByEan(t *testing.T) {
- t.Run("Unknown product (expect 404)", func(t *testing.T) {
- controller := defaultController{
- productRepository: GenerateExampleDemoRepository(),
- }
-
- writer := httptest.NewRecorder()
- request := httptest.NewRequest("GET", "/api/v1/products/ean?ean=123", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productEan", "123"))
-
- // Test request
- controller.GetProductByEan(writer, request)
-
- if writer.Code != http.StatusNotFound {
- t.Errorf("Expected status code %d, got %d", http.StatusNotFound, writer.Code)
- }
- })
-
- t.Run("Should return products by EAN", func(t *testing.T) {
- controller := defaultController{
- productRepository: GenerateExampleDemoRepository(),
- }
-
- writer := httptest.NewRecorder()
- request := httptest.NewRequest("GET", "/api/v1/products/ean?ean=4014819040771", nil)
- request = request.WithContext(context.WithValue(request.Context(), "productEan", "4014819040771"))
-
- // Test request
- controller.GetProductByEan(writer, request)
-
- if writer.Code != http.StatusOK {
- t.Errorf("Expected status code %d, got %d", http.StatusOK, writer.Code)
- }
-
- res := writer.Result()
- var response model.Product
- err := json.NewDecoder(res.Body).Decode(&response)
-
- if err != nil {
- t.Error(err)
- }
-
- // Add assertions based on your expected response.
- // For example, check the length, content, etc.
- // ...
-
- })
-}
-
-func TestDefaultController_GetProducts(t *testing.T) {
- t.Run("should return all products", func(t *testing.T) {
- controller := defaultController{
- productRepository: GenerateExampleDemoRepository(),
- }
-
- writer := httptest.NewRecorder()
- request := httptest.NewRequest("GET", "/api/v1/product", nil)
-
- // Test request
- controller.GetProducts(writer, request)
-
- res := writer.Result()
- var response []model.Product
- err := json.NewDecoder(res.Body).Decode(&response)
-
- if err != nil {
- t.Error(err)
- }
-
- if writer.Code != http.StatusOK {
- t.Errorf("Expected status code %d, got %d", http.StatusOK, writer.Code)
- }
-
- if writer.Header().Get("Content-Type") != "application/json" {
- t.Errorf("Expected content type %s, got %s",
- "application/json", writer.Header().Get("Content-Type"))
- }
-
- products := GenerateExampleProductSlice()
-
- sort.Slice(response, func(i, j int) bool {
- return response[i].Id < response[j].Id
- })
-
- if len(response) != len(products) {
- t.Errorf("Expected count of product is %d, got %d",
- 2, len(response))
- }
-
- for i, product := range products {
- if product.Id != response[i].Id {
- t.Errorf("Expected id of product %d, got %d", product.Id, response[i].Id)
- }
-
- if product.Description != response[i].Description {
- t.Errorf("Expected description of product %s, got %s", product.Description, response[i].Description)
- }
-
- if product.Ean != response[i].Ean {
- t.Errorf("Expected ean of product %s, got %s", product.Ean, response[i].Ean)
- }
- }
-
- })
-}
-
-func TestDefaultController_PostProduct(t *testing.T) {
- type fields struct {
- productRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
- tests := []struct {
- name string
- fields fields
- args args
- expectedStatus int
- expectedResponse string
- }{
- {
- name: "Valid Product",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/product",
- strings.NewReader(`{"id": 3, "description": "Test Product", "ean": "12345"}`),
- ),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Valid Product (Partly Fields)",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/product",
- strings.NewReader(`{"description": "Incomplete Product"}`),
- ),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Malformed JSON",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: httptest.NewRequest(
- "POST",
- "/api/v1/product",
- strings.NewReader(`{"description": "Incomplete Product"`),
- ),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := defaultController{
- productRepository: tt.fields.productRepository,
- }
- controller.PostProduct(tt.args.writer, tt.args.request)
-
- // You can then assert the response status and content, and check against your expectations.
- if tt.args.writer.Code != tt.expectedStatus {
- t.Errorf("Expected status code %d, but got %d", tt.expectedStatus, tt.args.writer.Code)
- }
-
- if tt.expectedResponse != "" {
- actualResponse := tt.args.writer.Body.String()
- if actualResponse != tt.expectedResponse {
- t.Errorf("Expected response: %s, but got: %s", tt.expectedResponse, actualResponse)
- }
- }
- })
- }
-}
-
-func TestDefaultController_PutProduct(t *testing.T) {
- type fields struct {
- productRepository Repository
- }
- type args struct {
- writer *httptest.ResponseRecorder
- request *http.Request
- }
-
- tests := []struct {
- name string
- fields fields
- args args
- expectedStatus int
- expectedResponse string // If you want to check the response content
- }{
- {
- name: "Valid Update",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "PUT",
- "/api/v1/product/1",
- strings.NewReader(`{"id": 1, "description": "Updated Product", "ean": "54321"}`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "1"))
- return request
- }(),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Valid Update (Partly Fields)",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "PUT",
- "/api/v1/product/2",
- strings.NewReader(`{"description": "Incomplete Update"}`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "2"))
- return request
- }(),
- },
- expectedStatus: http.StatusOK,
- expectedResponse: "",
- },
- {
- name: "Malformed JSON",
- fields: fields{
- productRepository: GenerateExampleDemoRepository(),
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "PUT",
- "/api/v1/product/2",
- strings.NewReader(`{"description": "Incomplete Update"`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "2"))
- return request
- }(),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- {
- name: "Incorrect Type for EAN (Non-numeric)",
- fields: fields{
- // Set up your repository mock or test double here if needed
- },
- args: args{
- writer: httptest.NewRecorder(),
- request: func() *http.Request {
- var request = httptest.NewRequest(
- "PUT",
- "/api/v1/product/2",
- strings.NewReader(`{"ean": "Wrong Type"`))
- request = request.WithContext(context.WithValue(request.Context(), "productId", "2"))
- return request
- }(),
- },
- expectedStatus: http.StatusBadRequest,
- expectedResponse: "",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- controller := defaultController{
- productRepository: tt.fields.productRepository,
- }
- controller.PutProduct(tt.args.writer, tt.args.request)
-
- // You can then assert the response status and content, and check against your expectations.
- if tt.args.writer.Code != tt.expectedStatus {
- t.Errorf("Expected status code %d, but got %d", tt.expectedStatus, tt.args.writer.Code)
- }
-
- if tt.expectedResponse != "" {
- actualResponse := tt.args.writer.Body.String()
- if actualResponse != tt.expectedResponse {
- t.Errorf("Expected response: %s, but got: %s", tt.expectedResponse, actualResponse)
- }
- }
- })
- }
-}
diff --git a/src/product-service/products/demo_repository.go b/src/product-service/products/demo_repository.go
deleted file mode 100644
index eb42d34f..00000000
--- a/src/product-service/products/demo_repository.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package products
-
-import (
- "errors"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/products/model"
- "sort"
-)
-
-type DemoRepository struct {
- products map[uint64]*model.Product
-}
-
-func NewDemoRepository() *DemoRepository {
- return &DemoRepository{products: make(map[uint64]*model.Product)}
-}
-
-func (repo *DemoRepository) Create(product *model.Product) (*model.Product, error) {
- if product.Id == 0 {
- product.Id = repo.findNextAvailableID()
- }
-
- if foundProductWithEan, _ := repo.FindByEan(product.Ean); foundProductWithEan != nil {
- return nil, errors.New(ErrorEanAlreadyExists)
- }
-
- if _, found := repo.products[product.Id]; found {
- return nil, errors.New(ErrorProductAlreadyExists)
- }
-
- repo.products[product.Id] = product
-
- return product, nil
-}
-
-func (repo *DemoRepository) FindAll() ([]*model.Product, error) {
- if repo.products != nil {
- r := make([]*model.Product, 0, len(repo.products))
- for _, v := range repo.products {
- r = append(r, v)
- }
-
- sort.Slice(r, func(i, j int) bool {
- return r[i].Description < r[j].Description
- })
- return r, nil
- }
-
- return nil, errors.New(ErrorProductsList)
-}
-
-func (repo *DemoRepository) FindById(id uint64) (*model.Product, error) {
- product, found := repo.products[id]
- if found {
- return product, nil
- }
-
- return nil, errors.New(ErrorProductNotFound)
-}
-
-func (repo *DemoRepository) FindByEan(ean string) (*model.Product, error) {
- for _, entry := range repo.products {
- if entry.Ean == ean {
- return entry, nil
- }
- }
-
- return nil, errors.New(ErrorProductNotFound)
-}
-
-func (repo *DemoRepository) Update(product *model.Product) (*model.Product, error) {
- existingProduct, foundError := repo.FindById(product.Id)
-
- if foundError != nil {
- return nil, errors.New(ErrorProductUpdate)
- }
-
- existingProduct.Description = product.Description
- existingProduct.Ean = product.Ean
-
- return existingProduct, nil
-}
-
-func (repo *DemoRepository) Delete(product *model.Product) error {
- _, found := repo.products[product.Id]
- if found {
- delete(repo.products, product.Id)
- return nil
- }
-
- return errors.New(ErrorProductDeletion)
-}
-
-func (repo *DemoRepository) findNextAvailableID() uint64 {
- var maxID uint64
- for id := range repo.products {
- if id > maxID {
- maxID = id
- }
- }
- return maxID + 1
-}
diff --git a/src/product-service/products/demo_repository_example.go b/src/product-service/products/demo_repository_example.go
deleted file mode 100644
index 82acebf3..00000000
--- a/src/product-service/products/demo_repository_example.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package products
-
-import "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/products/model"
-
-func GenerateExampleDemoRepository() Repository {
- repository := NewDemoRepository()
- productSlice := GenerateExampleProductSlice()
- for _, product := range productSlice {
- repository.Create(product)
- }
-
- return repository
-}
-
-func GenerateExampleProductSlice() []*model.Product {
- return []*model.Product{
- {
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- },
- {
- Id: 2,
- Description: "Lauchzwiebeln",
- Ean: "5001819040871",
- },
- }
-}
diff --git a/src/product-service/products/demo_repository_test.go b/src/product-service/products/demo_repository_test.go
deleted file mode 100644
index 9fda78a3..00000000
--- a/src/product-service/products/demo_repository_test.go
+++ /dev/null
@@ -1,326 +0,0 @@
-package products
-
-import (
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/product-service/products/model"
- "reflect"
- "sort"
- "testing"
-)
-
-func TestNewDemoRepository(t *testing.T) {
- t.Run("Demo repository correct initialized", func(t *testing.T) {
- want := make(map[uint64]*model.Product)
- if got := NewDemoRepository(); !reflect.DeepEqual(got.products, want) {
- t.Errorf("NewDemoRepository().products = %v, want %v", got.products, want)
- }
- })
-}
-
-func TestDemoRepository_Create(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- product := model.Product{
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- }
-
- productWithDuplicateEan := model.Product{
- Id: 2,
- Description: "Dinkelnudeln",
- Ean: "4014819040771",
- }
-
- t.Run("Create product with success", func(t *testing.T) {
- _, err := demoRepository.Create(&product)
- if err != nil {
- t.Error(err)
- }
- })
-
- t.Run("Check if products with duplicate ean can not be created", func(t *testing.T) {
- _, err := demoRepository.Create(&productWithDuplicateEan)
- if err.Error() != ErrorEanAlreadyExists {
- t.Error(err)
- }
- })
-
- t.Run("Check for doublet", func(t *testing.T) {
- _, err := demoRepository.Create(&product)
- if err.Error() != ErrorProductAlreadyExists && err.Error() != ErrorEanAlreadyExists {
- t.Error(err)
- }
- })
-}
-
-func TestDemoRepository_FindAll(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- products := []*model.Product{
- {
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- },
- {
- Id: 2,
- Description: "Lauchzwiebeln",
- Ean: "5001819040871",
- },
- }
-
- for _, product := range products {
- _, err := demoRepository.Create(product)
- if err != nil {
- t.Error("Failed to add prepared product for test")
- }
- }
-
- t.Run("Fetch all products", func(t *testing.T) {
- fetchedProducts, err := demoRepository.FindAll()
- if err != nil {
- t.Error("Can't fetch products")
- }
-
- if len(fetchedProducts) != len(products) {
- t.Errorf("Unexpected product count. Expected %d, got %d", len(products), len(fetchedProducts))
- }
- })
-
- productTests := []struct {
- name string
- want *model.Product
- }{
- {
- name: "first",
- want: products[0],
- },
- {
- name: "second",
- want: products[1],
- },
- }
-
- for i, tt := range productTests {
- t.Run("Is fetched product matching with "+tt.name+" added product?", func(t *testing.T) {
- fetchedProducts, _ := demoRepository.FindAll()
- sort.Slice(fetchedProducts, func(i, j int) bool {
- return fetchedProducts[i].Id < fetchedProducts[j].Id
- })
- if !reflect.DeepEqual(tt.want, fetchedProducts[i]) {
- t.Error("Fetched product does not match original product")
- }
- })
- }
-}
-
-func TestDemoRepository_FindById(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- product := model.Product{
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- }
-
- _, err := demoRepository.Create(&product)
- if err != nil {
- t.Fatal("Failed to add prepare product for test")
- }
-
- t.Run("Fetch product with existing id", func(t *testing.T) {
- _, err := demoRepository.FindById(product.Id)
- if err != nil {
- t.Errorf("Can't find expected product with id %d", product.Id)
- }
-
- t.Run("Is fetched product matching with added product?", func(t *testing.T) {
- fetchedProduct, _ := demoRepository.FindById(product.Id)
- if !reflect.DeepEqual(product, *fetchedProduct) {
- t.Error("Fetched product does not match original product")
- }
- })
- })
-
- t.Run("Non-existing product test", func(t *testing.T) {
- _, err = demoRepository.FindById(42)
- if err.Error() != ErrorProductNotFound {
- t.Error(err)
- }
- })
-}
-
-func TestDemoRepository_FindByEan(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- product := model.Product{
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- }
-
- _, err := demoRepository.Create(&product)
- if err != nil {
- t.Fatal("Failed to add prepare product for test")
- }
-
- t.Run("Fetch product with existing ean", func(t *testing.T) {
- _, err := demoRepository.FindByEan(product.Ean)
- if err != nil {
- t.Errorf("Can't find expected product with ean %s", product.Ean)
- }
-
- t.Run("Is fetched product matching with added product?", func(t *testing.T) {
- fetchedProduct, _ := demoRepository.FindByEan(product.Ean)
- if !reflect.DeepEqual(product, *fetchedProduct) {
- t.Error("Fetched product does not match original product")
- }
- })
- })
-
- t.Run("Non-existing product test", func(t *testing.T) {
- _, err = demoRepository.FindByEan("42")
- if err.Error() != ErrorProductNotFound {
- t.Error(err)
- }
- })
-}
-
-func TestDemoRepository_Update(t *testing.T) {
- // Prepare test
- demoRepository := NewDemoRepository()
-
- product := model.Product{
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- }
-
- fetchedProduct, err := demoRepository.Create(&product)
- if err != nil {
- t.Error("Failed to add prepare product for test")
- }
-
- t.Run("Check if updated product has updated description", func(t *testing.T) {
- updateProduct := model.Product{
- Id: 1,
- Description: "Wittenseer Mineralwasser",
- Ean: "4014819040771",
- }
- updatedProduct, err := demoRepository.Update(&updateProduct)
- if err != nil {
- t.Error(err.Error())
- }
-
- if fetchedProduct.Description != updatedProduct.Description {
- t.Errorf("Failed to update product description. Got %s, want %s.",
- fetchedProduct.Description, updateProduct.Description)
- }
- })
-}
-
-func TestDemoRepository_Delete(t *testing.T) {
- // Prepare test
- productsRepository := NewDemoRepository()
-
- product := model.Product{
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- }
-
- fetchedProduct, err := productsRepository.Create(&product)
- if err != nil {
- t.Error("Failed to add prepare product for test")
- }
-
- t.Run("Test for deletion", func(t *testing.T) {
- err = productsRepository.Delete(fetchedProduct)
- if err != nil {
- t.Errorf("Failed to delete product with id %d", product.Id)
- }
-
- t.Run("Try to fetch deleted product", func(t *testing.T) {
- fetchedProduct, err = productsRepository.FindById(product.Id)
- if err.Error() != ErrorProductNotFound {
- t.Errorf("Product with id %d was not deleted", product.Id)
- }
- })
- })
-
- t.Run("Try to delete non-existing product", func(t *testing.T) {
- fakeProduct := model.Product{
- Id: 1,
- Description: "Lauchzwiebeln",
- Ean: "5001819040871",
- }
-
- err = productsRepository.Delete(&fakeProduct)
- if err.Error() != ErrorProductDeletion {
- t.Errorf("Product with id %d was deleted", product.Id)
- }
- })
-}
-
-func TestDemoRepository_findNextAvailableID(t *testing.T) {
- type fields struct {
- products map[uint64]*model.Product
- }
- tests := []struct {
- name string
- fields fields
- want uint64
- }{
- {
- name: "Check if next available id is correct",
- fields: fields{products: map[uint64]*model.Product{
- 1: {
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- },
- 2: {
- Id: 2,
- Description: "Lauchzwiebeln",
- Ean: "5001819040871",
- },
- }},
- want: 3,
- },
- {
- name: "Check if next available id is correct with gaps in map",
- fields: fields{products: map[uint64]*model.Product{
- 1: {
- Id: 1,
- Description: "Strauchtomaten",
- Ean: "4014819040771",
- },
- 3: {
- Id: 3,
- Description: "Lauchzwiebeln",
- Ean: "5001819040871",
- },
- }},
- want: 4,
- },
- {
- name: "check if next available id is correct with empty map",
- fields: fields{products: make(map[uint64]*model.Product)},
- want: 1,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- repo := &DemoRepository{
- products: tt.fields.products,
- }
- if got := repo.findNextAvailableID(); got != tt.want {
- t.Errorf("findNextAvailableID() = %v, want %v", got, tt.want)
- }
- })
- }
-}
From fa84feb593f320cd2d1ef0fbbde26dd85461c50b Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 15:58:20 +0100
Subject: [PATCH 08/19] refactor(http-proxy-service): remove comments
---
.../proxy/default_manager_test.go | 145 +++++-------------
1 file changed, 39 insertions(+), 106 deletions(-)
diff --git a/src/http-proxy-service/proxy/default_manager_test.go b/src/http-proxy-service/proxy/default_manager_test.go
index b83973bf..eb23dfea 100644
--- a/src/http-proxy-service/proxy/default_manager_test.go
+++ b/src/http-proxy-service/proxy/default_manager_test.go
@@ -19,14 +19,11 @@ func TestNewDefaultManager(t *testing.T) {
},
}
- // Create a new manager
manager := NewDefaultManager(config)
if manager == nil {
t.Error("Expected a non-nil manager, got nil")
}
-
- // You can write more specific tests based on your requirements.
}
func TestDefaultManager_GetProxyRouter(t *testing.T) {
@@ -64,8 +61,7 @@ func TestProxyServer(t *testing.T) {
}))
defer testService2.Close()
- // Create a sample configuration for testing
- config := &Config{
+ sampleConfig := &Config{
ListenAddress: "localhost:8080",
ProxyRoutes: []Route{
{
@@ -81,109 +77,46 @@ func TestProxyServer(t *testing.T) {
},
}
- // Start a test HTTP server using the NewDefaultManager
- proxyManager := NewDefaultManager(config)
+ proxyManager := NewDefaultManager(sampleConfig)
testProxyServer := httptest.NewServer(proxyManager.GetProxyRouter())
defer testProxyServer.Close()
// Test an HTTP request to Route1
- resp, err := testProxyServer.Client().Get(testProxyServer.URL + "/context1/")
- if err != nil {
- t.Errorf("HTTP GET to /context1/test failed: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
- }
-
- // Check if the response body matches the expected value for Route1
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("Failed to read response body: %v", err)
- }
- if string(body) != want1 {
- t.Errorf("Expected response body for Route1 to be %s, but got %s", want1, string(body))
- }
-
- // Test an HTTP request to Route2
- resp, err = testProxyServer.Client().Get(testProxyServer.URL + "/context2/")
- if err != nil {
- t.Errorf("HTTP GET to /context2/test failed: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
- }
-
- // Check if the response body matches the expected value for Route2
- body, err = io.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("Failed to read response body: %v", err)
- }
- if string(body) != want2 {
- t.Errorf("Expected response body for Route2 to be %s, but got %s", want2, string(body))
- }
-}
-
-/*func TestDefaultManagerNewHandler(t *testing.T) {
- type fields struct {
- proxies []*httputil.ReverseProxy
- routing *router.Router
- }
- type args struct {
- p *httputil.ReverseProxy
- }
- tests := []struct {
- name string
- fields fields
- args args
- want func(http.ResponseWriter, *http.Request)
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- dp := defaultManager{
- proxies: tt.fields.proxies,
- routing: tt.fields.routing,
- }
- if got := dp.newHandler(tt.args.p); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("newHandler() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestDefaultManagerNewProxy(t *testing.T) {
- type fields struct {
- proxies []*httputil.ReverseProxy
- routing *router.Router
- }
- type args struct {
- targetUrl string
- }
- tests := []struct {
- name string
- fields fields
- args args
- want *httputil.ReverseProxy
- wantErr bool
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- dp := defaultManager{
- proxies: tt.fields.proxies,
- routing: tt.fields.routing,
- }
- got, err := dp.newProxy(tt.args.targetUrl)
- if (err != nil) != tt.wantErr {
- t.Errorf("newProxy() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("newProxy() got = %v, want %v", got, tt.want)
- }
- })
- }
+ t.Run("HTTP request to Route1", func(t *testing.T) {
+ resp, err := testProxyServer.Client().Get(testProxyServer.URL + "/context1/")
+ if err != nil {
+ t.Errorf("HTTP GET to /context1/test failed: %v", err)
+ }
+ if resp.StatusCode != http.StatusOK {
+ t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
+ }
+
+ // Check response body matches
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Errorf("Failed to read response body: %v", err)
+ }
+ if string(body) != want1 {
+ t.Errorf("Expected response body for Route1 to be %s, but got %s", want1, string(body))
+ }
+ })
+
+ t.Run("HTTP request to Route2", func(t *testing.T) {
+ resp, err := testProxyServer.Client().Get(testProxyServer.URL + "/context2/")
+ if err != nil {
+ t.Errorf("HTTP GET to /context2/test failed: %v", err)
+ }
+ if resp.StatusCode != http.StatusOK {
+ t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
+ }
+
+ // Check response body matches
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Errorf("Failed to read response body: %v", err)
+ }
+ if string(body) != want2 {
+ t.Errorf("Expected response body for Route2 to be %s, but got %s", want2, string(body))
+ }
+ })
}
-*/
From 5b349ed41dea3f470ec5ff461319191c04546eaa Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 15:58:42 +0100
Subject: [PATCH 09/19] feat(kubernetes): add static pv for database
---
.../manifests/price-whisper/database.yaml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/kubernetes/manifests/price-whisper/database.yaml b/kubernetes/manifests/price-whisper/database.yaml
index 737dd345..3b28ddc1 100644
--- a/kubernetes/manifests/price-whisper/database.yaml
+++ b/kubernetes/manifests/price-whisper/database.yaml
@@ -20,6 +20,22 @@ stringData:
]
---
apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: database-data
+ annotations:
+ author: Gruppe 6
+spec:
+ capacity:
+ storage: 1Gi
+ volumeMode: Filesystem
+ accessModes:
+ - ReadWriteOnce
+ persistentVolumeReclaimPolicy: Delete
+ hostPath:
+ path: /var/price-whisper/database-data/
+---
+apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: database-data
@@ -27,6 +43,7 @@ metadata:
annotations:
author: Gruppe 6
spec:
+ volumeName: database-data
accessModes:
- ReadWriteOnce
resources:
From b7f72652df3ebebd9f544db77548f4b79c2c54a3 Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 15:58:53 +0100
Subject: [PATCH 10/19] refactor(shoppinglist-service): remove unused
repositories and controller
---
.../userShoppingList/demo_repository.go | 105 -------
.../userShoppingList/demo_repository_test.go | 287 ------------------
.../userShoppingListEntry/demo_repository.go | 94 ------
.../demo_repository_test.go | 228 --------------
4 files changed, 714 deletions(-)
delete mode 100644 src/shoppinglist-service/userShoppingList/demo_repository.go
delete mode 100644 src/shoppinglist-service/userShoppingList/demo_repository_test.go
delete mode 100644 src/shoppinglist-service/userShoppingListEntry/demo_repository.go
delete mode 100644 src/shoppinglist-service/userShoppingListEntry/demo_repository_test.go
diff --git a/src/shoppinglist-service/userShoppingList/demo_repository.go b/src/shoppinglist-service/userShoppingList/demo_repository.go
deleted file mode 100644
index 629e0ae8..00000000
--- a/src/shoppinglist-service/userShoppingList/demo_repository.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package userShoppingList
-
-import (
- "errors"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/shoppinglist-service/userShoppingList/model"
- "sort"
-)
-
-type DemoRepository struct {
- shoppingLists map[uint64]*model.UserShoppingList
-}
-
-func NewDemoRepository() *DemoRepository {
- return &DemoRepository{shoppingLists: make(map[uint64]*model.UserShoppingList)}
-}
-
-func (repo *DemoRepository) Create(shoppingList *model.UserShoppingList) (*model.UserShoppingList, error) {
- var listId uint64
- if shoppingList.Id == 0 {
- listId = repo.findNextAvailableID()
- shoppingList.Id = listId
- } else {
- listId = shoppingList.Id
- }
-
- _, found := repo.shoppingLists[listId]
- if found {
- return nil, errors.New(ErrorListAlreadyExists)
- }
- repo.shoppingLists[listId] = shoppingList
-
- return shoppingList, nil
-}
-
-func (repo *DemoRepository) Update(shoppingList *model.UserShoppingList) (*model.UserShoppingList, error) {
- existingShoppingList, foundError := repo.FindById(shoppingList.Id)
-
- if foundError != nil {
- return nil, errors.New(ErrorListUpdate)
- }
-
- existingShoppingList.Description = shoppingList.Description
- existingShoppingList.Completed = shoppingList.Completed
-
- return existingShoppingList, nil
-}
-
-func (repo *DemoRepository) FindAllById(userId uint64) ([]*model.UserShoppingList, error) {
- lists := []*model.UserShoppingList{}
-
- for _, shoppingList := range repo.shoppingLists {
- if shoppingList.UserId == userId {
- lists = append(lists, shoppingList)
- }
- }
-
- if len(lists) == 0 {
- return nil, errors.New(ErrorListNotFound)
- }
-
- sort.Slice(lists, func(i, j int) bool {
- return lists[i].Description < lists[j].Description
- })
-
- return lists, nil
-}
-
-func (repo *DemoRepository) FindById(Id uint64) (*model.UserShoppingList, error) {
- shoppingList, found := repo.shoppingLists[Id]
- if found {
- return shoppingList, nil
- }
-
- return nil, errors.New(ErrorListNotFound)
-}
-
-func (repo *DemoRepository) FindByIds(userId uint64, listId uint64) (*model.UserShoppingList, error) {
- for _, shoppingList := range repo.shoppingLists {
- if shoppingList.UserId == userId && shoppingList.Id == listId {
- return shoppingList, nil
- }
- }
-
- return nil, errors.New(ErrorListNotFound)
-}
-
-func (repo *DemoRepository) Delete(shoppingList *model.UserShoppingList) error {
- _, found := repo.shoppingLists[shoppingList.Id]
- if found {
- delete(repo.shoppingLists, shoppingList.Id)
- return nil
- }
-
- return errors.New(ErrorListDeletion)
-}
-
-func (repo *DemoRepository) findNextAvailableID() uint64 {
- var maxID uint64
- for id := range repo.shoppingLists {
- if id > maxID {
- maxID = id
- }
- }
- return maxID + 1
-}
diff --git a/src/shoppinglist-service/userShoppingList/demo_repository_test.go b/src/shoppinglist-service/userShoppingList/demo_repository_test.go
deleted file mode 100644
index 65171276..00000000
--- a/src/shoppinglist-service/userShoppingList/demo_repository_test.go
+++ /dev/null
@@ -1,287 +0,0 @@
-package userShoppingList
-
-import (
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/shoppinglist-service/userShoppingList/model"
- "testing"
-)
-
-func TestNewDemoRepository(t *testing.T) {
- t.Run("Demo repository correctly initialized", func(t *testing.T) {
- repo := NewDemoRepository()
- if repo == nil {
- t.Error("NewDemoRepository returned nil")
- }
- })
-}
-
-func TestDemoRepository_Create(t *testing.T) {
- repo := NewDemoRepository()
-
- list := &model.UserShoppingList{
- Id: 1,
- UserId: 1,
- Description: "New Shoppinglist",
- Completed: false,
- }
-
- t.Run("Create shopping list with success", func(t *testing.T) {
- createdList, err := repo.Create(list)
- if err != nil {
- t.Error(err)
- }
-
- if createdList.Id != list.Id {
- t.Errorf("Expected created shopping list to have ID %d, but got %d", list.Id, createdList.Id)
- }
-
- if createdList.Description != list.Description {
- t.Errorf("Expected created shopping list to have description %s, but got %s", list.Description, createdList.Description)
- }
- })
-
- t.Run("Attempt to create a duplicate shopping list", func(t *testing.T) {
- _, err := repo.Create(list)
- if err == nil {
- t.Error("Expected an error for duplicate shopping list creation")
- }
- })
-}
-
-func TestDemoRepository_FindAllById(t *testing.T) {
- repo := NewDemoRepository()
-
- lists := []*model.UserShoppingList{
- {
- Id: 1,
- UserId: 1,
- Completed: false,
- },
- {
- Id: 2,
- UserId: 2,
- Completed: false,
- },
- {
- Id: 3,
- UserId: 1,
- Completed: true,
- },
- }
-
- for _, list := range lists {
- _, _ = repo.Create(list)
- }
-
- t.Run("Find all lists for a user with existing lists", func(t *testing.T) {
- userLists, err := repo.FindAllById(1)
- if err != nil {
- t.Error(err)
- }
-
- if len(userLists) != 2 {
- t.Errorf("Expected 2 lists for user 1, but got %d", len(userLists))
- }
- })
-
- t.Run("Find all lists for a user with no existing lists", func(t *testing.T) {
- _, err := repo.FindAllById(42)
- if err == nil {
- t.Error("Expected an error for user with no lists")
- }
- })
-}
-
-func TestDemoRepository_FindByIds(t *testing.T) {
- repo := NewDemoRepository()
-
- lists := []*model.UserShoppingList{
- {
- Id: 1,
- UserId: 1,
- Completed: false,
- },
- {
- Id: 2,
- UserId: 2,
- Completed: false,
- },
- }
-
- for _, list := range lists {
- _, _ = repo.Create(list)
- }
-
- t.Run("Find list by IDs with existing list", func(t *testing.T) {
- foundList, err := repo.FindByIds(1, 1)
- if err != nil {
- t.Error(err)
- }
-
- if foundList.Id != 1 {
- t.Errorf("Expected list with ID 1, but got list with ID %d", foundList.Id)
- }
- })
-
- t.Run("Find list by IDs with non-existing list", func(t *testing.T) {
- _, err := repo.FindByIds(1, 42)
- if err == nil {
- t.Error("Expected an error for non-existing list")
- }
- })
-}
-
-func TestDemoRepository_FindById(t *testing.T) {
- repo := NewDemoRepository()
-
- lists := []*model.UserShoppingList{
- {
- Id: 1,
- UserId: 1,
- Completed: false,
- },
- {
- Id: 2,
- UserId: 2,
- Completed: false,
- },
- }
-
- for _, list := range lists {
- _, _ = repo.Create(list)
- }
-
- t.Run("Find list by ID with existing list", func(t *testing.T) {
- foundList, err := repo.FindById(1)
- if err != nil {
- t.Error(err)
- }
-
- if foundList.Id != 1 {
- t.Errorf("Expected list with ID 1, but got list with ID %d", foundList.Id)
- }
- })
-
- t.Run("Find list by ID with non-existing list", func(t *testing.T) {
- _, err := repo.FindById(42)
- if err == nil {
- t.Error("Expected an error for non-existing list")
- }
- })
-}
-
-func TestDemoRepository_Update(t *testing.T) {
- repo := NewDemoRepository()
-
- list := &model.UserShoppingList{
- Id: 1,
- UserId: 1,
- Description: "Update list description",
- Completed: true,
- }
-
- _, _ = repo.Create(list)
-
- t.Run("Update an existing shopping list", func(t *testing.T) {
- updatedList, err := repo.Update(list)
- if err != nil {
- t.Error(err)
- }
-
- if updatedList.Description != list.Description {
- t.Errorf("Expected updated shopping list to have description %s, but got %s",
- list.Description, updatedList.Description)
- }
-
- if updatedList.Completed != list.Completed {
- t.Errorf("Expected updated shopping list to have completed value %t, but got %t",
- list.Completed, updatedList.Completed)
- }
- })
-
- t.Run("Attempt to update a non-existing shopping list", func(t *testing.T) {
- fakeList := &model.UserShoppingList{
- Id: 42,
- UserId: 2,
- Completed: true,
- }
-
- _, err := repo.Update(fakeList)
- if err == nil {
- t.Error("Expected an error for updating a non-existing shopping list")
- }
- })
-}
-
-func TestDemoRepository_Delete(t *testing.T) {
- repo := NewDemoRepository()
-
- list := &model.UserShoppingList{
- Id: 1,
- UserId: 1,
- Completed: false,
- }
-
- _, _ = repo.Create(list)
-
- t.Run("Delete an existing shopping list", func(t *testing.T) {
- err := repo.Delete(list)
- if err != nil {
- t.Error(err)
- }
-
- // Ensure the list is deleted
- _, err = repo.FindById(list.Id)
- if err == nil {
- t.Error("Expected the shopping list to be deleted")
- }
- })
-
- t.Run("Attempt to delete a non-existing shopping list", func(t *testing.T) {
- fakeList := &model.UserShoppingList{
- Id: 42,
- UserId: 2,
- Completed: false,
- }
-
- err := repo.Delete(fakeList)
- if err == nil {
- t.Error("Expected an error for deleting a non-existing shopping list")
- }
- })
-}
-
-func TestDemoRepository_findNextAvailableID(t *testing.T) {
- repo := NewDemoRepository()
-
- lists := []*model.UserShoppingList{
- {
- Id: 1,
- UserId: 1,
- Completed: false,
- },
- {
- Id: 2,
- UserId: 2,
- Completed: false,
- },
- }
-
- for _, list := range lists {
- _, _ = repo.Create(list)
- }
-
- t.Run("Next available ID when there are no gaps", func(t *testing.T) {
- nextID := repo.findNextAvailableID()
- if nextID != 3 {
- t.Errorf("Expected next available ID to be 4, but got %d", nextID)
- }
- })
-
- t.Run("Next available ID with gaps in IDs", func(t *testing.T) {
- delete(repo.shoppingLists, 2)
- nextID := repo.findNextAvailableID()
- if nextID != 2 {
- t.Errorf("Expected next available ID to be 2, but got %d", nextID)
- }
- })
-}
diff --git a/src/shoppinglist-service/userShoppingListEntry/demo_repository.go b/src/shoppinglist-service/userShoppingListEntry/demo_repository.go
deleted file mode 100644
index 95c75080..00000000
--- a/src/shoppinglist-service/userShoppingListEntry/demo_repository.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package userShoppingListEntry
-
-import (
- "errors"
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/shoppinglist-service/userShoppingListEntry/model"
-)
-
-type shoppingListEntryKey struct {
- ShoppingListID uint64
- ProductID uint64
-}
-
-type DemoRepository struct {
- entries map[shoppingListEntryKey]*model.UserShoppingListEntry
-}
-
-func NewDemoRepository() *DemoRepository {
- return &DemoRepository{entries: make(map[shoppingListEntryKey]*model.UserShoppingListEntry)}
-}
-
-func (repo *DemoRepository) Create(entry *model.UserShoppingListEntry) (*model.UserShoppingListEntry, error) {
- _, err := repo.FindByIds(entry.ShoppingListId, entry.ProductId)
- if err == nil {
- return nil, errors.New(ErrorEntryAlreadyExists)
- }
- key := shoppingListEntryKey{
- ShoppingListID: entry.ShoppingListId,
- ProductID: entry.ProductId,
- }
-
- repo.entries[key] = entry
-
- return entry, nil
-}
-
-func (repo *DemoRepository) Delete(entry *model.UserShoppingListEntry) error {
- key := shoppingListEntryKey{
- ShoppingListID: entry.ShoppingListId,
- ProductID: entry.ProductId,
- }
-
- _, exists := repo.entries[key]
- if !exists {
- return errors.New(ErrorEntryDeletion)
- }
-
- delete(repo.entries, key)
- return nil
-}
-
-func (repo *DemoRepository) Update(entry *model.UserShoppingListEntry) (*model.UserShoppingListEntry, error) {
- existingEntry, foundError := repo.FindByIds(entry.ShoppingListId, entry.ProductId)
-
- if foundError != nil {
- return nil, errors.New(ErrorEntryUpdate)
- }
-
- existingEntry.Count = entry.Count
- existingEntry.Note = entry.Note
- existingEntry.Checked = entry.Checked
-
- return existingEntry, nil
-}
-
-func (repo *DemoRepository) FindByIds(shoppingListId uint64, productId uint64) (*model.UserShoppingListEntry, error) {
- key := shoppingListEntryKey{
- ShoppingListID: shoppingListId,
- ProductID: productId,
- }
-
- entry, exists := repo.entries[key]
-
- if !exists {
- return nil, errors.New(ErrorEntryNotFound)
- }
-
- return entry, nil
-}
-
-func (repo *DemoRepository) FindAll(shoppingListId uint64) ([]*model.UserShoppingListEntry, error) {
- entries := []*model.UserShoppingListEntry{}
-
- for key, entry := range repo.entries {
- if key.ShoppingListID == shoppingListId {
- entries = append(entries, entry)
- }
- }
-
- if len(entries) == 0 {
- return nil, errors.New(ErrorEntryNotFound)
- }
-
- return entries, nil
-}
diff --git a/src/shoppinglist-service/userShoppingListEntry/demo_repository_test.go b/src/shoppinglist-service/userShoppingListEntry/demo_repository_test.go
deleted file mode 100644
index b4a844af..00000000
--- a/src/shoppinglist-service/userShoppingListEntry/demo_repository_test.go
+++ /dev/null
@@ -1,228 +0,0 @@
-package userShoppingListEntry
-
-import (
- "hsfl.de/group6/hsfl-master-ai-cloud-engineering/shoppinglist-service/userShoppingListEntry/model"
- "testing"
-)
-
-func TestNewDemoRepository(t *testing.T) {
- t.Run("Demo repository correctly initialized", func(t *testing.T) {
- repo := NewDemoRepository()
- if repo == nil {
- t.Error("NewDemoRepository returned nil")
- }
- })
-}
-
-func TestDemoRepository_Create(t *testing.T) {
- repo := NewDemoRepository()
-
- entry := &model.UserShoppingListEntry{
- ShoppingListId: 1,
- ProductId: 1,
- Count: 2,
- Note: "This is very important",
- Checked: false,
- }
-
- t.Run("Create entry with success", func(t *testing.T) {
- createdEntry, err := repo.Create(entry)
- if err != nil {
- t.Error(err)
- }
-
- if createdEntry.ShoppingListId != entry.ShoppingListId || createdEntry.ProductId != entry.ProductId {
- t.Errorf("Expected created entry to have ShoppingListId %d and ProductId %d, but got ShoppingListId %d and ProductId %d",
- entry.ShoppingListId, entry.ProductId, createdEntry.ShoppingListId, createdEntry.ProductId)
- }
- })
-
- t.Run("Attempt to create a duplicate entry", func(t *testing.T) {
- _, err := repo.Create(entry)
- if err == nil {
- t.Error("Expected an error for duplicate entry creation")
- }
- })
-}
-
-func TestDemoRepository_FindAll(t *testing.T) {
- repo := NewDemoRepository()
-
- entries := []*model.UserShoppingListEntry{
- {
- ShoppingListId: 1,
- ProductId: 1,
- Count: 2,
- Note: "This is very important",
- Checked: false,
- },
- {
- ShoppingListId: 1,
- ProductId: 2,
- Count: 5,
- Note: "I really want this",
- Checked: false,
- },
- {
- ShoppingListId: 2,
- ProductId: 1,
- Count: 9,
- Note: "Please get this",
- Checked: false,
- },
- }
-
- for _, entry := range entries {
- _, _ = repo.Create(entry)
- }
-
- t.Run("Find all entries for a shopping list with existing entries", func(t *testing.T) {
- shoppingListId := uint64(1)
- foundEntries, err := repo.FindAll(shoppingListId)
- if err != nil {
- t.Error(err)
- }
-
- if len(foundEntries) != 2 {
- t.Errorf("Expected 2 entries for shopping list %d, but got %d", shoppingListId, len(foundEntries))
- }
- })
-
- t.Run("Find all entries for a shopping list with no existing entries", func(t *testing.T) {
- shoppingListId := uint64(3)
- _, err := repo.FindAll(shoppingListId)
- if err == nil {
- t.Error("Expected an error for shopping list with no entries")
- }
- })
-}
-
-func TestDemoRepository_FindByIds(t *testing.T) {
- repo := NewDemoRepository()
-
- entry := &model.UserShoppingListEntry{
- ShoppingListId: 1,
- ProductId: 1,
- Count: 2,
- Note: "This is very important",
- Checked: false,
- }
-
- _, _ = repo.Create(entry)
-
- t.Run("Find entry by IDs with existing entry", func(t *testing.T) {
- foundEntry, err := repo.FindByIds(entry.ShoppingListId, entry.ProductId)
- if err != nil {
- t.Error(err)
- }
-
- if foundEntry.ShoppingListId != entry.ShoppingListId || foundEntry.ProductId != entry.ProductId {
- t.Errorf("Expected entry with ShoppingListId %d and ProductId %d, but got ShoppingListId %d and ProductId %d",
- entry.ShoppingListId, entry.ProductId, foundEntry.ShoppingListId, foundEntry.ProductId)
- }
- })
-
- t.Run("Find entry by IDs with non-existing entry", func(t *testing.T) {
- nonExistentEntry, err := repo.FindByIds(2, 2)
- if err == nil {
- t.Error("Expected an error for non-existing entry")
- }
-
- if nonExistentEntry != nil {
- t.Errorf("Expected non-existing entry to be nil, but got an entry")
- }
- })
-}
-
-func TestDemoRepository_Update(t *testing.T) {
- repo := NewDemoRepository()
-
- entry := &model.UserShoppingListEntry{
- ShoppingListId: 1,
- ProductId: 1,
- Count: 3,
- Note: "I really want this",
- Checked: true,
- }
-
- _, _ = repo.Create(entry)
-
- t.Run("Update an existing entry", func(t *testing.T) {
- updatedEntry, err := repo.Update(entry)
- if err != nil {
- t.Error(err)
- }
-
- if updatedEntry.Count != entry.Count {
- t.Errorf("Expected updated shopping list entry to have count %d, but got count %d",
- entry.Count, updatedEntry.Count)
- }
-
- if updatedEntry.Note != entry.Note {
- t.Errorf("Expected updated shopping list entry to have note %s, but got note %s",
- entry.Note, updatedEntry.Note)
- }
-
- if updatedEntry.Checked != entry.Checked {
- t.Errorf("Expected updated shopping list entry to have checked value %t, but got note %t",
- entry.Checked, updatedEntry.Checked)
- }
- })
-
- t.Run("Attempt to update a non-existing entry", func(t *testing.T) {
- fakeEntry := &model.UserShoppingListEntry{
- ShoppingListId: 2,
- ProductId: 2,
- Count: 2,
- Note: "This is very important",
- Checked: false,
- }
-
- _, err := repo.Update(fakeEntry)
- if err == nil {
- t.Error("Expected an error for updating a non-existing entry")
- }
- })
-}
-
-func TestDemoRepository_Delete(t *testing.T) {
- repo := NewDemoRepository()
-
- entry := &model.UserShoppingListEntry{
- ShoppingListId: 1,
- ProductId: 1,
- Count: 1,
- Note: "This is very important to me",
- Checked: false,
- }
-
- _, _ = repo.Create(entry)
-
- t.Run("Delete an existing entry", func(t *testing.T) {
- err := repo.Delete(entry)
- if err != nil {
- t.Error(err)
- }
-
- // Ensure the entry is deleted
- _, err = repo.FindByIds(entry.ShoppingListId, entry.ProductId)
- if err == nil {
- t.Error("Expected the entry to be deleted")
- }
- })
-
- t.Run("Attempt to delete a non-existing entry", func(t *testing.T) {
- fakeEntry := &model.UserShoppingListEntry{
- ShoppingListId: 2,
- ProductId: 2,
- Count: 2,
- Note: "This is very important",
- Checked: false,
- }
-
- err := repo.Delete(fakeEntry)
- if err == nil {
- t.Error("Expected an error for deleting a non-existing entry")
- }
- })
-}
From f1afcdac5f3ac7fb40668a6eddaba558bd2be4da Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 16:01:01 +0100
Subject: [PATCH 11/19] docs(kubernetes): add README.md for automatic cluster
via vm
---
kubernetes/kubernetes-cluster/README.md | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 kubernetes/kubernetes-cluster/README.md
diff --git a/kubernetes/kubernetes-cluster/README.md b/kubernetes/kubernetes-cluster/README.md
new file mode 100644
index 00000000..8fb21a5a
--- /dev/null
+++ b/kubernetes/kubernetes-cluster/README.md
@@ -0,0 +1,24 @@
+# Kubernetes Cluster Setup
+
+This README guides you through the process of setting up a Kubernetes cluster with vms using Vagrant and Ansible. Ensure that you have all necessary dependencies installed, including Ansible, Vagrant, and any SSH tools needed.
+
+## Steps to Follow
+
+1. First, change to the `kubernetes-cluster` directory
+2. Move your ssh public key into `kubernetes-cluster/ubuntu/vagrant/id_rsa.pub`
+3. Start the vm deployment with `vagrant up`
+4. Change, to the `ansible_playbooks` folder
+5. Optional: Sometimes you have to specify the ansible config
+ ```shell
+ export ANSIBLE_CONFIG=./ansible.cfg
+ ```
+6. Run `ansible-playbook setup.yaml` to setup cluster
+
+> The first time the manifests are automatically deployed after commissioning, the distribution of the pods may not be uniform.
+
+## Additional Notes
+Ensure that Ansible is properly installed and configured on your system.
+Verify that the SSH key has the correct permissions and is recognized by your system.
+If you encounter any issues, consult the official Kubernetes and Ansible documentation for troubleshooting steps.
+Conclusion
+Following these steps should successfully set up a Kubernetes cluster using Ansible. For more detailed instructions or advanced configurations, refer to the Kubernetes and Ansible official documentation.
\ No newline at end of file
From 4a330b07e4444ef35dae4d1fad0620b239cd3238 Mon Sep 17 00:00:00 2001
From: GitHub Action
Date: Wed, 17 Jan 2024 15:02:03 +0000
Subject: [PATCH 12/19] chore: Updated coverage badge.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 5278f37f..3754b97b 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# Price Whisper
-![Coverage](https://img.shields.io/badge/Coverage-75.1%25-brightgreen)
+![Coverage](https://img.shields.io/badge/Coverage-77.4%25-brightgreen)
![GitHub release (by tag)](https://img.shields.io/github/v/tag/onyxmoon/hsfl-master-ai-cloud-engineering.svg?sort=semver&label=Version&color=4ccc93d)
[![Run tests (lib folder)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-lib-folder.yml/badge.svg)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-lib-folder.yml)
[![Run tests (http proxy service)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-http-proxy-service.yml/badge.svg)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-http-proxy-service.yml)
From 1fe63c70564ba4d48bd949df44c62038e42ed823 Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 16:08:42 +0100
Subject: [PATCH 13/19] build: include integration tests in workflows
---
.github/workflows/run-tests-product-service.yml | 2 +-
.github/workflows/run-tests-shoppinglist-service.yml | 2 +-
.github/workflows/run-tests-user-service.yml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/run-tests-product-service.yml b/.github/workflows/run-tests-product-service.yml
index 7f720070..1a4c84a5 100644
--- a/.github/workflows/run-tests-product-service.yml
+++ b/.github/workflows/run-tests-product-service.yml
@@ -31,7 +31,7 @@ jobs:
- name: Test Go Module
run: |
cd src/product-service
- go test ./...
+ go test ./... --tags=integration
test_exit_code=$? # Capture the exit code of the go test command
if [ $test_exit_code -eq 0 ]; then
echo "Tests passed successfully."
diff --git a/.github/workflows/run-tests-shoppinglist-service.yml b/.github/workflows/run-tests-shoppinglist-service.yml
index b841f283..2d2b019d 100644
--- a/.github/workflows/run-tests-shoppinglist-service.yml
+++ b/.github/workflows/run-tests-shoppinglist-service.yml
@@ -31,7 +31,7 @@ jobs:
- name: Test Go Module
run: |
cd src/shoppinglist-service
- go test ./...
+ go test ./... --tags=integration
test_exit_code=$? # Capture the exit code of the go test command
if [ $test_exit_code -eq 0 ]; then
echo "Tests passed successfully."
diff --git a/.github/workflows/run-tests-user-service.yml b/.github/workflows/run-tests-user-service.yml
index 0d0f66f3..dec542db 100644
--- a/.github/workflows/run-tests-user-service.yml
+++ b/.github/workflows/run-tests-user-service.yml
@@ -31,7 +31,7 @@ jobs:
- name: Test Go Module
run: |
cd src/user-service
- go test ./...
+ go test ./... --tags=integration
test_exit_code=$? # Capture the exit code of the go test command
if [ $test_exit_code -eq 0 ]; then
echo "Tests passed successfully."
From e123372a2c2669e3a036690d6351a282c7ec8adf Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 16:09:10 +0100
Subject: [PATCH 14/19] build(kubernetes): adapt example config for database to
readme's one
---
kubernetes/manifests/price-whisper/database.yaml | 4 ++--
kubernetes/manifests/price-whisper/products.yaml | 4 ++--
kubernetes/manifests/price-whisper/shoppinglists.yaml | 4 ++--
kubernetes/manifests/price-whisper/test-data.yaml | 4 ++--
kubernetes/manifests/price-whisper/users.yaml | 4 ++--
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/kubernetes/manifests/price-whisper/database.yaml b/kubernetes/manifests/price-whisper/database.yaml
index 3b28ddc1..5d55e9b5 100644
--- a/kubernetes/manifests/price-whisper/database.yaml
+++ b/kubernetes/manifests/price-whisper/database.yaml
@@ -9,8 +9,8 @@ stringData:
config.json: |
[
{
- "username": "username",
- "password": "changeMe!",
+ "username": "db-username",
+ "password": "db-pw-changeMe!",
"perms": [ "all" ]
},
{
diff --git a/kubernetes/manifests/price-whisper/products.yaml b/kubernetes/manifests/price-whisper/products.yaml
index 558e233a..c5c03f4c 100644
--- a/kubernetes/manifests/price-whisper/products.yaml
+++ b/kubernetes/manifests/price-whisper/products.yaml
@@ -6,8 +6,8 @@ metadata:
annotations:
author: Gruppe 6
stringData:
- RQLITE_USER: "username"
- RQLITE_PASSWORD: "changeMe!"
+ RQLITE_USER: "db-username"
+ RQLITE_PASSWORD: "db-pw-changeMe!"
---
apiVersion: v1
kind: ConfigMap
diff --git a/kubernetes/manifests/price-whisper/shoppinglists.yaml b/kubernetes/manifests/price-whisper/shoppinglists.yaml
index affc7d9a..89fd5c9a 100644
--- a/kubernetes/manifests/price-whisper/shoppinglists.yaml
+++ b/kubernetes/manifests/price-whisper/shoppinglists.yaml
@@ -6,8 +6,8 @@ metadata:
annotations:
author: Gruppe 6
stringData:
- RQLITE_USER: "username"
- RQLITE_PASSWORD: "changeMe!"
+ RQLITE_USER: "db-username"
+ RQLITE_PASSWORD: "db-pw-changeMe!"
---
apiVersion: v1
kind: ConfigMap
diff --git a/kubernetes/manifests/price-whisper/test-data.yaml b/kubernetes/manifests/price-whisper/test-data.yaml
index 6d9412f4..57a9f07c 100644
--- a/kubernetes/manifests/price-whisper/test-data.yaml
+++ b/kubernetes/manifests/price-whisper/test-data.yaml
@@ -6,8 +6,8 @@ metadata:
annotations:
author: Gruppe 6
stringData:
- RQLITE_USER: "username"
- RQLITE_PASSWORD: "changeMe!"
+ RQLITE_USER: "db-username"
+ RQLITE_PASSWORD: "db-pw-changeMe!"
---
apiVersion: v1
kind: ConfigMap
diff --git a/kubernetes/manifests/price-whisper/users.yaml b/kubernetes/manifests/price-whisper/users.yaml
index 781e2d2b..91265be8 100644
--- a/kubernetes/manifests/price-whisper/users.yaml
+++ b/kubernetes/manifests/price-whisper/users.yaml
@@ -6,8 +6,8 @@ metadata:
annotations:
author: Gruppe 6
stringData:
- RQLITE_USER: "username"
- RQLITE_PASSWORD: "changeMe!"
+ RQLITE_USER: "db-username"
+ RQLITE_PASSWORD: "db-pw-changeMe!"
JWT_PRIVATE_KEY: |
# provide ecdsa private key as string or container local file path
From 95a5735d627c96045612007c6f991b3549a6abef Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 16:15:17 +0100
Subject: [PATCH 15/19] build: streamline workflows
---
.github/workflows/create coverage badge.yml | 2 +-
.../run-tests-http-proxy-service.yml | 8 -------
.github/workflows/run-tests-lib-folder.yml | 24 -------------------
.../run-tests-load-balancer-service.yml | 8 -------
.../workflows/run-tests-product-service.yml | 8 -------
.../run-tests-shoppinglist-service.yml | 8 -------
.github/workflows/run-tests-user-service.yml | 8 -------
.github/workflows/run-tests-web-service.yml | 8 -------
8 files changed, 1 insertion(+), 73 deletions(-)
diff --git a/.github/workflows/create coverage badge.yml b/.github/workflows/create coverage badge.yml
index 403d86ca..28b0640b 100644
--- a/.github/workflows/create coverage badge.yml
+++ b/.github/workflows/create coverage badge.yml
@@ -32,7 +32,7 @@ jobs:
- name: Run Test
run: |
- go test -v hsfl.de/group6/hsfl-master-ai-cloud-engineering/... -covermode=count -coverprofile=coverage.out
+ go test -v hsfl.de/group6/hsfl-master-ai-cloud-engineering/... -covermode=count -coverprofile=coverage.out --tags=integration
go tool cover -func=coverage.out -o=coverage.out
- name: Go Coverage Badge # Pass the `coverage.out` output to this action
diff --git a/.github/workflows/run-tests-http-proxy-service.yml b/.github/workflows/run-tests-http-proxy-service.yml
index 67480fc5..e3720183 100644
--- a/.github/workflows/run-tests-http-proxy-service.yml
+++ b/.github/workflows/run-tests-http-proxy-service.yml
@@ -20,14 +20,6 @@ jobs:
with:
go-version: 1.21
- - name: Install dependencies
- working-directory: ./src/http-proxy-service
- run: go mod tidy
-
- - name: Build
- working-directory: ./src/http-proxy-service
- run: go build -v ./...
-
- name: Test Go Module
run: |
cd src/http-proxy-service
diff --git a/.github/workflows/run-tests-lib-folder.yml b/.github/workflows/run-tests-lib-folder.yml
index e4a36e59..2469302a 100644
--- a/.github/workflows/run-tests-lib-folder.yml
+++ b/.github/workflows/run-tests-lib-folder.yml
@@ -21,30 +21,6 @@ jobs:
with:
go-version: 1.21
- - name: Install dependencies for config
- working-directory: ./lib/config
- run: go mod tidy
-
- - name: Install dependencies for router
- working-directory: ./lib/router
- run: go mod tidy
-
- - name: Install dependencies for rpc
- working-directory: ./lib/rpc
- run: go mod tidy
-
- - name: Build config library
- working-directory: ./lib/config
- run: go build -v ./...
-
- - name: Build router library
- working-directory: ./lib/router
- run: go build -v ./...
-
- - name: Build rpc library
- working-directory: ./lib/rpc
- run: go build -v ./...
-
- name: Test Go config Module
run: |
cd lib/config
diff --git a/.github/workflows/run-tests-load-balancer-service.yml b/.github/workflows/run-tests-load-balancer-service.yml
index 2203fff4..e2abae27 100644
--- a/.github/workflows/run-tests-load-balancer-service.yml
+++ b/.github/workflows/run-tests-load-balancer-service.yml
@@ -20,14 +20,6 @@ jobs:
with:
go-version: 1.21
- - name: Install dependencies
- working-directory: ./src/load-balancer-service
- run: go mod tidy
-
- - name: Build
- working-directory: ./src/load-balancer-service
- run: go build -v ./...
-
- name: Test Go Module
run: |
cd src/load-balancer-service
diff --git a/.github/workflows/run-tests-product-service.yml b/.github/workflows/run-tests-product-service.yml
index 1a4c84a5..55b02d34 100644
--- a/.github/workflows/run-tests-product-service.yml
+++ b/.github/workflows/run-tests-product-service.yml
@@ -20,14 +20,6 @@ jobs:
with:
go-version: 1.21
- - name: Install dependencies
- working-directory: ./src/product-service
- run: go mod tidy
-
- - name: Build
- working-directory: ./src/product-service
- run: go build -v ./...
-
- name: Test Go Module
run: |
cd src/product-service
diff --git a/.github/workflows/run-tests-shoppinglist-service.yml b/.github/workflows/run-tests-shoppinglist-service.yml
index 2d2b019d..f6be0ecd 100644
--- a/.github/workflows/run-tests-shoppinglist-service.yml
+++ b/.github/workflows/run-tests-shoppinglist-service.yml
@@ -20,14 +20,6 @@ jobs:
with:
go-version: 1.21
- - name: Install dependencies
- working-directory: ./src/shoppinglist-service
- run: go mod tidy
-
- - name: Build
- working-directory: ./src/shoppinglist-service
- run: go build -v ./...
-
- name: Test Go Module
run: |
cd src/shoppinglist-service
diff --git a/.github/workflows/run-tests-user-service.yml b/.github/workflows/run-tests-user-service.yml
index dec542db..7a9a36cd 100644
--- a/.github/workflows/run-tests-user-service.yml
+++ b/.github/workflows/run-tests-user-service.yml
@@ -20,14 +20,6 @@ jobs:
with:
go-version: 1.21
- - name: Install dependencies
- working-directory: ./src/user-service
- run: go mod tidy
-
- - name: Build
- working-directory: ./src/user-service
- run: go build -v ./...
-
- name: Test Go Module
run: |
cd src/user-service
diff --git a/.github/workflows/run-tests-web-service.yml b/.github/workflows/run-tests-web-service.yml
index 50aef4d8..75a323ba 100644
--- a/.github/workflows/run-tests-web-service.yml
+++ b/.github/workflows/run-tests-web-service.yml
@@ -20,14 +20,6 @@ jobs:
with:
go-version: 1.21
- - name: Install dependencies
- working-directory: ./src/web-service
- run: go mod tidy
-
- - name: Build
- working-directory: ./src/web-service
- run: go build -v ./...
-
- name: Test Go Module
run: |
cd src/web-service
From bdf9e6559111975d53c3f9f6306938e3422c537c Mon Sep 17 00:00:00 2001
From: GitHub Action
Date: Wed, 17 Jan 2024 15:16:38 +0000
Subject: [PATCH 16/19] chore: Updated coverage badge.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3754b97b..fe1cb61a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# Price Whisper
-![Coverage](https://img.shields.io/badge/Coverage-77.4%25-brightgreen)
+![Coverage](https://img.shields.io/badge/Coverage-83.2%25-brightgreen)
![GitHub release (by tag)](https://img.shields.io/github/v/tag/onyxmoon/hsfl-master-ai-cloud-engineering.svg?sort=semver&label=Version&color=4ccc93d)
[![Run tests (lib folder)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-lib-folder.yml/badge.svg)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-lib-folder.yml)
[![Run tests (http proxy service)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-http-proxy-service.yml/badge.svg)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-http-proxy-service.yml)
From 5f2ba751de304db971ce6311832246ec5f61413c Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 16:28:26 +0100
Subject: [PATCH 17/19] refactor(http-stress-test): remove bulk code
---
utils/http-stress-test/config.yaml | 12 ++++++++----
utils/http-stress-test/metrics/metrics.go | 4 ----
utils/http-stress-test/network/tcpclient.go | 12 ++++++------
utils/http-stress-test/tester/tester.go | 5 +++--
4 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/utils/http-stress-test/config.yaml b/utils/http-stress-test/config.yaml
index 92944752..c9317f14 100644
--- a/utils/http-stress-test/config.yaml
+++ b/utils/http-stress-test/config.yaml
@@ -1,10 +1,14 @@
phases:
- targetrps: 0
timeidx: 0
+ - targetrps: 100
+ timeidx: 15
- targetrps: 500
- timeidx: 10
- - targetrps: 500
- timeidx: 20
+ timeidx: 30
+ - targetrps: 100
+ timeidx: 50
+ - targetrps: 0
+ timeidx: 70
targets:
- - url: https://google.de:443
+ - url: http://onyxmoon.me
users: 10
diff --git a/utils/http-stress-test/metrics/metrics.go b/utils/http-stress-test/metrics/metrics.go
index 4ba1bc7a..5c1e01f0 100644
--- a/utils/http-stress-test/metrics/metrics.go
+++ b/utils/http-stress-test/metrics/metrics.go
@@ -127,7 +127,3 @@ func (m *Metrics) DisplayMetrics(ctx context.Context) {
}
}
}
-
-func formatInt64(i int64) string {
- return fmt.Sprintf("%d", i)
-}
diff --git a/utils/http-stress-test/network/tcpclient.go b/utils/http-stress-test/network/tcpclient.go
index 2b78156a..58de8eac 100644
--- a/utils/http-stress-test/network/tcpclient.go
+++ b/utils/http-stress-test/network/tcpclient.go
@@ -12,10 +12,10 @@ func NewTcpClient() *TCPClient {
return &TCPClient{}
}
-func (c *TCPClient) Send(targetURL string) {
+func (c *TCPClient) Send(targetURL string) error {
parsedURL, err := url.Parse(targetURL)
if err != nil {
- return
+ return err
}
target := parsedURL.Host
path := parsedURL.Path
@@ -25,14 +25,14 @@ func (c *TCPClient) Send(targetURL string) {
conn, err := net.Dial("tcp", target)
if err != nil {
- //fmt.Printf("Error connecting to %s: %v\n", target, err)
- return
+ return err
}
request := fmt.Sprintf("GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", path, target)
_, err = conn.Write([]byte(request))
if err != nil {
- //fmt.Printf("Error sending request to %s: %v\n", target, err)
- return
+ return err
}
+
+ return nil
}
diff --git a/utils/http-stress-test/tester/tester.go b/utils/http-stress-test/tester/tester.go
index b2cc72c1..331f3e56 100644
--- a/utils/http-stress-test/tester/tester.go
+++ b/utils/http-stress-test/tester/tester.go
@@ -120,12 +120,13 @@ func (t *Tester) NewRunUser(wg *sync.WaitGroup, metrics *metrics.Metrics, timePo
}
} else {
fastclient := network.NewTcpClient()
- go fastclient.Send(targetURL)
+ err := fastclient.Send(targetURL)
responseTime := time.Since(startTime)
requestCount++
if metrics != nil {
- metrics.RecordResponse(responseTime, true)
+ success := err == nil
+ metrics.RecordResponse(responseTime, success)
}
}
From 19018c7ca8dba02be6963c1397d76a6f995c6fbc Mon Sep 17 00:00:00 2001
From: Philipp Borucki
Date: Wed, 17 Jan 2024 16:52:36 +0100
Subject: [PATCH 18/19] test(user-service): add test for local auth middleware
---
.../api/http/middleware/auth_test.go | 148 ++++++++++++++++++
1 file changed, 148 insertions(+)
create mode 100644 src/user-service/api/http/middleware/auth_test.go
diff --git a/src/user-service/api/http/middleware/auth_test.go b/src/user-service/api/http/middleware/auth_test.go
new file mode 100644
index 00000000..e02f460a
--- /dev/null
+++ b/src/user-service/api/http/middleware/auth_test.go
@@ -0,0 +1,148 @@
+package middleware
+
+import (
+ "errors"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/auth"
+ authMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/auth/_mock"
+ "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user"
+ userMock "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/_mock"
+ "hsfl.de/group6/hsfl-master-ai-cloud-engineering/user-service/user/model"
+ "net/http/httptest"
+ "testing"
+)
+
+func TestCreateLocalAuthMiddleware(t *testing.T) {
+ mockUserRepository := userMock.NewMockRepository(t)
+ var userRepository user.Repository = mockUserRepository
+ mockTokenGenerator := authMock.NewMockTokenGenerator(t)
+ var tokenGenerator auth.TokenGenerator = mockTokenGenerator
+
+ validToken := "valid-token"
+ invalidToken := "invalid-token"
+
+ expectedUser := &model.User{
+ Id: 1,
+ Email: "ada.lovelace@gmail.com",
+ Name: "Ada Lovelace",
+ Role: 0,
+ }
+
+ middleware := CreateLocalAuthMiddleware(&userRepository, tokenGenerator)
+
+ t.Run("ValidToken", func(t *testing.T) {
+ mockUserRepository.EXPECT().FindById(expectedUser.Id).Return(expectedUser, nil).Once()
+
+ claims := map[string]interface{}{"id": float64(expectedUser.Id), "email": expectedUser.Email, "name": expectedUser.Name, "role": float64(expectedUser.Role)}
+ mockTokenGenerator.EXPECT().VerifyToken(mock.Anything).Return(claims, nil).Once()
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "GET",
+ "/restricted/url", nil)
+ request.Header.Set("Authorization", "Bearer "+validToken)
+
+ response := middleware(writer, request)
+
+ assert.NotNil(t, response)
+ assert.Equal(t, expectedUser.Id, response.Context().Value("auth_userId"))
+ assert.Equal(t, expectedUser.Email, response.Context().Value("auth_userEmail"))
+ assert.Equal(t, expectedUser.Name, response.Context().Value("auth_userName"))
+ assert.Equal(t, expectedUser.Role, response.Context().Value("auth_userRole"))
+ })
+
+ t.Run("InvalidToken", func(t *testing.T) {
+ mockTokenGenerator.EXPECT().VerifyToken(mock.Anything).Return(nil, errors.New("invalid token")).Once()
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "GET",
+ "/restricted/url", nil)
+ request.Header.Set("Authorization", "Bearer "+invalidToken)
+
+ response := middleware(writer, request)
+
+ assert.NotNil(t, response)
+ assert.Nil(t, response.Context().Value("auth_userId"))
+ assert.Nil(t, response.Context().Value("auth_userEmail"))
+ assert.Nil(t, response.Context().Value("auth_userName"))
+ assert.Nil(t, response.Context().Value("auth_userRole"))
+ })
+
+ t.Run("InvalidClaims", func(t *testing.T) {
+ claims := map[string]interface{}{}
+ mockTokenGenerator.EXPECT().VerifyToken(mock.Anything).Return(claims, nil).Once()
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "GET",
+ "/restricted/url", nil)
+ request.Header.Set("Authorization", "Bearer "+validToken)
+ response := middleware(writer, request)
+
+ assert.NotNil(t, response)
+ assert.Nil(t, response.Context().Value("auth_userId"))
+ assert.Nil(t, response.Context().Value("auth_userEmail"))
+ assert.Nil(t, response.Context().Value("auth_userName"))
+ assert.Nil(t, response.Context().Value("auth_userRole"))
+ })
+
+ t.Run("NonExistentUser", func(t *testing.T) {
+ nonExistingUserId := uint64(999)
+ claims := map[string]interface{}{"id": float64(nonExistingUserId)}
+ mockTokenGenerator.EXPECT().VerifyToken(mock.Anything).Return(claims, nil).Once()
+ mockUserRepository.EXPECT().FindById(nonExistingUserId).Return(nil, errors.New(user.ErrorUserNotFound)).Once()
+
+ writer := httptest.NewRecorder()
+ request := httptest.NewRequest(
+ "GET",
+ "/restricted/url", nil)
+ request.Header.Set("Authorization", "Bearer "+validToken)
+ response := middleware(writer, request)
+
+ assert.NotNil(t, response)
+ assert.Nil(t, response.Context().Value("auth_userId"))
+ assert.Nil(t, response.Context().Value("auth_userEmail"))
+ assert.Nil(t, response.Context().Value("auth_userName"))
+ assert.Nil(t, response.Context().Value("auth_userRole"))
+ })
+}
+
+func Test_getToken(t *testing.T) {
+ tests := []struct {
+ name string
+ authHeader string
+ expectedToken string
+ expectError bool
+ }{
+ {"No Authorization Header", "", "", true},
+ {"Invalid Format", "InvalidToken", "", true},
+ {"Only Bearer", "Bearer", "", true},
+ {"Valid Token", "Bearer mytesttoken", "mytesttoken", false},
+ {"Extra Parts", "Bearer mytesttoken extra", "", true},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ req := httptest.NewRequest("GET", "http://example.com", nil)
+ if tc.authHeader != "" {
+ req.Header.Add("Authorization", tc.authHeader)
+ }
+
+ token, err := getToken(req)
+
+ if tc.expectError {
+ if err == nil {
+ t.Errorf("Expected an error but didn't get one")
+ }
+ } else {
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if token != tc.expectedToken {
+ t.Errorf("Expected token %v, got %v", tc.expectedToken, token)
+ }
+ }
+ })
+ }
+}
From a2617fa0d7bd37efc12e513cb133ad228acc1996 Mon Sep 17 00:00:00 2001
From: GitHub Action
Date: Wed, 17 Jan 2024 15:53:53 +0000
Subject: [PATCH 19/19] chore: Updated coverage badge.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index fe1cb61a..7d92190c 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# Price Whisper
-![Coverage](https://img.shields.io/badge/Coverage-83.2%25-brightgreen)
+![Coverage](https://img.shields.io/badge/Coverage-83.3%25-brightgreen)
![GitHub release (by tag)](https://img.shields.io/github/v/tag/onyxmoon/hsfl-master-ai-cloud-engineering.svg?sort=semver&label=Version&color=4ccc93d)
[![Run tests (lib folder)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-lib-folder.yml/badge.svg)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-lib-folder.yml)
[![Run tests (http proxy service)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-http-proxy-service.yml/badge.svg)](https://github.com/Onyxmoon/hsfl-master-ai-cloud-engineering/actions/workflows/run-tests-http-proxy-service.yml)