package repository_test import ( "bytes" "context" "encoding/json" "fmt" "math/rand" "net/url" "os" "path/filepath" "testing" "time" "git.cryptic.systems/volker.raschek/dockerutils" "git.cryptic.systems/volker.raschek/flucky/pkg/repository" "git.cryptic.systems/volker.raschek/flucky/pkg/types" "git.cryptic.systems/volker.raschek/go-logger" uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/require" _ "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/database/sqlite3" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" ) func TestPostgresBackend(t *testing.T) { require := require.New(t) dockerClient, err := dockerutils.New() require.NoError(err) rand.Seed(time.Now().Unix()) postgresDBPasswort := "postgres" postgresContainerID, err := dockerClient.NewBuilder("postgres:13-alpine"). AddEnv("PGTZ", "Europe/Berlin"). AddEnv("POSTGRES_PASSWORD", postgresDBPasswort). AddEnv("TZ", "Europe/Berlin"). Mount("/etc/localtime", "/etc/localtime"). Pull(). Start(context.Background()) cleanup := func() { dockerClient.ContainerRemoveByIDs(context.Background(), postgresContainerID) } t.Cleanup(cleanup) require.NoError(err) time.Sleep(time.Second * 10) // inspect container to get his container ip cjson, err := dockerClient.ContainerInspect(context.Background(), postgresContainerID) require.NoError(err) // postgres://[user]:[password]@[host]:[port]/[path]?[query] dsnURL, err := url.Parse(fmt.Sprintf("postgres://postgres:%v@%v:5432?sslmode=disable", postgresDBPasswort, cjson.NetworkSettings.IPAddress)) require.NoError(err) repo, err := repository.New(dsnURL, logger.NewLogger(logger.LogLevelDebug)) require.NoError(err) testBackend(t, repo) } func TestSQLiteBackend(t *testing.T) { require := require.New(t) workspace := filepath.Join(os.TempDir(), uuid.NewV4().String()) err := os.MkdirAll(workspace, 0755) require.NoError(err) t.Cleanup(func() { os.RemoveAll(workspace) }) dsnURL, err := url.Parse(fmt.Sprintf("sqlite3://%v/test.db", workspace)) require.NoError(err) repo, err := repository.New(dsnURL, logger.NewLogger(logger.LogLevelDebug)) require.NoError(err) testBackend(t, repo) } func testBackend(t *testing.T, repo repository.Repository) { ctx := context.Background() require := require.New(t) location := uuid.NewV4().String() expectedDevices := []*types.Device{ { ID: "39b8f150-8abf-4539-9f16-7f68cedb1649", Name: "62e3978f-2198-4aa9-9d6f-cdc91a468b00", Location: &location, CreationDate: *timeNow(require), }, { ID: "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f", Name: "f2b245eb-b15f-40e1-9212-9a645907b710", Location: &location, CreationDate: *timeNow(require), }, } // Test: AddDevice err := repo.AddDevices(ctx, expectedDevices...) require.NoError(err) // Test: GetDevices devices, err := repo.GetDevices(ctx) require.NoError(err) require.Len(devices, len(expectedDevices)) require.JSONEq(jsonEncoder(expectedDevices), jsonEncoder(devices)) // Test: GetDeviceByID device, err := repo.GetDeviceByID(ctx, expectedDevices[0].ID) require.NoError(err) require.JSONEq(jsonEncoder(expectedDevices[0]), jsonEncoder(device)) // Test: GetDeviceByName device, err = repo.GetDeviceByName(ctx, expectedDevices[0].Name) require.NoError(err) require.JSONEq(jsonEncoder(expectedDevices[0]), jsonEncoder(device)) // Test: RemoveDevicesByIDs err = repo.RemoveDevicesByIDs(ctx, expectedDevices[0].ID) require.NoError(err) devices, err = repo.GetDevices(ctx) require.NoError(err) require.Len(devices, 1) device, err = repo.GetDeviceByID(ctx, expectedDevices[0].ID) require.NoError(err) require.Nil(device) // Test: RemoveDevicesByNames err = repo.RemoveDevicesByNames(ctx, expectedDevices[0].Name) require.NoError(err) devices, err = repo.GetDevices(ctx) require.NoError(err) require.Len(devices, 1) device, err = repo.GetDeviceByID(ctx, expectedDevices[0].ID) require.NoError(err) require.Nil(device) err = repo.AddDevices(ctx, expectedDevices[0]) require.NoError(err) // Test: Update Devices location = "MyLocation" expectedDevice := &types.Device{ ID: "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f", Name: "Hello World", Location: &location, CreationDate: *timeNow(require), } err = repo.UpdateDevices(ctx, expectedDevice) require.NoError(err) device, err = repo.GetDeviceByID(ctx, expectedDevice.ID) require.NoError(err) require.NotEmpty(device) require.Equal(expectedDevice.ID, device.ID) require.Equal(expectedDevice.Name, device.Name) require.Equal(expectedDevice.Location, device.Location) // Test: AddOrUpdateDevices location = "MySweetLocation" expectedDevice = &types.Device{ ID: "9d8c59a5-7927-4a7c-ad48-dbf5405ffd68", Name: "MySweetDevice", Location: &location, CreationDate: *timeNow(require), } err = repo.AddOrUpdateDevices(ctx, expectedDevice) require.NoError(err) // time.Sleep(time.Minute * 10) device, err = repo.GetDeviceByID(ctx, expectedDevice.ID) require.NoError(err) require.NotEmpty(device) require.Equal(expectedDevice.ID, device.ID) require.Equal(expectedDevice.Name, device.Name) require.Equal(expectedDevice.Location, device.Location) location = "MyUglyLocation" expectedDevice = &types.Device{ ID: "9d8c59a5-7927-4a7c-ad48-dbf5405ffd68", Name: "MyUglyDevice", Location: &location, CreationDate: *timeNow(require), } err = repo.AddOrUpdateDevices(ctx, expectedDevice) require.NoError(err) device, err = repo.GetDeviceByID(ctx, expectedDevice.ID) require.NoError(err) require.NotEmpty(device) require.Equal(expectedDevice.ID, device.ID) require.Equal(expectedDevice.Name, device.Name) require.Equal(expectedDevice.Location, device.Location) var ( wireID = "50473fdc-f6ef-4227-b3c4-484d8e9c1323" i2cBus = 1 i2cAddress uint8 = 76 expectedSensors = []*types.Sensor{ { ID: "0f8b88b0-c20d-42b2-ab51-b09ca99c0752", Name: "01fbdbe9-cebf-42ed-8065-bf4882ccf76b", Location: "6d5b5450-1f87-47cb-b185-f64c35fae3c1", GPIONumber: "GPIO14", Model: "DHT11", Enabled: true, TickDuration: "1m", DeviceID: "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f", CreationDate: *timeNow(require), }, { ID: "80b1c4bd-abec-4ff0-afb4-bd70aeed0c83", Name: "544365ee-ece9-44ea-911d-5d920a68d4ba", Location: "7ae2d05e-9e6b-4d2d-b26a-cb4acca83778", WireID: &wireID, Model: "DS18B20", Enabled: true, TickDuration: "5m", DeviceID: "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f", CreationDate: *timeNow(require), }, { ID: "8c74397f-8e60-4c9d-960d-3197747cef9a", Name: "4b808675-de02-4866-893d-1c77f23b9304", Location: "8a085c0f-dd3c-447f-8c4e-c4c1d869c7b6", I2CBus: &i2cBus, I2CAddress: &i2cAddress, Model: "BME280", Enabled: false, TickDuration: "10m", DeviceID: "39b8f150-8abf-4539-9f16-7f68cedb1649", CreationDate: *timeNow(require), }, } ) // Test: AddSensors err = repo.AddSensors(ctx, expectedSensors...) require.NoError(err) // Test: GetSensors sensors, err := repo.GetSensors(ctx) require.NoError(err) require.Len(sensors, len(expectedSensors)) // Test: GetSensorsByNames sensors, err = repo.GetSensorsByNames(ctx, "01fbdbe9-cebf-42ed-8065-bf4882ccf76b") require.NoError(err) require.Len(sensors, 1) require.JSONEq(jsonEncoder(expectedSensors[0]), jsonEncoder(sensors[0])) // Test: GetSensorsByModels sensors, err = repo.GetSensorsByModels(ctx, "BME280") require.NoError(err) require.Len(sensors, 1) require.JSONEq(jsonEncoder(expectedSensors[2]), jsonEncoder(sensors[0])) sensors, err = repo.GetSensorsByModels(ctx, "DS18B20") require.NoError(err) require.Len(sensors, 1) require.JSONEq(jsonEncoder(expectedSensors[1]), jsonEncoder(sensors[0])) sensors, err = repo.GetSensorsByModels(ctx, "DHT11") require.NoError(err) require.Len(sensors, 1) require.JSONEq(jsonEncoder(expectedSensors[0]), jsonEncoder(sensors[0])) sensors, err = repo.GetSensorsByModels(ctx, "DHT11", "DS18B20") require.NoError(err) require.Len(sensors, 2) require.JSONEq(jsonEncoder(expectedSensors[0:2]), jsonEncoder(sensors[0:2])) // Test: GetSensorByID sensor, err := repo.GetSensorByID(ctx, expectedSensors[0].ID) require.NoError(err) require.JSONEq(jsonEncoder(expectedSensors[0]), jsonEncoder(sensor)) // Test: GetSensorsByDeviceID sensors, err = repo.GetSensorsByDeviceIDs(ctx, "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f") require.NoError(err) require.Len(sensors, 2) require.JSONEq(jsonEncoder(expectedSensors[0:2]), jsonEncoder(sensors)) // Test: RemoveSensorsByIDs err = repo.RemoveSensorsByIDs(ctx, expectedSensors[0].ID) require.NoError(err) sensors, err = repo.GetSensors(ctx) require.NoError(err) require.Len(sensors, 2) sensors, err = repo.GetSensorsByDeviceIDs(ctx, "ec0be3ab-d26d-4f9b-a96e-23ae5c577f8f") require.NoError(err) require.Len(sensors, 1) sensor, err = repo.GetSensorByID(ctx, expectedSensors[0].ID) require.NoError(err) require.Nil(sensor) // Test: RemoveSensorsByNames err = repo.RemoveSensorsByNames(ctx, expectedSensors[1].Name) require.NoError(err) sensors, err = repo.GetSensors(ctx) require.NoError(err) require.Len(sensors, 1) sensor, err = repo.GetSensorByID(ctx, expectedSensors[1].ID) require.NoError(err) require.Nil(sensor) // Test: UpdateSensors expectedSensor := &types.Sensor{ ID: "8c74397f-8e60-4c9d-960d-3197747cef9a", Name: "4b808675-de02-4866-893d-1c77f23b9304", Location: "Über den Wolken muss die Freiheit wohl grenzenlos sein...", I2CBus: nil, I2CAddress: nil, Model: "SDS011", Enabled: true, TickDuration: "6h", DeviceID: "39b8f150-8abf-4539-9f16-7f68cedb1649", CreationDate: *timeNow(require), } err = repo.UpdateSensors(ctx, expectedSensor) require.NoError(err) sensor, err = repo.GetSensorByID(ctx, expectedSensor.ID) require.NoError(err) require.NotNil(sensor) // require.JSONEq(jsonEncoder(expectedSensor), jsonEncoder(sensor)) // Test: AddOrUpdateSensors expectedSensor = &types.Sensor{ ID: "9bba0e0a-e996-4242-966f-db21bab6752f", Name: "b4ac3d0f-cef6-4e93-bd7b-e821ae5ab593", Location: "HelloWorld", I2CBus: nil, I2CAddress: nil, Model: "SDS011", Enabled: true, TickDuration: "6h", DeviceID: "39b8f150-8abf-4539-9f16-7f68cedb1649", CreationDate: *timeNow(require), } err = repo.AddOrUpdateSensors(ctx, expectedSensor) require.NoError(err) sensor, err = repo.GetSensorByID(ctx, expectedSensor.ID) require.NoError(err) require.NotEmpty(sensor) require.Equal(expectedSensor.ID, sensor.ID) require.Equal(expectedSensor.Name, sensor.Name) require.Equal(expectedSensor.Location, sensor.Location) require.Equal(expectedSensor.I2CBus, sensor.I2CBus) require.Equal(expectedSensor.I2CAddress, sensor.I2CAddress) require.Equal(expectedSensor.Model, sensor.Model) require.Equal(expectedSensor.Enabled, sensor.Enabled) require.Equal(expectedSensor.TickDuration, sensor.TickDuration) require.Equal(expectedSensor.DeviceID, sensor.DeviceID) expectedSensor = &types.Sensor{ ID: "9bba0e0a-e996-4242-966f-db21bab6752f", Name: "MySweetSensor", Location: "MySweetLocation", I2CBus: nil, I2CAddress: nil, Model: "Jap", Enabled: false, TickDuration: "8h", DeviceID: "39b8f150-8abf-4539-9f16-7f68cedb1649", CreationDate: *timeNow(require), } err = repo.AddOrUpdateSensors(ctx, expectedSensor) require.NoError(err) sensor, err = repo.GetSensorByID(ctx, expectedSensor.ID) require.NoError(err) require.NotEmpty(sensor) require.Equal(expectedSensor.ID, sensor.ID) require.Equal(expectedSensor.Name, sensor.Name) require.Equal(expectedSensor.Location, sensor.Location) require.Equal(expectedSensor.I2CBus, sensor.I2CBus) require.Equal(expectedSensor.I2CAddress, sensor.I2CAddress) require.Equal(expectedSensor.Model, sensor.Model) require.Equal(expectedSensor.Enabled, sensor.Enabled) require.Equal(expectedSensor.TickDuration, sensor.TickDuration) require.Equal(expectedSensor.DeviceID, sensor.DeviceID) var ( expectedMeasuredValues = []*types.MeasuredValue{ { ID: "2e5a297a-3da0-46ae-89d2-0fcab0f1d5f7", Value: 32, ValueType: types.Humidity, Date: *timeNow(require), SensorID: "8c74397f-8e60-4c9d-960d-3197747cef9a", CreationDate: *timeNow(require), UpdateDate: nil, }, { ID: "d69f1b62-0c6c-4058-b42c-4a2821bd220c", Value: 38, ValueType: types.Pressure, Date: *timeNow(require), SensorID: "8c74397f-8e60-4c9d-960d-3197747cef9a", CreationDate: *timeNow(require), UpdateDate: nil, }, { ID: "ea945ae0-412b-4561-a191-1f8f1f909fa4", Value: 35.4, ValueType: types.Temperature, Date: *timeNow(require), SensorID: "8c74397f-8e60-4c9d-960d-3197747cef9a", CreationDate: *timeNow(require), UpdateDate: nil, }, } ) // Test: AddMeasuredValues err = repo.AddMeasuredValues(ctx, expectedMeasuredValues...) require.NoError(err) for i := range expectedMeasuredValues { var ( err error measuredValue *types.MeasuredValue ) switch expectedMeasuredValues[i].ValueType { case types.Humidity: measuredValue, err = repo.GetHumidityByID(ctx, expectedMeasuredValues[i].ID) require.NoError(err) require.NotNil(measuredValue) case types.Pressure: measuredValue, err = repo.GetPressureByID(ctx, expectedMeasuredValues[i].ID) require.NoError(err) require.NotNil(measuredValue) case types.Temperature: measuredValue, err = repo.GetTemperatureByID(ctx, expectedMeasuredValues[i].ID) require.NoError(err) require.NotNil(measuredValue) } require.Equal(expectedMeasuredValues[i].ID, measuredValue.ID) require.Equal(expectedMeasuredValues[i].Value, measuredValue.Value) require.Equal(expectedMeasuredValues[i].ValueType, measuredValue.ValueType) require.Equal(expectedMeasuredValues[i].SensorID, measuredValue.SensorID) } // Test: AddOrUpdateMeasuredValues expectedMeasuredValues = []*types.MeasuredValue{ { ID: "2e5a297a-3da0-46ae-89d2-0fcab0f1d5f7", Value: 35, ValueType: types.Humidity, Date: *timeNow(require), SensorID: "8c74397f-8e60-4c9d-960d-3197747cef9a", CreationDate: *timeNow(require), UpdateDate: timeNow(require), }, { ID: "d69f1b62-0c6c-4058-b42c-4a2821bd220c", Value: 37, ValueType: types.Pressure, Date: *timeNow(require), SensorID: "8c74397f-8e60-4c9d-960d-3197747cef9a", CreationDate: *timeNow(require), UpdateDate: timeNow(require), }, { ID: "ea945ae0-412b-4561-a191-1f8f1f909fa4", Value: 35.4, ValueType: types.Temperature, Date: *timeNow(require), SensorID: "8c74397f-8e60-4c9d-960d-3197747cef9a", CreationDate: *timeNow(require), UpdateDate: timeNow(require), }, } err = repo.AddOrUpdateMeasuredValues(ctx, expectedMeasuredValues...) require.NoError(err) for i := range expectedMeasuredValues { var ( err error measuredValue *types.MeasuredValue ) switch expectedMeasuredValues[i].ValueType { case types.Humidity: measuredValue, err = repo.GetHumidityByID(ctx, expectedMeasuredValues[i].ID) require.NoError(err) require.NotNil(measuredValue) case types.Pressure: measuredValue, err = repo.GetPressureByID(ctx, expectedMeasuredValues[i].ID) require.NoError(err) require.NotNil(measuredValue) case types.Temperature: measuredValue, err = repo.GetTemperatureByID(ctx, expectedMeasuredValues[i].ID) require.NoError(err) require.NotNil(measuredValue) } require.Equal(expectedMeasuredValues[i].ID, measuredValue.ID) require.Equal(expectedMeasuredValues[i].Value, measuredValue.Value) require.Equal(expectedMeasuredValues[i].ValueType, measuredValue.ValueType) require.Equal(expectedMeasuredValues[i].SensorID, measuredValue.SensorID) } } func jsonEncoder(v interface{}) string { body := make([]byte, 0) buffer := bytes.NewBuffer(body) jsonEncoder := json.NewEncoder(buffer) jsonEncoder.SetIndent("", " ") jsonEncoder.Encode(v) return buffer.String() } func timeNow(require *require.Assertions) *time.Time { now, err := time.Parse("2006-01-02 15:04:05.999999-07", time.Now().Format("2006-01-02 15:04:05.999999-07")) require.NoError(err) return &now }