package filtering import ( "net" "net/http" "net/url" "os" "path/filepath" "testing" "time" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // testTimeout is the common timeout for tests. const testTimeout = 5 * time.Second // serveHTTPLocally starts a new HTTP server, that handles its index with h. It // also gracefully closes the listener when the test under t finishes. func serveHTTPLocally(t *testing.T, h http.Handler) (urlStr string) { t.Helper() l, err := net.Listen("tcp", ":0") require.NoError(t, err) go func() { _ = http.Serve(l, h) }() testutil.CleanupAndRequireSuccess(t, l.Close) addr := l.Addr() require.IsType(t, (*net.TCPAddr)(nil), addr) return (&url.URL{ Scheme: aghhttp.SchemeHTTP, Host: addr.String(), }).String() } // serveFiltersLocally is a helper that concurrently listens on a free port to // respond with fltContent. func serveFiltersLocally(t *testing.T, fltContent []byte) (urlStr string) { t.Helper() return serveHTTPLocally(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { pt := testutil.PanicT{} n, werr := w.Write(fltContent) require.NoError(pt, werr) require.Equal(pt, len(fltContent), n) })) } // updateAndAssert loads filter content from its URL and then asserts rules // count. func updateAndAssert( t *testing.T, dnsFilter *DNSFilter, f *FilterYAML, wantUpd require.BoolAssertionFunc, wantRulesCount int, ) { t.Helper() ok, err := dnsFilter.update(f) require.NoError(t, err) wantUpd(t, ok) assert.Equal(t, wantRulesCount, f.RulesCount) dir, err := os.ReadDir(filepath.Join(dnsFilter.conf.DataDir, filterDir)) require.NoError(t, err) require.FileExists(t, f.Path(dnsFilter.conf.DataDir)) assert.Len(t, dir, 1) err = dnsFilter.load(f) require.NoError(t, err) } // newDNSFilter returns a new properly initialized DNS filter instance. func newDNSFilter(t *testing.T) (d *DNSFilter) { t.Helper() dnsFilter, err := New(&Config{ DataDir: t.TempDir(), HTTPClient: &http.Client{ Timeout: testTimeout, }, }, nil) require.NoError(t, err) return dnsFilter } func TestDNSFilter_Update(t *testing.T) { const content = `||example.org^$third-party # Inline comment example ||example.com^$third-party 0.0.0.0 example.com ` fltContent := []byte(content) addr := serveFiltersLocally(t, fltContent) f := &FilterYAML{ URL: addr, Name: "test-filter", } dnsFilter := newDNSFilter(t) t.Run("download", func(t *testing.T) { updateAndAssert(t, dnsFilter, f, require.True, 3) }) t.Run("refresh_idle", func(t *testing.T) { updateAndAssert(t, dnsFilter, f, require.False, 3) }) t.Run("refresh_actually", func(t *testing.T) { anotherContent := []byte(`||example.com^`) oldURL := f.URL f.URL = serveFiltersLocally(t, anotherContent) t.Cleanup(func() { f.URL = oldURL }) updateAndAssert(t, dnsFilter, f, require.True, 1) }) t.Run("load_unload", func(t *testing.T) { err := dnsFilter.load(f) require.NoError(t, err) f.unload() }) } func TestFilterYAML_EnsureName(t *testing.T) { dnsFilter := newDNSFilter(t) t.Run("title_custom", func(t *testing.T) { content := []byte("! Title: src-title\n||example.com^") f := &FilterYAML{ URL: serveFiltersLocally(t, content), Name: "user-custom", } updateAndAssert(t, dnsFilter, f, require.True, 1) assert.Equal(t, "user-custom", f.Name) }) t.Run("title_from_src", func(t *testing.T) { content := []byte("! Title: src-title\n||example.com^") f := &FilterYAML{ URL: serveFiltersLocally(t, content), } updateAndAssert(t, dnsFilter, f, require.True, 1) assert.Equal(t, "src-title", f.Name) }) t.Run("title_default", func(t *testing.T) { content := []byte("||example.com^") f := &FilterYAML{ URL: serveFiltersLocally(t, content), } updateAndAssert(t, dnsFilter, f, require.True, 1) assert.Equal(t, "List 0", f.Name) }) }