Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add integration test #5

Merged
merged 15 commits into from
Oct 25, 2023
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@ jobs:
with:
coverage-files: lcov.info
artifact-name: code-coverage-report
update-comment: true
update-comment: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ Cargo.lock

# Secret configuration
config/secrets.toml

.idea/
64 changes: 64 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in library 'ha-mitaffald'",
"cargo": {
"args": [
"test",
"--no-run",
"--lib",
"--package=ha-mitaffald"
],
"filter": {
"name": "ha-mitaffald",
"kind": "lib"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'ha-mitaffald'",
"cargo": {
"args": [
"build",
"--bin=ha-mitaffald",
"--package=ha-mitaffald"
],
"filter": {
"name": "ha-mitaffald",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'ha-mitaffald'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=ha-mitaffald",
"--package=ha-mitaffald"
],
"filter": {
"name": "ha-mitaffald",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"rust-analyzer.checkOnSave.command": "clippy",
"editor.formatOnSave": true,
}
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ easy-scraper = "0.2.0"
reqwest = { version = "0.11.18", features = ["blocking"] }
rumqttc = "0.22.0"
serde = { version = "1.0.183", features = ["derive"] }
serde_json = "1.0.107"
url = { version = "2.4.1", features = ["serde"] }

[dev-dependencies]
fluent-asserter = "0.1.9"
mockito = "1.2.0"
testcontainers = "0.14.0"
assert-json-diff = "2.0.2"
120 changes: 120 additions & 0 deletions src/homeassistant/messages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// use derive_builder::Builder;
// use serde::{Deserialize, Serialize};

// #[derive(Debug, Default, Builder, Clone)]
// pub struct Device {
// /// Webpage link to manage the configuration.
// configuration_url: Option<String>,

// /// List of connections of the device.
// connections: Option<Vec<(String, String)>>,

// /// Hardware version.
// hw_version: Option<String>,

// /// List of IDs that uniquely identify the device.
// identifiers: Option<Vec<String>>,

// /// Manufacturer of the device.
// manufacturer: Option<String>,

// /// Model of the device.
// model: Option<String>,

// /// Name of the device.
// name: Option<String>,

// /// Suggest an area if the device isn’t in one yet.
// suggested_area: Option<String>,

// /// Firmware version.
// sw_version: Option<String>,

// /// Identifier of a device that routes messages between this device and Home Assistant. Examples of such devices are hubs, or parent devices of a sub-device. This is used to show device topology in Home Assistant.
// via_device: Option<String>,
// }

// /// MQTT sensor configuration.
// #[derive(Debug, Default, Builder)]
// pub struct Sensor {
// /// The MQTT topic subscribed to receive sensor values.
// state_topic: String,

// /// A list of MQTT topics subscribed to receive availability updates.
// availability: Option<Vec<String>>,

// /// Represents the available state.
// payload_available: Option<String>,

// /// Represents the unavailable state.
// payload_not_available: Option<String>,

// /// An MQTT topic subscribed to receive availability updates.
// topic: String,

// /// Template to extract device’s availability from the topic.
// value_template: Option<String>,

// /// Controls the conditions to set the entity to available.
// availability_mode: Option<String>,

// /// Template to extract device’s availability from the availability_topic.
// availability_template: Option<String>,

// /// The MQTT topic subscribed to receive availability updates.
// availability_topic: Option<String>,

// /// Information about the device.
// device: Option<Device>,

// /// A link to the webpage that can manage the configuration of this device.
// configuration_url: Option<String>,

// /// Flag which defines if the entity should be enabled when first added.
// enabled_by_default: Option<bool>,

// /// Encoding of the payloads received.
// encoding: Option<String>,

// /// Category of the entity.
// entity_category: Option<String>,

// /// Defines the number of seconds after the sensor’s state expires.
// expire_after: Option<i32>,

// /// Sends update events even if the value hasn’t changed.
// force_update: Option<bool>,

// /// Icon for the entity.
// icon: Option<String>,

// /// Template to extract the JSON dictionary.
// json_attributes_template: Option<String>,

// /// Topic subscribed to receive a JSON dictionary payload.
// json_attributes_topic: Option<String>,

// /// Template to extract the last_reset.
// last_reset_value_template: Option<String>,

// /// Name of the MQTT sensor.
// name: Option<String>,

// /// Used for automatic generation of entity_id.
// object_id: Option<String>,

// /// Number of decimals used in the sensor’s state after rounding.
// suggested_display_precision: Option<i32>,

// /// Maximum QoS level to be used.
// qos: Option<i32>,

// /// State class of the sensor.
// state_class: Option<String>,

// /// Unique ID for this sensor.
// unique_id: Option<String>,

// /// Units of measurement of the sensor.
// unit_of_measurement: Option<String>,
// }
13 changes: 3 additions & 10 deletions src/homeassistant/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::mitaffald::Container;
use crate::settings::MQTTConfig;
use rumqttc::{Client, LastWill, MqttOptions};

const HA_AVAILABILITY_TOPIC: &str = "garbage_bin/availability";

impl From<MQTTConfig> for MqttOptions {
Expand All @@ -18,6 +19,7 @@ impl From<MQTTConfig> for MqttOptions {
config
}
}

pub struct HASensor {
pub container_id: String,
configure_topic: String,
Expand Down Expand Up @@ -59,8 +61,7 @@ impl HASensor {
client: &mut Client,
) -> Result<(), rumqttc::ClientError> {
let payload = format!(
r#"
{{
r#"{{
"object_id": "ha_affaldvarme_{id}",
"unique_id": "ha_affaldvarme_{id}",
"name": "{sensor_name}",
Expand Down Expand Up @@ -134,11 +135,3 @@ impl HASensor {
client.publish(&self.state_topic, rumqttc::QoS::AtLeastOnce, false, payload)
}
}

//can we use asref here?

// impl Into<HASensor> for Container {
// fn into(&self) -> HASensor {
// HASensor::new(&self)
// }
// }
43 changes: 43 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
use std::collections::HashMap;

use homeassistant::HASensor;
use mitaffald::get_containers;
use rumqttc::Client;
use settings::Settings;

pub mod homeassistant;
pub mod mitaffald;
pub mod settings;

pub fn sync_data(
settings: Settings,
sensor_map: &mut HashMap<String, HASensor>,
) -> Result<(), String> {
let (mut client, mut connection) = Client::new(settings.mqtt.into(), 200);
let mut has_errors = false;

get_containers(settings.affaldvarme)?
.into_iter()
.for_each(|x| {
let report_result = sensor_map
.entry(x.id.clone())
.or_insert_with(|| HASensor::new(&x))
.report(x, &mut client);

has_errors = has_errors || report_result.is_err();
});

//calling disconnect() causes an error in the connection iterator
if let Err(x) = client.disconnect() {
return Err(x.to_string());
}

//create own error and provide conversion from this?
//client.disconnect()?;

//iterate the connection untill we hit the above generated error
connection.iter().take_while(|x| x.is_ok()).count();

if has_errors {
Err("Failed to report all containers".into())
} else {
Ok(())
}
}
36 changes: 1 addition & 35 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use ha_mitaffald::homeassistant::HASensor;
use ha_mitaffald::mitaffald::get_containers;
use ha_mitaffald::settings::Settings;
use rumqttc::Client;
use ha_mitaffald::sync_data;
use std::collections::HashMap;

fn main() {
Expand All @@ -17,36 +16,3 @@ fn main() {
);
}
}

fn sync_data(settings: Settings, sensor_map: &mut HashMap<String, HASensor>) -> Result<(), String> {
let (mut client, mut connection) = Client::new(settings.mqtt.into(), 200);
let mut has_errors = false;

get_containers(settings.affaldvarme)?
.into_iter()
.for_each(|x| {
let report_result = sensor_map
.entry(x.id.clone())
.or_insert_with(|| HASensor::new(&x))
.report(x, &mut client);

has_errors = has_errors || report_result.is_err();
});

//calling disconnect() causes an error in the connection iterator
if let Err(x) = client.disconnect() {
return Err(x.to_string());
}

//create own error and provide conversion from this?
//client.disconnect()?;

//iterate the connection untill we hit the above generated error
connection.iter().take_while(|x| x.is_ok()).count();

if has_errors {
Err("Failed to report all containers".into())
} else {
Ok(())
}
}
2 changes: 1 addition & 1 deletion src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl Settings {
}
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, Clone)]
#[allow(unused)]
pub struct MQTTConfig {
pub host: String,
Expand Down
Loading