Compare commits
No commits in common. "6e89d5a8f568ba79420da75c169d589a2428b040" and "40ffee4d5601a34372b4be87dbb5b376164feaf0" have entirely different histories.
6e89d5a8f5
...
40ffee4d56
2
Makefile
2
Makefile
@ -33,7 +33,7 @@ mock:
|
|||||||
.PHONY: test
|
.PHONY: test
|
||||||
# Run tests
|
# Run tests
|
||||||
tests:
|
tests:
|
||||||
go test ./... -cover
|
go test ./...
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
# Start app in development environment
|
# Start app in development environment
|
||||||
|
|||||||
83
README.md
83
README.md
@ -123,9 +123,9 @@ Por otro lado también hay un sistema de caché muy rudimentario, en memoria que
|
|||||||
es un mapa de valores.
|
es un mapa de valores.
|
||||||
|
|
||||||
Para el registro de valores y mantener ambos se ha usado el patrón decorador que
|
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 registra cambios en
|
bajo un mismo _struct_ se incluye las dos implementaciones y se llama a ambas
|
||||||
ambas partes. Desde la capa servicios sólo tiene que llamar al decorador sin
|
funciones. Desde la capa servicios sólo tiene que llamar al decorador sin saber
|
||||||
saber los detalles de la implementación.
|
los detalles de la implementación.
|
||||||
|
|
||||||
### Continuamos con los servicios
|
### Continuamos con los servicios
|
||||||
|
|
||||||
@ -151,20 +151,6 @@ documentación me quedé con los conceptos clave:
|
|||||||
Esto es todo, entonces los controladores de la entidad _sensors_ están
|
Esto es todo, entonces los controladores de la entidad _sensors_ están
|
||||||
constituidos por una serie de _endpoints_ haciendo las acciones que se solicita.
|
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
|
## Pruebas
|
||||||
|
|
||||||
La realización de pruebas unitarias de lo que son los controladores de NATS me
|
La realización de pruebas unitarias de lo que son los controladores de NATS me
|
||||||
@ -175,6 +161,12 @@ he optado por no incorporarlas.
|
|||||||
Las pruebas más interesantes son las de reglas de negocio y validación, lo que
|
Las pruebas más interesantes son las de reglas de negocio y validación, lo que
|
||||||
viene a ser los servicios y dominio.
|
viene a ser los servicios y dominio.
|
||||||
|
|
||||||
|
## LLMS
|
||||||
|
|
||||||
|
He usado Claude para la toma de decisiones y ayuda con el _boilerplate_, que no
|
||||||
|
es poca cosa, además también se ha usado para la generación de las pruebas
|
||||||
|
unitarias, además de resolución de algunos problemas complejos.
|
||||||
|
|
||||||
## Generadores y otras librerías
|
## Generadores y otras librerías
|
||||||
|
|
||||||
Existen generadores de código para Golang, de hecho, se fomenta su desarrollo,
|
Existen generadores de código para Golang, de hecho, se fomenta su desarrollo,
|
||||||
@ -205,60 +197,3 @@ sí, pero la prueba no consiste en eso.
|
|||||||
También se ha planteado incorporar la librería _testify_, descartado porque para
|
También se ha planteado incorporar la librería _testify_, descartado porque para
|
||||||
comprobar si existe el error y algunas comparaciones no era necesario meter una
|
comprobar si existe el error y algunas comparaciones no era necesario meter una
|
||||||
dependencia más.
|
dependencia más.
|
||||||
|
|
||||||
## Diagrama
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
El diagrama explica básicamente la estrucutra del proyecto en términos generales,
|
|
||||||
se demuestra que el dominio sensors solo se comunica al exterior mediante el
|
|
||||||
NATS y el _logger_.
|
|
||||||
|
|
||||||
Amarillo: exterior.
|
|
||||||
Morado: infraestructura.
|
|
||||||
Verde: dominio
|
|
||||||
Blanco: simulador, los _handlers_ está conectado sólo para llamar a la
|
|
||||||
gorutina, pero en la realidad debería ir independiente.
|
|
||||||
|
|
||||||
## Conclusión y cierre
|
|
||||||
|
|
||||||
Interesante reto donde realmente lo que más me ha costado son la realización
|
|
||||||
de pruebas unitarias, hay veces que me cuesta coger el concepto. Tengo la teoría
|
|
||||||
muy clara, pero a la hora de la verdad se me complica un poco las cosas. Además
|
|
||||||
que _mockear_ el sistema de mensajería debe tener su especial complejidad.
|
|
||||||
También ha sido algo complejo entender la concurrencia y los canales, no es algo
|
|
||||||
que haya trabajado en profundidad pero sí que se le ha puesto bastante empeño,
|
|
||||||
cariño y nivel de detalle.
|
|
||||||
|
|
||||||
He puesto mucho en valor la arquitectura limpia con un toque personal, no es DDD
|
|
||||||
puro ya que hay elementos que no deberían estar en dominio, pero en el proyecto
|
|
||||||
que estoy trabajando ahora mismo se está diseñando de la misma manera y está
|
|
||||||
funcionando muy bien.
|
|
||||||
|
|
||||||
También se ha evitado todo lo posible el uso de LLMs para la generación de
|
|
||||||
código, y su uso ha sido para la toma de decisiones arquitectónicas, discusión y
|
|
||||||
lectura rápida sobre los distintos funcionamientos de algunas librerías. En más
|
|
||||||
de una ocasión he cuestionado las respuestas que da, teniendo que verificar con
|
|
||||||
la documentación oficial. Pongo en valor mi capacidad para aprovechar la IA de
|
|
||||||
la mejor forma posible, verificando la información, además recalco que justo el
|
|
||||||
proyecto actual es una migración de un código en PHP completamente hecho con IA,
|
|
||||||
y se puede ver patrones y errores comunes que comete.
|
|
||||||
|
|
||||||
Soy consciente de que hay margen de mejora, por ejemplo con los tests o con la
|
|
||||||
documentación, se ha puesto especial esfuerzo y atención a que los nombres de
|
|
||||||
las funciones, variables, métodos, estructuras y paquetes sean lo más
|
|
||||||
autodescriptivos posibles. Se han puesto algunos comentarios. También hay
|
|
||||||
esfuerzo por permitir ejecutar el proyecto por primera vez con la mínima
|
|
||||||
intervención.
|
|
||||||
|
|
||||||
Un problema interesante que tuve que resolver, que como el sensor puede mandar
|
|
||||||
un valor ausente, el tipo `float64` al hacer el `unmarshal` se establece a 0.0,
|
|
||||||
con lo que se puede considerar válido, con lo cual su solución fue el uso de
|
|
||||||
puntero, si se descubre que es `nil` se considera no válido.
|
|
||||||
|
|
||||||
Digamos que este proyecto resuelve el problema que se propone, un sistema que
|
|
||||||
permite registrar y actualziar un sensor. Se puede ver su estado y los datos que
|
|
||||||
se recogen (simulados) se guardan en una base de datos.
|
|
||||||
|
|
||||||
Espero que el proyecto sea de vuestro agrado y podamos tener una siguiente
|
|
||||||
reunión.
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB |
@ -1,7 +1,6 @@
|
|||||||
package sensors
|
package sensors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -21,7 +20,6 @@ func Test_RegisterSensor(t *testing.T) {
|
|||||||
given Sensor
|
given Sensor
|
||||||
setupMock func(q *MockRepository, params Sensor)
|
setupMock func(q *MockRepository, params Sensor)
|
||||||
expecErr bool
|
expecErr bool
|
||||||
expectErr error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []testCase{
|
tests := []testCase{
|
||||||
@ -49,25 +47,9 @@ func Test_RegisterSensor(t *testing.T) {
|
|||||||
ThresholdBelow: ptr(0.0),
|
ThresholdBelow: ptr(0.0),
|
||||||
},
|
},
|
||||||
setupMock: func(q *MockRepository, params Sensor) {
|
setupMock: func(q *MockRepository, params Sensor) {
|
||||||
q.EXPECT().CreateSensor(params).Return(errors.New("duplicate key value"))
|
q.EXPECT().CreateSensor(params).Return(ErrSensorAlreadyExists)
|
||||||
},
|
},
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,413 +71,8 @@ func Test_RegisterSensor(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.expecErr && tt.expectErr != nil && err != tt.expectErr {
|
if tt.expecErr && err != ErrSensorAlreadyExists {
|
||||||
t.Errorf("expected error %v, got %v", tt.expectErr, err)
|
t.Errorf("expected ErrSensorAlreadyExists, got %v", 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: ×tamp,
|
|
||||||
},
|
|
||||||
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: ×tamp,
|
|
||||||
},
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user