test(13-01): add failing tests for InterfaceInfo collector
- InterfaceInfo struct field compilation test - MAC address lowercasing test - Running bool parsing test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,12 +29,12 @@
|
||||
|
||||
### Wireless Collection
|
||||
|
||||
- [ ] **WRCL-01**: Poller collects per-client registration table data from APs (MAC, signal, CCQ, TX/RX rates, distance, uptime) on a 5-minute cadence
|
||||
- [ ] **WRCL-02**: Poller collects per-interface RF stats (noise floor, channel width, TX power, registered client count) via monitor command
|
||||
- [ ] **WRCL-03**: Per-client wireless data publishes to a dedicated NATS stream (separate from DEVICE_EVENTS) to prevent stream saturation
|
||||
- [ ] **WRCL-04**: Per-client wireless data stores in a dedicated hypertable with 30-day retention (separate from existing wireless_metrics)
|
||||
- [ ] **WRCL-05**: Poller handles RouterOS v6/v7 field differences gracefully (CCQ absent in v7 wifi package)
|
||||
- [ ] **WRCL-06**: Signal strength parsing handles RouterOS format variations (e.g., `-67@5GHz` suffix)
|
||||
- [x] **WRCL-01**: Poller collects per-client registration table data from APs (MAC, signal, CCQ, TX/RX rates, distance, uptime) on a 5-minute cadence
|
||||
- [x] **WRCL-02**: Poller collects per-interface RF stats (noise floor, channel width, TX power, registered client count) via monitor command
|
||||
- [x] **WRCL-03**: Per-client wireless data publishes to a dedicated NATS stream (separate from DEVICE_EVENTS) to prevent stream saturation
|
||||
- [x] **WRCL-04**: Per-client wireless data stores in a dedicated hypertable with 30-day retention (separate from existing wireless_metrics)
|
||||
- [x] **WRCL-05**: Poller handles RouterOS v6/v7 field differences gracefully (CCQ absent in v7 wifi package)
|
||||
- [x] **WRCL-06**: Signal strength parsing handles RouterOS format variations (e.g., `-67@5GHz` suffix)
|
||||
|
||||
### Link Discovery
|
||||
|
||||
@@ -107,12 +107,12 @@
|
||||
| SECT-01 | Phase 14 | Pending |
|
||||
| SECT-02 | Phase 14 | Pending |
|
||||
| SECT-03 | Phase 14 | Pending |
|
||||
| WRCL-01 | Phase 12 | Pending |
|
||||
| WRCL-02 | Phase 12 | Pending |
|
||||
| WRCL-03 | Phase 12 | Pending |
|
||||
| WRCL-04 | Phase 12 | Pending |
|
||||
| WRCL-05 | Phase 12 | Pending |
|
||||
| WRCL-06 | Phase 12 | Pending |
|
||||
| WRCL-01 | Phase 12 | Complete |
|
||||
| WRCL-02 | Phase 12 | Complete |
|
||||
| WRCL-03 | Phase 12 | Complete |
|
||||
| WRCL-04 | Phase 12 | Complete |
|
||||
| WRCL-05 | Phase 12 | Complete |
|
||||
| WRCL-06 | Phase 12 | Complete |
|
||||
| LINK-01 | Phase 13 | Pending |
|
||||
| LINK-02 | Phase 13 | Pending |
|
||||
| LINK-03 | Phase 13 | Pending |
|
||||
|
||||
@@ -35,7 +35,7 @@ v9.7 transforms TOD from a flat device list into a site-aware fleet management p
|
||||
- Decimal phases (11.1, 11.2): Urgent insertions (marked with INSERTED)
|
||||
|
||||
- [x] **Phase 11: Site Data Model + Foundation** - Sites CRUD, device assignment, site list with health rollup (completed 2026-03-19)
|
||||
- [ ] **Phase 12: Per-Client Wireless Collection** - Poller extension to collect registration table and per-interface RF stats
|
||||
- [x] **Phase 12: Per-Client Wireless Collection** - Poller extension to collect registration table and per-interface RF stats (completed 2026-03-19)
|
||||
- [ ] **Phase 13: Link Discovery + Registration Ingestion** - Backend NATS consumer, MAC resolution, AP-CPE link state machine
|
||||
- [ ] **Phase 14: Site Dashboard + Sector Views + Wireless UI** - Site detail page, sector-centric view, per-station wireless tables
|
||||
- [ ] **Phase 15: Signal Trending + Site Alerting** - Signal history charts, degradation detection, site/sector alert rules
|
||||
@@ -69,7 +69,7 @@ Plans:
|
||||
3. Per-client data publishes to a dedicated WIRELESS_REGISTRATIONS NATS stream (not DEVICE_EVENTS)
|
||||
4. Per-client data stores in a dedicated hypertable with 30-day retention
|
||||
5. Collection works correctly on both RouterOS v6 (wireless package) and v7 (wifi package) with graceful handling of missing fields
|
||||
**Plans:** 2 plans
|
||||
**Plans:** 2/2 plans complete
|
||||
|
||||
Plans:
|
||||
- [ ] 12-01-PLAN.md — Go poller per-client registration collector, signal parser, RF monitor, NATS stream and publisher
|
||||
@@ -84,11 +84,12 @@ Plans:
|
||||
2. Link state follows a temporal state machine (discovered, active, degraded, down, stale) with consecutive-miss threshold to prevent false flapping
|
||||
3. Discovered links are stored in a materialized wireless_links table for fast dashboard queries
|
||||
4. Wireless clients whose MACs do not match any managed device appear as "unknown clients" with their signal and rate data preserved
|
||||
**Plans**: TBD
|
||||
**Plans:** 3 plans
|
||||
|
||||
Plans:
|
||||
- [ ] 13-01: TBD
|
||||
- [ ] 13-02: TBD
|
||||
- [ ] 13-01-PLAN.md — Go poller interface collector (/interface/print) and DEVICE_EVENTS publisher
|
||||
- [ ] 13-02-PLAN.md — Backend device_interfaces and wireless_links table migrations with ORM models
|
||||
- [ ] 13-03-PLAN.md — Link discovery subscriber, interface subscriber, link REST API, and app wiring
|
||||
|
||||
### Phase 14: Site Dashboard + Sector Views + Wireless UI
|
||||
**Goal**: Operators can drill into any site to see device health, sector-organized AP/CPE views, and per-station wireless details on device pages
|
||||
@@ -129,8 +130,7 @@ Plans:
|
||||
| Sites | SITE-01, SITE-02, SITE-03, SITE-04, SITE-05, SITE-06 | 11 | 3/3 | Complete | 2026-03-19 | DASH-01 | 11 | 1 |
|
||||
| Site Dashboard | DASH-02, DASH-03, DASH-04 | 14 | 3 |
|
||||
| Sectors | SECT-01, SECT-02, SECT-03 | 14 | 3 |
|
||||
| Wireless Collection | WRCL-01, WRCL-02, WRCL-03, WRCL-04, WRCL-05, WRCL-06 | 12 | 6 |
|
||||
| Link Discovery | LINK-01, LINK-02, LINK-03, LINK-04 | 13 | 4 |
|
||||
| Wireless Collection | WRCL-01, WRCL-02, WRCL-03, WRCL-04, WRCL-05, WRCL-06 | 12 | 2/2 | Complete | 2026-03-19 | LINK-01, LINK-02, LINK-03, LINK-04 | 13 | 4 |
|
||||
| Wireless UI | WRUI-01, WRUI-02, WRUI-03 | 14 | 3 |
|
||||
| Signal Trending | TRND-01, TRND-02 | 15 | 2 |
|
||||
| Site Alerting | ALRT-01, ALRT-02 | 15 | 2 |
|
||||
@@ -145,7 +145,7 @@ Phases execute in numeric order: 11 -> 11.x -> 12 -> 12.x -> 13 -> 13.x -> 14 ->
|
||||
|-------|----------------|--------|-----------|
|
||||
| 11. Site Data Model + Foundation | 0/3 | Planning complete | - |
|
||||
| 12. Per-Client Wireless Collection | 0/2 | Planning complete | - |
|
||||
| 13. Link Discovery + Registration Ingestion | 0/? | Not started | - |
|
||||
| 13. Link Discovery + Registration Ingestion | 0/3 | Planning complete | - |
|
||||
| 14. Site Dashboard + Sector Views + Wireless UI | 0/? | Not started | - |
|
||||
| 15. Signal Trending + Site Alerting | 0/? | Not started | - |
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
gsd_state_version: 1.0
|
||||
milestone: v9.7
|
||||
milestone_name: Tower & Site Management
|
||||
status: phase-complete
|
||||
stopped_at: Completed 11-03-PLAN.md
|
||||
last_updated: "2026-03-19T02:53:16Z"
|
||||
status: unknown
|
||||
stopped_at: Completed 12-01-PLAN.md
|
||||
last_updated: "2026-03-19T10:40:03.896Z"
|
||||
progress:
|
||||
total_phases: 5
|
||||
completed_phases: 1
|
||||
total_plans: 3
|
||||
completed_plans: 3
|
||||
completed_phases: 2
|
||||
total_plans: 5
|
||||
completed_plans: 5
|
||||
---
|
||||
|
||||
# Project State
|
||||
@@ -19,12 +19,12 @@ progress:
|
||||
See: .planning/PROJECT.md (updated 2026-03-18)
|
||||
|
||||
**Core value:** Operators can monitor, configure, and troubleshoot their entire MikroTik fleet from a single pane of glass
|
||||
**Current focus:** Phase 11 — site-data-model-foundation
|
||||
**Current focus:** Phase 12 — per-client-wireless-collection
|
||||
|
||||
## Current Position
|
||||
|
||||
Phase: 11 (site-data-model-foundation) — COMPLETE
|
||||
Plan: 3 of 3 (all complete)
|
||||
Phase: 12 (per-client-wireless-collection) — COMPLETE
|
||||
Plan: 2 of 2 (all complete)
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
@@ -45,6 +45,9 @@ Plan: 3 of 3 (all complete)
|
||||
| Phase 11 P01 | 3min | 2 tasks | 9 files |
|
||||
| Phase 11 P02 | 6min | 3 tasks | 8 files |
|
||||
| Phase 11 P03 | 3min | 2 tasks | 5 files |
|
||||
| Phase 12 P01 | 3min | 2 tasks | 6 files |
|
||||
| Phase 12 P02 | 3min | 2 tasks | 3 files |
|
||||
| Phase 12 P01 | 3min | 2 tasks | 6 files |
|
||||
|
||||
### Decisions
|
||||
|
||||
@@ -58,6 +61,9 @@ Decisions are logged in PROJECT.md Key Decisions table.
|
||||
- [Phase 11]: Used Dialog for delete confirmation (no AlertDialog component in UI library)
|
||||
- [Phase 11]: Site column placed after Model in fleet table for logical grouping
|
||||
- [Phase 11]: Viewers see site name text, operators get Select dropdown for assignment
|
||||
- [Phase 12]: Used unified tenant_isolation RLS policy with super_admin OR clause (matching codebase convention) instead of separate super_admin_bypass policy
|
||||
- [Phase 12]: WIRELESS_REGISTRATIONS NATS stream uses 30-day retention (vs 24h for DEVICE_EVENTS) for historical client analytics
|
||||
- [Phase 12]: RF monitor collection gated on wireless interface presence to avoid unnecessary API calls
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -71,6 +77,6 @@ None yet.
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-03-19T02:53:16Z
|
||||
Stopped at: Completed 11-03-PLAN.md (Phase 11 complete)
|
||||
Last session: 2026-03-19T10:40:03.893Z
|
||||
Stopped at: Completed 12-01-PLAN.md
|
||||
Resume file: None
|
||||
|
||||
72
poller/internal/device/interfaces_test.go
Normal file
72
poller/internal/device/interfaces_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInterfaceInfoFields(t *testing.T) {
|
||||
// Verify struct compiles with expected fields and JSON tags.
|
||||
info := InterfaceInfo{
|
||||
Name: "ether1",
|
||||
MacAddress: "aa:bb:cc:dd:ee:ff",
|
||||
Type: "ether",
|
||||
Running: true,
|
||||
}
|
||||
|
||||
if info.Name != "ether1" {
|
||||
t.Errorf("Name = %q, want %q", info.Name, "ether1")
|
||||
}
|
||||
if info.MacAddress != "aa:bb:cc:dd:ee:ff" {
|
||||
t.Errorf("MacAddress = %q, want %q", info.MacAddress, "aa:bb:cc:dd:ee:ff")
|
||||
}
|
||||
if info.Type != "ether" {
|
||||
t.Errorf("Type = %q, want %q", info.Type, "ether")
|
||||
}
|
||||
if !info.Running {
|
||||
t.Error("Running = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceMACLowercasing(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{name: "already lowercase", input: "aa:bb:cc:dd:ee:ff", want: "aa:bb:cc:dd:ee:ff"},
|
||||
{name: "uppercase", input: "AA:BB:CC:DD:EE:FF", want: "aa:bb:cc:dd:ee:ff"},
|
||||
{name: "mixed case", input: "Aa:Bb:Cc:Dd:Ee:Ff", want: "aa:bb:cc:dd:ee:ff"},
|
||||
{name: "empty", input: "", want: ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := normalizeMACAddress(tt.input)
|
||||
if got != tt.want {
|
||||
t.Errorf("normalizeMACAddress(%q) = %q, want %q", tt.input, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceRunningParsing(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want bool
|
||||
}{
|
||||
{name: "true string", input: "true", want: true},
|
||||
{name: "false string", input: "false", want: false},
|
||||
{name: "empty string", input: "", want: false},
|
||||
{name: "yes string", input: "yes", want: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := parseRunning(tt.input)
|
||||
if got != tt.want {
|
||||
t.Errorf("parseRunning(%q) = %v, want %v", tt.input, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user