200 lines
6.1 KiB
Go
200 lines
6.1 KiB
Go
package checker
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
func mustJSON(t *testing.T, v any) json.RawMessage {
|
|
t.Helper()
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func TestTLSProbeView_AddressEndpointWins(t *testing.T) {
|
|
v := tlsProbeView{Endpoint: "mx.example.com:25", Host: "ignored", Port: 999}
|
|
if got := v.address(); got != "mx.example.com:25" {
|
|
t.Errorf("got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestTLSProbeView_AddressFromHostPort(t *testing.T) {
|
|
v := tlsProbeView{Host: "mx.example.com", Port: 25}
|
|
if got := v.address(); got != "mx.example.com:25" {
|
|
t.Errorf("got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestTLSProbeView_AddressEmpty(t *testing.T) {
|
|
v := tlsProbeView{}
|
|
if got := v.address(); got != "" {
|
|
t.Errorf("expected empty, got %q", got)
|
|
}
|
|
v2 := tlsProbeView{Host: "only-host"}
|
|
if got := v2.address(); got != "" {
|
|
t.Errorf("host without port should be empty, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestParseTLSRelated_KeyedByRef(t *testing.T) {
|
|
payload := map[string]any{
|
|
"probes": map[string]any{
|
|
"ref-A": map[string]any{"host": "mx.example.com", "port": 25, "tls_version": "TLS1.3"},
|
|
},
|
|
}
|
|
r := sdk.RelatedObservation{Ref: "ref-A", Data: mustJSON(t, payload)}
|
|
v := parseTLSRelated(r)
|
|
if v == nil {
|
|
t.Fatal("expected match")
|
|
}
|
|
if v.TLSVersion != "TLS1.3" {
|
|
t.Errorf("got %q", v.TLSVersion)
|
|
}
|
|
}
|
|
|
|
func TestParseTLSRelated_KeyedRefMissing(t *testing.T) {
|
|
payload := map[string]any{
|
|
"probes": map[string]any{
|
|
"some-other-ref": map[string]any{"host": "mx", "port": 25},
|
|
},
|
|
}
|
|
r := sdk.RelatedObservation{Ref: "ref-A", Data: mustJSON(t, payload)}
|
|
if got := parseTLSRelated(r); got != nil {
|
|
t.Errorf("expected nil for missing ref, got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestParseTLSRelated_FlatTopLevel(t *testing.T) {
|
|
payload := map[string]any{"host": "mx.example.com", "port": 25}
|
|
r := sdk.RelatedObservation{Data: mustJSON(t, payload)}
|
|
v := parseTLSRelated(r)
|
|
if v == nil || v.Host != "mx.example.com" {
|
|
t.Errorf("got %+v", v)
|
|
}
|
|
}
|
|
|
|
func TestParseTLSRelated_BadJSON(t *testing.T) {
|
|
r := sdk.RelatedObservation{Data: json.RawMessage("not json at all")}
|
|
if got := parseTLSRelated(r); got != nil {
|
|
t.Errorf("expected nil for bad json, got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestTLSIssuesFromRelated_FromIssuesList(t *testing.T) {
|
|
payload := map[string]any{
|
|
"host": "mx.example.com", "port": 25,
|
|
"issues": []map[string]any{
|
|
{"code": "cert.expired", "severity": "crit", "message": "expired", "fix": "renew"},
|
|
{"code": "cert.weakcipher", "severity": "WARN", "message": "weak"},
|
|
{"code": "ignore-me", "severity": "bogus"}, // unknown severity → skipped
|
|
},
|
|
}
|
|
related := []sdk.RelatedObservation{{Data: mustJSON(t, payload)}}
|
|
issues := tlsIssuesFromRelated(related)
|
|
if len(issues) != 2 {
|
|
t.Fatalf("want 2 issues, got %d (%+v)", len(issues), issues)
|
|
}
|
|
if issues[0].Code != "smtp.tls.cert.expired" || issues[0].Severity != SeverityCrit {
|
|
t.Errorf("first: %+v", issues[0])
|
|
}
|
|
if issues[1].Severity != SeverityWarn {
|
|
t.Errorf("second severity: %q", issues[1].Severity)
|
|
}
|
|
}
|
|
|
|
func TestTLSIssuesFromRelated_EmptyCode(t *testing.T) {
|
|
payload := map[string]any{
|
|
"host": "mx", "port": 25,
|
|
"issues": []map[string]any{{"severity": "warn", "message": "x"}},
|
|
}
|
|
related := []sdk.RelatedObservation{{Data: mustJSON(t, payload)}}
|
|
issues := tlsIssuesFromRelated(related)
|
|
if len(issues) != 1 || issues[0].Code != "smtp.tls.tls.unknown" {
|
|
t.Errorf("expected fallback code, got %+v", issues)
|
|
}
|
|
}
|
|
|
|
func TestTLSIssuesFromRelated_FromShorthand_ChainInvalid(t *testing.T) {
|
|
chainBad := false
|
|
payload := map[string]any{
|
|
"host": "mx", "port": 25, "chain_valid": chainBad,
|
|
}
|
|
related := []sdk.RelatedObservation{{Data: mustJSON(t, payload)}}
|
|
issues := tlsIssuesFromRelated(related)
|
|
if len(issues) != 1 || issues[0].Severity != SeverityCrit {
|
|
t.Errorf("expected single crit issue, got %+v", issues)
|
|
}
|
|
}
|
|
|
|
func TestTLSIssuesFromRelated_HostnameMismatch(t *testing.T) {
|
|
hn := false
|
|
payload := map[string]any{"host": "mx", "port": 25, "hostname_match": hn}
|
|
related := []sdk.RelatedObservation{{Data: mustJSON(t, payload)}}
|
|
issues := tlsIssuesFromRelated(related)
|
|
if len(issues) != 1 || issues[0].Severity != SeverityCrit {
|
|
t.Errorf("expected hostname-mismatch crit, got %+v", issues)
|
|
}
|
|
}
|
|
|
|
func TestTLSIssuesFromRelated_ExpiringSoon(t *testing.T) {
|
|
soon := time.Now().Add(48 * time.Hour)
|
|
payload := map[string]any{"host": "mx", "port": 25, "not_after": soon}
|
|
issues := tlsIssuesFromRelated([]sdk.RelatedObservation{{Data: mustJSON(t, payload)}})
|
|
if len(issues) != 1 || issues[0].Severity != SeverityWarn {
|
|
t.Errorf("expected warn, got %+v", issues)
|
|
}
|
|
}
|
|
|
|
func TestTLSIssuesFromRelated_Expired(t *testing.T) {
|
|
past := time.Now().Add(-1 * time.Hour)
|
|
payload := map[string]any{"host": "mx", "port": 25, "not_after": past}
|
|
issues := tlsIssuesFromRelated([]sdk.RelatedObservation{{Data: mustJSON(t, payload)}})
|
|
if len(issues) != 1 || issues[0].Severity != SeverityCrit {
|
|
t.Errorf("expected crit (expired), got %+v", issues)
|
|
}
|
|
}
|
|
|
|
func TestTLSIssuesFromRelated_NoSeverityNoIssue(t *testing.T) {
|
|
yes := true
|
|
notAfter := time.Now().Add(365 * 24 * time.Hour)
|
|
payload := map[string]any{
|
|
"host": "mx", "port": 25,
|
|
"chain_valid": yes, "hostname_match": yes, "not_after": notAfter,
|
|
}
|
|
if got := tlsIssuesFromRelated([]sdk.RelatedObservation{{Data: mustJSON(t, payload)}}); len(got) != 0 {
|
|
t.Errorf("happy path: expected no issues, got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestWorstSeverity_Ordering(t *testing.T) {
|
|
v := tlsProbeView{
|
|
Issues: []struct {
|
|
Code string `json:"code"`
|
|
Severity string `json:"severity"`
|
|
Message string `json:"message,omitempty"`
|
|
Fix string `json:"fix,omitempty"`
|
|
}{
|
|
{Code: "a", Severity: "info"},
|
|
{Code: "b", Severity: "warn"},
|
|
},
|
|
}
|
|
if got := v.worstSeverity(); got != SeverityWarn {
|
|
t.Errorf("info+warn → warn, got %q", got)
|
|
}
|
|
|
|
v.Issues = append(v.Issues, struct {
|
|
Code string `json:"code"`
|
|
Severity string `json:"severity"`
|
|
Message string `json:"message,omitempty"`
|
|
Fix string `json:"fix,omitempty"`
|
|
}{Code: "c", Severity: "CRIT"})
|
|
if got := v.worstSeverity(); got != SeverityCrit {
|
|
t.Errorf("with crit → crit, got %q", got)
|
|
}
|
|
}
|