From 5e0b96e6673839c0ac7fca1ff889f8ff2fb6f731 Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Wed, 23 Aug 2023 10:16:01 -0300 Subject: [PATCH 01/15] =?UTF-8?q?=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 42 ++++- README.md | 57 +++++- backend/integration-test/integration_test.go | 39 +++-- backend/integration-test/users_test.go | 163 ++++++++++++++++++ backend/internal/controller/http/v1/users.go | 26 ++- backend/internal/usecase/mocks/mocks.go | 84 ++++++++- .../internal/usecase/repo/user_postgres.go | 4 +- backend/internal/usecase/users.go | 13 +- dev.docker-compose.yml | 92 +++++----- .../init-scripts/create-backend-db.sql | 1 + integration-test.sh | 3 - 11 files changed, 422 insertions(+), 102 deletions(-) create mode 100644 backend/integration-test/users_test.go create mode 100644 infrastructure/init-scripts/create-backend-db.sql delete mode 100644 integration-test.sh diff --git a/Makefile b/Makefile index fbe40e02..7fab752a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,41 @@ +# Build and up targets for all profiles in development environment +dev-all: dev-all dev-up-all -integration-test: ### Run docker-compose with integration test - bash ./integration-test.sh +dev-up-all: + docker-compose -f dev.docker-compose.yml --profile all build + docker-compose -f dev.docker-compose.yml --profile all up -d -.PHONY: integration-test \ No newline at end of file +dev-up-starlabs: + docker-compose -f dev.docker-compose.yml --profile starlabs build + docker-compose -f dev.docker-compose.yml --profile starlabs up -d + +dev-up-tests: + @echo "Building containers for tests..." + @docker-compose -f dev.docker-compose.yml --profile tests build > /dev/null 2>&1 || (echo "Container build failed!" && exit 1) + @echo "Starting containers for tests..." + @docker-compose -f dev.docker-compose.yml --profile tests up -d > /dev/null 2>&1 || (echo "Starting containers failed!" && exit 1) + @echo "Starting the integration test..." + @docker-compose -f dev.docker-compose.yml logs -f integration + @echo "Removing containers..." + @docker-compose -f dev.docker-compose.yml down --remove-orphans > /dev/null 2>&1 + + +dev-up-kafka: + docker-compose -f dev.docker-compose.yml --profile kafka build + docker-compose -f dev.docker-compose.yml --profile kafka up -d + +dev-up-backend: + docker-compose -f dev.docker-compose.yml --profile backend build + docker-compose -f dev.docker-compose.yml --profile backend up -d + +dev-up-frontend: + docker-compose -f dev.docker-compose.yml --profile frontend build + docker-compose -f dev.docker-compose.yml --profile frontend up -d + +dev-stop: + docker-compose -f dev.docker-compose.yml down --remove-orphans + +dev-destroy: + docker-compose -f dev.docker-compose.yml down -v --remove-orphans + +.PHONY: dev-all dev-up-all dev-up-starlabs dev-up-tests dev-up-kafka dev-up-backend dev-up-frontend dev-stop dev-destroy diff --git a/README.md b/README.md index 55268beb..327cf6ed 100644 --- a/README.md +++ b/README.md @@ -1 +1,56 @@ -# token-factory-v2 \ No newline at end of file +# Cheesecake Stellar Token Factory - V2 + +Cheesecake Stellar Token Factory - V2 is a multifaceted system, designed to enable the creation, management, and trading of Stellar-based tokens. The version 2 architecture leverages a blend of cutting-edge technologies to offer a scalable, secure, and user-friendly solution. + +## Components + +- **Postgres:** Utilized as the primary relational database for all structured data management needs. +- **Apache Kafka:** Deployed as a distributed event store to enable real-time data processing and insights. +- **UI for Apache Kafka:** An intuitive open-source web UI that provides detailed monitoring and management functions for Apache Kafka clusters. +- **Frontend:** A sleek and interactive React Web application offering a front-facing user interface. +- **Backend:** A Golang backend handling the business logic, encompassing everything from API endpoints to core Stellar token functionalities. +- **Starlabs:** A specialized Stellar service, integrated as a git submodule and written in Go, to further enrich the platform's capabilities. +- **KMS:** The CKL Key Management Service (KMS), also a git submodule in Go, safeguarding the security of cryptographic keys and other sensitive data. + +### Topics + +Further details on the various functionalities, system requirements, and guides to set up and use the Cheesecake Stellar Token Factory - V2 can be found in the following sections: + +- [Development Environment with Makefile](#development-environment-with-makefile) + - [Development Environment](#development-environment) + +## Development Environment with Makefile + +The Makefile included in the project simplifies the development environment management using Docker Compose, providing a collection of easy-to-use commands tailored for the Cheesecake Stellar Token Factory - V2 development workflow. + +### Build and Start All Services + +```bash +make dev-up-all +``` + +### Build and Start Individual Services + +You can build and start individual services by appending the service name to `make dev-up-starlabs:`. For example: + +- Starlabs: `make dev-up-starlabs` +- Tests: `make dev-up-tests` +- Kafka: `make dev-up-kafka` +- Backend: `make dev-up-backend` +- Frontend: `make dev-up-frontend` + +### Stop All Services (Development Backend) + +```bash +make dev-stop +``` + +### Stop and Remove Volumes (Development Backend) + +**Warning**: This command will delete all data in your development environment. + +```bash +make dev-destroy +``` + +These commands streamline the process of building, starting, and managing the development environment, allowing developers to concentrate on coding. Customize the `dev.docker-compose.yml` file to adapt the configuration of the Docker containers to your specific requirements. diff --git a/backend/integration-test/integration_test.go b/backend/integration-test/integration_test.go index 691dc5d2..d4418e92 100644 --- a/backend/integration-test/integration_test.go +++ b/backend/integration-test/integration_test.go @@ -1,31 +1,29 @@ package integration_test import ( + "fmt" "log" "net/http" "os" "testing" "time" - - . "github.com/Eun/go-hit" ) +type errrorResponse struct { + Message string `json:"message"` + Error string `json:"error,omitempty"` +} + const ( - // Attempts connection - host = "backend:8080" + host = "localhost:8080" healthPath = "http://" + host + "/healthz" attempts = 20 // HTTP REST basePath = "http://" + host + "/v1" - - // Kafka - kafkaHost = "kafka:9092" - consumerTopic = "consumer_topic" - producerTopic = "producer_topic" ) -func TestMain(t *testing.M) { +func TestMain(m *testing.M) { err := healthCheck(attempts) if err != nil { log.Fatalf("Integration tests: host %s is not available: %s", host, err) @@ -33,25 +31,34 @@ func TestMain(t *testing.M) { log.Printf("Integration tests: host %s is available", host) - code := t.Run() + code := m.Run() os.Exit(code) } func healthCheck(attempts int) error { - var err error + client := &http.Client{} for attempts > 0 { - err = Do(Get(healthPath), Expect().Status().Equal(http.StatusOK)) - if err == nil { + req, err := http.NewRequest(http.MethodGet, healthPath, nil) + if err != nil { + return err + } + + resp, err := client.Do(req) + if err == nil && resp.StatusCode == http.StatusOK { return nil } - log.Printf("Integration tests: url %s is not available, attempts left: %d", healthPath, attempts) + if err != nil { + log.Printf("Integration tests: error connecting to %s: %s", healthPath, err) + } else { + log.Printf("Integration tests: url %s returned status %d, attempts left: %d", healthPath, resp.StatusCode, attempts) + } time.Sleep(time.Second) attempts-- } - return err + return fmt.Errorf("Integration tests: failed to connect to %s after %d attempts", healthPath, attempts) } diff --git a/backend/integration-test/users_test.go b/backend/integration-test/users_test.go new file mode 100644 index 00000000..d3681da2 --- /dev/null +++ b/backend/integration-test/users_test.go @@ -0,0 +1,163 @@ +package integration_test + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/CheesecakeLabs/token-factory-v2/backend/internal/entity" + "github.com/stretchr/testify/assert" +) + +var _user = entity.User{ + Name: "User Test", + Password: "123456", + RoleId: 1, + Email: "testuser@test.com", +} + +func TestCreateUserFailedUserAlreadyInDatabase(t *testing.T) { + createUserPath := basePath + "/users/create" + + requestBodyBytes, err := json.Marshal(_user) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createUserPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) + + // Parsing the response into an expected structure + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verifying the response content + assert.Equal(t, "database problems", response.Message) + assert.Contains(t, response.Error, "duplicate key value violates unique constraint \"useraccount_email_key\"") +} + +func TestCreateUserFailedPasswordIsEmpty(t *testing.T) { + createUserPath := basePath + "/users/create" + + // Create a request body with an empty password + requestBody := entity.User{ + Name: "User Test", + Password: "", + RoleId: 1, + Email: "", + } + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createUserPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // Adjust the status code as per your application's behavior + + // Parsing the response into an expected structure + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verifying the response content + assert.Equal(t, "password is required", response.Message) +} + +func TestUserLoginFailed(t *testing.T) { + // URL for logging in a user + loginUserPath := basePath + "/users/login" + + // Request body + requestBody := entity.User{ + Email: "test@example.com", + Password: "testpassword", + } + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, loginUserPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + assert.Equal(t, "database problems", response.Message) + assert.Equal(t, "email or password incorrect", response.Error) +} + +func TestUserLoginSuccess(t *testing.T) { + // URL for logging in a user + loginUserPath := basePath + "/users/login" + + // Test user credentials + email := _user.Email + password := _user.Password + + // Request body + requestBody := map[string]string{ + "email": email, + "password": password, + } + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, loginUserPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.User + err = json.NewDecoder(resp.Body).Decode(&response) + fmt.Println("response", resp.Body) + fmt.Println("err", err) + assert.NoError(t, err) + assert.Equal(t, _user.Name, response.Name) + assert.Equal(t, _user.Email, response.Email) + assert.Equal(t, _user.RoleId, response.RoleId) + assert.NotEmpty(t, response.Token) +} diff --git a/backend/internal/controller/http/v1/users.go b/backend/internal/controller/http/v1/users.go index 9e99aaca..d977506e 100644 --- a/backend/internal/controller/http/v1/users.go +++ b/backend/internal/controller/http/v1/users.go @@ -69,25 +69,25 @@ func (r *usersRoutes) detail(c *gin.Context) { func (r *usersRoutes) createUser(c *gin.Context) { var user entity.User if err := c.ShouldBindJSON(&user); err != nil { - // r.l.Error(err, "http - v1 - create") - // errorResponse(c, http.StatusBadRequest, "invalid request body") - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - c.Abort() + errorResponse(c, http.StatusBadRequest, "invalid request body", err) + return + } + + if user.Password == "" { + errorResponse(c, http.StatusBadRequest, "password is required", nil) return } tokenString, err := GenerateJWT(user, r.a.ValidateToken()) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - c.Abort() + errorResponse(c, http.StatusBadRequest, "error to generate the JWT", err) return } user.Token = tokenString - if err := r.t.CreateUser(user); err != nil { - // r.l.Error(err, "http - v1 - create") - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - c.Abort() + err = r.t.CreateUser(user) + if err != nil { + errorResponse(c, http.StatusInternalServerError, "database problems", err) return } @@ -106,17 +106,13 @@ func (r *usersRoutes) createUser(c *gin.Context) { func (r *usersRoutes) autentication(c *gin.Context) { var user entity.User if err := c.ShouldBindJSON(&user); err != nil { - // r.l.Error(err, "http - v1 - create") errorResponse(c, http.StatusBadRequest, "invalid request body", err) - fmt.Println(err) return } // check if user exists and password is correct user, err := r.t.Autentication(user.Email, user.Password) if err != nil { - // r.l.Error(err, "http - v1 - create") - errorResponse(c, http.StatusInternalServerError, "database problems", err) - fmt.Println(err) + errorResponse(c, http.StatusUnauthorized, "database problems", err) return } tokenString, err := GenerateJWT(user, r.a.ValidateToken()) diff --git a/backend/internal/usecase/mocks/mocks.go b/backend/internal/usecase/mocks/mocks.go index d477e7b8..18ba3973 100644 --- a/backend/internal/usecase/mocks/mocks.go +++ b/backend/internal/usecase/mocks/mocks.go @@ -641,6 +641,21 @@ func (mr *MockVaultRepoInterfaceMockRecorder) CreateVault(arg0 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVault", reflect.TypeOf((*MockVaultRepoInterface)(nil).CreateVault), arg0) } +// DeleteVault mocks base method. +func (m *MockVaultRepoInterface) DeleteVault(arg0 entity.Vault) (entity.Vault, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteVault", arg0) + ret0, _ := ret[0].(entity.Vault) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteVault indicates an expected call of DeleteVault. +func (mr *MockVaultRepoInterfaceMockRecorder) DeleteVault(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVault", reflect.TypeOf((*MockVaultRepoInterface)(nil).DeleteVault), arg0) +} + // GetVaultById mocks base method. func (m *MockVaultRepoInterface) GetVaultById(id int) (entity.Vault, error) { m.ctrl.T.Helper() @@ -683,20 +698,73 @@ func (m *MockVaultRepoInterface) UpdateVault(arg0 entity.Vault) (entity.Vault, e // UpdateVault indicates an expected call of UpdateVault. func (mr *MockVaultRepoInterfaceMockRecorder) UpdateVault(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVault", reflect.TypeOf((*MockVaultRepoInterface)(nil).DeleteVault), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVault", reflect.TypeOf((*MockVaultRepoInterface)(nil).UpdateVault), arg0) } -// DeleteVault mocks base method. -func (m *MockVaultRepoInterface) DeleteVault(arg0 entity.Vault) (entity.Vault, error) { +// MockContractRepoInterface is a mock of ContractRepoInterface interface. +type MockContractRepoInterface struct { + ctrl *gomock.Controller + recorder *MockContractRepoInterfaceMockRecorder +} + +// MockContractRepoInterfaceMockRecorder is the mock recorder for MockContractRepoInterface. +type MockContractRepoInterfaceMockRecorder struct { + mock *MockContractRepoInterface +} + +// NewMockContractRepoInterface creates a new mock instance. +func NewMockContractRepoInterface(ctrl *gomock.Controller) *MockContractRepoInterface { + mock := &MockContractRepoInterface{ctrl: ctrl} + mock.recorder = &MockContractRepoInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockContractRepoInterface) EXPECT() *MockContractRepoInterfaceMockRecorder { + return m.recorder +} + +// CreateContract mocks base method. +func (m *MockContractRepoInterface) CreateContract(arg0 entity.Contract) (entity.Contract, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteVault", arg0) - ret0, _ := ret[0].(entity.Vault) + ret := m.ctrl.Call(m, "CreateContract", arg0) + ret0, _ := ret[0].(entity.Contract) ret1, _ := ret[1].(error) return ret0, ret1 } -// DeleteVault indicates an expected call of DeleteVault. -func (mr *MockVaultRepoInterfaceMockRecorder) DeleteVault(arg0 interface{}) *gomock.Call { +// CreateContract indicates an expected call of CreateContract. +func (mr *MockContractRepoInterfaceMockRecorder) CreateContract(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVault", reflect.TypeOf((*MockVaultRepoInterface)(nil).DeleteVault), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateContract", reflect.TypeOf((*MockContractRepoInterface)(nil).CreateContract), arg0) +} + +// GetContractById mocks base method. +func (m *MockContractRepoInterface) GetContractById(id string) (entity.Contract, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetContractById", id) + ret0, _ := ret[0].(entity.Contract) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetContractById indicates an expected call of GetContractById. +func (mr *MockContractRepoInterfaceMockRecorder) GetContractById(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContractById", reflect.TypeOf((*MockContractRepoInterface)(nil).GetContractById), id) +} + +// GetContracts mocks base method. +func (m *MockContractRepoInterface) GetContracts() ([]entity.Contract, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetContracts") + ret0, _ := ret[0].([]entity.Contract) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetContracts indicates an expected call of GetContracts. +func (mr *MockContractRepoInterfaceMockRecorder) GetContracts() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContracts", reflect.TypeOf((*MockContractRepoInterface)(nil).GetContracts)) } diff --git a/backend/internal/usecase/repo/user_postgres.go b/backend/internal/usecase/repo/user_postgres.go index b3379786..3478219c 100644 --- a/backend/internal/usecase/repo/user_postgres.go +++ b/backend/internal/usecase/repo/user_postgres.go @@ -44,8 +44,7 @@ func (r UserRepo) CreateUser(user entity.User) error { stmt := `INSERT INTO UserAccount (name, password, role_id, email, token) VALUES ($1, $2, $3, $4, $5)` _, err := r.Db.Exec(stmt, user.Name, user.Password, user.RoleId, user.Email, user.Token) if err != nil { - panic(err) - // return fmt.Errorf("UserRepo - CreateUser - db.Exec: %w", err) + return fmt.Errorf("UserRepo - CreateUser - db.Exec: %w", err) } fmt.Println("User created successfully") return nil @@ -99,7 +98,6 @@ func (r UserRepo) GetAllUsers() ([]entity.UserResponse, error) { ORDER BY u.name ASC` rows, err := r.Db.Query(stmt) - if err != nil { return nil, fmt.Errorf("UserRepo - GetAllUsers - db.Query: %w", err) } diff --git a/backend/internal/usecase/users.go b/backend/internal/usecase/users.go index 25c6a52c..8d1fcf75 100644 --- a/backend/internal/usecase/users.go +++ b/backend/internal/usecase/users.go @@ -34,25 +34,26 @@ func (uc *UserUseCase) Detail(email string) (entity.User, error) { // CreateUser - creating user. func (uc *UserUseCase) CreateUser(user entity.User) error { var err error - if user.Password == "" { - return fmt.Errorf("password is empty") - } user.Password, err = uc.HashPassword(user.Password) if err != nil { return err } - uc.repo.CreateUser(user) + err = uc.repo.CreateUser(user) + if err != nil { + return err + } return nil } func (uc *UserUseCase) Autentication(email string, password string) (user entity.User, err error) { user, err = uc.repo.GetUser(email) if err != nil { - return user, err + fmt.Println("Oi") + return user, fmt.Errorf("email or password incorrect") } err = uc.CheckPassword(user, password) if err != nil { - return + return entity.User{}, fmt.Errorf("email or password incorrect") } return } diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 39254746..4172d3ef 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -1,17 +1,18 @@ -version: "3.7" +version: "3.8" services: postgres: + image: postgres:latest container_name: postgres - profiles: ["all", "starlabs", "tests", "kafka"] - image: postgres environment: + POSTGRES_HOST: ${POSTGRES_HOST:-postgres} + POSTGRES_PORT: ${POSTGRES_PORT:-5432} POSTGRES_USER: ${POSTGRES_USER:-postgres} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} POSTGRES_DB: ${POSTGRES_DB:-postgres} - PGDATA: /data/postgres volumes: - postgres:/data/postgres + - ./infrastructure/init-scripts:/docker-entrypoint-initdb.d ports: - "5432:5432" restart: unless-stopped @@ -21,9 +22,9 @@ services: "CMD-SHELL", "sh -c 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'", ] - interval: 10s - timeout: 3s - retries: 3 + interval: 5s + timeout: 5s + retries: 5 tfv2_frontend: profiles: ["all", "frontend"] @@ -45,7 +46,7 @@ services: backend: platform: linux/amd64 - profiles: ["all", "tests"] + profiles: ["all", "tests", "backend"] container_name: token-factory-v2 build: context: ./backend @@ -55,13 +56,30 @@ services: - "8080:8080" volumes: - ./:/go/src/CheesecakeLabs/token-factory-v2/backend + environment: + PG_HOST: ${PG_HOST:-postgres} + PG_PORT: ${PG_PORT:-5432} + PG_USER: ${PG_USER:-postgres} + PG_PASSWORD: ${PG_PASSWORD:-password} + PG_DB_NAME: ${PG_DATABASE:-backend} + KAFKA_CLIENT_GROUP_ID: ${KAFKA_CLIENT_GROUP_ID:-starlabs} + KAFKA_CLIENT_BROKERS: ${KAFKA_CLIENT_BROKERS:-kafka1:19092} + KAFKA_CREATE_KP_PRODUCER_TOPIC: ${KAFKA_CREATE_KP_CONSUMER_TOPICS:-generateKeypair} + KAFKA_CREATE_KP_CONSUMER_TOPICS: ${KAFKA_CREATE_KP_PRODUCER_TOPIC:-generatedKeypairs} + KAFKA_ENVELOPE_PRODUCER_TOPIC: ${KAFKA_ENVELOPE_PRODUCER_TOPIC:-createEnvelope} + KAFKA_ENVELOPE_CONSUMER_TOPICS: ${KAFKA_ENVELOPE_CONSUMER_TOPICS:-submitResponse} + KAFKA_HORIZON_PRODUCER_TOPIC: ${KAFKA_HORIZON_PRODUCER_TOPIC:-horizonRequest} + KAFKA_HORIZON_CONSUMER_TOPICS: ${KAFKA_HORIZON_CONSUMER_TOPICS:-horizonResponse} + HTTP_PORT: ${HTTP_PORT:-8080} + HTTP_FRONTEND_URL: ${HTTP_FRONTEND_URL:-http://localhost:3000} + JTW_SECRET_KEY: ${JTW_SECRET_KEY:-keyosid} depends_on: postgres: condition: service_healthy stellar-kms: platform: linux/amd64 - profiles: ["all", "starlabs", "tests"] + profiles: ["all", "starlabs", "tests", "backend"] container_name: kms build: context: ./stellar-kms @@ -89,7 +107,7 @@ services: condition: service_healthy zoo1: - profiles: ["all", "starlabs", "tests", "kafka"] + profiles: ["all", "starlabs", "tests", "kafka", "backend"] image: confluentinc/cp-zookeeper:7.3.2 hostname: zoo1 container_name: zoo1 @@ -101,7 +119,7 @@ services: ZOOKEEPER_SERVERS: zoo1:2888:3888 kafka1: - profiles: ["all", "starlabs", "tests", "kafka"] + profiles: ["all", "starlabs", "tests", "kafka", "backend"] image: confluentinc/cp-kafka:7.3.2 hostname: kafka1 container_name: kafka1 @@ -132,43 +150,21 @@ services: timeout: 10s retries: 20 - #Kafka - Schema Registry# - schema_registry: - profiles: ["all", "kafka", "starlabs", "kms"] - image: confluentinc/cp-schema-registry:7.1.2 - container_name: schema_registry - ports: - - "8085:8085" - environment: - SCHEMA_REGISTRY_LISTENERS: http://0.0.0.0:8085 - SCHEMA_REGISTRY_HOST_NAME: schema_registry - SCHEMA_REGISTRY_KAFKASTORE_CONNECTION_URL: zoo1:2181 - SCHEMA_REGISTRY_ACCESS_CONTROL_ALLOW_ORIGIN: "*" - SCHEMA_REGISTRY_ACCESS_CONTROL_ALLOW_METHODS: "GET,POST,PUT,OPTIONS" - SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: kafka1:19092 - healthcheck: - start_period: 10s - interval: 10s - retries: 20 - test: curl --user superUser:superUser --fail --silent --insecure http://schema_registry:8085/subjects --output /dev/null || exit 1 - depends_on: - kafka1: - condition: service_healthy - #Kafka - Init# kafka_init: - profiles: ["all", "kafka", "starlabs", "kms"] + profiles: ["all", "kafka", "starlabs", "kms", "backend", "tests"] image: confluentinc/cp-kafka:7.1.2 container_name: kafka_init entrypoint: ["/bin/sh", "-c"] environment: - KAFKA_CREATE_KP_PRODUCER_TOPIC: ${KAFKA_CREATE_KP_PRODUCER_TOPIC:-generateKeypair} - KAFKA_CREATE_KP_CONSUMER_TOPICS: ${KAFKA_CREATE_KP_CONSUMER_TOPICS:-generatedKeypairs} - KAFKA_ENVELOPE_PRODUCER_TOPIC: ${KAFKA_ENVELOPE_PRODUCER_TOPIC:-createEnvelope} - KAFKA_ENVELOPE_CONSUMER_TOPICS: ${KAFKA_ENVELOPE_CONSUMER_TOPICS:-submitResponse} - KAFKA_HORIZON_PRODUCER_TOPIC: ${KAFKA_HORIZON_PRODUCER_TOPIC:-horizonRequest} - KAFKA_HORIZON_CONSUMER_TOPICS: ${KAFKA_HORIZON_CONSUMER_TOPICS:-horizonResponse} - + KAFKA_CREATE_KP_PRODUCER_TOPIC: ${KAFKA_CREATE_KP_PRODUCER_TOPIC:-generatedKeypairs} + KAFKA_CREATE_KP_CONSUMER_TOPICS: ${KAFKA_CREATE_KP_CONSUMER_TOPICS:-generateKeypair} + KAFKA_ENVELOPE_PRODUCER_TOPIC: ${KAFKA_ENVELOPE_PRODUCER_TOPIC:-signEnvelope} + KAFKA_ENVELOPE_CONSUMER_TOPICS: ${KAFKA_ENVELOPE_CONSUMER_TOPICS:-createEnvelope} + KAFKA_HORIZON_PRODUCER_TOPIC: ${KAFKA_HORIZON_PRODUCER_TOPIC:-horizonResponse} + KAFKA_HORIZON_CONSUMER_TOPICS: ${KAFKA_HORIZON_CONSUMER_TOPICS:-horizonRequest} + KAFKA_SUBMIT_CONSUMER_TOPICS: ${KAFKA_SUBMIT_CONSUMER_TOPICS:-signedEnvelopes} + KAFKA_SUBMIT_PRODUCER_TOPIC: ${KAFKA_SUBMIT_PRODUCER_TOPIC:-submitResponse} depends_on: kafka1: condition: service_healthy @@ -182,12 +178,14 @@ services: kafka-topics --create --topic $$KAFKA_ENVELOPE_CONSUMER_TOPICS --partitions 1 --replication-factor 1 --if-not-exists --bootstrap-server kafka1:19092 kafka-topics --create --topic $$KAFKA_HORIZON_PRODUCER_TOPIC --partitions 1 --replication-factor 1 --if-not-exists --bootstrap-server kafka1:19092 kafka-topics --create --topic $$KAFKA_HORIZON_CONSUMER_TOPICS --partitions 1 --replication-factor 1 --if-not-exists --bootstrap-server kafka1:19092 - kafka1 cub kafka-ready -b kafka1:129092 1 20 + kafka-topics --create --topic $$KAFKA_SUBMIT_CONSUMER_TOPICS --partitions 1 --replication-factor 1 --if-not-exists --bootstrap-server kafka1:19092 + kafka-topics --create --topic $$KAFKA_SUBMIT_PRODUCER_TOPIC --partitions 1 --replication-factor 1 --if-not-exists --bootstrap-server kafka1:19092 + cub kafka-ready -b kafka1:19092 1 20 " # Kafka - UI# kafka_ui: - profiles: ["all", "kafka", "starlabs", "kms"] + profiles: ["all", "kafka", "starlabs", "kms", "backend"] image: provectuslabs/kafka-ui container_name: kafka_ui ports: @@ -207,7 +205,7 @@ services: #Starlabs Service# starlabs: platform: linux/amd64 - profiles: ["all", "starlabs", "tests"] + profiles: ["all", "starlabs", "tests", "backend"] container_name: starlabs build: context: ./starlabs @@ -230,11 +228,11 @@ services: integration: profiles: ["tests"] + container_name: integration platform: linux/amd64 build: context: ./backend dockerfile: integration-test/Dockerfile - container_name: integration image: integration depends_on: - backend @@ -243,4 +241,4 @@ services: volumes: postgres: - node_modules_tfv2_frontend: \ No newline at end of file + node_modules_tfv2_frontend: diff --git a/infrastructure/init-scripts/create-backend-db.sql b/infrastructure/init-scripts/create-backend-db.sql new file mode 100644 index 00000000..2437d9ce --- /dev/null +++ b/infrastructure/init-scripts/create-backend-db.sql @@ -0,0 +1 @@ +CREATE DATABASE backend; \ No newline at end of file diff --git a/integration-test.sh b/integration-test.sh deleted file mode 100644 index cb966c04..00000000 --- a/integration-test.sh +++ /dev/null @@ -1,3 +0,0 @@ -echo -e "\033[31;7m Start the Backend and start the integration test... \e[0m"; -docker-compose -f dev.docker-compose.yml --profile tests build -docker-compose -f dev.docker-compose.yml --profile tests up \ No newline at end of file From 94f93f29dd3c71cc91028da95572bd82ebe1bf4f Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Wed, 23 Aug 2023 14:07:15 -0300 Subject: [PATCH 02/15] =?UTF-8?q?=F0=9F=93=9D=20=20Improve=20the=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 327cf6ed..7f088f3d 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,18 @@ Cheesecake Stellar Token Factory - V2 is a multifaceted system, designed to enab - **Starlabs:** A specialized Stellar service, integrated as a git submodule and written in Go, to further enrich the platform's capabilities. - **KMS:** The CKL Key Management Service (KMS), also a git submodule in Go, safeguarding the security of cryptographic keys and other sensitive data. +**API Documentation** + +The backend is equipped with Swagger documentation to provide an interactive interface for understanding and testing the API endpoints. + +### Accessing the Swagger UI: + +You can access the Swagger interface by navigating to: + +[http://localhost:8080/v1/swagger/index.html](http://localhost:8080/v1/swagger/index.html) + +Here, you'll find detailed information about each API route, including required parameters, request/response formats, and the ability to test the endpoints directly from the browser. + ### Topics Further details on the various functionalities, system requirements, and guides to set up and use the Cheesecake Stellar Token Factory - V2 can be found in the following sections: From dede52d26499548ce8f6b0467b8846b76d21c394 Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Thu, 24 Aug 2023 09:26:03 -0300 Subject: [PATCH 03/15] =?UTF-8?q?=F0=9F=90=9B=20Fix=20create=20a=20new=20d?= =?UTF-8?q?atabase=20every=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infrastructure/init-scripts/create-backend-db.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infrastructure/init-scripts/create-backend-db.sql b/infrastructure/init-scripts/create-backend-db.sql index 2437d9ce..16148d8d 100644 --- a/infrastructure/init-scripts/create-backend-db.sql +++ b/infrastructure/init-scripts/create-backend-db.sql @@ -1 +1,2 @@ -CREATE DATABASE backend; \ No newline at end of file +SELECT 'CREATE DATABASE backend' +WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'backend')\gexec \ No newline at end of file From fdd6955e0696e27d9a4a05eaa327343d6650308f Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Thu, 24 Aug 2023 09:43:22 -0300 Subject: [PATCH 04/15] =?UTF-8?q?=F0=9F=93=9D=20=20Improve=20the=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7f088f3d..1f74afc2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Cheesecake Stellar Token Factory - V2 -Cheesecake Stellar Token Factory - V2 is a multifaceted system, designed to enable the creation, management, and trading of Stellar-based tokens. The version 2 architecture leverages a blend of cutting-edge technologies to offer a scalable, secure, and user-friendly solution. +Cheesecake Stellar Token Factory - V2 is a multifaceted system, designed to enable the creation, management of Stellar-based tokens. +The version 2 architecture leverages a blend of cutting-edge technologies to offer a scalable, secure, and user-friendly solution. ## Components @@ -12,24 +13,13 @@ Cheesecake Stellar Token Factory - V2 is a multifaceted system, designed to enab - **Starlabs:** A specialized Stellar service, integrated as a git submodule and written in Go, to further enrich the platform's capabilities. - **KMS:** The CKL Key Management Service (KMS), also a git submodule in Go, safeguarding the security of cryptographic keys and other sensitive data. -**API Documentation** - -The backend is equipped with Swagger documentation to provide an interactive interface for understanding and testing the API endpoints. - -### Accessing the Swagger UI: - -You can access the Swagger interface by navigating to: - -[http://localhost:8080/v1/swagger/index.html](http://localhost:8080/v1/swagger/index.html) - -Here, you'll find detailed information about each API route, including required parameters, request/response formats, and the ability to test the endpoints directly from the browser. - ### Topics Further details on the various functionalities, system requirements, and guides to set up and use the Cheesecake Stellar Token Factory - V2 can be found in the following sections: - [Development Environment with Makefile](#development-environment-with-makefile) - - [Development Environment](#development-environment) +- [API Documentation](#api-documentation) +- [Kafka Topics & Communication Flows](#kafka-topics--communication-flows) ## Development Environment with Makefile @@ -43,7 +33,7 @@ make dev-up-all ### Build and Start Individual Services -You can build and start individual services by appending the service name to `make dev-up-starlabs:`. For example: +You can build and start individual services by appending the service name to `make dev-up-starlabs`. For example: - Starlabs: `make dev-up-starlabs` - Tests: `make dev-up-tests` @@ -66,3 +56,66 @@ make dev-destroy ``` These commands streamline the process of building, starting, and managing the development environment, allowing developers to concentrate on coding. Customize the `dev.docker-compose.yml` file to adapt the configuration of the Docker containers to your specific requirements. + +### **API Documentation** + +The backend is equipped with Swagger documentation to provide an interactive interface for understanding and testing the API endpoints. + +#### Accessing the Swagger UI: + +You can access the Swagger interface by navigating to: + +[http://localhost:8080/v1/swagger/index.html](http://localhost:8080/v1/swagger/index.html) + +Here, you'll find detailed information about each API route, including required parameters, request/response formats, and the ability to test the endpoints directly from the browser. +The Kafka Topics section you've provided gives a detailed flow between various components using Kafka topics. However, to make it more structured and improve readability, you can consider the following improvements: + +### **Kafka Topics & Communication Flows** + +Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for facilitating asynchronous data processing and streamlining communication between various components. The following elucidates the primary Kafka topics and the flow of communication between associated services: + +#### **Backend to Stellar KMS**: + +- **`generateKeypair`**: + - **Purpose**: Handles requests to generate a new Stellar key pair. + - **Usage**: Whenever a component or service needs a new key pair, a message is produced to this topic. + +#### **Stellar KMS to Backend**: + +- **`generatedKeypairs`**: + - **Purpose**: Announces the generation of a new key pair. + - **Usage**: Once a key pair is produced, it's dispatched to this topic allowing subscribers to process new key pairs for secure storage or any subsequent activities. + +#### **Starlabs to KMS**: + +- **`signEnvelope`**: + - **Purpose**: Manages signing requests for transaction envelopes. + - **Usage**: It receives the unsigned transaction envelope with the pertinent details needed for the signing process. + +#### **KMS to Starlabs**: + +- **`signedEnvelopes`**: + - **Purpose**: Communicates the status of a successfully signed transaction envelope. + - **Usage**: Services, particularly those monitoring or processing signed envelopes, subscribe to this topic. + +#### **Backend to Starlabs**: + +- **`createEnvelope`**: + + - **Purpose**: Deals with transaction envelope creation requests. + - **Usage**: This topic may contain specific transaction details like payment operations, account merges, or asset issuances. + +- **`horizonRequest`**: + - **Purpose**: Manages any request destined for the Stellar Horizon server. + - **Usage**: Typical requests include fetching account details or initiating transactions. + +#### **Starlabs to Backend**: + +- **`horizonResponse`**: + + - **Purpose**: Conveys responses from the Stellar Horizon server. + - **Usage**: It relays data from fetched accounts, transaction submission outcomes, and all other responses from Horizon. + +- **`submitResponse`**: + - **Purpose**: Reports the outcome of transactions submitted to the Stellar network. + - **Usage**: After submitting a transaction to the Stellar network, this topic communicates the result, be it a success or a failure. From a9ce65d490f6e33733d533f3325521d535f9e593 Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Thu, 24 Aug 2023 11:50:45 -0300 Subject: [PATCH 05/15] =?UTF-8?q?=E2=9C=85=20Add=20the=20User=20and=20Wall?= =?UTF-8?q?et=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/integration-test/integration_test.go | 1 - backend/integration-test/users_test.go | 117 +++++++---- backend/integration-test/wallet_test.go | 203 +++++++++++++++++++ 3 files changed, 277 insertions(+), 44 deletions(-) create mode 100644 backend/integration-test/wallet_test.go diff --git a/backend/integration-test/integration_test.go b/backend/integration-test/integration_test.go index d4418e92..18f8ca11 100644 --- a/backend/integration-test/integration_test.go +++ b/backend/integration-test/integration_test.go @@ -28,7 +28,6 @@ func TestMain(m *testing.M) { if err != nil { log.Fatalf("Integration tests: host %s is not available: %s", host, err) } - log.Printf("Integration tests: host %s is available", host) code := m.Run() diff --git a/backend/integration-test/users_test.go b/backend/integration-test/users_test.go index d3681da2..74287874 100644 --- a/backend/integration-test/users_test.go +++ b/backend/integration-test/users_test.go @@ -3,7 +3,6 @@ package integration_test import ( "bytes" "encoding/json" - "fmt" "net/http" "testing" @@ -18,6 +17,80 @@ var _user = entity.User{ Email: "testuser@test.com", } +type UserLoginResponse struct { + User entity.User `json:"user"` +} + +func TestCreateUserSuccess(t *testing.T) { + createUserPath := basePath + "/users/create" + + requestBodyBytes, err := json.Marshal(_user) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createUserPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parsing the response into an expected structure + var response UserLoginResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) +} + +func TestUserLoginSuccess(t *testing.T) { + // URL for logging in a user + loginUserPath := basePath + "/users/login" + + // Test user credentials + email := _user.Email + password := _user.Password + + // Request body + requestBody := map[string]string{ + "email": email, + "password": password, + } + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, loginUserPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + var loginResponse UserLoginResponse + err = json.NewDecoder(resp.Body).Decode(&loginResponse) + assert.NoError(t, err) + + assert.NoError(t, err) + assert.Equal(t, _user.Name, loginResponse.User.Name) + assert.Equal(t, _user.Email, loginResponse.User.Email) + assert.Equal(t, _user.RoleId, loginResponse.User.RoleId) + assert.NotEmpty(t, loginResponse.User.Token) + _user.Token = loginResponse.User.Token + t.Log("token", _user.Token) +} + func TestCreateUserFailedUserAlreadyInDatabase(t *testing.T) { createUserPath := basePath + "/users/create" @@ -119,45 +192,3 @@ func TestUserLoginFailed(t *testing.T) { assert.Equal(t, "database problems", response.Message) assert.Equal(t, "email or password incorrect", response.Error) } - -func TestUserLoginSuccess(t *testing.T) { - // URL for logging in a user - loginUserPath := basePath + "/users/login" - - // Test user credentials - email := _user.Email - password := _user.Password - - // Request body - requestBody := map[string]string{ - "email": email, - "password": password, - } - requestBodyBytes, err := json.Marshal(requestBody) - assert.NoError(t, err) - - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, loginUserPath, bytes.NewBuffer(requestBodyBytes)) - assert.NoError(t, err) - req.Header.Set("Content-Type", "application/json") - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Parse and verify the response body - var response entity.User - err = json.NewDecoder(resp.Body).Decode(&response) - fmt.Println("response", resp.Body) - fmt.Println("err", err) - assert.NoError(t, err) - assert.Equal(t, _user.Name, response.Name) - assert.Equal(t, _user.Email, response.Email) - assert.Equal(t, _user.RoleId, response.RoleId) - assert.NotEmpty(t, response.Token) -} diff --git a/backend/integration-test/wallet_test.go b/backend/integration-test/wallet_test.go new file mode 100644 index 00000000..b47c4249 --- /dev/null +++ b/backend/integration-test/wallet_test.go @@ -0,0 +1,203 @@ +package integration_test + +import ( + "bytes" + "encoding/json" + "net/http" + "testing" + + "github.com/CheesecakeLabs/token-factory-v2/backend/internal/entity" + "github.com/stretchr/testify/assert" +) + +var _wallet = entity.Wallet{} + +func TestCreateWalletSuccess(t *testing.T) { + // URL for creating a wallet + createWalletPath := basePath + "/wallets" + + // Use a predefined wallet for testing or create one + requestBody := map[string]string{ + "type": "sponsor", + } + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createWalletPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.Wallet + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents (you can expand this to verify other fields) + assert.Equal(t, "sponsor", response.Type) + assert.False(t, response.Funded) + + _wallet = response +} + +func TestCreateWalletFail(t *testing.T) { + // URL for creating a wallet + createWalletPath := basePath + "/wallets" + + // Use a predefined wallet for testing or create one + requestBody := map[string]string{} + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createWalletPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents (you can expand this to verify other fields) + assert.Equal(t, "invalid request body", response.Message) + assert.Equal(t, "Key: 'CreateWalletRequest.Type' Error:Field validation for 'Type' failed on the 'required' tag", response.Error) +} + +func TestFundWalletSucess(t *testing.T) { + // URL for creating a wallet + fundWalletPath := basePath + "/wallets/fund" + + // Use a predefined wallet for testing or create one + requestBody := map[string]int{ + "id": 1, + } + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, fundWalletPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.Wallet + err = json.NewDecoder(resp.Body).Decode(&response) + + assert.NoError(t, err) + + // Verify the response contents (you can expand this to verify other fields) + assert.Equal(t, "sponsor", response.Type) + assert.True(t, response.Funded) +} + +func TestFundWalletFail(t *testing.T) { + // URL for creating a wallet + fundWalletPath := basePath + "/wallets/fund" + + // Use a predefined wallet for testing or create one + requestBody := map[string]int{ + "id": 1, + } + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, fundWalletPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + + assert.NoError(t, err) + + // Verify the response contents (you can expand this to verify other fields) + assert.Equal(t, "wallet is already funded", response.Message) +} + +// List all wallets +func TestListWalletsSuccess(t *testing.T) { + // URL for listing all wallets + listWalletsPath := basePath + "/wallets" + + // Create the HTTP request + req, err := http.NewRequest(http.MethodGet, listWalletsPath, nil) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // set que query params to sponsor + q := req.URL.Query() + q.Add("type", "sponsor") + req.URL.RawQuery = q.Encode() + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response []entity.Wallet + err = json.NewDecoder(resp.Body).Decode(&response) + + assert.NoError(t, err) + + // Verify the response contents (you can expand this to verify other fields) + assert.Equal(t, 1, len(response)) + assert.Equal(t, "sponsor", response[0].Type) + assert.True(t, response[0].Funded) +} From 4c5ca61498a5d4c4e53384b80f7fa7fe7a548b37 Mon Sep 17 00:00:00 2001 From: Wellington Leite Junior <68206230+wjuniorbh92@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:53:01 -0300 Subject: [PATCH 06/15] Update README.md Co-authored-by: Alessandra Carneiro <31604209+alessandrak@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f74afc2..edd71cec 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for faci - **`generatedKeypairs`**: - **Purpose**: Announces the generation of a new key pair. - - **Usage**: Once a key pair is produced, it's dispatched to this topic allowing subscribers to process new key pairs for secure storage or any subsequent activities. + - **Usage**: Once a key pair is produced and saved in secure storage, it's dispatched to this topic. #### **Starlabs to KMS**: From 69e55e4c84de533e0827b9a9cd07cd808ab18c1e Mon Sep 17 00:00:00 2001 From: Wellington Leite Junior <68206230+wjuniorbh92@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:53:12 -0300 Subject: [PATCH 07/15] Update README.md Co-authored-by: Alessandra Carneiro <31604209+alessandrak@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index edd71cec..08d2451f 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for faci #### **KMS to Starlabs**: - **`signedEnvelopes`**: - - **Purpose**: Communicates the status of a successfully signed transaction envelope. + - **Purpose**: Sends a signed transaction envelope. - **Usage**: Services, particularly those monitoring or processing signed envelopes, subscribe to this topic. #### **Backend to Starlabs**: From b41665dbf90699de680b084ab1f37997e9e09899 Mon Sep 17 00:00:00 2001 From: Wellington Leite Junior <68206230+wjuniorbh92@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:53:18 -0300 Subject: [PATCH 08/15] Update README.md Co-authored-by: Alessandra Carneiro <31604209+alessandrak@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08d2451f..5f8453fb 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for faci - **`createEnvelope`**: - **Purpose**: Deals with transaction envelope creation requests. - - **Usage**: This topic may contain specific transaction details like payment operations, account merges, or asset issuances. + - **Usage**: This topic processes messages with details of transactions such as source, operations, and sponsor, in order to generate an envelope. - **`horizonRequest`**: - **Purpose**: Manages any request destined for the Stellar Horizon server. From f105e4d16f6d446f12eed9e8231cd630f5ebb309 Mon Sep 17 00:00:00 2001 From: Wellington Leite Junior <68206230+wjuniorbh92@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:53:23 -0300 Subject: [PATCH 09/15] Update README.md Co-authored-by: Alessandra Carneiro <31604209+alessandrak@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f8453fb..2dbb469b 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for faci - **`horizonResponse`**: - **Purpose**: Conveys responses from the Stellar Horizon server. - - **Usage**: It relays data from fetched accounts, transaction submission outcomes, and all other responses from Horizon. + - **Usage**: It relays data from fetched accounts and all other responses from Horizon. - **`submitResponse`**: - **Purpose**: Reports the outcome of transactions submitted to the Stellar network. From 4c447c61fa8646de9c0a0625c1962fe4d5bd264d Mon Sep 17 00:00:00 2001 From: Wellington Leite Junior <68206230+wjuniorbh92@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:53:30 -0300 Subject: [PATCH 10/15] Update README.md Co-authored-by: Alessandra Carneiro <31604209+alessandrak@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2dbb469b..6ca7c9a9 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for faci - **Usage**: This topic processes messages with details of transactions such as source, operations, and sponsor, in order to generate an envelope. - **`horizonRequest`**: - - **Purpose**: Manages any request destined for the Stellar Horizon server. + - **Purpose**: Manages any GET request destined for the Stellar Horizon server. - **Usage**: Typical requests include fetching account details or initiating transactions. #### **Starlabs to Backend**: From 7fb504d078a5be160771d43d6eaa4e23b6e3559f Mon Sep 17 00:00:00 2001 From: Wellington Leite Junior <68206230+wjuniorbh92@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:53:36 -0300 Subject: [PATCH 11/15] Update README.md Co-authored-by: Alessandra Carneiro <31604209+alessandrak@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ca7c9a9..dd529f27 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for faci - **`horizonRequest`**: - **Purpose**: Manages any GET request destined for the Stellar Horizon server. - - **Usage**: Typical requests include fetching account details or initiating transactions. + - **Usage**: Typical requests include fetching account or transaction details. #### **Starlabs to Backend**: From 8f4a3de47e2cb25d67bf3d3168956efe179cfad4 Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Thu, 24 Aug 2023 12:05:10 -0300 Subject: [PATCH 12/15] =?UTF-8?q?=F0=9F=93=9D=20Improve=20the=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index dd529f27..50c9dcc2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The version 2 architecture leverages a blend of cutting-edge technologies to off - **UI for Apache Kafka:** An intuitive open-source web UI that provides detailed monitoring and management functions for Apache Kafka clusters. - **Frontend:** A sleek and interactive React Web application offering a front-facing user interface. - **Backend:** A Golang backend handling the business logic, encompassing everything from API endpoints to core Stellar token functionalities. -- **Starlabs:** A specialized Stellar service, integrated as a git submodule and written in Go, to further enrich the platform's capabilities. +- **Starlabs**: A specialized service dedicated to Stellar, integrated as a git submodule and written in Go. Starlabs centralizes the logic for interacting with the Stellar network, streamlining and enriching the platform's capabilities with Stellar-specific functionalities. Its integration ensures efficient and consistent communication with the Stellar network across the platform. - **KMS:** The CKL Key Management Service (KMS), also a git submodule in Go, safeguarding the security of cryptographic keys and other sensitive data. ### Topics @@ -68,7 +68,6 @@ You can access the Swagger interface by navigating to: [http://localhost:8080/v1/swagger/index.html](http://localhost:8080/v1/swagger/index.html) Here, you'll find detailed information about each API route, including required parameters, request/response formats, and the ability to test the endpoints directly from the browser. -The Kafka Topics section you've provided gives a detailed flow between various components using Kafka topics. However, to make it more structured and improve readability, you can consider the following improvements: ### **Kafka Topics & Communication Flows** @@ -86,6 +85,17 @@ Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for faci - **Purpose**: Announces the generation of a new key pair. - **Usage**: Once a key pair is produced and saved in secure storage, it's dispatched to this topic. +#### **Backend to Starlabs**: + +- **`createEnvelope`**: + + - **Purpose**: Deals with transaction envelope creation requests. + - **Usage**: This topic processes messages with details of transactions such as source, operations, and sponsor, in order to generate an envelope. + +- **`horizonRequest`**: + - **Purpose**: Manages any GET request destined for the Stellar Horizon server. + - **Usage**: Typical requests include fetching account or transaction details. + #### **Starlabs to KMS**: - **`signEnvelope`**: @@ -98,17 +108,6 @@ Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for faci - **Purpose**: Sends a signed transaction envelope. - **Usage**: Services, particularly those monitoring or processing signed envelopes, subscribe to this topic. -#### **Backend to Starlabs**: - -- **`createEnvelope`**: - - - **Purpose**: Deals with transaction envelope creation requests. - - **Usage**: This topic processes messages with details of transactions such as source, operations, and sponsor, in order to generate an envelope. - -- **`horizonRequest`**: - - **Purpose**: Manages any GET request destined for the Stellar Horizon server. - - **Usage**: Typical requests include fetching account or transaction details. - #### **Starlabs to Backend**: - **`horizonResponse`**: From db067aaeed607b5736aaf5b8e81ba565ec687ff3 Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Thu, 24 Aug 2023 16:47:02 -0300 Subject: [PATCH 13/15] =?UTF-8?q?=E2=9C=85=20Update=20Asset=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{users_test.go => 01_users_test.go} | 1 - .../{wallet_test.go => 02_wallet_test.go} | 0 backend/integration-test/03_asset_test.go | 441 ++++++++++++++++++ 3 files changed, 441 insertions(+), 1 deletion(-) rename backend/integration-test/{users_test.go => 01_users_test.go} (99%) rename backend/integration-test/{wallet_test.go => 02_wallet_test.go} (100%) create mode 100644 backend/integration-test/03_asset_test.go diff --git a/backend/integration-test/users_test.go b/backend/integration-test/01_users_test.go similarity index 99% rename from backend/integration-test/users_test.go rename to backend/integration-test/01_users_test.go index 74287874..22f2074d 100644 --- a/backend/integration-test/users_test.go +++ b/backend/integration-test/01_users_test.go @@ -88,7 +88,6 @@ func TestUserLoginSuccess(t *testing.T) { assert.Equal(t, _user.RoleId, loginResponse.User.RoleId) assert.NotEmpty(t, loginResponse.User.Token) _user.Token = loginResponse.User.Token - t.Log("token", _user.Token) } func TestCreateUserFailedUserAlreadyInDatabase(t *testing.T) { diff --git a/backend/integration-test/wallet_test.go b/backend/integration-test/02_wallet_test.go similarity index 100% rename from backend/integration-test/wallet_test.go rename to backend/integration-test/02_wallet_test.go diff --git a/backend/integration-test/03_asset_test.go b/backend/integration-test/03_asset_test.go new file mode 100644 index 00000000..c8876e80 --- /dev/null +++ b/backend/integration-test/03_asset_test.go @@ -0,0 +1,441 @@ +package integration_test + +import ( + "bytes" + "encoding/json" + "net/http" + "testing" + + v1 "github.com/CheesecakeLabs/token-factory-v2/backend/internal/controller/http/v1" + "github.com/CheesecakeLabs/token-factory-v2/backend/internal/entity" + "github.com/stretchr/testify/assert" +) + +var ( + _asset_1 entity.Asset + _asset_2 entity.Asset + _asset_3 entity.Asset +) + +func TestCreateAsset1Success(t *testing.T) { + // URL for creating a asset + createAssetPath := basePath + "/assets" + + // Use a predefined asset for testing or create one + requestBody := map[string]string{ + "name": "USD COIN", + "code": "USDC", + "amount": "10000", + "asset_type": "STABLECOIN", + "controlMechanism": "[\"AUTH_REQUIRED\",\"AUTH_REVOCABLE\",\"AUTH_CLAWBACK_ENABLED\"]", + } + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createAssetPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.Asset + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "USD COIN", response.Name) + assert.Equal(t, "USDC", response.Code) + assert.Equal(t, "STABLECOIN", response.AssetType) + + _asset_1 = response +} + +func TestCreateAssetFail(t *testing.T) { + // URL for creating a asset + createAssetPath := basePath + "/assets" + + // Use a predefined asset for testing or create one + requestBody := map[string]string{} + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createAssetPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "invalid request body: Key: 'CreateAssetRequest.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'CreateAssetRequest.AssetType' Error:Field validation for 'AssetType' failed on the 'required' tag\nKey: 'CreateAssetRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag", response.Message) + assert.Equal(t, "Key: 'CreateAssetRequest.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'CreateAssetRequest.AssetType' Error:Field validation for 'AssetType' failed on the 'required' tag\nKey: 'CreateAssetRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag", response.Error) +} + +func TestGetAllAssetsSuccess(t *testing.T) { + // URL for getting all assets + getAllAssetsPath := basePath + "/assets" + + // Create the HTTP request + req, err := http.NewRequest(http.MethodGet, getAllAssetsPath, nil) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response []entity.Asset + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.NotEmpty(t, response) + + // Verify the response contents + assert.Equal(t, "USD COIN", response[0].Name) + assert.Equal(t, "USDC", response[0].Code) + assert.Equal(t, "STABLECOIN", response[0].AssetType) +} + +func TestGetAssetByIdSuccess(t *testing.T) { + // URL for getting a asset + getAssetPath := basePath + "/assets/" + "1" + + // Create the HTTP request + req, err := http.NewRequest(http.MethodGet, getAssetPath, nil) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.Asset + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "USD COIN", response.Name) + assert.Equal(t, "USDC", response.Code) + assert.Equal(t, "STABLECOIN", response.AssetType) +} + +func TestGetAssetByIdFailed(t *testing.T) { + // URL for getting a asset + getAssetPath := basePath + "/assets/" + "1000" + + // Create the HTTP request + req, err := http.NewRequest(http.MethodGet, getAssetPath, nil) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "error getting asset", response.Message) + assert.Equal(t, "AssetUseCase - Get - uc.repo.GetAssetByCode: AssetRepo - GetAssetById - asset not found", response.Error) +} + +func TestMintAssetSuccess(t *testing.T) { + // URL for mint a asset + mintAssetPath := basePath + "/assets/mint" + + // Use a predefined asset for testing or create one + requestBody := v1.MintAssetRequest{ + Id: "1", + SponsorId: 1, + Code: "USDC", // Adding the missing Code field + Amount: "10000", + } + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, mintAssetPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "asset minted", response.Message) +} + +func TestMintAssetUnauthorized(t *testing.T) { + // URL for minting a asset + mintAssetPath := basePath + "/assets/mint" + + // Use a predefined asset for testing or create one + requestBody := map[string]string{ + "id": "1", + "amount": "1000000", + "sponsor_id": "1", + } + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, mintAssetPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "request does not contain an access token", response.Error) +} + +func TestBurnAssetSuccess(t *testing.T) { + // URL for burning a asset + burnAssetPath := basePath + "/assets/burn" + + // Use a predefined asset for testing or create one + requestBody := v1.BurnAssetRequest{ + Id: "1", + SponsorId: 1, + Amount: "500", + } + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, burnAssetPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "Asset burned successfully", response.Message) +} + +func TestBurnAssetFailed(t *testing.T) { + // URL for burning a asset + burnAssetPath := basePath + "/assets/burn" + + // Use a predefined asset for testing or create one + requestBody := v1.BurnAssetRequest{ + Id: "1", + SponsorId: 1, + Amount: "10000000000", // Burn more than the amount available + } + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, burnAssetPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Parse and verify the response body + var response errrorResponse + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "starlabs messaging problems", response.Message) +} + +func TestCreateAsset2Success(t *testing.T) { + // URL for creating a asset + createAssetPath := basePath + "/assets" + + // Use a predefined asset for testing or create one + requestBody := map[string]string{ + "name": "Argentine Peso", + "code": "ARS", + "amount": "10000", + "asset_type": "PAYMENT_TOKEN", + "controlMechanism": "[\"AUTH_REQUIRED\",\"AUTH_REVOCABLE\",\"AUTH_CLAWBACK_ENABLED\"]", + } + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createAssetPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.Asset + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "Argentine Peso", response.Name) + assert.Equal(t, "ARS", response.Code) + assert.Equal(t, "PAYMENT_TOKEN", response.AssetType) + _asset_2 = response +} + +func TestCreateAsset3Success(t *testing.T) { + // URL for creating a asset + createAssetPath := basePath + "/assets" + + // Use a predefined asset for testing or create one + requestBody := map[string]string{ + "name": "British Pound Sterling", + "code": "GBPC", + "amount": "40000", + "asset_type": "PAYMENT_TOKEN", + "controlMechanism": "[\"AUTH_REQUIRED\",\"AUTH_REVOCABLE\",\"AUTH_CLAWBACK_ENABLED\"]", + } + + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createAssetPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.Asset + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents + assert.Equal(t, "British Pound Sterling", response.Name) + assert.Equal(t, "GBPC", response.Code) + assert.Equal(t, "PAYMENT_TOKEN", response.AssetType) + _asset_3 = response +} From 2ff234d0b7cdb186804460daf5ea8dc335b8b566 Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Mon, 28 Aug 2023 17:13:12 -0300 Subject: [PATCH 14/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20Refactor=20the=20?= =?UTF-8?q?Asset=20Integration=20Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 27 +- README.md | 79 ++- backend/integration-test/01_users_test.go | 6 +- backend/integration-test/02_wallet_test.go | 90 ++- backend/integration-test/03_asset_test.go | 511 ++++++------------ backend/integration-test/Dockerfile | 2 +- backend/integration-test/integration_test.go | 2 +- backend/integration-test/requests_test.go | 113 ++++ backend/internal/controller/http/v1/assets.go | 1 + backend/internal/controller/http/v1/auth.go | 11 +- backend/internal/usecase/auth.go | 6 +- .../internal/usecase/repo/user_postgres.go | 2 +- dev-env.sh | 30 +- dev.docker-compose.yml | 1 - 14 files changed, 452 insertions(+), 429 deletions(-) create mode 100644 backend/integration-test/requests_test.go mode change 100644 => 100755 dev-env.sh diff --git a/Makefile b/Makefile index 7fab752a..6d1e2e20 100644 --- a/Makefile +++ b/Makefile @@ -1,36 +1,23 @@ # Build and up targets for all profiles in development environment -dev-all: dev-all dev-up-all +dev-all: dev-up-all dev-up-all: - docker-compose -f dev.docker-compose.yml --profile all build - docker-compose -f dev.docker-compose.yml --profile all up -d + @./dev-env.sh all dev-up-starlabs: - docker-compose -f dev.docker-compose.yml --profile starlabs build - docker-compose -f dev.docker-compose.yml --profile starlabs up -d + @./dev-env.sh starlabs dev-up-tests: - @echo "Building containers for tests..." - @docker-compose -f dev.docker-compose.yml --profile tests build > /dev/null 2>&1 || (echo "Container build failed!" && exit 1) - @echo "Starting containers for tests..." - @docker-compose -f dev.docker-compose.yml --profile tests up -d > /dev/null 2>&1 || (echo "Starting containers failed!" && exit 1) - @echo "Starting the integration test..." - @docker-compose -f dev.docker-compose.yml logs -f integration - @echo "Removing containers..." - @docker-compose -f dev.docker-compose.yml down --remove-orphans > /dev/null 2>&1 - + @./dev-env.sh tests dev-up-kafka: - docker-compose -f dev.docker-compose.yml --profile kafka build - docker-compose -f dev.docker-compose.yml --profile kafka up -d + @./dev-env.sh kafka dev-up-backend: - docker-compose -f dev.docker-compose.yml --profile backend build - docker-compose -f dev.docker-compose.yml --profile backend up -d + @./dev-env.sh backend dev-up-frontend: - docker-compose -f dev.docker-compose.yml --profile frontend build - docker-compose -f dev.docker-compose.yml --profile frontend up -d + @./dev-env.sh frontend dev-stop: docker-compose -f dev.docker-compose.yml down --remove-orphans diff --git a/README.md b/README.md index 50c9dcc2..1401f502 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,20 @@ # Cheesecake Stellar Token Factory - V2 -Cheesecake Stellar Token Factory - V2 is a multifaceted system, designed to enable the creation, management of Stellar-based tokens. -The version 2 architecture leverages a blend of cutting-edge technologies to offer a scalable, secure, and user-friendly solution. +The Cheesecake Stellar Token Factory - V2 is a comprehensive system designed for the creation and management of Stellar-based tokens. Its second version architecture combines advanced technologies to deliver a scalable, secure, and user-friendly platform. ## Components -- **Postgres:** Utilized as the primary relational database for all structured data management needs. -- **Apache Kafka:** Deployed as a distributed event store to enable real-time data processing and insights. -- **UI for Apache Kafka:** An intuitive open-source web UI that provides detailed monitoring and management functions for Apache Kafka clusters. -- **Frontend:** A sleek and interactive React Web application offering a front-facing user interface. -- **Backend:** A Golang backend handling the business logic, encompassing everything from API endpoints to core Stellar token functionalities. -- **Starlabs**: A specialized service dedicated to Stellar, integrated as a git submodule and written in Go. Starlabs centralizes the logic for interacting with the Stellar network, streamlining and enriching the platform's capabilities with Stellar-specific functionalities. Its integration ensures efficient and consistent communication with the Stellar network across the platform. -- **KMS:** The CKL Key Management Service (KMS), also a git submodule in Go, safeguarding the security of cryptographic keys and other sensitive data. +- **Postgres:** Our primary relational database for structured data management. +- **Apache Kafka:** A distributed event store for real-time data processing and insights. +- **UI for Apache Kafka:** An open-source web UI offering in-depth monitoring and management capabilities for Apache Kafka clusters. +- **Frontend:** An interactive React Web application for a user-friendly experience. +- **Backend:** A Golang backend that manages business logic, ranging from API endpoints to core Stellar token functions. +- **Starlabs**: An exclusive Stellar service, integrated as a git submodule and written in Go. It centralizes Stellar network interactions, enhancing platform capabilities and ensuring consistent communication. +- **KMS:** The CKL Key Management Service (KMS) safeguards cryptographic keys and other sensitive data. ### Topics -Further details on the various functionalities, system requirements, and guides to set up and use the Cheesecake Stellar Token Factory - V2 can be found in the following sections: +For further details on functionalities, system requirements, and setup guides, refer to: - [Development Environment with Makefile](#development-environment-with-makefile) - [API Documentation](#api-documentation) @@ -23,7 +22,7 @@ Further details on the various functionalities, system requirements, and guides ## Development Environment with Makefile -The Makefile included in the project simplifies the development environment management using Docker Compose, providing a collection of easy-to-use commands tailored for the Cheesecake Stellar Token Factory - V2 development workflow. +Our Makefile, in conjunction with Docker Compose, offers a set of commands to simplify the development workflow. ### Build and Start All Services @@ -33,88 +32,86 @@ make dev-up-all ### Build and Start Individual Services -You can build and start individual services by appending the service name to `make dev-up-starlabs`. For example: +Invoke individual services using `make dev-up-[service-name]`: - Starlabs: `make dev-up-starlabs` -- Tests: `make dev-up-tests` +- Tests: `make dev-up-tests` (Note: This command will execute tests and then exit.) - Kafka: `make dev-up-kafka` - Backend: `make dev-up-backend` - Frontend: `make dev-up-frontend` -### Stop All Services (Development Backend) +### Stop All Services ```bash make dev-stop ``` -### Stop and Remove Volumes (Development Backend) +### Reset Development Environment -**Warning**: This command will delete all data in your development environment. +**Warning**: This action deletes all data in the development environment. ```bash make dev-destroy ``` -These commands streamline the process of building, starting, and managing the development environment, allowing developers to concentrate on coding. Customize the `dev.docker-compose.yml` file to adapt the configuration of the Docker containers to your specific requirements. +Modify the `dev.docker-compose.yml` file to adjust the Docker container configurations to your preferences. -### **API Documentation** - -The backend is equipped with Swagger documentation to provide an interactive interface for understanding and testing the API endpoints. +> **Note**: Ensure the `dev-env.sh` script is executable: `chmod +x dev-env.sh`. -#### Accessing the Swagger UI: +### **API Documentation** -You can access the Swagger interface by navigating to: +The backend features Swagger documentation for an interactive overview and testing of the API endpoints. -[http://localhost:8080/v1/swagger/index.html](http://localhost:8080/v1/swagger/index.html) +#### Access the Swagger UI: -Here, you'll find detailed information about each API route, including required parameters, request/response formats, and the ability to test the endpoints directly from the browser. +Navigate to [http://localhost:8080/v1/swagger/index.html](http://localhost:8080/v1/swagger/index.html) for detailed insights into each API route. ### **Kafka Topics & Communication Flows** -Apache Kafka is instrumental in "Cheesecake Stellar Token Factory - V2" for facilitating asynchronous data processing and streamlining communication between various components. The following elucidates the primary Kafka topics and the flow of communication between associated services: +Apache Kafka plays a vital role in the Cheesecake Stellar Token Factory - V2 by enabling asynchronous data processing and efficient communication among components. Below are key Kafka topics and their communication pathways: #### **Backend to Stellar KMS**: - **`generateKeypair`**: - - **Purpose**: Handles requests to generate a new Stellar key pair. - - **Usage**: Whenever a component or service needs a new key pair, a message is produced to this topic. + - **Purpose**: Generate a new Stellar key pair. + - **Usage**: Triggered when a new key pair is required. #### **Stellar KMS to Backend**: - **`generatedKeypairs`**: - - **Purpose**: Announces the generation of a new key pair. - - **Usage**: Once a key pair is produced and saved in secure storage, it's dispatched to this topic. + - **Purpose**: Announce a newly generated key pair. + - **Usage**: Used after a key pair is stored securely. #### **Backend to Starlabs**: - **`createEnvelope`**: - - **Purpose**: Deals with transaction envelope creation requests. - - **Usage**: This topic processes messages with details of transactions such as source, operations, and sponsor, in order to generate an envelope. + - **Purpose**: Handle transaction envelope creation. + - **Usage**: Process messages detailing transactions for envelope generation. - **`horizonRequest`**: - - **Purpose**: Manages any GET request destined for the Stellar Horizon server. - - **Usage**: Typical requests include fetching account or transaction details. + - **Purpose**: Manage GET requests to the Stellar Horizon server. + - **Usage**: Fetch account or transaction details. #### **Starlabs to KMS**: - **`signEnvelope`**: - - **Purpose**: Manages signing requests for transaction envelopes. - - **Usage**: It receives the unsigned transaction envelope with the pertinent details needed for the signing process. + - **Purpose**: Handle transaction envelope signing requests. + - **Usage**: Process unsigned transaction envelopes awaiting signatures. #### **KMS to Starlabs**: - **`signedEnvelopes`**: - - **Purpose**: Sends a signed transaction envelope. - - **Usage**: Services, particularly those monitoring or processing signed envelopes, subscribe to this topic. + - **Purpose**: Communicate signed transaction envelopes. + - **Usage**: Services monitoring signed envelopes use this topic. #### **Starlabs to Backend**: - **`horizonResponse`**: - - **Purpose**: Conveys responses from the Stellar Horizon server. - - **Usage**: It relays data from fetched accounts and all other responses from Horizon. + - **Purpose**: Relay Stellar Horizon server responses. + - **Usage**: Communicate fetched account data and other Horizon responses. - **`submitResponse`**: - - **Purpose**: Reports the outcome of transactions submitted to the Stellar network. - - **Usage**: After submitting a transaction to the Stellar network, this topic communicates the result, be it a success or a failure. + - **Purpose**: Report transaction results from the Stellar network. + - **Usage**: Communicate transaction results. diff --git a/backend/integration-test/01_users_test.go b/backend/integration-test/01_users_test.go index 22f2074d..45c3b050 100644 --- a/backend/integration-test/01_users_test.go +++ b/backend/integration-test/01_users_test.go @@ -112,7 +112,7 @@ func TestCreateUserFailedUserAlreadyInDatabase(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) // Parsing the response into an expected structure - var response errrorResponse + var response errorResponse err = json.NewDecoder(resp.Body).Decode(&response) assert.NoError(t, err) @@ -150,7 +150,7 @@ func TestCreateUserFailedPasswordIsEmpty(t *testing.T) { assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // Adjust the status code as per your application's behavior // Parsing the response into an expected structure - var response errrorResponse + var response errorResponse err = json.NewDecoder(resp.Body).Decode(&response) assert.NoError(t, err) @@ -185,7 +185,7 @@ func TestUserLoginFailed(t *testing.T) { assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) // Parse and verify the response body - var response errrorResponse + var response errorResponse err = json.NewDecoder(resp.Body).Decode(&response) assert.NoError(t, err) assert.Equal(t, "database problems", response.Message) diff --git a/backend/integration-test/02_wallet_test.go b/backend/integration-test/02_wallet_test.go index b47c4249..f901bd2e 100644 --- a/backend/integration-test/02_wallet_test.go +++ b/backend/integration-test/02_wallet_test.go @@ -10,7 +10,10 @@ import ( "github.com/stretchr/testify/assert" ) -var _wallet = entity.Wallet{} +var ( + _wallet = entity.Wallet{} + _walletUser = entity.Wallet{} +) func TestCreateWalletSuccess(t *testing.T) { // URL for creating a wallet @@ -79,7 +82,7 @@ func TestCreateWalletFail(t *testing.T) { assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // Parse and verify the response body - var response errrorResponse + var response errorResponse err = json.NewDecoder(resp.Body).Decode(&response) assert.NoError(t, err) @@ -94,7 +97,7 @@ func TestFundWalletSucess(t *testing.T) { // Use a predefined wallet for testing or create one requestBody := map[string]int{ - "id": 1, + "id": _wallet.Id, } requestBodyBytes, err := json.Marshal(requestBody) assert.NoError(t, err) @@ -132,7 +135,7 @@ func TestFundWalletFail(t *testing.T) { // Use a predefined wallet for testing or create one requestBody := map[string]int{ - "id": 1, + "id": _wallet.Id, } requestBodyBytes, err := json.Marshal(requestBody) assert.NoError(t, err) @@ -154,7 +157,7 @@ func TestFundWalletFail(t *testing.T) { assert.Equal(t, http.StatusBadRequest, resp.StatusCode) // Parse and verify the response body - var response errrorResponse + var response errorResponse err = json.NewDecoder(resp.Body).Decode(&response) assert.NoError(t, err) @@ -201,3 +204,80 @@ func TestListWalletsSuccess(t *testing.T) { assert.Equal(t, "sponsor", response[0].Type) assert.True(t, response[0].Funded) } + +func TestCreateWalletUserSucess(t *testing.T) { + // URL for creating a wallet + createWalletPath := basePath + "/wallets" + + // Use a predefined wallet for testing or create one + requestBody := map[string]string{ + "type": "distributor", + } + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, createWalletPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.Wallet + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + // Verify the response contents (you can expand this to verify other fields) + assert.Equal(t, "distributor", response.Type) + assert.False(t, response.Funded) + + _walletUser = response +} + +func TestFundWalletUserSucess(t *testing.T) { + // URL for creating a wallet + fundWalletPath := basePath + "/wallets/fund" + + // Use a predefined wallet for testing or create one + requestBody := map[string]int{ + "id": _walletUser.Id, + } + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, fundWalletPath, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + + // Add necessary headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", _user.Token) + // Execute the request + client := &http.Client{} + resp, err := client.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + // Verify the status code + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // Parse and verify the response body + var response entity.Wallet + err = json.NewDecoder(resp.Body).Decode(&response) + + assert.NoError(t, err) + + // Verify the response contents (you can expand this to verify other fields) + assert.Equal(t, "distributor", response.Type) + assert.True(t, response.Funded) +} diff --git a/backend/integration-test/03_asset_test.go b/backend/integration-test/03_asset_test.go index c8876e80..826810a2 100644 --- a/backend/integration-test/03_asset_test.go +++ b/backend/integration-test/03_asset_test.go @@ -1,8 +1,7 @@ package integration_test import ( - "bytes" - "encoding/json" + "log" "net/http" "testing" @@ -17,425 +16,247 @@ var ( _asset_3 entity.Asset ) -func TestCreateAsset1Success(t *testing.T) { - // URL for creating a asset - createAssetPath := basePath + "/assets" +// *---------- Create Asset Tests ----------* - // Use a predefined asset for testing or create one - requestBody := map[string]string{ - "name": "USD COIN", - "code": "USDC", - "amount": "10000", - "asset_type": "STABLECOIN", - "controlMechanism": "[\"AUTH_REQUIRED\",\"AUTH_REVOCABLE\",\"AUTH_CLAWBACK_ENABLED\"]", - } - - requestBodyBytes, err := json.Marshal(requestBody) - assert.NoError(t, err) - - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, createAssetPath, bytes.NewBuffer(requestBodyBytes)) - assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Parse and verify the response body - var response entity.Asset - err = json.NewDecoder(resp.Body).Decode(&response) - assert.NoError(t, err) - - // Verify the response contents - assert.Equal(t, "USD COIN", response.Name) - assert.Equal(t, "USDC", response.Code) - assert.Equal(t, "STABLECOIN", response.AssetType) +func TestCreateAsset1Sucess(t *testing.T) { + success, asset := createAsset(t, "USD COIN", "USDC", "100000", "STABLECOIN", []string{"AUTH_REQUIRED_FLAG", "AUTH_CLAWBACK_ENABLED", "AUTH_REVOCABLE_FLAG"}, http.StatusOK, "Asset created successfully", "", false) - _asset_1 = response + if success { + _asset_1 = asset + } else { + t.Errorf("Expected asset creation to be successful") + } } -func TestCreateAssetFail(t *testing.T) { - // URL for creating a asset - createAssetPath := basePath + "/assets" - - // Use a predefined asset for testing or create one - requestBody := map[string]string{} - - requestBodyBytes, err := json.Marshal(requestBody) - assert.NoError(t, err) - - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, createAssetPath, bytes.NewBuffer(requestBodyBytes)) - assert.NoError(t, err) +func TestCreateAsset2Success(t *testing.T) { + sucess, asset := createAsset(t, "Argentine Peso", "ARS", "10000", "PAYMENT_TOKEN", []string{"AUTH_REQUIRED_FLAG", "AUTH_REVOCABLE_FLAG"}, http.StatusOK, "Asset created successfully", "", false) + if sucess { + _asset_2 = asset + } else { + t.Errorf("Expected asset creation to be successful") + } +} - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) +func TestCreateAsset3Success(t *testing.T) { + sucess, asset := createAsset(t, "British Pound Sterling", "GBPC", "40000", "PAYMENT_TOKEN", []string{"AUTH_REQUIRED_FLAG", "AUTH_REVOCABLE_FLAG"}, http.StatusOK, "Asset created successfully", "", false) + if sucess { + _asset_3 = asset + } else { + t.Errorf("Expected asset creation to be successful") + } +} - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() +func TestCreateAssetFailedWithoutCode(t *testing.T) { + sucess, _ := createAsset(t, "USD COIN", "", "100000", "STABLECOIN", []string{"AUTH_REQUIRED_FLAG", "AUTH_CLAWBACK_ENABLED", "AUTH_REVOCABLE_FLAG"}, http.StatusBadRequest, "invalid request body: Key: 'CreateAssetRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag", "Key: 'CreateAssetRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag", true) - // Verify the status code - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + if !sucess { + t.Errorf("Expected asset creation to fail") + } +} - // Parse and verify the response body - var response errrorResponse - err = json.NewDecoder(resp.Body).Decode(&response) - assert.NoError(t, err) +func TestCreateAssetFailedWithoutName(t *testing.T) { + sucess, _ := createAsset(t, "", "USDC", "100000", "STABLECOIN", []string{"AUTH_REQUIRED_FLAG", "AUTH_CLAWBACK_ENABLED", "AUTH_REVOCABLE_FLAG"}, http.StatusBadRequest, "invalid request body: Key: 'CreateAssetRequest.Name' Error:Field validation for 'Name' failed on the 'required' tag", "Key: 'CreateAssetRequest.Name' Error:Field validation for 'Name' failed on the 'required' tag", true) - // Verify the response contents - assert.Equal(t, "invalid request body: Key: 'CreateAssetRequest.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'CreateAssetRequest.AssetType' Error:Field validation for 'AssetType' failed on the 'required' tag\nKey: 'CreateAssetRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag", response.Message) - assert.Equal(t, "Key: 'CreateAssetRequest.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'CreateAssetRequest.AssetType' Error:Field validation for 'AssetType' failed on the 'required' tag\nKey: 'CreateAssetRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag", response.Error) + if !sucess { + t.Errorf("Expected asset creation to fail") + } } +// *---------- Get Asset Tests ----------* func TestGetAllAssetsSuccess(t *testing.T) { - // URL for getting all assets - getAllAssetsPath := basePath + "/assets" - - // Create the HTTP request - req, err := http.NewRequest(http.MethodGet, getAllAssetsPath, nil) - assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Parse and verify the response body var response []entity.Asset - err = json.NewDecoder(resp.Body).Decode(&response) + statusCode, err := executeGetRequest(t, basePath+"/assets", _user.Token, &response) assert.NoError(t, err) - - // Verify the response contents + assert.Equal(t, http.StatusOK, statusCode) assert.NotEmpty(t, response) - - // Verify the response contents - assert.Equal(t, "USD COIN", response[0].Name) - assert.Equal(t, "USDC", response[0].Code) - assert.Equal(t, "STABLECOIN", response[0].AssetType) + assert.Equal(t, 3, len(response)) } func TestGetAssetByIdSuccess(t *testing.T) { - // URL for getting a asset - getAssetPath := basePath + "/assets/" + "1" - - // Create the HTTP request - req, err := http.NewRequest(http.MethodGet, getAssetPath, nil) - assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Parse and verify the response body var response entity.Asset - err = json.NewDecoder(resp.Body).Decode(&response) + statusCode, err := executeGetRequest(t, basePath+"/assets/1", _user.Token, &response) assert.NoError(t, err) - - // Verify the response contents + assert.Equal(t, http.StatusOK, statusCode) assert.Equal(t, "USD COIN", response.Name) assert.Equal(t, "USDC", response.Code) assert.Equal(t, "STABLECOIN", response.AssetType) } func TestGetAssetByIdFailed(t *testing.T) { - // URL for getting a asset - getAssetPath := basePath + "/assets/" + "1000" - - // Create the HTTP request - req, err := http.NewRequest(http.MethodGet, getAssetPath, nil) + var response errorResponse + statusCode, err := executeGetRequest(t, basePath+"/assets/1000", _user.Token, &response) assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Parse and verify the response body - var response errrorResponse - err = json.NewDecoder(resp.Body).Decode(&response) - assert.NoError(t, err) - - // Verify the response contents + assert.Equal(t, http.StatusInternalServerError, statusCode) assert.Equal(t, "error getting asset", response.Message) assert.Equal(t, "AssetUseCase - Get - uc.repo.GetAssetByCode: AssetRepo - GetAssetById - asset not found", response.Error) } +// *---------- Mint Asset Tests ----------* func TestMintAssetSuccess(t *testing.T) { - // URL for mint a asset - mintAssetPath := basePath + "/assets/mint" - - // Use a predefined asset for testing or create one + var response errorResponse requestBody := v1.MintAssetRequest{ Id: "1", SponsorId: 1, - Code: "USDC", // Adding the missing Code field + Code: "USDC", Amount: "10000", } - - requestBodyBytes, err := json.Marshal(requestBody) + statusCode, err := executePostRequest(t, basePath+"/assets/mint", _user.Token, requestBody, &response) assert.NoError(t, err) - - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, mintAssetPath, bytes.NewBuffer(requestBodyBytes)) - assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Parse and verify the response body - var response errrorResponse - err = json.NewDecoder(resp.Body).Decode(&response) - assert.NoError(t, err) - - // Verify the response contents + assert.Equal(t, http.StatusOK, statusCode) assert.Equal(t, "asset minted", response.Message) } func TestMintAssetUnauthorized(t *testing.T) { - // URL for minting a asset - mintAssetPath := basePath + "/assets/mint" - - // Use a predefined asset for testing or create one + var response errorResponse requestBody := map[string]string{ "id": "1", "amount": "1000000", "sponsor_id": "1", } - - requestBodyBytes, err := json.Marshal(requestBody) + statusCode, err := executePostRequest(t, basePath+"/assets/mint", "", requestBody, &response) assert.NoError(t, err) + assert.Equal(t, http.StatusUnauthorized, statusCode) + assert.Equal(t, "request does not contain an access token", response.Error) +} - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, mintAssetPath, bytes.NewBuffer(requestBodyBytes)) - assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) +func TestMintAssetFailedWithInvalidSponsorId(t *testing.T) { + var response errorResponse + requestBody := v1.MintAssetRequest{ + Id: "1", + SponsorId: 1000, + Amount: "1000000", + Code: _asset_1.Code, + } + statusCode, err := executePostRequest(t, basePath+"/assets/mint", _user.Token, requestBody, &response) assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + assert.Equal(t, http.StatusNotFound, statusCode) + assert.Equal(t, "sponsor wallet not found", response.Message) + assert.Equal(t, "WalletUseCase - Get - uc.repo.GetWallet: WalletRepo - GetWallet - wallet not found", response.Error) +} - // Parse and verify the response body - var response errrorResponse - err = json.NewDecoder(resp.Body).Decode(&response) +func TestMintAssetFailedWithInvalidAssetId(t *testing.T) { + var response errorResponse + requestBody := v1.MintAssetRequest{ + Id: "1000", + SponsorId: 1, + Amount: "1000000", + Code: _asset_1.Code, + } + statusCode, err := executePostRequest(t, basePath+"/assets/mint", _user.Token, requestBody, &response) assert.NoError(t, err) - - // Verify the response contents - assert.Equal(t, "request does not contain an access token", response.Error) + assert.Equal(t, http.StatusNotFound, statusCode) + assert.Equal(t, "asset not found", response.Message) + assert.Equal(t, "AssetUseCase - Get - uc.repo.GetAssetByCode: AssetRepo - GetAssetById - asset not found", response.Error) } +// *---------- Burn Asset Tests ----------* func TestBurnAssetSuccess(t *testing.T) { - // URL for burning a asset - burnAssetPath := basePath + "/assets/burn" - - // Use a predefined asset for testing or create one + var response errorResponse requestBody := v1.BurnAssetRequest{ Id: "1", SponsorId: 1, Amount: "500", } - - requestBodyBytes, err := json.Marshal(requestBody) - assert.NoError(t, err) - - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, burnAssetPath, bytes.NewBuffer(requestBodyBytes)) + statusCode, err := executePostRequest(t, basePath+"/assets/burn", _user.Token, requestBody, &response) assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Parse and verify the response body - var response errrorResponse - err = json.NewDecoder(resp.Body).Decode(&response) - assert.NoError(t, err) - - // Verify the response contents + assert.Equal(t, http.StatusOK, statusCode) assert.Equal(t, "Asset burned successfully", response.Message) } func TestBurnAssetFailed(t *testing.T) { - // URL for burning a asset - burnAssetPath := basePath + "/assets/burn" - - // Use a predefined asset for testing or create one + var response errorResponse requestBody := v1.BurnAssetRequest{ Id: "1", SponsorId: 1, Amount: "10000000000", // Burn more than the amount available } - - requestBodyBytes, err := json.Marshal(requestBody) - assert.NoError(t, err) - - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, burnAssetPath, bytes.NewBuffer(requestBodyBytes)) - assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Parse and verify the response body - var response errrorResponse - err = json.NewDecoder(resp.Body).Decode(&response) + statusCode, err := executePostRequest(t, basePath+"/assets/burn", _user.Token, requestBody, &response) assert.NoError(t, err) - - // Verify the response contents + assert.Equal(t, http.StatusInternalServerError, statusCode) assert.Equal(t, "starlabs messaging problems", response.Message) } -func TestCreateAsset2Success(t *testing.T) { - // URL for creating a asset - createAssetPath := basePath + "/assets" - - // Use a predefined asset for testing or create one - requestBody := map[string]string{ - "name": "Argentine Peso", - "code": "ARS", - "amount": "10000", - "asset_type": "PAYMENT_TOKEN", - "controlMechanism": "[\"AUTH_REQUIRED\",\"AUTH_REVOCABLE\",\"AUTH_CLAWBACK_ENABLED\"]", +// *---------- Authorization Flags Tests ----------* +func TestSetAuthRequiredFlagSuccess(t *testing.T) { + var response errorResponse + log.Println("Oi Asset , ", _asset_1.Issuer) + log.Println("Oi Wallet , ", _walletUser.Id) + requestBody := v1.UpdateAuthFlagsRequest{ + TrustorPK: _walletUser.Key.PublicKey, + Code: _asset_1.Code, + Issuer: _asset_1.Issuer.Id, + SetFlags: []string{"TRUST_LINE_AUTHORIZED"}, } - requestBodyBytes, err := json.Marshal(requestBody) - assert.NoError(t, err) - - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, createAssetPath, bytes.NewBuffer(requestBodyBytes)) + statusCode, err := executePostRequest(t, basePath+"/assets/update-auth-flags", _user.Token, requestBody, &response) assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Parse and verify the response body - var response entity.Asset - err = json.NewDecoder(resp.Body).Decode(&response) - assert.NoError(t, err) - - // Verify the response contents - assert.Equal(t, "Argentine Peso", response.Name) - assert.Equal(t, "ARS", response.Code) - assert.Equal(t, "PAYMENT_TOKEN", response.AssetType) - _asset_2 = response + assert.Equal(t, http.StatusOK, statusCode) + assert.Equal(t, "authorization flags updated successfully", response.Message) } -func TestCreateAsset3Success(t *testing.T) { - // URL for creating a asset - createAssetPath := basePath + "/assets" - - // Use a predefined asset for testing or create one - requestBody := map[string]string{ - "name": "British Pound Sterling", - "code": "GBPC", - "amount": "40000", - "asset_type": "PAYMENT_TOKEN", - "controlMechanism": "[\"AUTH_REQUIRED\",\"AUTH_REVOCABLE\",\"AUTH_CLAWBACK_ENABLED\"]", - } - - requestBodyBytes, err := json.Marshal(requestBody) - assert.NoError(t, err) - - // Create the HTTP request - req, err := http.NewRequest(http.MethodPost, createAssetPath, bytes.NewBuffer(requestBodyBytes)) - assert.NoError(t, err) - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", _user.Token) - - // Execute the request - client := &http.Client{} - resp, err := client.Do(req) - assert.NoError(t, err) - defer resp.Body.Close() - - // Verify the status code - assert.Equal(t, http.StatusOK, resp.StatusCode) - - // Parse and verify the response body - var response entity.Asset - err = json.NewDecoder(resp.Body).Decode(&response) - assert.NoError(t, err) - - // Verify the response contents - assert.Equal(t, "British Pound Sterling", response.Name) - assert.Equal(t, "GBPC", response.Code) - assert.Equal(t, "PAYMENT_TOKEN", response.AssetType) - _asset_3 = response -} +// *---------- Transfer Asset Tests ----------* +// func TestTransferAssetSuccess(t *testing.T) { +// if !transferAsset(t, 4, _walletUser.Key.PublicKey, strconv.Itoa(_asset_1.Id), "10000", http.StatusOK, "", "Asset transferred successfully") { +// t.Errorf("Expected asset to be transferred successfully") +// } +// } + +// func TestTransferAssetMissingParameters(t *testing.T) { +// if transferAsset(0, "", "", "") { +// t.Errorf("Expected asset transfer to fail due to missing parameters") +// } +// } + +// func TestTransferAssetInvalidSourceWalletID(t *testing.T) { +// if transferAsset(-1, "valid_wallet_pk", "valid_asset_id", "100") { +// t.Errorf("Expected asset transfer to fail due to invalid SourceWalletID") +// } +// } + +// func TestTransferAssetInvalidDestinationWalletPK(t *testing.T) { +// if transferAsset(1, "invalid_wallet_pk", "valid_asset_id", "100") { +// t.Errorf("Expected asset transfer to fail due to invalid DestinationWalletPK") +// } +// } + +// *---------- Clawback Asset Tests ----------* + +// func TestClawbackAsset2Failed(t *testing.T) { +// // URL for clawback a asset +// clawbackAssetPath := basePath + "/assets/clawback" + +// // Use a predefined asset for testing or create one +// requestBody := v1.ClawbackAssetRequest{ +// SponsorId: 1, +// Code: _asset_2.Code, +// From: _asset_2.Distributor.Key.PublicKey, +// Amount: "10000", +// } + +// requestBodyBytes, err := json.Marshal(requestBody) +// assert.NoError(t, err) + +// // Create the HTTP request +// req, err := http.NewRequest(http.MethodPost, clawbackAssetPath, bytes.NewBuffer(requestBodyBytes)) +// assert.NoError(t, err) + +// // Add necessary headers +// req.Header.Set("Content-Type", "application/json") +// req.Header.Set("Authorization", _user.Token) + +// // Execute the request +// client := &http.Client{} +// resp, err := client.Do(req) +// assert.NoError(t, err) + +// // Verify the status code +// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) + +// // Parse and verify the response body +// var response errorResponse + +// err = json.NewDecoder(resp.Body).Decode(&response) +// assert.NoError(t, err) +// // Verify the response contents +// assert.Equal(t, "starlabs messaging problems", response.Message) +// } diff --git a/backend/integration-test/Dockerfile b/backend/integration-test/Dockerfile index 827e6934..917b17f6 100644 --- a/backend/integration-test/Dockerfile +++ b/backend/integration-test/Dockerfile @@ -11,4 +11,4 @@ RUN go build -o main . EXPOSE 8080 -CMD ["go", "test", "-v", "./integration-test/..."] \ No newline at end of file +CMD ["go", "test", "-v", "./integration-test/...", "-count=1"] \ No newline at end of file diff --git a/backend/integration-test/integration_test.go b/backend/integration-test/integration_test.go index 18f8ca11..b205f899 100644 --- a/backend/integration-test/integration_test.go +++ b/backend/integration-test/integration_test.go @@ -9,7 +9,7 @@ import ( "time" ) -type errrorResponse struct { +type errorResponse struct { Message string `json:"message"` Error string `json:"error,omitempty"` } diff --git a/backend/integration-test/requests_test.go b/backend/integration-test/requests_test.go new file mode 100644 index 00000000..bb54a2a5 --- /dev/null +++ b/backend/integration-test/requests_test.go @@ -0,0 +1,113 @@ +package integration_test + +import ( + "bytes" + "encoding/json" + "net/http" + "testing" + + v1 "github.com/CheesecakeLabs/token-factory-v2/backend/internal/controller/http/v1" + "github.com/CheesecakeLabs/token-factory-v2/backend/internal/entity" + "github.com/stretchr/testify/assert" +) + +func assertResponse(t *testing.T, StatusCode int, Error string, Message string, respStatusCode int, response *errorResponse) { + assert.Equal(t, StatusCode, respStatusCode) + if response != nil { + assert.Equal(t, Error, response.Error) + assert.Equal(t, Message, response.Message) + } + + assert.Equal(t, StatusCode, respStatusCode) + assert.Equal(t, Error, response.Error) + assert.Equal(t, Message, response.Message) +} + +func createAsset(t *testing.T, Name string, Code string, Amount string, AssetType string, SetFlags []string, StatusCode int, Message string, Error string, errorB bool) (bool, entity.Asset) { + requestBody := v1.CreateAssetRequest{ + SponsorId: 1, + Name: Name, + Code: Code, + Amount: Amount, + AssetType: AssetType, + SetFlags: SetFlags, + } + + var responseObj interface{} + + if errorB { + responseObj = &errorResponse{} + } else { + responseObj = &entity.Asset{} + } + + statusCode, err := executePostRequest(t, basePath+"/assets", _user.Token, requestBody, responseObj) + assert.NoError(t, err) + + if errorB { + assertResponse(t, StatusCode, Error, Message, statusCode, responseObj.(*errorResponse)) + return true, entity.Asset{} + } else { + response := responseObj.(*entity.Asset) + assert.Equal(t, Name, response.Name) + assert.Equal(t, Code, response.Code) + assert.Equal(t, AssetType, response.AssetType) + return true, *response + } +} + +func transferAsset(t *testing.T, SourceWalletID int, DestinationWalletPK string, AssetID string, Amount string, StatusCode int, Error string, Message string) bool { + requestBody := v1.TransferAssetRequest{ + SourceWalletID: SourceWalletID, + DestinationWalletPK: DestinationWalletPK, + AssetID: AssetID, + Amount: Amount, + SponsorId: 1, + } + + var response errorResponse + statusCode, err := executePostRequest(t, basePath+"/assets/transfer", _user.Token, requestBody, &response) + assert.NoError(t, err) + + assertResponse(t, StatusCode, Error, Message, statusCode, &response) + return true +} + +func executeGetRequest(t *testing.T, url string, token string, responseObj interface{}) (int, error) { + req, err := http.NewRequest(http.MethodGet, url, nil) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", token) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return 0, err + } + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(responseObj) + return resp.StatusCode, err +} + +func executePostRequest(t *testing.T, url string, token string, requestBody interface{}, responseObj interface{}) (int, error) { + requestBodyBytes, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(requestBodyBytes)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + if token != "" { + req.Header.Set("Authorization", token) + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return 0, err + } + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(responseObj) + return resp.StatusCode, err +} diff --git a/backend/internal/controller/http/v1/assets.go b/backend/internal/controller/http/v1/assets.go index 5a019ae9..a100cb94 100644 --- a/backend/internal/controller/http/v1/assets.go +++ b/backend/internal/controller/http/v1/assets.go @@ -497,6 +497,7 @@ func (r *assetsRoutes) transferAsset(c *gin.Context) { userID, err := strconv.Atoi(user.ID) if err != nil { errorResponse(c, http.StatusNotFound, "error to parse user id", err) + return } amount, err := strconv.ParseFloat(request.Amount, 64) diff --git a/backend/internal/controller/http/v1/auth.go b/backend/internal/controller/http/v1/auth.go index c1831db3..5ccf2a48 100644 --- a/backend/internal/controller/http/v1/auth.go +++ b/backend/internal/controller/http/v1/auth.go @@ -2,6 +2,7 @@ package v1 import ( "errors" + "net/http" "time" "github.com/CheesecakeLabs/token-factory-v2/backend/internal/entity" @@ -25,15 +26,15 @@ func ValidateToken(signedToken string, jwtSecretKey string) (err error) { }, ) if err != nil { - return + return err } _, ok := token.Claims.(*JWTClaim) if !ok { err = errors.New("couldn't parse claims") - return + return err } - return + return nil } func GenerateJWT(user entity.User, jwtSecretKey string) (tokenString string, err error) { @@ -55,13 +56,13 @@ func Auth(jwtSecretKey string) gin.HandlerFunc { return func(context *gin.Context) { tokenString := context.GetHeader("Authorization") if tokenString == "" { - context.JSON(401, gin.H{"error": "request does not contain an access token"}) + context.String(http.StatusUnauthorized, "Unauthorized") context.Abort() return } err := ValidateToken(tokenString, jwtSecretKey) if err != nil { - context.JSON(401, gin.H{"error": err.Error()}) + context.String(http.StatusUnauthorized, err.Error()) context.Abort() return } diff --git a/backend/internal/usecase/auth.go b/backend/internal/usecase/auth.go index c968360a..7273fdd6 100644 --- a/backend/internal/usecase/auth.go +++ b/backend/internal/usecase/auth.go @@ -1,6 +1,8 @@ package usecase -import "github.com/CheesecakeLabs/token-factory-v2/backend/internal/entity" +import ( + "github.com/CheesecakeLabs/token-factory-v2/backend/internal/entity" +) // Auth Use Case type AuthUseCase struct { @@ -27,7 +29,7 @@ func (uc *AuthUseCase) UpdateToken(id string, token string) error { func (uc *AuthUseCase) ValidateToken() string { err := uc.repo.ValidateToken(uc.jwtSecretKey) if err != nil { - return "" + return "invalid token" } return uc.jwtSecretKey } diff --git a/backend/internal/usecase/repo/user_postgres.go b/backend/internal/usecase/repo/user_postgres.go index 3478219c..c9e6f84d 100644 --- a/backend/internal/usecase/repo/user_postgres.go +++ b/backend/internal/usecase/repo/user_postgres.go @@ -71,7 +71,7 @@ func (r UserRepo) ValidateToken(token string) error { defer rows.Close() - // Check if any row was returned + // Check if rows is empty if !rows.Next() { return errors.New("UserRepo - ValidateToken - no user found for token") } diff --git a/dev-env.sh b/dev-env.sh old mode 100644 new mode 100755 index 55c02809..f7ddd39c --- a/dev-env.sh +++ b/dev-env.sh @@ -1,4 +1,26 @@ -echo -e "\033[31;7m Start the Backend and start the integration test... \e[0m"; -docker-compose -f dev.docker-compose.yml --profile starlabs build -docker-compose -f dev.docker-compose.yml --profile starlabs up -d -docker exec -it postgres psql -U postgres -c "SELECT 1 FROM pg_database WHERE datname='backend'" | grep -q 1 || docker exec -it postgres psql -U postgres -c "CREATE DATABASE backend;" +#!/bin/bash + +PROFILE=$1 + +echo -e "\033[31;7m Start the $PROFILE \e[0m" + +if [ "$PROFILE" == "tests" ]; then + echo "Building containers for tests..." + + docker-compose -f dev.docker-compose.yml --profile $PROFILE build > /dev/null + + echo "Resetting the database for tests..." + + echo "Starting the integration test..." + docker-compose -f dev.docker-compose.yml --profile $PROFILE up -d + docker exec -it postgres psql -U postgres -c "DROP DATABASE backend WITH (FORCE);" > /dev/null + docker exec -it postgres psql -U postgres -c "CREATE DATABASE backend;" > /dev/null + docker-compose -f dev.docker-compose.yml logs -f integration + + echo "Removing containers..." + docker-compose -f dev.docker-compose.yml stop --remove-orphans > /dev/null 2>&1 +else + docker-compose -f dev.docker-compose.yml --profile $PROFILE build + docker-compose -f dev.docker-compose.yml --profile $PROFILE up -d + docker exec -it postgres psql -U postgres -c "SELECT 1 FROM pg_database WHERE datname='backend'" | grep -q 1 || docker exec -it postgres psql -U postgres -c "CREATE DATABASE backend;" +fi diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 4172d3ef..62bb8bdb 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -12,7 +12,6 @@ services: POSTGRES_DB: ${POSTGRES_DB:-postgres} volumes: - postgres:/data/postgres - - ./infrastructure/init-scripts:/docker-entrypoint-initdb.d ports: - "5432:5432" restart: unless-stopped From 7ab3560f41f7130050e8a16178d2950b1fd71ee8 Mon Sep 17 00:00:00 2001 From: Wellington Junior Date: Thu, 31 Aug 2023 08:14:00 -0300 Subject: [PATCH 15/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20change=20the=20url?= =?UTF-8?q?=20in=20the=20integration=20test=20and=20remove=20some=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/integration-test/03_asset_test.go | 92 +++----------------- backend/integration-test/integration_test.go | 2 +- 2 files changed, 12 insertions(+), 82 deletions(-) diff --git a/backend/integration-test/03_asset_test.go b/backend/integration-test/03_asset_test.go index 826810a2..1817624e 100644 --- a/backend/integration-test/03_asset_test.go +++ b/backend/integration-test/03_asset_test.go @@ -1,7 +1,6 @@ package integration_test import ( - "log" "net/http" "testing" @@ -176,87 +175,18 @@ func TestBurnAssetFailed(t *testing.T) { assert.Equal(t, "starlabs messaging problems", response.Message) } -// *---------- Authorization Flags Tests ----------* -func TestSetAuthRequiredFlagSuccess(t *testing.T) { - var response errorResponse - log.Println("Oi Asset , ", _asset_1.Issuer) - log.Println("Oi Wallet , ", _walletUser.Id) - requestBody := v1.UpdateAuthFlagsRequest{ - TrustorPK: _walletUser.Key.PublicKey, - Code: _asset_1.Code, - Issuer: _asset_1.Issuer.Id, - SetFlags: []string{"TRUST_LINE_AUTHORIZED"}, - } - - statusCode, err := executePostRequest(t, basePath+"/assets/update-auth-flags", _user.Token, requestBody, &response) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, statusCode) - assert.Equal(t, "authorization flags updated successfully", response.Message) -} - -// *---------- Transfer Asset Tests ----------* -// func TestTransferAssetSuccess(t *testing.T) { -// if !transferAsset(t, 4, _walletUser.Key.PublicKey, strconv.Itoa(_asset_1.Id), "10000", http.StatusOK, "", "Asset transferred successfully") { -// t.Errorf("Expected asset to be transferred successfully") -// } -// } - -// func TestTransferAssetMissingParameters(t *testing.T) { -// if transferAsset(0, "", "", "") { -// t.Errorf("Expected asset transfer to fail due to missing parameters") -// } -// } - -// func TestTransferAssetInvalidSourceWalletID(t *testing.T) { -// if transferAsset(-1, "valid_wallet_pk", "valid_asset_id", "100") { -// t.Errorf("Expected asset transfer to fail due to invalid SourceWalletID") -// } -// } - -// func TestTransferAssetInvalidDestinationWalletPK(t *testing.T) { -// if transferAsset(1, "invalid_wallet_pk", "valid_asset_id", "100") { -// t.Errorf("Expected asset transfer to fail due to invalid DestinationWalletPK") -// } -// } - -// *---------- Clawback Asset Tests ----------* - -// func TestClawbackAsset2Failed(t *testing.T) { -// // URL for clawback a asset -// clawbackAssetPath := basePath + "/assets/clawback" - -// // Use a predefined asset for testing or create one -// requestBody := v1.ClawbackAssetRequest{ -// SponsorId: 1, -// Code: _asset_2.Code, -// From: _asset_2.Distributor.Key.PublicKey, -// Amount: "10000", -// } - -// requestBodyBytes, err := json.Marshal(requestBody) -// assert.NoError(t, err) - -// // Create the HTTP request -// req, err := http.NewRequest(http.MethodPost, clawbackAssetPath, bytes.NewBuffer(requestBodyBytes)) -// assert.NoError(t, err) - -// // Add necessary headers -// req.Header.Set("Content-Type", "application/json") -// req.Header.Set("Authorization", _user.Token) - -// // Execute the request -// client := &http.Client{} -// resp, err := client.Do(req) -// assert.NoError(t, err) - -// // Verify the status code -// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) - -// // Parse and verify the response body +// // *---------- Authorization Flags Tests ----------* +// func TestSetAuthRequiredFlagSuccess(t *testing.T) { // var response errorResponse +// requestBody := v1.UpdateAuthFlagsRequest{ +// TrustorPK: _wallet.Key.PublicKey, +// Code: _asset_1.Code, +// Issuer: _asset_1.Issuer.Id, +// SetFlags: []string{"TRUST_LINE_CLAWBACK_ENABLED"}, +// } -// err = json.NewDecoder(resp.Body).Decode(&response) +// statusCode, err := executePostRequest(t, basePath+"/assets/update-auth-flags", _user.Token, requestBody, &response) // assert.NoError(t, err) -// // Verify the response contents -// assert.Equal(t, "starlabs messaging problems", response.Message) +// assert.Equal(t, http.StatusOK, statusCode) +// assert.Equal(t, "authorization flags updated successfully", response.Message) // } diff --git a/backend/integration-test/integration_test.go b/backend/integration-test/integration_test.go index b205f899..49a2b486 100644 --- a/backend/integration-test/integration_test.go +++ b/backend/integration-test/integration_test.go @@ -15,7 +15,7 @@ type errorResponse struct { } const ( - host = "localhost:8080" + host = "backend:8080" healthPath = "http://" + host + "/healthz" attempts = 20