blocky/lists/list_cache_test.go

480 lines
14 KiB
Go
Raw Permalink Normal View History

2020-01-12 18:23:35 +01:00
package lists
import (
"bufio"
2023-04-17 18:21:56 +02:00
"context"
"errors"
"fmt"
"io"
2021-08-25 22:06:34 +02:00
"net/http/httptest"
"os"
"strings"
2020-05-04 22:20:13 +02:00
2023-04-17 18:21:56 +02:00
"github.com/0xERR0R/blocky/config"
. "github.com/0xERR0R/blocky/evt"
"github.com/0xERR0R/blocky/lists/parsers"
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
"github.com/0xERR0R/blocky/log"
2023-09-12 09:12:48 +02:00
"github.com/google/uuid"
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
"github.com/sirupsen/logrus"
2020-05-04 22:20:13 +02:00
2021-08-25 22:06:34 +02:00
. "github.com/0xERR0R/blocky/helpertest"
2022-03-03 11:27:27 +01:00
. "github.com/onsi/ginkgo/v2"
2020-05-04 22:20:13 +02:00
. "github.com/onsi/gomega"
2020-01-12 18:23:35 +01:00
)
2020-05-04 22:20:13 +02:00
var _ = Describe("ListCache", func() {
var (
tmpDir *TmpFolder
emptyFile, file1, file2, file3 *TmpFile
2020-05-04 22:20:13 +02:00
server1, server2, server3 *httptest.Server
2023-04-17 18:21:56 +02:00
sut *ListCache
sutConfig config.SourceLoading
2023-04-17 18:21:56 +02:00
listCacheType ListCacheType
lists map[string][]config.BytesSource
downloader FileDownloader
mockDownloader *MockDownloader
ctx context.Context
cancelFn context.CancelFunc
err error
expectFail bool
2020-05-04 22:20:13 +02:00
)
2023-04-17 18:21:56 +02:00
2020-05-04 22:20:13 +02:00
BeforeEach(func() {
expectFail = false
ctx, cancelFn = context.WithCancel(context.Background())
DeferCleanup(cancelFn)
listCacheType = ListCacheTypeDenylist
2023-04-17 18:21:56 +02:00
sutConfig, err = config.WithDefaults[config.SourceLoading]()
2023-04-17 18:21:56 +02:00
Expect(err).Should(Succeed())
sutConfig.RefreshPeriod = -1
downloader = NewDownloader(config.Downloader{}, nil)
2023-04-17 18:21:56 +02:00
mockDownloader = nil
2020-05-04 22:20:13 +02:00
server1 = TestServer("blocked1.com\nblocked1a.com\n192.168.178.55")
server2 = TestServer("blocked2.com")
server3 = TestServer("blocked3.com\nblocked1a.com")
2023-04-17 18:21:56 +02:00
tmpDir = NewTmpFolder("ListCache")
DeferCleanup(tmpDir.Clean)
emptyFile = tmpDir.CreateStringFile("empty", "#empty file")
emptyFile = tmpDir.CreateStringFile("empty", "#empty file")
file1 = tmpDir.CreateStringFile("file1", "blocked1.com", "blocked1a.com")
file2 = tmpDir.CreateStringFile("file2", "blocked2.com")
file3 = tmpDir.CreateStringFile("file3", "blocked3.com", "blocked1a.com")
2020-05-04 22:20:13 +02:00
})
2023-04-17 18:21:56 +02:00
JustBeforeEach(func() {
Expect(lists).ShouldNot(BeNil(), "bad test: forgot to set `lists`")
if mockDownloader != nil {
downloader = mockDownloader
}
sut, err = NewListCache(ctx, listCacheType, sutConfig, lists, downloader)
if expectFail {
Expect(err).Should(HaveOccurred())
} else {
Expect(err).Should(Succeed())
}
2023-04-17 18:21:56 +02:00
})
2020-05-04 22:20:13 +02:00
Describe("List cache and matching", func() {
2023-04-17 18:21:56 +02:00
When("List is empty", func() {
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr0": config.NewBytesSources(emptyFile.Path),
2021-04-10 21:48:38 +02:00
}
2023-04-17 18:21:56 +02:00
})
2021-04-10 21:48:38 +02:00
2023-04-17 18:21:56 +02:00
When("Query with empty", func() {
It("should not panic", func() {
group := sut.Match("", []string{"gr0"})
Expect(group).Should(BeEmpty())
})
2021-04-10 21:48:38 +02:00
})
2020-05-04 22:20:13 +02:00
It("should not match anything", func() {
group := sut.Match("google.com", []string{"gr1"})
2020-05-04 22:20:13 +02:00
Expect(group).Should(BeEmpty())
})
})
When("List becomes empty on refresh", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
mockDownloader = newMockDownloader(func(res chan<- string, err chan<- error) {
res <- "blocked1.com"
res <- "# nothing"
})
2023-04-17 18:21:56 +02:00
lists = map[string][]config.BytesSource{
"gr1": {mockDownloader.ListSource()},
}
2023-04-17 18:21:56 +02:00
})
2023-04-17 18:21:56 +02:00
It("should delete existing elements from group cache", func(ctx context.Context) {
group := sut.Match("blocked1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
2023-04-17 18:21:56 +02:00
err := sut.refresh(ctx)
Expect(err).Should(Succeed())
group = sut.Match("blocked1.com", []string{"gr1"})
Expect(group).Should(BeEmpty())
})
})
When("List has invalid lines", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": {
2023-04-17 18:21:56 +02:00
config.TextBytesSource(
"inlinedomain1.com",
"invaliddomain!",
"inlinedomain2.com",
),
},
}
2023-04-17 18:21:56 +02:00
})
2023-04-17 18:21:56 +02:00
It("should still other domains", func() {
group := sut.Match("inlinedomain1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
group = sut.Match("inlinedomain2.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
})
})
When("a temporary/transient err occurs on download", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
// should produce a transient error on second and third attempt
2023-04-17 18:21:56 +02:00
mockDownloader = newMockDownloader(func(res chan<- string, err chan<- error) {
res <- "blocked1.com\nblocked2.com\n"
err <- &TransientError{inner: errors.New("boom")}
err <- &TransientError{inner: errors.New("boom")}
})
2023-04-17 18:21:56 +02:00
lists = map[string][]config.BytesSource{
"gr1": {mockDownloader.ListSource()},
}
2023-04-17 18:21:56 +02:00
})
2023-04-17 18:21:56 +02:00
It("should not delete existing elements from group cache", func(ctx context.Context) {
By("Lists loaded without timeout", func() {
Eventually(func(g Gomega) {
group := sut.Match("blocked1.com", []string{"gr1"})
g.Expect(group).Should(ContainElement("gr1"))
}, "1s").Should(Succeed())
})
2023-04-17 18:21:56 +02:00
Expect(sut.refresh(ctx)).Should(HaveOccurred())
By("List couldn't be loaded due to timeout", func() {
group := sut.Match("blocked1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
})
_ = sut.Refresh()
By("List couldn't be loaded due to timeout", func() {
group := sut.Match("blocked1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
})
})
})
When("non transient err occurs on download", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
// should produce a non transient error on second attempt
2023-04-17 18:21:56 +02:00
mockDownloader = newMockDownloader(func(res chan<- string, err chan<- error) {
res <- "blocked1.com"
err <- errors.New("boom")
})
2023-04-17 18:21:56 +02:00
lists = map[string][]config.BytesSource{
"gr1": {mockDownloader.ListSource()},
}
2023-04-17 18:21:56 +02:00
})
2023-04-17 18:21:56 +02:00
It("should keep existing elements from group cache", func(ctx context.Context) {
By("Lists loaded without err", func() {
group := sut.Match("blocked1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
})
2023-04-17 18:21:56 +02:00
Expect(sut.refresh(ctx)).Should(HaveOccurred())
By("Lists from first load is kept", func() {
group := sut.Match("blocked1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
})
})
})
When("Configuration has 3 external working urls", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": config.NewBytesSources(server1.URL, server2.URL),
"gr2": config.NewBytesSources(server3.URL),
2020-05-04 22:20:13 +02:00
}
2023-04-17 18:21:56 +02:00
})
2020-05-04 22:20:13 +02:00
2023-04-17 18:21:56 +02:00
It("should download the list and match against", func() {
group := sut.Match("blocked1.com", []string{"gr1", "gr2"})
Expect(group).Should(ContainElement("gr1"))
group = sut.Match("blocked1a.com", []string{"gr1", "gr2"})
Expect(group).Should(ContainElement("gr1"))
group = sut.Match("blocked1a.com", []string{"gr2"})
Expect(group).Should(ContainElement("gr2"))
})
})
When("Configuration has some faulty urls", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": config.NewBytesSources(server1.URL, server2.URL, "doesnotexist"),
"gr2": config.NewBytesSources(server3.URL, "someotherfile"),
}
2023-04-17 18:21:56 +02:00
})
2023-04-17 18:21:56 +02:00
It("should download the list and match against", func() {
group := sut.Match("blocked1.com", []string{"gr1", "gr2"})
Expect(group).Should(ContainElement("gr1"))
2020-05-04 22:20:13 +02:00
group = sut.Match("blocked1a.com", []string{"gr1", "gr2"})
Expect(group).Should(ContainElements("gr1", "gr2"))
2020-05-04 22:20:13 +02:00
group = sut.Match("blocked1a.com", []string{"gr2"})
Expect(group).Should(ContainElement("gr2"))
2020-05-04 22:20:13 +02:00
})
})
2021-01-19 21:52:24 +01:00
When("List will be updated", func() {
2023-04-17 18:21:56 +02:00
resultCnt := 0
2020-05-04 22:20:13 +02:00
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": config.NewBytesSources(server1.URL),
}
2021-01-19 21:52:24 +01:00
_ = Bus().SubscribeOnce(BlockingCacheGroupChanged, func(listType ListCacheType, group string, cnt int) {
2021-01-19 21:52:24 +01:00
resultCnt = cnt
})
2023-04-17 18:21:56 +02:00
})
2021-01-19 21:52:24 +01:00
2023-04-17 18:21:56 +02:00
It("event should be fired and contain count of elements in downloaded lists", func() {
group := sut.Match("blocked1.com", []string{})
2020-05-04 22:20:13 +02:00
Expect(group).Should(BeEmpty())
2021-01-19 21:52:24 +01:00
Expect(resultCnt).Should(Equal(3))
2020-05-04 22:20:13 +02:00
})
})
When("multiple groups are passed", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": config.NewBytesSources(file1.Path, file2.Path),
"gr2": config.NewBytesSources("file://" + file3.Path),
2020-05-04 22:20:13 +02:00
}
2023-04-17 18:21:56 +02:00
})
2020-05-04 22:20:13 +02:00
2023-04-17 18:21:56 +02:00
It("should match", func() {
Expect(sut.groupedCache.ElementCount("gr1")).Should(Equal(3))
Expect(sut.groupedCache.ElementCount("gr2")).Should(Equal(2))
group := sut.Match("blocked1.com", []string{"gr1", "gr2"})
Expect(group).Should(ContainElement("gr1"))
2020-05-04 22:20:13 +02:00
group = sut.Match("blocked1a.com", []string{"gr1", "gr2"})
Expect(group).Should(ContainElement("gr1"))
2020-05-04 22:20:13 +02:00
group = sut.Match("blocked1a.com", []string{"gr2"})
Expect(group).Should(ContainElement("gr2"))
2020-05-04 22:20:13 +02:00
})
})
When("group with bigger files", func() {
var (
file1, file2, file3 string
lines1, lines2, lines3 int
)
BeforeEach(func() {
file1, lines1 = createTestListFile(GinkgoT().TempDir(), 10000)
file2, lines2 = createTestListFile(GinkgoT().TempDir(), 15000)
file3, lines3 = createTestListFile(GinkgoT().TempDir(), 13000)
lists = map[string][]config.BytesSource{
2023-04-17 18:21:56 +02:00
"gr1": config.NewBytesSources(file1, file2, file3),
}
})
It("should match", func() {
sut, err = NewListCache(ctx, ListCacheTypeDenylist, sutConfig, lists, downloader)
Expect(err).Should(Succeed())
Expect(sut.groupedCache.ElementCount("gr1")).Should(Equal(lines1 + lines2 + lines3))
})
})
2021-08-28 22:20:59 +02:00
When("inline list content is defined", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": {config.TextBytesSource(
"inlinedomain1.com",
"#some comment",
"inlinedomain2.com",
)},
2021-08-28 22:20:59 +02:00
}
2023-04-17 18:21:56 +02:00
})
2021-08-28 22:20:59 +02:00
2023-04-17 18:21:56 +02:00
It("should match", func() {
Expect(sut.groupedCache.ElementCount("gr1")).Should(Equal(2))
group := sut.Match("inlinedomain1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
2021-08-28 22:20:59 +02:00
group = sut.Match("inlinedomain2.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
})
})
When("Text file can't be parsed", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": {
2023-04-17 18:21:56 +02:00
config.TextBytesSource(
"inlinedomain1.com",
"lineTooLong"+strings.Repeat("x", bufio.MaxScanTokenSize), // too long
),
},
}
2023-04-17 18:21:56 +02:00
})
2023-04-17 18:21:56 +02:00
It("should still match already imported strings", func() {
group := sut.Match("inlinedomain1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
2021-08-28 22:20:59 +02:00
})
})
When("Text file has too many errors", func() {
BeforeEach(func() {
2023-04-17 18:21:56 +02:00
sutConfig.MaxErrorsPerSource = 0
sutConfig.Strategy = config.InitStrategyFailOnError
lists = map[string][]config.BytesSource{
"gr1": {
2023-04-17 18:21:56 +02:00
config.TextBytesSource("invaliddomain!"), // too many errors since `maxErrorsPerSource` is 0
},
}
expectFail = true
})
It("should fail parsing", func() {
Expect(err).Should(MatchError(parsers.ErrTooManyErrors))
})
})
When("file has end of line comment", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": {config.TextBytesSource("inlinedomain1.com#a comment")},
}
2023-04-17 18:21:56 +02:00
})
2023-04-17 18:21:56 +02:00
It("should still parse the domain", func() {
group := sut.Match("inlinedomain1.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
})
})
2021-09-18 22:37:02 +02:00
When("inline regex content is defined", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
lists = map[string][]config.BytesSource{
"gr1": {config.TextBytesSource("/^apple\\.(de|com)$/")},
2021-09-18 22:37:02 +02:00
}
2023-04-17 18:21:56 +02:00
})
2021-09-18 22:37:02 +02:00
2023-04-17 18:21:56 +02:00
It("should match", func() {
group := sut.Match("apple.com", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
2021-09-18 22:37:02 +02:00
group = sut.Match("apple.de", []string{"gr1"})
Expect(group).Should(ContainElement("gr1"))
2021-09-18 22:37:02 +02:00
})
})
2020-05-04 22:20:13 +02:00
})
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
Describe("LogConfig", func() {
var (
logger *logrus.Entry
hook *log.MockLoggerHook
)
BeforeEach(func() {
logger, hook = log.NewMockEntry()
2020-05-04 22:20:13 +02:00
lists = map[string][]config.BytesSource{
2023-04-17 18:21:56 +02:00
"gr1": config.NewBytesSources(server1.URL, server2.URL),
"gr2": {config.TextBytesSource("inline", "definition")},
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
}
})
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
It("should print list configuration", func() {
sut, err = NewListCache(ctx, ListCacheTypeDenylist, sutConfig, lists, downloader)
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
Expect(err).Should(Succeed())
sut.LogConfig(logger)
Expect(hook.Calls).ShouldNot(BeEmpty())
Expect(hook.Messages).Should(ContainElements(
ContainSubstring("gr1:"),
ContainSubstring("gr2:"),
ContainSubstring("TOTAL:"),
))
2020-05-04 22:20:13 +02:00
})
})
Describe("loading strategy", func() {
When("async load is enabled", func() {
2023-04-17 18:21:56 +02:00
BeforeEach(func() {
sutConfig.Strategy = config.InitStrategyFast
2023-04-17 18:21:56 +02:00
lists = map[string][]config.BytesSource{
2023-04-17 18:21:56 +02:00
"gr1": config.NewBytesSources("doesnotexist"),
}
})
It("should never return an error", func() {
_, err := NewListCache(ctx, ListCacheTypeDenylist, sutConfig, lists, downloader)
Expect(err).Should(Succeed())
})
})
})
2020-05-04 22:20:13 +02:00
})
type MockDownloader struct {
MockCallSequence[string]
}
func newMockDownloader(driver func(res chan<- string, err chan<- error)) *MockDownloader {
return &MockDownloader{NewMockCallSequence(driver)}
}
func (m *MockDownloader) DownloadFile(_ context.Context, _ string) (io.ReadCloser, error) {
str, err := m.Call()
if err != nil {
return nil, err
}
return io.NopCloser(strings.NewReader(str)), nil
}
2023-04-17 18:21:56 +02:00
func (m *MockDownloader) ListSource() config.BytesSource {
return config.BytesSource{
Type: config.BytesSourceTypeHttp,
From: "http://mock-downloader",
}
}
func createTestListFile(dir string, totalLines int) (string, int) {
2022-08-19 22:04:35 +02:00
file, err := os.CreateTemp(dir, "blocky")
if err != nil {
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
log.Log().Fatal(err)
}
w := bufio.NewWriter(file)
for i := 0; i < totalLines; i++ {
2023-09-12 09:12:48 +02:00
fmt.Fprintln(w, uuid.NewString()+".com")
}
w.Flush()
return file.Name(), totalLines
}