add testing, reverse engineered / generated from OpenDTU source to improve compatibility testing and detecting regressions early.
This commit is contained in:
parent
6b496a39ee
commit
99959726c8
12 changed files with 2888 additions and 0 deletions
566
main_test.go
Normal file
566
main_test.go
Normal file
|
@ -0,0 +1,566 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBasicAuth(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
username string
|
||||
password string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "basic auth test",
|
||||
username: "testuser",
|
||||
password: "testpass",
|
||||
expected: "Basic dGVzdHVzZXI6dGVzdHBhc3M=",
|
||||
},
|
||||
{
|
||||
name: "empty credentials",
|
||||
username: "",
|
||||
password: "",
|
||||
expected: "Basic Og==",
|
||||
},
|
||||
{
|
||||
name: "special characters",
|
||||
username: "user@domain.com",
|
||||
password: "p@ssw0rd!",
|
||||
expected: "Basic dXNlckBkb21haW4uY29tOnBAc3N3MHJkIQ==",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := basicAuth(tt.username, tt.password)
|
||||
if result != tt.expected {
|
||||
t.Errorf("basicAuth() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLogLevel(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
logLevel string
|
||||
defaultLevel slog.Level
|
||||
expected slog.Level
|
||||
}{
|
||||
{
|
||||
name: "DEBUG level",
|
||||
logLevel: "DEBUG",
|
||||
defaultLevel: slog.LevelInfo,
|
||||
expected: slog.LevelDebug,
|
||||
},
|
||||
{
|
||||
name: "INFO level",
|
||||
logLevel: "INFO",
|
||||
defaultLevel: slog.LevelDebug,
|
||||
expected: slog.LevelInfo,
|
||||
},
|
||||
{
|
||||
name: "WARN level",
|
||||
logLevel: "WARN",
|
||||
defaultLevel: slog.LevelDebug,
|
||||
expected: slog.LevelWarn,
|
||||
},
|
||||
{
|
||||
name: "ERROR level",
|
||||
logLevel: "ERROR",
|
||||
defaultLevel: slog.LevelDebug,
|
||||
expected: slog.LevelError,
|
||||
},
|
||||
{
|
||||
name: "invalid level returns default",
|
||||
logLevel: "INVALID",
|
||||
defaultLevel: slog.LevelInfo,
|
||||
expected: slog.LevelInfo,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Temporarily set the config.LogLevel
|
||||
originalLogLevel := config.LogLevel
|
||||
config.LogLevel = tt.logLevel
|
||||
defer func() { config.LogLevel = originalLogLevel }()
|
||||
|
||||
result := getLogLevel(tt.defaultLevel)
|
||||
if result != tt.expected {
|
||||
t.Errorf("getLogLevel() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVUDStruct(t *testing.T) {
|
||||
jsonData := `{"v": 123.45, "u": "W", "d": 2}`
|
||||
|
||||
var vud VUD
|
||||
err := json.Unmarshal([]byte(jsonData), &vud)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal VUD: %v", err)
|
||||
}
|
||||
|
||||
if vud.V != 123.45 {
|
||||
t.Errorf("Expected V=123.45, got %v", vud.V)
|
||||
}
|
||||
if vud.U != "W" {
|
||||
t.Errorf("Expected U=W, got %v", vud.U)
|
||||
}
|
||||
if vud.D != 2 {
|
||||
t.Errorf("Expected D=2, got %v", vud.D)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInverterStruct(t *testing.T) {
|
||||
jsonData := `{
|
||||
"serial": "123456789",
|
||||
"name": "Test Inverter",
|
||||
"producing": true,
|
||||
"limit_relative": 80.5,
|
||||
"limit_absolute": 800.0,
|
||||
"AC": {
|
||||
"0": {
|
||||
"Power": {"v": 250.5, "u": "W", "d": 1},
|
||||
"Voltage": {"v": 230.0, "u": "V", "d": 1},
|
||||
"Current": {"v": 1.09, "u": "A", "d": 2},
|
||||
"Frequency": {"v": 50.0, "u": "Hz", "d": 1},
|
||||
"PowerFactor": {"v": 1.0, "u": "", "d": 2},
|
||||
"ReactivePower": {"v": 0.0, "u": "var", "d": 1}
|
||||
}
|
||||
},
|
||||
"DC": {
|
||||
"0": {
|
||||
"Name": {"u": "String 1"},
|
||||
"Power": {"v": 260.0, "u": "W", "d": 1},
|
||||
"Voltage": {"v": 35.2, "u": "V", "d": 1},
|
||||
"Current": {"v": 7.4, "u": "A", "d": 1},
|
||||
"YieldDay": {"v": 2.5, "u": "kWh", "d": 3},
|
||||
"YieldTotal": {"v": 1250.8, "u": "kWh", "d": 3},
|
||||
"Irradiation": {"v": 85.2, "u": "%", "d": 1, "max": 100}
|
||||
}
|
||||
},
|
||||
"events": 2,
|
||||
"poll_enabled": true,
|
||||
"reachable": true,
|
||||
"data_age": 0,
|
||||
"INV": {
|
||||
"0": {
|
||||
"Temperature": {"v": 32.5, "u": "°C", "d": 1},
|
||||
"Efficiency": {"v": 96.2, "u": "%", "d": 1},
|
||||
"Power DC": {"v": 260.0, "u": "W", "d": 1},
|
||||
"YieldDay": {"v": 2.5, "u": "kWh", "d": 3},
|
||||
"YieldTotal": {"v": 1250.8, "u": "kWh", "d": 3}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
var inverter Inverter
|
||||
err := json.Unmarshal([]byte(jsonData), &inverter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal Inverter: %v", err)
|
||||
}
|
||||
|
||||
if inverter.Serial != "123456789" {
|
||||
t.Errorf("Expected Serial=123456789, got %v", inverter.Serial)
|
||||
}
|
||||
if inverter.Name != "Test Inverter" {
|
||||
t.Errorf("Expected Name=Test Inverter, got %v", inverter.Name)
|
||||
}
|
||||
if !inverter.Producing {
|
||||
t.Errorf("Expected Producing=true, got %v", inverter.Producing)
|
||||
}
|
||||
if inverter.LimitRelative != 80.5 {
|
||||
t.Errorf("Expected LimitRelative=80.5, got %v", inverter.LimitRelative)
|
||||
}
|
||||
|
||||
// Test AC data
|
||||
if len(inverter.AC) != 1 {
|
||||
t.Errorf("Expected 1 AC entry, got %v", len(inverter.AC))
|
||||
}
|
||||
if ac, exists := inverter.AC["0"]; exists {
|
||||
if ac.Power.V != 250.5 {
|
||||
t.Errorf("Expected AC Power=250.5, got %v", ac.Power.V)
|
||||
}
|
||||
if ac.Voltage.V != 230.0 {
|
||||
t.Errorf("Expected AC Voltage=230.0, got %v", ac.Voltage.V)
|
||||
}
|
||||
} else {
|
||||
t.Error("Expected AC[0] to exist")
|
||||
}
|
||||
|
||||
// Test DC data
|
||||
if len(inverter.DC) != 1 {
|
||||
t.Errorf("Expected 1 DC entry, got %v", len(inverter.DC))
|
||||
}
|
||||
if dc, exists := inverter.DC["0"]; exists {
|
||||
if dc.Name.U != "String 1" {
|
||||
t.Errorf("Expected DC Name=String 1, got %v", dc.Name.U)
|
||||
}
|
||||
if dc.Power.V != 260.0 {
|
||||
t.Errorf("Expected DC Power=260.0, got %v", dc.Power.V)
|
||||
}
|
||||
} else {
|
||||
t.Error("Expected DC[0] to exist")
|
||||
}
|
||||
|
||||
// Test INV data
|
||||
if len(inverter.INV) != 1 {
|
||||
t.Errorf("Expected 1 INV entry, got %v", len(inverter.INV))
|
||||
}
|
||||
if inv, exists := inverter.INV["0"]; exists {
|
||||
if inv.Temperature.V != 32.5 {
|
||||
t.Errorf("Expected INV Temperature=32.5, got %v", inv.Temperature.V)
|
||||
}
|
||||
if inv.Efficiency.V != 96.2 {
|
||||
t.Errorf("Expected INV Efficiency=96.2, got %v", inv.Efficiency.V)
|
||||
}
|
||||
} else {
|
||||
t.Error("Expected INV[0] to exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventStruct(t *testing.T) {
|
||||
jsonData := `{
|
||||
"message_id": 1,
|
||||
"message": "Test event message",
|
||||
"start_time": 1634567890,
|
||||
"end_time": 1634567950
|
||||
}`
|
||||
|
||||
var event Event
|
||||
err := json.Unmarshal([]byte(jsonData), &event)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal Event: %v", err)
|
||||
}
|
||||
|
||||
if event.MessageID != 1 {
|
||||
t.Errorf("Expected MessageID=1, got %v", event.MessageID)
|
||||
}
|
||||
if event.Message != "Test event message" {
|
||||
t.Errorf("Expected Message=Test event message, got %v", event.Message)
|
||||
}
|
||||
if event.StartTime != 1634567890 {
|
||||
t.Errorf("Expected StartTime=1634567890, got %v", event.StartTime)
|
||||
}
|
||||
if event.EndTime != 1634567950 {
|
||||
t.Errorf("Expected EndTime=1634567950, got %v", event.EndTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLiveDataStruct(t *testing.T) {
|
||||
jsonData := `{
|
||||
"inverters": [
|
||||
{
|
||||
"serial": "123456789",
|
||||
"name": "Test Inverter",
|
||||
"producing": true,
|
||||
"limit_relative": 80.5,
|
||||
"limit_absolute": 800.0,
|
||||
"AC": {},
|
||||
"DC": {},
|
||||
"events": 0,
|
||||
"poll_enabled": true,
|
||||
"reachable": true,
|
||||
"data_age": 0,
|
||||
"INV": {}
|
||||
}
|
||||
],
|
||||
"total": {
|
||||
"Power": {"v": 250.5, "u": "W", "d": 1},
|
||||
"YieldDay": {"v": 2.5, "u": "kWh", "d": 3},
|
||||
"YieldTotal": {"v": 1250.8, "u": "kWh", "d": 3}
|
||||
},
|
||||
"hints": {
|
||||
"time_sync": true,
|
||||
"radio_problem": false,
|
||||
"default_password": false
|
||||
}
|
||||
}`
|
||||
|
||||
var liveData LiveData
|
||||
err := json.Unmarshal([]byte(jsonData), &liveData)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal LiveData: %v", err)
|
||||
}
|
||||
|
||||
if len(liveData.Inverters) != 1 {
|
||||
t.Errorf("Expected 1 inverter, got %v", len(liveData.Inverters))
|
||||
}
|
||||
|
||||
if liveData.Total.Power.V != 250.5 {
|
||||
t.Errorf("Expected Total Power=250.5, got %v", liveData.Total.Power.V)
|
||||
}
|
||||
|
||||
if !liveData.Hints.TimeSync {
|
||||
t.Errorf("Expected Hints.TimeSync=true, got %v", liveData.Hints.TimeSync)
|
||||
}
|
||||
if liveData.Hints.RadioProblem {
|
||||
t.Errorf("Expected Hints.RadioProblem=false, got %v", liveData.Hints.RadioProblem)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigLoadFromEnv(t *testing.T) {
|
||||
// Save original environment variables
|
||||
originalDB := os.Getenv("DB_URL")
|
||||
originalAddress := os.Getenv("OPENDTU_ADDRESS")
|
||||
originalAuth := os.Getenv("OPENDTU_AUTH")
|
||||
originalUser := os.Getenv("OPENDTU_USERNAME")
|
||||
originalPassword := os.Getenv("OPENDTU_PASSWORD")
|
||||
originalTimescale := os.Getenv("TIMESCALEDB_ENABLED")
|
||||
originalTZ := os.Getenv("TZ")
|
||||
originalLogLevel := os.Getenv("LOG_LEVEL")
|
||||
originalConfigFile := os.Getenv("CONFIG_FILE")
|
||||
|
||||
// Clean up after test
|
||||
defer func() {
|
||||
os.Setenv("DB_URL", originalDB)
|
||||
os.Setenv("OPENDTU_ADDRESS", originalAddress)
|
||||
os.Setenv("OPENDTU_AUTH", originalAuth)
|
||||
os.Setenv("OPENDTU_USERNAME", originalUser)
|
||||
os.Setenv("OPENDTU_PASSWORD", originalPassword)
|
||||
os.Setenv("TIMESCALEDB_ENABLED", originalTimescale)
|
||||
os.Setenv("TZ", originalTZ)
|
||||
os.Setenv("LOG_LEVEL", originalLogLevel)
|
||||
os.Setenv("CONFIG_FILE", originalConfigFile)
|
||||
}()
|
||||
|
||||
// Set test environment variables
|
||||
os.Setenv("CONFIG_FILE", "/nonexistent/config.json") // Force env var fallback
|
||||
os.Setenv("DB_URL", "postgres://testuser:testpass@localhost/testdb")
|
||||
os.Setenv("OPENDTU_ADDRESS", "192.168.1.100")
|
||||
os.Setenv("OPENDTU_AUTH", "true")
|
||||
os.Setenv("OPENDTU_USERNAME", "admin")
|
||||
os.Setenv("OPENDTU_PASSWORD", "secret")
|
||||
os.Setenv("TIMESCALEDB_ENABLED", "true")
|
||||
os.Setenv("TZ", "Europe/Amsterdam")
|
||||
os.Setenv("LOG_LEVEL", "DEBUG")
|
||||
|
||||
config, err := loadConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("loadConfig() failed: %v", err)
|
||||
}
|
||||
|
||||
if config.DB != "postgres://testuser:testpass@localhost/testdb" {
|
||||
t.Errorf("Expected DB from env, got %v", config.DB)
|
||||
}
|
||||
if config.OpenDTUAddress != "192.168.1.100" {
|
||||
t.Errorf("Expected OpenDTUAddress from env, got %v", config.OpenDTUAddress)
|
||||
}
|
||||
if !config.OpenDTUAuth {
|
||||
t.Errorf("Expected OpenDTUAuth=true from env, got %v", config.OpenDTUAuth)
|
||||
}
|
||||
if config.OpenDTUUser != "admin" {
|
||||
t.Errorf("Expected OpenDTUUser from env, got %v", config.OpenDTUUser)
|
||||
}
|
||||
if config.OpenDTUPassword != "secret" {
|
||||
t.Errorf("Expected OpenDTUPassword from env, got %v", config.OpenDTUPassword)
|
||||
}
|
||||
if !config.TimescaleDB {
|
||||
t.Errorf("Expected TimescaleDB=true from env, got %v", config.TimescaleDB)
|
||||
}
|
||||
if config.TZ != "Europe/Amsterdam" {
|
||||
t.Errorf("Expected TZ from env, got %v", config.TZ)
|
||||
}
|
||||
if config.LogLevel != "DEBUG" {
|
||||
t.Errorf("Expected LogLevel from env, got %v", config.LogLevel)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeZoneValidation(t *testing.T) {
|
||||
validTimezones := []string{
|
||||
"UTC",
|
||||
"Europe/Amsterdam",
|
||||
"America/New_York",
|
||||
"Asia/Tokyo",
|
||||
}
|
||||
|
||||
for _, tz := range validTimezones {
|
||||
t.Run(tz, func(t *testing.T) {
|
||||
_, err := time.LoadLocation(tz)
|
||||
if err != nil {
|
||||
t.Errorf("Timezone %s should be valid but got error: %v", tz, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
invalidTimezones := []string{
|
||||
"Invalid/Timezone",
|
||||
"Not/A/Real/Place",
|
||||
"",
|
||||
}
|
||||
|
||||
for _, tz := range invalidTimezones {
|
||||
if tz == "" {
|
||||
continue // Empty string is handled differently
|
||||
}
|
||||
t.Run(tz, func(t *testing.T) {
|
||||
_, err := time.LoadLocation(tz)
|
||||
if err == nil {
|
||||
t.Errorf("Timezone %s should be invalid but was accepted", tz)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test RadioStatistics structure (from official OpenDTU)
|
||||
func TestRadioStatisticsStruct(t *testing.T) {
|
||||
jsonData := `{
|
||||
"tx_request": 12345,
|
||||
"tx_re_request": 234,
|
||||
"rx_success": 12000,
|
||||
"rx_fail_nothing": 50,
|
||||
"rx_fail_partial": 30,
|
||||
"rx_fail_corrupt": 21,
|
||||
"rssi": -65.5
|
||||
}`
|
||||
|
||||
var stats RadioStatistics
|
||||
err := json.Unmarshal([]byte(jsonData), &stats)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal RadioStatistics: %v", err)
|
||||
}
|
||||
|
||||
if stats.TxRequest != 12345 {
|
||||
t.Errorf("Expected TxRequest=12345, got %v", stats.TxRequest)
|
||||
}
|
||||
if stats.TxReRequest != 234 {
|
||||
t.Errorf("Expected TxReRequest=234, got %v", stats.TxReRequest)
|
||||
}
|
||||
if stats.RxSuccess != 12000 {
|
||||
t.Errorf("Expected RxSuccess=12000, got %v", stats.RxSuccess)
|
||||
}
|
||||
if stats.RxFailNothing != 50 {
|
||||
t.Errorf("Expected RxFailNothing=50, got %v", stats.RxFailNothing)
|
||||
}
|
||||
if stats.RxFailPartial != 30 {
|
||||
t.Errorf("Expected RxFailPartial=30, got %v", stats.RxFailPartial)
|
||||
}
|
||||
if stats.RxFailCorrupt != 21 {
|
||||
t.Errorf("Expected RxFailCorrupt=21, got %v", stats.RxFailCorrupt)
|
||||
}
|
||||
if stats.RSSI != -65.5 {
|
||||
t.Errorf("Expected RSSI=-65.5, got %v", stats.RSSI)
|
||||
}
|
||||
}
|
||||
|
||||
// Test inverter struct with new OpenDTU fields: order, data_age_ms, radio_stats
|
||||
func TestInverterWithOpenDTUFields(t *testing.T) {
|
||||
jsonData := `{
|
||||
"serial": "114173123456",
|
||||
"name": "Hoymiles HM-800",
|
||||
"order": 0,
|
||||
"data_age": 0,
|
||||
"data_age_ms": 124,
|
||||
"poll_enabled": true,
|
||||
"reachable": true,
|
||||
"producing": true,
|
||||
"limit_relative": 100.0,
|
||||
"limit_absolute": 800.0,
|
||||
"events": 2,
|
||||
"radio_stats": {
|
||||
"tx_request": 12345,
|
||||
"tx_re_request": 234,
|
||||
"rx_success": 12000,
|
||||
"rx_fail_nothing": 50,
|
||||
"rx_fail_partial": 30,
|
||||
"rx_fail_corrupt": 21,
|
||||
"rssi": -65.5
|
||||
},
|
||||
"AC": {},
|
||||
"DC": {},
|
||||
"INV": {}
|
||||
}`
|
||||
|
||||
var inverter Inverter
|
||||
err := json.Unmarshal([]byte(jsonData), &inverter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal Inverter with OpenDTU fields: %v", err)
|
||||
}
|
||||
|
||||
// Test new fields from official OpenDTU
|
||||
if inverter.Order != 0 {
|
||||
t.Errorf("Expected Order=0, got %v", inverter.Order)
|
||||
}
|
||||
if inverter.DataAge != 0 {
|
||||
t.Errorf("Expected DataAge=0, got %v", inverter.DataAge)
|
||||
}
|
||||
if inverter.DataAgeMs != 124 {
|
||||
t.Errorf("Expected DataAgeMs=124, got %v", inverter.DataAgeMs)
|
||||
}
|
||||
|
||||
// Test radio_stats
|
||||
if inverter.RadioStats.TxRequest != 12345 {
|
||||
t.Errorf("Expected RadioStats.TxRequest=12345, got %v", inverter.RadioStats.TxRequest)
|
||||
}
|
||||
if inverter.RadioStats.RxSuccess != 12000 {
|
||||
t.Errorf("Expected RadioStats.RxSuccess=12000, got %v", inverter.RadioStats.RxSuccess)
|
||||
}
|
||||
if inverter.RadioStats.RSSI != -65.5 {
|
||||
t.Errorf("Expected RadioStats.RSSI=-65.5, got %v", inverter.RadioStats.RSSI)
|
||||
}
|
||||
|
||||
// Test original fields still work
|
||||
if inverter.Serial != "114173123456" {
|
||||
t.Errorf("Expected Serial=114173123456, got %v", inverter.Serial)
|
||||
}
|
||||
if inverter.Name != "Hoymiles HM-800" {
|
||||
t.Errorf("Expected Name=Hoymiles HM-800, got %v", inverter.Name)
|
||||
}
|
||||
if !inverter.Producing {
|
||||
t.Errorf("Expected Producing=true, got %v", inverter.Producing)
|
||||
}
|
||||
}
|
||||
|
||||
// Test Hints struct with new pin_mapping_issue field from OpenDTU
|
||||
func TestHintsWithPinMappingIssue(t *testing.T) {
|
||||
jsonData := `{
|
||||
"time_sync": true,
|
||||
"radio_problem": false,
|
||||
"default_password": false,
|
||||
"pin_mapping_issue": false
|
||||
}`
|
||||
|
||||
var hints Hints
|
||||
err := json.Unmarshal([]byte(jsonData), &hints)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal Hints with pin_mapping_issue: %v", err)
|
||||
}
|
||||
|
||||
if !hints.TimeSync {
|
||||
t.Errorf("Expected TimeSync=true, got %v", hints.TimeSync)
|
||||
}
|
||||
if hints.RadioProblem {
|
||||
t.Errorf("Expected RadioProblem=false, got %v", hints.RadioProblem)
|
||||
}
|
||||
if hints.DefaultPassword {
|
||||
t.Errorf("Expected DefaultPassword=false, got %v", hints.DefaultPassword)
|
||||
}
|
||||
if hints.PinMappingIssue {
|
||||
t.Errorf("Expected PinMappingIssue=false, got %v", hints.PinMappingIssue)
|
||||
}
|
||||
|
||||
// Test when pin_mapping_issue is true
|
||||
jsonDataWithIssue := `{
|
||||
"time_sync": true,
|
||||
"radio_problem": false,
|
||||
"default_password": false,
|
||||
"pin_mapping_issue": true
|
||||
}`
|
||||
|
||||
err = json.Unmarshal([]byte(jsonDataWithIssue), &hints)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal Hints with pin_mapping_issue=true: %v", err)
|
||||
}
|
||||
|
||||
if !hints.PinMappingIssue {
|
||||
t.Errorf("Expected PinMappingIssue=true, got %v", hints.PinMappingIssue)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue