From 00ebe0c5fe862566a2d112a832c8728bec0f0ec4 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 13 Jan 2026 08:39:02 -0600 Subject: [PATCH] Add comprehensive Agent protobuf module tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 37 tests covering all 12 protobuf modules: - AgentConfig, Equipment, SnmpConfig - Sensor, Sensor.MetadataEntry, Interface - MetricBatch, Metric (with oneof field handling) - SensorReading, InterfaceStat - HeartbeatMetadata, HeartbeatResponse Tests cover struct creation, default values, and encode/decode cycles. All protobuf modules now at 100% coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- test/towerops/proto/agent_pb_test.exs | 488 ++++++++++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100644 test/towerops/proto/agent_pb_test.exs diff --git a/test/towerops/proto/agent_pb_test.exs b/test/towerops/proto/agent_pb_test.exs new file mode 100644 index 00000000..0ece189a --- /dev/null +++ b/test/towerops/proto/agent_pb_test.exs @@ -0,0 +1,488 @@ +defmodule Towerops.Proto.AgentPbTest do + use ExUnit.Case, async: true + + alias Towerops.Agent.AgentConfig + alias Towerops.Agent.Equipment + alias Towerops.Agent.HeartbeatMetadata + alias Towerops.Agent.HeartbeatResponse + alias Towerops.Agent.Interface + alias Towerops.Agent.InterfaceStat + alias Towerops.Agent.Metric + alias Towerops.Agent.MetricBatch + alias Towerops.Agent.Sensor + alias Towerops.Agent.SensorReading + alias Towerops.Agent.SnmpConfig + + describe "AgentConfig" do + test "creates struct with default values" do + config = %AgentConfig{} + assert config.version == "" + assert config.poll_interval_seconds == 0 + assert config.equipment == [] + end + + test "creates struct with values" do + equipment = %Equipment{id: "eq1", name: "Router"} + + config = %AgentConfig{ + version: "1.0.0", + poll_interval_seconds: 60, + equipment: [equipment] + } + + assert config.version == "1.0.0" + assert config.poll_interval_seconds == 60 + assert length(config.equipment) == 1 + assert hd(config.equipment).id == "eq1" + end + + test "encodes and decodes" do + config = %AgentConfig{ + version: "1.0.0", + poll_interval_seconds: 60 + } + + encoded = AgentConfig.encode(config) + assert is_binary(encoded) + + decoded = AgentConfig.decode(encoded) + assert decoded.version == "1.0.0" + assert decoded.poll_interval_seconds == 60 + end + end + + describe "Equipment" do + test "creates struct with default values" do + equipment = %Equipment{} + assert equipment.id == "" + assert equipment.name == "" + assert equipment.ip_address == "" + assert equipment.snmp == nil + assert equipment.poll_interval_seconds == 0 + assert equipment.sensors == [] + assert equipment.interfaces == [] + end + + test "creates struct with SNMP config" do + snmp = %SnmpConfig{ + enabled: true, + version: "2c", + community: "public", + port: 161 + } + + equipment = %Equipment{ + id: "eq1", + name: "Router 1", + ip_address: "192.168.1.1", + snmp: snmp, + poll_interval_seconds: 60 + } + + assert equipment.id == "eq1" + assert equipment.name == "Router 1" + assert equipment.ip_address == "192.168.1.1" + assert equipment.snmp.enabled == true + assert equipment.poll_interval_seconds == 60 + end + + test "creates struct with sensors and interfaces" do + sensor = %Sensor{id: "s1", type: "temperature", oid: "1.3.6.1.2.1"} + interface = %Interface{id: "i1", if_index: 1, if_name: "eth0"} + + equipment = %Equipment{ + id: "eq1", + sensors: [sensor], + interfaces: [interface] + } + + assert length(equipment.sensors) == 1 + assert length(equipment.interfaces) == 1 + assert hd(equipment.sensors).id == "s1" + assert hd(equipment.interfaces).id == "i1" + end + + test "encodes and decodes" do + equipment = %Equipment{ + id: "eq1", + name: "Router 1", + ip_address: "192.168.1.1" + } + + encoded = Equipment.encode(equipment) + decoded = Equipment.decode(encoded) + + assert decoded.id == "eq1" + assert decoded.name == "Router 1" + assert decoded.ip_address == "192.168.1.1" + end + end + + describe "SnmpConfig" do + test "creates struct with default values" do + snmp = %SnmpConfig{} + assert snmp.enabled == false + assert snmp.version == "" + assert snmp.community == "" + assert snmp.port == 0 + end + + test "creates struct with values" do + snmp = %SnmpConfig{ + enabled: true, + version: "2c", + community: "public", + port: 161 + } + + assert snmp.enabled == true + assert snmp.version == "2c" + assert snmp.community == "public" + assert snmp.port == 161 + end + + test "encodes and decodes" do + snmp = %SnmpConfig{ + enabled: true, + version: "2c", + community: "public", + port: 161 + } + + encoded = SnmpConfig.encode(snmp) + decoded = SnmpConfig.decode(encoded) + + assert decoded.enabled == true + assert decoded.version == "2c" + assert decoded.community == "public" + assert decoded.port == 161 + end + end + + describe "Sensor" do + test "creates struct with default values" do + sensor = %Sensor{} + assert sensor.id == "" + assert sensor.type == "" + assert sensor.oid == "" + assert sensor.divisor == 0.0 + assert sensor.unit == "" + assert sensor.metadata == %{} + end + + test "creates struct with values" do + sensor = %Sensor{ + id: "s1", + type: "temperature", + oid: "1.3.6.1.2.1.99.1.1.1.4.1", + divisor: 10.0, + unit: "°C", + metadata: [%{key: "location", value: "CPU"}] + } + + assert sensor.id == "s1" + assert sensor.type == "temperature" + assert sensor.oid == "1.3.6.1.2.1.99.1.1.1.4.1" + assert sensor.divisor == 10.0 + assert sensor.unit == "°C" + assert length(sensor.metadata) == 1 + end + + test "encodes and decodes" do + sensor = %Sensor{ + id: "s1", + type: "temperature", + oid: "1.3.6.1.2.1", + divisor: 10.0, + unit: "°C" + } + + encoded = Sensor.encode(sensor) + decoded = Sensor.decode(encoded) + + assert decoded.id == "s1" + assert decoded.type == "temperature" + assert decoded.divisor == 10.0 + end + end + + describe "Sensor.MetadataEntry" do + test "creates struct with default values" do + entry = %Sensor.MetadataEntry{} + assert entry.key == "" + assert entry.value == "" + end + + test "creates struct with values" do + entry = %Sensor.MetadataEntry{key: "location", value: "CPU"} + assert entry.key == "location" + assert entry.value == "CPU" + end + end + + describe "Interface" do + test "creates struct with default values" do + interface = %Interface{} + assert interface.id == "" + assert interface.if_index == 0 + assert interface.if_name == "" + end + + test "creates struct with values" do + interface = %Interface{ + id: "i1", + if_index: 1, + if_name: "eth0" + } + + assert interface.id == "i1" + assert interface.if_index == 1 + assert interface.if_name == "eth0" + end + + test "encodes and decodes" do + interface = %Interface{ + id: "i1", + if_index: 1, + if_name: "eth0" + } + + encoded = Interface.encode(interface) + decoded = Interface.decode(encoded) + + assert decoded.id == "i1" + assert decoded.if_index == 1 + assert decoded.if_name == "eth0" + end + end + + describe "MetricBatch" do + test "creates struct with default values" do + batch = %MetricBatch{} + assert batch.metrics == [] + end + + test "creates struct with metrics" do + reading = %SensorReading{sensor_id: "s1", value: 45.5} + metric = %Metric{metric_type: {:sensor_reading, reading}} + batch = %MetricBatch{metrics: [metric]} + + assert length(batch.metrics) == 1 + assert {:sensor_reading, reading} = hd(batch.metrics).metric_type + assert reading.sensor_id == "s1" + end + + test "encodes and decodes" do + reading = %SensorReading{sensor_id: "s1", value: 45.5} + metric = %Metric{metric_type: {:sensor_reading, reading}} + batch = %MetricBatch{metrics: [metric]} + + encoded = MetricBatch.encode(batch) + decoded = MetricBatch.decode(encoded) + + assert length(decoded.metrics) == 1 + end + end + + describe "Metric" do + test "creates struct with sensor_reading" do + reading = %SensorReading{ + sensor_id: "s1", + value: 45.5, + status: "ok", + timestamp: 1_640_000_000 + } + + metric = %Metric{metric_type: {:sensor_reading, reading}} + assert {:sensor_reading, reading} = metric.metric_type + assert reading.sensor_id == "s1" + assert reading.value == 45.5 + end + + test "creates struct with interface_stat" do + stat = %InterfaceStat{ + interface_id: "i1", + if_in_octets: 1000, + if_out_octets: 2000, + timestamp: 1_640_000_000 + } + + metric = %Metric{metric_type: {:interface_stat, stat}} + assert {:interface_stat, stat} = metric.metric_type + assert stat.interface_id == "i1" + end + + test "encodes and decodes sensor_reading" do + reading = %SensorReading{sensor_id: "s1", value: 45.5} + metric = %Metric{metric_type: {:sensor_reading, reading}} + + encoded = Metric.encode(metric) + decoded = Metric.decode(encoded) + + assert {:sensor_reading, decoded_reading} = decoded.metric_type + assert decoded_reading.sensor_id == "s1" + end + + test "encodes and decodes interface_stat" do + stat = %InterfaceStat{interface_id: "i1", if_in_octets: 1000} + metric = %Metric{metric_type: {:interface_stat, stat}} + + encoded = Metric.encode(metric) + decoded = Metric.decode(encoded) + + assert {:interface_stat, decoded_stat} = decoded.metric_type + assert decoded_stat.interface_id == "i1" + end + end + + describe "SensorReading" do + test "creates struct with default values" do + reading = %SensorReading{} + assert reading.sensor_id == "" + assert reading.value == 0.0 + assert reading.status == "" + assert reading.timestamp == 0 + end + + test "creates struct with values" do + reading = %SensorReading{ + sensor_id: "s1", + value: 45.5, + status: "ok", + timestamp: 1_640_000_000 + } + + assert reading.sensor_id == "s1" + assert reading.value == 45.5 + assert reading.status == "ok" + assert reading.timestamp == 1_640_000_000 + end + + test "encodes and decodes" do + reading = %SensorReading{ + sensor_id: "s1", + value: 45.5, + status: "ok", + timestamp: 1_640_000_000 + } + + encoded = SensorReading.encode(reading) + decoded = SensorReading.decode(encoded) + + assert decoded.sensor_id == "s1" + assert decoded.value == 45.5 + assert decoded.status == "ok" + assert decoded.timestamp == 1_640_000_000 + end + end + + describe "InterfaceStat" do + test "creates struct with default values" do + stat = %InterfaceStat{} + assert stat.interface_id == "" + assert stat.if_in_octets == 0 + assert stat.if_out_octets == 0 + assert stat.if_in_errors == 0 + assert stat.if_out_errors == 0 + assert stat.if_in_discards == 0 + assert stat.if_out_discards == 0 + assert stat.timestamp == 0 + end + + test "creates struct with values" do + stat = %InterfaceStat{ + interface_id: "i1", + if_in_octets: 1000, + if_out_octets: 2000, + if_in_errors: 1, + if_out_errors: 2, + if_in_discards: 3, + if_out_discards: 4, + timestamp: 1_640_000_000 + } + + assert stat.interface_id == "i1" + assert stat.if_in_octets == 1000 + assert stat.if_out_octets == 2000 + assert stat.if_in_errors == 1 + assert stat.if_out_errors == 2 + assert stat.if_in_discards == 3 + assert stat.if_out_discards == 4 + assert stat.timestamp == 1_640_000_000 + end + + test "encodes and decodes" do + stat = %InterfaceStat{ + interface_id: "i1", + if_in_octets: 1000, + if_out_octets: 2000, + timestamp: 1_640_000_000 + } + + encoded = InterfaceStat.encode(stat) + decoded = InterfaceStat.decode(encoded) + + assert decoded.interface_id == "i1" + assert decoded.if_in_octets == 1000 + assert decoded.if_out_octets == 2000 + assert decoded.timestamp == 1_640_000_000 + end + end + + describe "HeartbeatMetadata" do + test "creates struct with default values" do + metadata = %HeartbeatMetadata{} + assert metadata.version == "" + assert metadata.hostname == "" + assert metadata.uptime_seconds == 0 + end + + test "creates struct with values" do + metadata = %HeartbeatMetadata{ + version: "1.0.0", + hostname: "agent-1", + uptime_seconds: 3600 + } + + assert metadata.version == "1.0.0" + assert metadata.hostname == "agent-1" + assert metadata.uptime_seconds == 3600 + end + + test "encodes and decodes" do + metadata = %HeartbeatMetadata{ + version: "1.0.0", + hostname: "agent-1", + uptime_seconds: 3600 + } + + encoded = HeartbeatMetadata.encode(metadata) + decoded = HeartbeatMetadata.decode(encoded) + + assert decoded.version == "1.0.0" + assert decoded.hostname == "agent-1" + assert decoded.uptime_seconds == 3600 + end + end + + describe "HeartbeatResponse" do + test "creates struct with default values" do + response = %HeartbeatResponse{} + assert response.status == "" + end + + test "creates struct with values" do + response = %HeartbeatResponse{status: "ok"} + assert response.status == "ok" + end + + test "encodes and decodes" do + response = %HeartbeatResponse{status: "ok"} + + encoded = HeartbeatResponse.encode(response) + decoded = HeartbeatResponse.decode(encoded) + + assert decoded.status == "ok" + end + end +end