tests: added mock data sources to go backend
This commit is contained in:
		
							parent
							
								
									ba15a542b9
								
							
						
					
					
						commit
						48aae18736
					
				
							
								
								
									
										26
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								README.md
									
									
									
									
									
								
							@ -95,6 +95,7 @@ All dependencies are bundled with the **devcontainer**. For manual setup, ensure
 | 
			
		||||
   code .
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## 🖥️ Running ActaTempus
 | 
			
		||||
@ -123,7 +124,7 @@ dart run bin/backend_dart.dart # Starts on port 8080
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## 🗄️ Database Management
 | 
			
		||||
### 🗄️ Database Management
 | 
			
		||||
 | 
			
		||||
ActaTempus uses **Prisma ORM** for database schema management and code generation across both backends.
 | 
			
		||||
Before the backend can connect you need to start the PostgresSQL server. To make things easier you can launch
 | 
			
		||||
@ -135,7 +136,7 @@ docker compose up
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Deploy the Schema
 | 
			
		||||
#### Deploy the Schema
 | 
			
		||||
You only need to apply the schema with one of the following commands, as both result in the same schema.
 | 
			
		||||
In case if you want to change the corresponding server edit the ```.env```-file within the backend projects.
 | 
			
		||||
 | 
			
		||||
@ -151,7 +152,7 @@ cd backend-go
 | 
			
		||||
go run github.com/steebchen/prisma-client-go db push # within backend-go
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Prisma Studio (UI)
 | 
			
		||||
#### Prisma Studio (UI)
 | 
			
		||||
Prisma Studio is WebUI that improves development with Databases as it allows looking right into the data as well as well as altering it.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
@ -163,18 +164,33 @@ bunx prisma studio
 | 
			
		||||
To generate ORM Code for the specifig backend run the following commands.
 | 
			
		||||
This is usually necessary after changes are made to the projects ``schema.prisma``-file.
 | 
			
		||||
 | 
			
		||||
#### Dart
 | 
			
		||||
##### Dart
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cd backend-dart
 | 
			
		||||
bunx prisma generate
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Go 
 | 
			
		||||
##### Go 
 | 
			
		||||
```bash
 | 
			
		||||
cd backend-go
 | 
			
		||||
go run github.com/steebchen/prisma-client-go generate
 | 
			
		||||
```
 | 
			
		||||
---
 | 
			
		||||
## ⚠️ Testing
 | 
			
		||||
##### Dart
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cd backend-dart
 | 
			
		||||
dart test
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
##### Go 
 | 
			
		||||
```bash
 | 
			
		||||
cd backend-go
 | 
			
		||||
go test ./... -v
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## 🔧 Known Issues
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ require (
 | 
			
		||||
	github.com/go-playground/validator/v10 v10.23.0 // indirect
 | 
			
		||||
	github.com/goccy/go-json v0.10.4 // indirect
 | 
			
		||||
	github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
 | 
			
		||||
	github.com/google/uuid v1.6.0 // indirect
 | 
			
		||||
	github.com/json-iterator/go v1.1.12 // indirect
 | 
			
		||||
	github.com/klauspost/cpuid/v2 v2.2.9 // indirect
 | 
			
		||||
	github.com/leodido/go-urn v1.4.0 // indirect
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,8 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
 | 
			
		||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
			
		||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
			
		||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
			
		||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package services_test
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func TestCreateUserService(t *testing.T) {
 | 
			
		||||
	mockRepo := mocks.NewMockUserRepository()
 | 
			
		||||
	service := services.NewUserService(mockRepo)
 | 
			
		||||
 | 
			
		||||
	user := entities.UserCreate{
 | 
			
		||||
		Email:    "service@test.com",
 | 
			
		||||
		Name:     "Jane Doe",
 | 
			
		||||
		Password: "securepassword",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := service.CreateUser(context.Background(), user)
 | 
			
		||||
 | 
			
		||||
	assert.True(t, result.IsRight(), "Expected service to create user")
 | 
			
		||||
	result.Map(func(user entities.User) {
 | 
			
		||||
		assert.Equal(t, "service@test.com", user.Email)
 | 
			
		||||
		assert.Equal(t, "Jane Doe", user.Name)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package data
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"actatempus_backend/internal/domain/data"
 | 
			
		||||
	"actatempus_backend/internal/domain/entities"
 | 
			
		||||
	"actatempus_backend/internal/infrastructure/data/db"
 | 
			
		||||
	"context"
 | 
			
		||||
@ -15,7 +16,7 @@ type PrismaProjectDataSource struct {
 | 
			
		||||
	client *db.PrismaClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewPrismaProjectDataSource(client *db.PrismaClient) *PrismaProjectDataSource {
 | 
			
		||||
func NewPrismaProjectDataSource(client *db.PrismaClient) data.ProjectDataSource {
 | 
			
		||||
	return &PrismaProjectDataSource{client: client}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package data
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"actatempus_backend/internal/domain/data"
 | 
			
		||||
	"actatempus_backend/internal/domain/entities"
 | 
			
		||||
	"actatempus_backend/internal/infrastructure/data/db"
 | 
			
		||||
	"context"
 | 
			
		||||
@ -15,7 +16,7 @@ type PrismaProjectTaskDataSource struct {
 | 
			
		||||
	client *db.PrismaClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewPrismaProjectTaskDataSource(client *db.PrismaClient) *PrismaProjectTaskDataSource {
 | 
			
		||||
func NewPrismaProjectTaskDataSource(client *db.PrismaClient) data.ProjectTaskDataSource {
 | 
			
		||||
	return &PrismaProjectTaskDataSource{client: client}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package data
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"actatempus_backend/internal/domain/data"
 | 
			
		||||
	"actatempus_backend/internal/domain/entities"
 | 
			
		||||
	"actatempus_backend/internal/infrastructure/data/db"
 | 
			
		||||
	"context"
 | 
			
		||||
@ -15,7 +16,7 @@ type PrismaTimeEntryDataSource struct {
 | 
			
		||||
	client *db.PrismaClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewPrismaTimeEntryDataSource(client *db.PrismaClient) *PrismaTimeEntryDataSource {
 | 
			
		||||
func NewPrismaTimeEntryDataSource(client *db.PrismaClient) data.TimeEntryDataSource {
 | 
			
		||||
	return &PrismaTimeEntryDataSource{client: client}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package data
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"actatempus_backend/internal/domain/data"
 | 
			
		||||
	"actatempus_backend/internal/domain/entities"
 | 
			
		||||
	"actatempus_backend/internal/infrastructure/data/db"
 | 
			
		||||
	"actatempus_backend/internal/utils"
 | 
			
		||||
@ -16,7 +17,7 @@ type PrismaUserDataSource struct {
 | 
			
		||||
	client *db.PrismaClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewPrismaUserDataSource(client *db.PrismaClient) *PrismaUserDataSource {
 | 
			
		||||
func NewPrismaUserDataSource(client *db.PrismaClient) data.UserDataSource {
 | 
			
		||||
	return &PrismaUserDataSource{client: client}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								backend-go/internal/tests/mocks/helper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								backend-go/internal/tests/mocks/helper.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GenerateID generates a new UUID string.
 | 
			
		||||
func GenerateID() string {
 | 
			
		||||
	return uuid.New().String()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								backend-go/internal/tests/mocks/mock_project_data_source.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								backend-go/internal/tests/mocks/mock_project_data_source.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,125 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"actatempus_backend/internal/domain/data"
 | 
			
		||||
	"actatempus_backend/internal/domain/entities"
 | 
			
		||||
	impl "actatempus_backend/internal/infrastructure/data"
 | 
			
		||||
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	E "github.com/IBM/fp-go/either"
 | 
			
		||||
	F "github.com/IBM/fp-go/function"
 | 
			
		||||
	O "github.com/IBM/fp-go/option"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MockProjectDataSource struct {
 | 
			
		||||
	store map[string]*entities.Project
 | 
			
		||||
	mu    sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMockProjectDataSource() data.ProjectDataSource {
 | 
			
		||||
	return &MockProjectDataSource{
 | 
			
		||||
		store: make(map[string]*entities.Project),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create adds a new project to the store
 | 
			
		||||
func (ds *MockProjectDataSource) Create(ctx context.Context, project entities.ProjectCreate) E.Either[error, entities.Project] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	id := GenerateID()
 | 
			
		||||
	newProject := entities.Project{
 | 
			
		||||
		ID:          id,
 | 
			
		||||
		Name:        project.Name,
 | 
			
		||||
		UserID:      project.UserID,
 | 
			
		||||
		Description: project.Description,
 | 
			
		||||
		ClientID:    project.ClientID,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ds.store[id] = &newProject
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](newProject)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindByID retrieves a project by its ID
 | 
			
		||||
func (ds *MockProjectDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.Project] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return F.Pipe2(
 | 
			
		||||
		O.FromNillable(ds.store[id]),
 | 
			
		||||
		E.FromOption[*entities.Project](F.Constant(fmt.Errorf("project with ID %s not found", id))),
 | 
			
		||||
		E.Chain(impl.TryDereference[entities.Project]),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update modifies an existing project
 | 
			
		||||
func (ds *MockProjectDataSource) Update(ctx context.Context, project entities.ProjectUpdate) E.Either[error, entities.Project] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	existing, found := ds.store[project.ID]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return E.Left[entities.Project](fmt.Errorf("project with ID %s not found", project.ID))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if project.Name != nil {
 | 
			
		||||
		existing.Name = *project.Name
 | 
			
		||||
	}
 | 
			
		||||
	if project.Description != nil {
 | 
			
		||||
		existing.Description = project.Description
 | 
			
		||||
	}
 | 
			
		||||
	if project.ClientID != nil {
 | 
			
		||||
		existing.ClientID = project.ClientID
 | 
			
		||||
	}
 | 
			
		||||
	if project.UserID != nil {
 | 
			
		||||
		existing.UserID = *project.UserID
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](*existing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete removes a project from the store
 | 
			
		||||
func (ds *MockProjectDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.Project] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	existing, found := ds.store[id]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return E.Left[entities.Project](fmt.Errorf("project with ID %s not found", id))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(ds.store, id)
 | 
			
		||||
	return E.Right[error](*existing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindAll retrieves all projects
 | 
			
		||||
func (ds *MockProjectDataSource) FindAll(ctx context.Context) E.Either[error, []entities.Project] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	projects := make([]entities.Project, 0, len(ds.store))
 | 
			
		||||
	for _, project := range ds.store {
 | 
			
		||||
		projects = append(projects, *project)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](projects)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindByUserID retrieves all projects for a specific user
 | 
			
		||||
func (ds *MockProjectDataSource) FindByUserID(ctx context.Context, userID string) E.Either[error, []entities.Project] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	projects := make([]entities.Project, 0)
 | 
			
		||||
	for _, project := range ds.store {
 | 
			
		||||
		if project.UserID == userID {
 | 
			
		||||
			projects = append(projects, *project)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](projects)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										120
									
								
								backend-go/internal/tests/mocks/mock_project_task_data_source.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								backend-go/internal/tests/mocks/mock_project_task_data_source.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,120 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"actatempus_backend/internal/domain/data"
 | 
			
		||||
	"actatempus_backend/internal/domain/entities"
 | 
			
		||||
	impl "actatempus_backend/internal/infrastructure/data"
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	E "github.com/IBM/fp-go/either"
 | 
			
		||||
	F "github.com/IBM/fp-go/function"
 | 
			
		||||
	O "github.com/IBM/fp-go/option"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MockProjectTaskDataSource struct {
 | 
			
		||||
	store map[string]*entities.ProjectTask
 | 
			
		||||
	mu    sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMockProjectTaskDataSource() data.ProjectTaskDataSource {
 | 
			
		||||
	return &MockProjectTaskDataSource{
 | 
			
		||||
		store: make(map[string]*entities.ProjectTask),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a new ProjectTask
 | 
			
		||||
func (ds *MockProjectTaskDataSource) Create(ctx context.Context, task entities.ProjectTaskCreate) E.Either[error, entities.ProjectTask] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	id := GenerateID()
 | 
			
		||||
	newTask := entities.ProjectTask{
 | 
			
		||||
		ID:          id,
 | 
			
		||||
		Name:        task.Name,
 | 
			
		||||
		ProjectID:   task.ProjectID,
 | 
			
		||||
		Description: task.Description,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ds.store[id] = &newTask
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](newTask)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Find ProjectTask by ID
 | 
			
		||||
func (ds *MockProjectTaskDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.ProjectTask] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return F.Pipe2(
 | 
			
		||||
		O.FromNillable(ds.store[id]),
 | 
			
		||||
		E.FromOption[*entities.ProjectTask](F.Constant(fmt.Errorf("project task with ID %s not found", id))),
 | 
			
		||||
		E.Chain(impl.TryDereference[entities.ProjectTask]),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update an existing ProjectTask
 | 
			
		||||
func (ds *MockProjectTaskDataSource) Update(ctx context.Context, task entities.ProjectTaskUpdate) E.Either[error, entities.ProjectTask] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	existing, found := ds.store[task.ID]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return E.Left[entities.ProjectTask](fmt.Errorf("project task with ID %s not found", task.ID))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if task.Name != nil {
 | 
			
		||||
		existing.Name = *task.Name
 | 
			
		||||
	}
 | 
			
		||||
	if task.Description != nil {
 | 
			
		||||
		existing.Description = task.Description
 | 
			
		||||
	}
 | 
			
		||||
	if task.ProjectID != nil {
 | 
			
		||||
		existing.ProjectID = *task.ProjectID
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](*existing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete a ProjectTask
 | 
			
		||||
func (ds *MockProjectTaskDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.ProjectTask] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	existing, found := ds.store[id]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return E.Left[entities.ProjectTask](fmt.Errorf("project task with ID %s not found", id))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(ds.store, id)
 | 
			
		||||
	return E.Right[error](*existing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindAll retrieves all ProjectTasks
 | 
			
		||||
func (ds *MockProjectTaskDataSource) FindAll(ctx context.Context) E.Either[error, []entities.ProjectTask] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	tasks := make([]entities.ProjectTask, 0, len(ds.store))
 | 
			
		||||
	for _, task := range ds.store {
 | 
			
		||||
		tasks = append(tasks, *task)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](tasks)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindByProjectID retrieves all ProjectTasks for a given Project
 | 
			
		||||
func (ds *MockProjectTaskDataSource) FindByProjectID(ctx context.Context, projectID string) E.Either[error, []entities.ProjectTask] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	tasks := make([]entities.ProjectTask, 0)
 | 
			
		||||
	for _, task := range ds.store {
 | 
			
		||||
		if task.ProjectID == projectID {
 | 
			
		||||
			tasks = append(tasks, *task)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](tasks)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										144
									
								
								backend-go/internal/tests/mocks/mock_time_entry_data_source.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								backend-go/internal/tests/mocks/mock_time_entry_data_source.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,144 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	impl "actatempus_backend/internal/infrastructure/data"
 | 
			
		||||
 | 
			
		||||
	"actatempus_backend/internal/domain/data"
 | 
			
		||||
	"actatempus_backend/internal/domain/entities"
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	E "github.com/IBM/fp-go/either"
 | 
			
		||||
	F "github.com/IBM/fp-go/function"
 | 
			
		||||
	O "github.com/IBM/fp-go/option"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MockTimeEntryDataSource struct {
 | 
			
		||||
	store map[string]*entities.TimeEntry
 | 
			
		||||
	mu    sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMockTimeEntryDataSource() data.TimeEntryDataSource {
 | 
			
		||||
	return &MockTimeEntryDataSource{
 | 
			
		||||
		store: make(map[string]*entities.TimeEntry),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a new TimeEntry
 | 
			
		||||
func (ds *MockTimeEntryDataSource) Create(ctx context.Context, entry entities.TimeEntryCreate) E.Either[error, entities.TimeEntry] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	id := GenerateID()
 | 
			
		||||
	newEntry := entities.TimeEntry{
 | 
			
		||||
		ID:          id,
 | 
			
		||||
		StartTime:   entry.StartTime,
 | 
			
		||||
		EndTime:     entry.EndTime,
 | 
			
		||||
		Description: entry.Description,
 | 
			
		||||
		UserID:      entry.UserID,
 | 
			
		||||
		ProjectID:   entry.ProjectID,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ds.store[id] = &newEntry
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](newEntry)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Find TimeEntry by ID
 | 
			
		||||
func (ds *MockTimeEntryDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.TimeEntry] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return F.Pipe2(
 | 
			
		||||
		O.FromNillable(ds.store[id]),
 | 
			
		||||
		E.FromOption[*entities.TimeEntry](F.Constant(fmt.Errorf("time entry with ID %s not found", id))),
 | 
			
		||||
		E.Chain(impl.TryDereference[entities.TimeEntry]),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update an existing TimeEntry
 | 
			
		||||
func (ds *MockTimeEntryDataSource) Update(ctx context.Context, entry entities.TimeEntryUpdate) E.Either[error, entities.TimeEntry] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	existing, found := ds.store[entry.ID]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return E.Left[entities.TimeEntry](fmt.Errorf("time entry with ID %s not found", entry.ID))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if entry.StartTime != nil {
 | 
			
		||||
		existing.StartTime = *entry.StartTime
 | 
			
		||||
	}
 | 
			
		||||
	if entry.EndTime != nil {
 | 
			
		||||
		existing.EndTime = entry.EndTime
 | 
			
		||||
	}
 | 
			
		||||
	if entry.Description != nil {
 | 
			
		||||
		existing.Description = entry.Description
 | 
			
		||||
	}
 | 
			
		||||
	if entry.UserID != nil {
 | 
			
		||||
		existing.UserID = *entry.UserID
 | 
			
		||||
	}
 | 
			
		||||
	if entry.ProjectID != nil {
 | 
			
		||||
		existing.ProjectID = *entry.ProjectID
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](*existing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete a TimeEntry
 | 
			
		||||
func (ds *MockTimeEntryDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.TimeEntry] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	existing, found := ds.store[id]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return E.Left[entities.TimeEntry](fmt.Errorf("time entry with ID %s not found", id))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(ds.store, id)
 | 
			
		||||
	return E.Right[error](*existing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindAll retrieves all TimeEntries
 | 
			
		||||
func (ds *MockTimeEntryDataSource) FindAll(ctx context.Context) E.Either[error, []entities.TimeEntry] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	entries := make([]entities.TimeEntry, 0, len(ds.store))
 | 
			
		||||
	for _, entry := range ds.store {
 | 
			
		||||
		entries = append(entries, *entry)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](entries)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindByUserID retrieves all TimeEntries by UserID
 | 
			
		||||
func (ds *MockTimeEntryDataSource) FindByUserID(ctx context.Context, userID string) E.Either[error, []entities.TimeEntry] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	entries := make([]entities.TimeEntry, 0)
 | 
			
		||||
	for _, entry := range ds.store {
 | 
			
		||||
		if entry.UserID == userID {
 | 
			
		||||
			entries = append(entries, *entry)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](entries)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindByProjectID retrieves all TimeEntries by ProjectID
 | 
			
		||||
func (ds *MockTimeEntryDataSource) FindByProjectID(ctx context.Context, projectID string) E.Either[error, []entities.TimeEntry] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	entries := make([]entities.TimeEntry, 0)
 | 
			
		||||
	for _, entry := range ds.store {
 | 
			
		||||
		if entry.ProjectID == projectID {
 | 
			
		||||
			entries = append(entries, *entry)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](entries)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										113
									
								
								backend-go/internal/tests/mocks/mock_user_data_source.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								backend-go/internal/tests/mocks/mock_user_data_source.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	domain "actatempus_backend/internal/domain/data"
 | 
			
		||||
	"actatempus_backend/internal/domain/entities"
 | 
			
		||||
	impl "actatempus_backend/internal/infrastructure/data"
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	E "github.com/IBM/fp-go/either"
 | 
			
		||||
	F "github.com/IBM/fp-go/function"
 | 
			
		||||
	O "github.com/IBM/fp-go/option"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MockUserDataSource struct {
 | 
			
		||||
	store map[string]*entities.User
 | 
			
		||||
	mu    sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMockUserDataSource() domain.UserDataSource {
 | 
			
		||||
	return &MockUserDataSource{
 | 
			
		||||
		store: make(map[string]*entities.User),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ds *MockUserDataSource) Create(ctx context.Context, user entities.UserCreate) E.Either[error, entities.User] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	id := GenerateID()
 | 
			
		||||
	newUser := entities.User{
 | 
			
		||||
		ID:       id,
 | 
			
		||||
		Name:     user.Name,
 | 
			
		||||
		Email:    user.Email,
 | 
			
		||||
		Password: impl.GenerateSecureHash(user.Password),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ds.store[id] = &newUser
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](newUser)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ds *MockUserDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.User] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
	return F.Pipe2(
 | 
			
		||||
		O.FromNillable(ds.store[id]),
 | 
			
		||||
		E.FromOption[*entities.User](F.Constant(fmt.Errorf("user with ID %s not found", id))),
 | 
			
		||||
		E.Chain(
 | 
			
		||||
			impl.TryDereference[entities.User],
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ds *MockUserDataSource) FindByEmail(ctx context.Context, email string) E.Either[error, entities.User] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	for _, user := range ds.store {
 | 
			
		||||
		if user.Email == email {
 | 
			
		||||
			return E.Right[error](*user)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return E.Left[entities.User](fmt.Errorf("user with email %s not found", email))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ds *MockUserDataSource) Update(ctx context.Context, user entities.UserUpdate) E.Either[error, entities.User] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	existing, found := ds.store[user.ID]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return E.Left[entities.User](fmt.Errorf("user with ID %s not found", user.ID))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if user.Name != nil {
 | 
			
		||||
		existing.Name = *user.Name
 | 
			
		||||
	}
 | 
			
		||||
	if user.Email != nil {
 | 
			
		||||
		existing.Email = *user.Email
 | 
			
		||||
	}
 | 
			
		||||
	if user.Password != nil {
 | 
			
		||||
		existing.Password = impl.GenerateSecureHash(*user.Password)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](*existing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ds *MockUserDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.User] {
 | 
			
		||||
	ds.mu.Lock()
 | 
			
		||||
	defer ds.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	existing, found := ds.store[id]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return E.Left[entities.User](fmt.Errorf("user with ID %s not found", id))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(ds.store, id)
 | 
			
		||||
	return E.Right[error](*existing)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ds *MockUserDataSource) FindAll(ctx context.Context) E.Either[error, []entities.User] {
 | 
			
		||||
	ds.mu.RLock()
 | 
			
		||||
	defer ds.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	users := make([]entities.User, 0, len(ds.store))
 | 
			
		||||
	for _, user := range ds.store {
 | 
			
		||||
		users = append(users, *user)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E.Right[error](users)
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user