mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2026-08-28 05:12:32 +00:00
Squashed commit of the following: commit 8606668738add53456063aa8f68ded3812170df3 Merge: 9aff68363b60af4bf3Author: f.setrakov <f.setrakov@adguard.com> Date: Wed May 13 11:45:39 2026 +0300 Merge branch 'master' into AGDNS-3951-querylog-search-criterion commit 9aff6836320a8313900101311adaa28ac826c53e Author: f.setrakov <f.setrakov@adguard.com> Date: Fri May 8 17:40:09 2026 +0300 querylog: rm unicode check commit b093974b08cf02ea42baf492690ad64c93879fa5 Author: f.setrakov <f.setrakov@adguard.com> Date: Fri May 8 16:12:41 2026 +0300 all: imp code commit 2b2c35708ba009e2e93fdd46674311b976c1a123 Author: f.setrakov <f.setrakov@adguard.com> Date: Thu May 7 16:39:20 2026 +0300 all: imp docs, tests commit c1d1fd8294972f66e4333c44309ca65e1a4495d7 Author: f.setrakov <f.setrakov@adguard.com> Date: Wed May 6 15:16:30 2026 +0300 all: imp docs, quick match commit 2cb80fd6b7a7bb2e6eae359b3596f24299c4c817 Merge: 17fac082de5c79502fAuthor: f.setrakov <f.setrakov@adguard.com> Date: Tue May 5 12:56:41 2026 +0300 Merge branch 'master' into AGDNS-3951-querylog-search-criterion commit 17fac082d98b31a3b31e915496a75b4be85102a0 Author: f.setrakov <f.setrakov@adguard.com> Date: Tue May 5 12:50:51 2026 +0300 all: imp code commit 3d093cc9886f0ec5c1ac74fa3e7069edb925963f Author: f.setrakov <f.setrakov@adguard.com> Date: Mon May 4 17:51:45 2026 +0300 all: add tests commit 2a33db2182811dbeb24311a72734d9be46d155c6 Author: f.setrakov <f.setrakov@adguard.com> Date: Wed Apr 29 19:10:02 2026 +0300 all: add reason criterion
210 lines
5.1 KiB
Go
210 lines
5.1 KiB
Go
package querylog
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
|
"github.com/AdguardTeam/golibs/testutil"
|
|
"github.com/AdguardTeam/golibs/timeutil"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Test domains for querylog search testing.
|
|
const (
|
|
testDomainBlocked = "blocked.org"
|
|
testDomainNotFound = "notfound.org"
|
|
testDomainRewritten = "rewritten.org"
|
|
)
|
|
|
|
// response is the GET /control/querylog HTTP response structure.
|
|
type response struct {
|
|
Data []data `json:"data"`
|
|
}
|
|
|
|
// data is a single querylog entry in the response.
|
|
type data struct {
|
|
Question question `json:"question"`
|
|
}
|
|
|
|
// question is the DNS question part of a querylog entry.
|
|
type question struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
// parseHostnamesFromEntry is a helper that parses the /control/querylog
|
|
// response and extracts the host names from it.
|
|
func parseHostnamesFromEntry(tb testing.TB, in io.Reader) (hostnames []string) {
|
|
tb.Helper()
|
|
|
|
var resp response
|
|
err := json.NewDecoder(in).Decode(&resp)
|
|
require.NoError(tb, err)
|
|
|
|
for _, d := range resp.Data {
|
|
hostnames = append(hostnames, d.Question.Name)
|
|
}
|
|
|
|
return hostnames
|
|
}
|
|
|
|
func TestQuerylog_HandleQueryLog_reasonSearchCriterion(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
query url.Values
|
|
wantMsg string
|
|
wantHosts []string
|
|
wantStatus int
|
|
}{{
|
|
name: "no_params",
|
|
query: url.Values{},
|
|
wantMsg: "",
|
|
wantStatus: http.StatusOK,
|
|
wantHosts: []string{testDomainRewritten, testDomainBlocked, testDomainNotFound},
|
|
}, {
|
|
name: "reason_not_found",
|
|
query: url.Values{"reason": []string{filtering.NotFilteredNotFound.String()}},
|
|
wantMsg: "",
|
|
wantStatus: http.StatusOK,
|
|
wantHosts: []string{testDomainNotFound},
|
|
}, {
|
|
name: "reason_block_list",
|
|
query: url.Values{"reason": []string{filtering.FilteredBlockList.String()}},
|
|
wantMsg: "",
|
|
wantStatus: http.StatusOK,
|
|
wantHosts: []string{testDomainBlocked},
|
|
}, {
|
|
name: "reason_rewritten",
|
|
query: url.Values{"reason": []string{filtering.Rewritten.String()}},
|
|
wantMsg: "",
|
|
wantStatus: http.StatusOK,
|
|
wantHosts: []string{testDomainRewritten},
|
|
}, {
|
|
name: "multiple_reasons",
|
|
query: url.Values{"reason": []string{
|
|
filtering.Rewritten.String(),
|
|
filtering.FilteredBlockList.String(),
|
|
}},
|
|
wantMsg: "",
|
|
wantStatus: http.StatusOK,
|
|
wantHosts: []string{testDomainRewritten, testDomainBlocked},
|
|
}, {
|
|
name: "invalid_reason",
|
|
query: url.Values{"reason": []string{"InvalidReason"}},
|
|
wantMsg: `parsing params: reason: bad enum value: "InvalidReason"` + "\n",
|
|
wantStatus: http.StatusBadRequest,
|
|
wantHosts: nil,
|
|
}, {
|
|
name: "reason_and_status_conflict",
|
|
query: url.Values{
|
|
"reason": []string{filtering.Rewritten.String()},
|
|
"response_status": []string{filteringStatusAll},
|
|
},
|
|
wantMsg: `parsing params: "reason" and "response_status"` +
|
|
` criteria cannot be used together` + "\n",
|
|
wantStatus: http.StatusBadRequest,
|
|
wantHosts: nil,
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
l := newTestQueryLog(t)
|
|
|
|
u := (&url.URL{
|
|
Path: "/control/querylog",
|
|
RawQuery: tc.query.Encode(),
|
|
}).String()
|
|
|
|
require.True(t, t.Run("memory", func(t *testing.T) {
|
|
testSearchAPI(t, l, tc.wantStatus, tc.wantHosts, tc.wantMsg, u)
|
|
}))
|
|
|
|
require.True(t, t.Run("file", func(t *testing.T) {
|
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
|
err := l.flushLogBuffer(ctx)
|
|
require.NoError(t, err)
|
|
|
|
testSearchAPI(t, l, tc.wantStatus, tc.wantHosts, tc.wantMsg, u)
|
|
}))
|
|
})
|
|
}
|
|
}
|
|
|
|
// newTestQueryLog is a helper that returns new *queryLog initialized with
|
|
// common test values. It also adds several test entries.
|
|
func newTestQueryLog(tb testing.TB) (l *queryLog) {
|
|
tb.Helper()
|
|
|
|
l, err := newQueryLog(Config{
|
|
Logger: testLogger,
|
|
Enabled: true,
|
|
FileEnabled: true,
|
|
RotationIvl: timeutil.Day,
|
|
MemSize: 100,
|
|
BaseDir: tb.TempDir(),
|
|
Anonymizer: aghnet.NewIPMut(nil),
|
|
})
|
|
require.NoError(tb, err)
|
|
|
|
addTestEntry(
|
|
l,
|
|
testDomainNotFound,
|
|
testAnswerIPv4,
|
|
testClientIPv4,
|
|
filtering.NotFilteredNotFound,
|
|
)
|
|
addTestEntry(
|
|
l,
|
|
testDomainBlocked,
|
|
testAnswerIPv4,
|
|
testClientIPv4,
|
|
filtering.FilteredBlockList,
|
|
)
|
|
addTestEntry(
|
|
l,
|
|
testDomainRewritten,
|
|
testAnswerIPv4,
|
|
testClientIPv4,
|
|
filtering.Rewritten,
|
|
)
|
|
|
|
return l
|
|
}
|
|
|
|
// testSearchAPI is a helper that makes sure that l handles GET
|
|
// /control/querylog HTTP API requests correctly.
|
|
func testSearchAPI(
|
|
tb testing.TB,
|
|
l *queryLog,
|
|
wantStatus int,
|
|
wantHosts []string,
|
|
wantMsg string,
|
|
u string,
|
|
) {
|
|
tb.Helper()
|
|
|
|
ctx := testutil.ContextWithTimeout(tb, testTimeout)
|
|
req := httptest.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
l.handleQueryLog(w, req)
|
|
|
|
assert.Equal(tb, wantStatus, w.Code)
|
|
if wantStatus != http.StatusOK {
|
|
msg, err := io.ReadAll(w.Body)
|
|
require.NoError(tb, err)
|
|
|
|
assert.Equal(tb, wantMsg, string(msg))
|
|
|
|
return
|
|
}
|
|
|
|
gotHosts := parseHostnamesFromEntry(tb, w.Body)
|
|
assert.Equal(tb, wantHosts, gotHosts)
|
|
}
|