fix test error

This commit is contained in:
Pedro Pérez 2025-10-10 03:09:34 +02:00
parent 40ffee4d56
commit 7bc9f1c987
3 changed files with 445 additions and 8 deletions

View File

@ -33,7 +33,7 @@ mock:
.PHONY: test
# Run tests
tests:
go test ./...
go test ./... -cover
.PHONY: run
# Start app in development environment

View File

@ -123,9 +123,9 @@ Por otro lado también hay un sistema de caché muy rudimentario, en memoria que
es un mapa de valores.
Para el registro de valores y mantener ambos se ha usado el patrón decorador que
bajo un mismo _struct_ se incluye las dos implementaciones y se llama a ambas
funciones. Desde la capa servicios sólo tiene que llamar al decorador sin saber
los detalles de la implementación.
bajo un mismo _struct_ se incluye las dos implementaciones y registra cambios en
ambas partes. Desde la capa servicios sólo tiene que llamar al decorador sin
saber los detalles de la implementación.
### Continuamos con los servicios
@ -151,6 +151,20 @@ documentación me quedé con los conceptos clave:
Esto es todo, entonces los controladores de la entidad _sensors_ están
constituidos por una serie de _endpoints_ haciendo las acciones que se solicita.
### El simulador
Basada en _gorutinas_ y canales, cuando se inicia el simulador, se crea un canal
para detener simuladores que están en ejecución para su actualización o
detención.
Cuando se registra un nuevo sensor, está la función SimulateSensor, que se
inicia como una _gorutina_ y usa el `SamplingInterval` para el canal `ticker`,
así llamar a `generateData` cada vez que toque.
Una vez que el dato está generado se hace una publicación al asunto _sensor.data_,
que al mismo tiempo, el _handler_ registerData lo captura al estar registrado
al mismo asunto _sensor.data_.
## Pruebas
La realización de pruebas unitarias de lo que son los controladores de NATS me

View File

@ -1,6 +1,7 @@
package sensors
import (
"errors"
"testing"
"time"
@ -20,6 +21,7 @@ func Test_RegisterSensor(t *testing.T) {
given Sensor
setupMock func(q *MockRepository, params Sensor)
expecErr bool
expectErr error
}
tests := []testCase{
@ -47,9 +49,25 @@ func Test_RegisterSensor(t *testing.T) {
ThresholdBelow: ptr(0.0),
},
setupMock: func(q *MockRepository, params Sensor) {
q.EXPECT().CreateSensor(params).Return(ErrSensorAlreadyExists)
q.EXPECT().CreateSensor(params).Return(errors.New("duplicate key value"))
},
expecErr: true,
expecErr: true,
expectErr: ErrSensorAlreadyExists,
},
{
name: "error - some db error",
given: Sensor{
SensorID: "temp-001",
SensorType: Temperature,
SamplingInterval: ptr(time.Minute),
ThresholdAbove: ptr(100.0),
ThresholdBelow: ptr(0.0),
},
setupMock: func(q *MockRepository, params Sensor) {
q.EXPECT().CreateSensor(params).Return(errors.New("some db error"))
},
expecErr: true,
expectErr: ErrRegisteringSensor,
},
}
@ -71,8 +89,413 @@ func Test_RegisterSensor(t *testing.T) {
return
}
if tt.expecErr && err != ErrSensorAlreadyExists {
t.Errorf("expected ErrSensorAlreadyExists, got %v", err)
if tt.expecErr && tt.expectErr != nil && err != tt.expectErr {
t.Errorf("expected error %v, got %v", tt.expectErr, err)
}
})
}
}
func Test_RegisterSensorData(t *testing.T) {
type testCase struct {
name string
given SensorData
setupMock func(q *MockRepository, params SensorData)
expecErr bool
}
timestamp := time.Now()
value := 25.5
tests := []testCase{
{
name: "success - registers sensor data",
given: SensorData{
SensorID: "temp-001",
Value: &value,
Timestamp: &timestamp,
},
setupMock: func(q *MockRepository, params SensorData) {
q.EXPECT().CreateSensorData(params).Return(nil)
},
expecErr: false,
},
{
name: "error - database error",
given: SensorData{
SensorID: "temp-001",
Value: &value,
Timestamp: &timestamp,
},
setupMock: func(q *MockRepository, params SensorData) {
q.EXPECT().CreateSensorData(params).Return(errors.New("database error"))
},
expecErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, q := setup(t)
tt.setupMock(q, tt.given)
err := s.RegisterSensorData(tt.given)
if tt.expecErr && err == nil {
t.Error("expected error, got nil")
return
}
if !tt.expecErr && err != nil {
t.Errorf("expected no error, got %v", err)
}
})
}
}
func Test_UpdateSensor(t *testing.T) {
type testCase struct {
name string
given Sensor
setupMock func(q *MockRepository, params Sensor)
expecErr bool
expectErr error
}
tests := []testCase{
{
name: "success - updates sensor",
given: Sensor{
SensorID: "temp-001",
SensorType: Temperature,
SamplingInterval: ptr(time.Minute * 2),
ThresholdAbove: ptr(120.0),
ThresholdBelow: ptr(10.0),
},
setupMock: func(q *MockRepository, params Sensor) {
q.EXPECT().UpdateSensor(params).Return(nil)
},
expecErr: false,
},
{
name: "error - sensor already exists (duplicate)",
given: Sensor{
SensorID: "temp-002",
SensorType: Temperature,
SamplingInterval: ptr(time.Minute),
ThresholdAbove: ptr(100.0),
ThresholdBelow: ptr(0.0),
},
setupMock: func(q *MockRepository, params Sensor) {
q.EXPECT().UpdateSensor(params).Return(errors.New("duplicate key value"))
},
expecErr: true,
expectErr: ErrSensorAlreadyExists,
},
{
name: "error - general database error",
given: Sensor{
SensorID: "temp-001",
SensorType: Temperature,
SamplingInterval: ptr(time.Minute),
ThresholdAbove: ptr(100.0),
ThresholdBelow: ptr(0.0),
},
setupMock: func(q *MockRepository, params Sensor) {
q.EXPECT().UpdateSensor(params).Return(errors.New("connection failed"))
},
expecErr: true,
expectErr: ErrUpdatingSensor,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, q := setup(t)
tt.setupMock(q, tt.given)
err := s.UpdateSensor(tt.given)
if tt.expecErr && err == nil {
t.Error("expected error, got nil")
return
}
if !tt.expecErr && err != nil {
t.Errorf("expected no error, got %v", err)
return
}
if tt.expecErr && tt.expectErr != nil && err != tt.expectErr {
t.Errorf("expected error %v, got %v", tt.expectErr, err)
}
})
}
}
func Test_GetSensor(t *testing.T) {
type testCase struct {
name string
given SensorRequest
setupMock func(q *MockRepository, sensorID string)
expected Sensor
expecErr bool
}
tests := []testCase{
{
name: "success - retrieves sensor",
given: SensorRequest{
SensorID: "temp-001",
},
setupMock: func(q *MockRepository, sensorID string) {
q.EXPECT().ReadSensor(sensorID).Return(Sensor{
SensorID: "temp-001",
SensorType: Temperature,
SamplingInterval: ptr(time.Minute),
ThresholdAbove: ptr(100.0),
ThresholdBelow: ptr(0.0),
}, nil)
},
expected: Sensor{
SensorID: "temp-001",
SensorType: Temperature,
SamplingInterval: ptr(time.Minute),
ThresholdAbove: ptr(100.0),
ThresholdBelow: ptr(0.0),
},
expecErr: false,
},
{
name: "error - sensor not found",
given: SensorRequest{
SensorID: "temp-999",
},
setupMock: func(q *MockRepository, sensorID string) {
q.EXPECT().ReadSensor(sensorID).Return(Sensor{}, ErrSensorNotFound)
},
expected: Sensor{},
expecErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, q := setup(t)
tt.setupMock(q, tt.given.SensorID)
result, err := s.GetSensor(tt.given)
if tt.expecErr && err == nil {
t.Error("expected error, got nil")
return
}
if !tt.expecErr && err != nil {
t.Errorf("expected no error, got %v", err)
return
}
if !tt.expecErr {
if result.SensorID != tt.expected.SensorID {
t.Errorf("expected sensor_id %q, got %q", tt.expected.SensorID, result.SensorID)
}
if result.SensorType != tt.expected.SensorType {
t.Errorf("expected sensor_type %q, got %q", tt.expected.SensorType, result.SensorType)
}
}
})
}
}
func Test_GetValues(t *testing.T) {
type testCase struct {
name string
given SensorDataRequest
setupMock func(q *MockRepository, sensorID string, from, to time.Time)
expected []SensorData
expecErr bool
}
now := time.Now()
weekAgo := now.AddDate(0, 0, -7)
fromStr := weekAgo.Format(time.RFC3339)
toStr := now.Format(time.RFC3339)
value1 := 25.5
value2 := 26.0
tests := []testCase{
{
name: "success - retrieves sensor data",
given: SensorDataRequest{
SensorID: "temp-001",
From: &fromStr,
To: &toStr,
},
setupMock: func(q *MockRepository, sensorID string, from, to time.Time) {
q.EXPECT().ReadSensorValues(sensorID, from, to).Return([]SensorData{
{
SensorID: "temp-001",
Value: &value1,
Timestamp: &weekAgo,
},
{
SensorID: "temp-001",
Value: &value2,
Timestamp: &now,
},
}, nil)
},
expected: []SensorData{
{
SensorID: "temp-001",
Value: &value1,
Timestamp: &weekAgo,
},
{
SensorID: "temp-001",
Value: &value2,
Timestamp: &now,
},
},
expecErr: false,
},
{
name: "error - database error",
given: SensorDataRequest{
SensorID: "temp-001",
From: &fromStr,
To: &toStr,
},
setupMock: func(q *MockRepository, sensorID string, from, to time.Time) {
q.EXPECT().ReadSensorValues(sensorID, from, to).Return([]SensorData{}, errors.New("database error"))
},
expected: []SensorData{},
expecErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, q := setup(t)
from, _ := time.Parse(time.RFC3339, *tt.given.From)
to, _ := time.Parse(time.RFC3339, *tt.given.To)
tt.setupMock(q, tt.given.SensorID, from, to)
result, err := s.GetValues(tt.given)
if tt.expecErr && err == nil {
t.Error("expected error, got nil")
return
}
if !tt.expecErr && err != nil {
t.Errorf("expected no error, got %v", err)
return
}
if !tt.expecErr {
if len(result) != len(tt.expected) {
t.Errorf("expected %d values, got %d", len(tt.expected), len(result))
}
}
})
}
}
func Test_ListSensors(t *testing.T) {
type testCase struct {
name string
setupMock func(q *MockRepository)
expected []Sensor
expecErr bool
}
tests := []testCase{
{
name: "success - retrieves all sensors",
setupMock: func(q *MockRepository) {
q.EXPECT().ReadAllSensors().Return([]Sensor{
{
SensorID: "temp-001",
SensorType: Temperature,
SamplingInterval: ptr(time.Minute),
ThresholdAbove: ptr(100.0),
ThresholdBelow: ptr(0.0),
},
{
SensorID: "hum-001",
SensorType: Humidity,
SamplingInterval: ptr(time.Minute * 2),
ThresholdAbove: ptr(80.0),
ThresholdBelow: ptr(20.0),
},
}, nil)
},
expected: []Sensor{
{
SensorID: "temp-001",
SensorType: Temperature,
SamplingInterval: ptr(time.Minute),
ThresholdAbove: ptr(100.0),
ThresholdBelow: ptr(0.0),
},
{
SensorID: "hum-001",
SensorType: Humidity,
SamplingInterval: ptr(time.Minute * 2),
ThresholdAbove: ptr(80.0),
ThresholdBelow: ptr(20.0),
},
},
expecErr: false,
},
{
name: "success - empty list when no sensors",
setupMock: func(q *MockRepository) {
q.EXPECT().ReadAllSensors().Return([]Sensor{}, nil)
},
expected: []Sensor{},
expecErr: false,
},
{
name: "error - database error",
setupMock: func(q *MockRepository) {
q.EXPECT().ReadAllSensors().Return([]Sensor{}, errors.New("database error"))
},
expected: []Sensor{},
expecErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, q := setup(t)
tt.setupMock(q)
result, err := s.ListSensors()
if tt.expecErr && err == nil {
t.Error("expected error, got nil")
return
}
if !tt.expecErr && err != nil {
t.Errorf("expected no error, got %v", err)
return
}
if !tt.expecErr {
if len(result) != len(tt.expected) {
t.Errorf("expected %d sensors, got %d", len(tt.expected), len(result))
}
}
})
}