checker-blacklist/checker/disconnect_test.go
Pierre-Olivier Mercier c2cc88e1df Add Disconnect.me tracking-protection blocklist source
Downloads and caches the Disconnect.me services.json feed (24h TTL),
matching domains against the Advertising, Analytics, Social, Content,
and Disconnect categories. Severity is warn (privacy classification,
not malware). Reuses the shared feedCache infrastructure.
2026-05-15 21:36:24 +08:00

127 lines
3.6 KiB
Go

package checker
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
sdk "git.happydns.org/checker-sdk-go/checker"
)
const disconnectFakeFeed = `{
"categories": {
"Advertising": [
{
"Evil Corp": {
"https://evilcorp.com": ["tracker.com", "sub.example.org"]
}
}
],
"Analytics": [
{
"Metrics Inc": {
"https://metrics.io": ["analytics.net"]
}
}
]
}
}`
func newDisconnectTestSource(srv *httptest.Server) *disconnectSource {
return &disconnectSource{
cache: newFeedCache(time.Hour, disconnectFetch(srv.URL)),
}
}
func TestDisconnectSource_Listed(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(disconnectFakeFeed))
}))
defer srv.Close()
s := newDisconnectTestSource(srv)
r := s.Query(context.Background(), "tracker.com", "tracker.com", sdk.CheckerOptions{"enable_disconnect": true})[0]
if !r.Enabled || r.Error != "" {
t.Fatalf("expected enabled with no error, got %+v", r)
}
if len(r.Evidence) == 0 {
t.Fatalf("expected evidence, got none")
}
found := false
for _, e := range r.Evidence {
if e.Value == "Advertising" {
found = true
if e.Extra["company"] != "Evil Corp" {
t.Errorf("expected company 'Evil Corp', got %q", e.Extra["company"])
}
}
}
if !found {
t.Errorf("expected Advertising evidence, got %+v", r.Evidence)
}
if listed, sev := s.Evaluate(r); !listed || sev != SeverityWarn {
t.Errorf("expected (true, warn), got (%v, %q)", listed, sev)
}
}
func TestDisconnectSource_SubdomainInFeed(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(disconnectFakeFeed))
}))
defer srv.Close()
// Feed has "sub.example.org"; querying registered domain "example.org" should match.
s := newDisconnectTestSource(srv)
r := s.Query(context.Background(), "example.org", "example.org", sdk.CheckerOptions{"enable_disconnect": true})[0]
if !r.Enabled || r.Error != "" {
t.Fatalf("expected enabled with no error, got %+v", r)
}
if len(r.Evidence) == 0 {
t.Errorf("expected subdomain match evidence, got none")
}
}
func TestDisconnectSource_NotListed(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(disconnectFakeFeed))
}))
defer srv.Close()
s := newDisconnectTestSource(srv)
r := s.Query(context.Background(), "clean.example.com", "clean.example.com", sdk.CheckerOptions{"enable_disconnect": true})[0]
if !r.Enabled || r.Error != "" {
t.Fatalf("expected enabled with no error, got %+v", r)
}
if len(r.Evidence) != 0 {
t.Errorf("expected no evidence for clean domain, got %+v", r.Evidence)
}
if listed, _ := s.Evaluate(r); listed {
t.Errorf("expected not listed for clean domain")
}
}
func TestDisconnectSource_Disabled(t *testing.T) {
s := &disconnectSource{cache: newFeedCache(time.Hour, disconnectFetch("http://nope"))}
r := s.Query(context.Background(), "tracker.com", "tracker.com", sdk.CheckerOptions{"enable_disconnect": false})[0]
if r.Enabled {
t.Errorf("expected disabled result, got %+v", r)
}
}
func TestDisconnectSource_HTTPError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
s := newDisconnectTestSource(srv)
r := s.Query(context.Background(), "tracker.com", "tracker.com", sdk.CheckerOptions{"enable_disconnect": true})[0]
if r.Error == "" {
t.Errorf("expected error on HTTP 500, got empty error")
}
}