Add URL builder edge-case tests and server endpoint coverage

This commit is contained in:
Rustem Kamalov
2026-04-15 02:50:52 +03:00
parent 3af8612ce5
commit e5e0c96303
5 changed files with 845 additions and 93 deletions

View File

@@ -7,41 +7,164 @@ import (
"github.com/karust/openserp/core"
)
var testQuery = core.Query{Text: "go", Site: "tutorialspoint.com", DateInterval: "20140101..20230101", Limit: 10}
func TestUrlBuild(t *testing.T) {
res, err := BuildURL(testQuery)
if err != nil {
t.Fatal(err)
func TestBuildURL(t *testing.T) {
tests := []struct {
name string
query core.Query
wantErr bool
check func(*testing.T, url.Values, string)
}{
{
name: "combined params with unicode and start zero",
query: core.Query{
Text: "go 搜索",
Site: "tutorialspoint.com",
Filetype: "pdf",
DateInterval: "20140101..20230101",
Limit: 10,
Start: 0,
},
check: func(t *testing.T, params url.Values, host string) {
t.Helper()
if host != "www.baidu.com" {
t.Fatalf("unexpected host: %s", host)
}
if got := params.Get("wd"); got != "go 搜索 site:tutorialspoint.com filetype:pdf" {
t.Fatalf("unexpected wd value: %q", got)
}
if got := params.Get("gpc"); got != "stf=1388534400,1672531200|stftype=2" {
t.Fatalf("unexpected gpc value: %q", got)
}
if got := params.Get("rn"); got != "10" {
t.Fatalf("unexpected rn value: %q", got)
}
if got := params.Get("pn"); got != "" {
t.Fatalf("pn should be omitted when Start=0, got %q", got)
}
if got := params.Get("f"); got != "8" {
t.Fatalf("unexpected f value: %q", got)
}
if got := params.Get("ie"); got != "utf-8" {
t.Fatalf("unexpected ie value: %q", got)
}
},
},
{
name: "very large start",
query: core.Query{
Text: "golang",
Start: 2147483647,
},
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("pn"); got != "2147483647" {
t.Fatalf("unexpected pn value: %q", got)
}
},
},
{
name: "negative start returns error",
query: core.Query{
Text: "golang",
Start: -1,
},
wantErr: true,
},
{
name: "empty fields return error",
query: core.Query{},
wantErr: true,
},
}
want := "https://www.baidu.com/s?f=8&gpc=stf%3D1388534400%2C1672531200%7Cstftype%3D2&ie=utf-8&rn=10&wd=go+site%3Atutorialspoint.com"
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := BuildURL(tt.query)
if (err != nil) != tt.wantErr {
t.Fatalf("BuildURL() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
if want != res {
t.Fatalf("Wanted result `%s` doesn't match to resulted `%s`", want, res)
parsed, err := url.Parse(got)
if err != nil {
t.Fatalf("invalid URL returned: %v", err)
}
if tt.check != nil {
tt.check(t, parsed.Query(), parsed.Host)
}
})
}
}
func TestImageUrlBuild(t *testing.T) {
query := core.Query{Text: "金毛猎犬"}
got, err := BuildImageURL(query, 0)
if err != nil {
t.Fatal(err)
func TestBuildImageURL(t *testing.T) {
tests := []struct {
name string
query core.Query
pageNum int
wantErr bool
check func(*testing.T, url.Values, string)
}{
{
name: "unicode query",
query: core.Query{Text: "金毛猎犬"},
pageNum: 0,
check: func(t *testing.T, params url.Values, host string) {
t.Helper()
if host != "image.baidu.com" {
t.Fatalf("unexpected host: %s", host)
}
if got := params.Get("word"); got != "金毛猎犬" {
t.Fatalf("expected unicode query, got %q", got)
}
if got := params.Get("tn"); got != "resultjson_com" {
t.Fatalf("unexpected tn value: %q", got)
}
},
},
{
name: "combined params with pagination",
query: core.Query{
Text: "golang",
Limit: 25,
},
pageNum: 3,
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("rn"); got != "30" {
t.Fatalf("unexpected rn value: %q", got)
}
if got := params.Get("pn"); got != "90" {
t.Fatalf("unexpected pn value: %q", got)
}
},
},
{
name: "empty fields return error",
query: core.Query{},
pageNum: 0,
wantErr: true,
},
}
parsed, err := url.Parse(got)
if err != nil {
t.Fatalf("invalid URL returned: %v", err)
}
if parsed.Host != "image.baidu.com" {
t.Fatalf("unexpected host: %s", parsed.Host)
}
q := parsed.Query()
if q.Get("word") != "金毛猎犬" {
t.Fatalf("expected word query to be preserved, got %q", q.Get("word"))
}
if q.Get("tn") != "resultjson_com" {
t.Fatalf("expected tn=resultjson_com, got %q", q.Get("tn"))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := BuildImageURL(tt.query, tt.pageNum)
if (err != nil) != tt.wantErr {
t.Fatalf("BuildImageURL() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
parsed, err := url.Parse(got)
if err != nil {
t.Fatalf("invalid URL returned: %v", err)
}
if tt.check != nil {
tt.check(t, parsed.Query(), parsed.Host)
}
})
}
}

View File

@@ -2,69 +2,183 @@ package bing
import (
"net/url"
"strings"
"testing"
"github.com/karust/openserp/core"
)
func TestBuildImageURL(t *testing.T) {
func TestBuildURL(t *testing.T) {
tests := []struct {
name string
query core.Query
wantErr bool
wantCont string
name string
query core.Query
wantErr bool
check func(*testing.T, url.Values, string)
}{
{
name: "basic image query",
query: core.Query{Text: "test"},
wantErr: false,
wantCont: "q=test",
name: "combined params with unicode and start zero",
query: core.Query{
Text: "golang тест",
Site: "example.com",
Filetype: "pdf",
DateInterval: "20240101..20240131",
LangCode: "RU",
Limit: 30,
Start: 0,
},
check: func(t *testing.T, params url.Values, host string) {
t.Helper()
if host != "www.bing.com" {
t.Fatalf("unexpected host: %s", host)
}
if got := params.Get("q"); got != "golang тест site:example.com filetype:pdf after:2024-01-01 before:2024-01-31" {
t.Fatalf("unexpected q value: %q", got)
}
if got := params.Get("pq"); got != params.Get("q") {
t.Fatalf("pq should match q, got %q vs %q", got, params.Get("q"))
}
if got := params.Get("setlang"); got != "ru" {
t.Fatalf("unexpected setlang value: %q", got)
}
if got := params.Get("count"); got != "30" {
t.Fatalf("unexpected count value: %q", got)
}
if got := params.Get("first"); got != "" {
t.Fatalf("first should be omitted when Start=0, got %q", got)
}
if got := params.Get("form"); got != "QBLH" {
t.Fatalf("unexpected form value: %q", got)
}
if got := params.Get("qs"); got != "HS" {
t.Fatalf("unexpected qs value: %q", got)
}
if got := params.Get("sp"); got != "-1" {
t.Fatalf("unexpected sp value: %q", got)
}
},
},
{
name: "image query with site",
query: core.Query{Text: "cats", Site: "example.com"},
wantErr: false,
wantCont: "q=cats+site%3Aexample.com",
name: "very large start",
query: core.Query{
Text: "golang",
Start: 2147483647,
Limit: 20,
},
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("first"); got != "2147483648" {
t.Fatalf("unexpected first value: %q", got)
}
if got := params.Get("count"); got != "" {
t.Fatalf("count should be omitted when first is used, got %q", got)
}
},
},
{
name: "image query with filetype",
query: core.Query{Text: "dogs", Filetype: "png"},
wantErr: false,
wantCont: "q=dogs",
name: "negative start returns error",
query: core.Query{
Text: "golang",
Start: -1,
},
wantErr: true,
},
{
name: "empty query",
query: core.Query{Text: ""},
wantErr: true,
wantCont: "",
name: "empty fields return error",
query: core.Query{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := BuildURL(tt.query)
if (err != nil) != tt.wantErr {
t.Fatalf("BuildURL() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
parsed, err := url.Parse(got)
if err != nil {
t.Fatalf("BuildURL() returned invalid URL: %v", err)
}
if tt.check != nil {
tt.check(t, parsed.Query(), parsed.Host)
}
})
}
}
func TestBuildImageURL(t *testing.T) {
tests := []struct {
name string
query core.Query
wantErr bool
check func(*testing.T, url.Values, string)
}{
{
name: "basic image query",
query: core.Query{Text: "test"},
check: func(t *testing.T, params url.Values, host string) {
t.Helper()
if host != "www.bing.com" {
t.Fatalf("unexpected host: %s", host)
}
if got := params.Get("q"); got != "test" {
t.Fatalf("unexpected q value: %q", got)
}
},
},
{
name: "combined params with unicode",
query: core.Query{
Text: "коты",
Site: "example.com",
Filetype: "png",
LangCode: "EN",
},
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("q"); got != "коты site:example.com" {
t.Fatalf("unexpected q value: %q", got)
}
if got := params.Get("setlang"); got != "en" {
t.Fatalf("unexpected setlang value: %q", got)
}
if got := params.Get("form"); got != "HDRSC2" {
t.Fatalf("unexpected form value: %q", got)
}
if got := params.Get("first"); got != "1" {
t.Fatalf("unexpected first value: %q", got)
}
if got := params.Get("scenario"); got != "ImageBasicHover" {
t.Fatalf("unexpected scenario value: %q", got)
}
},
},
{
name: "empty fields return error",
query: core.Query{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := BuildImageURL(tt.query)
if (err != nil) != tt.wantErr {
t.Errorf("BuildImageURL() error = %v, wantErr %v", err, tt.wantErr)
t.Fatalf("BuildImageURL() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
if !tt.wantErr && got != "" {
if !strings.Contains(got, tt.wantCont) {
t.Errorf("BuildImageURL() = %v, should contain %v", got, tt.wantCont)
}
// Test that URL is valid
_, err := url.Parse(got)
if err != nil {
t.Errorf("BuildImageURL() returned invalid URL: %v", err)
}
// Should be a Bing images URL
if !strings.Contains(got, "bing.com/images/search") {
t.Errorf("BuildImageURL() should return Bing images URL, got: %v", got)
}
parsed, err := url.Parse(got)
if err != nil {
t.Fatalf("BuildImageURL() returned invalid URL: %v", err)
}
if tt.check != nil {
tt.check(t, parsed.Query(), parsed.Host)
}
})
}

View File

@@ -5,6 +5,7 @@ import (
"errors"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
@@ -71,6 +72,223 @@ func requestWithHeader(t *testing.T, s *Server, path string, header string, valu
return resp
}
func TestInvalidQueryParametersReturnJSONError(t *testing.T) {
engine := &engineMock{name: "google", initialized: true}
srv := NewServerWithOptions("127.0.0.1", 7104, DefaultServerOptions(), engine)
tests := []struct {
name string
path string
message string
}{
{
name: "invalid limit",
path: "/google/search?text=golang&limit=abc",
message: "invalid syntax",
},
{
name: "negative start",
path: "/google/search?text=golang&start=-1",
message: "start must be >= 0",
},
{
name: "invalid filter flag",
path: "/google/search?text=golang&filter=notabool",
message: "invalid syntax",
},
{
name: "invalid answers flag on mega endpoint",
path: "/mega/search?text=golang&answers=notabool",
message: "invalid syntax",
},
{
name: "empty text query",
path: "/google/search?text=",
message: "Query cannot be empty",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp := request(t, srv, tt.path)
if resp.StatusCode != http.StatusInternalServerError {
t.Fatalf("expected 500 for invalid query params, got %d", resp.StatusCode)
}
var payload JSONErrorResponse
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
t.Fatalf("decode error response: %v", err)
}
if payload.Code != http.StatusInternalServerError {
t.Fatalf("expected code=500, got %d", payload.Code)
}
if payload.Error != "server_error" {
t.Fatalf("expected error=server_error, got %q", payload.Error)
}
if payload.Message == "" {
t.Fatal("expected error message to be present")
}
if tt.message != "" && !strings.Contains(payload.Message, tt.message) {
t.Fatalf("expected message to contain %q, got %q", tt.message, payload.Message)
}
})
}
}
func TestMegaEnginesEndpointResponseFormat(t *testing.T) {
google := &engineMock{name: "google", initialized: true}
yandex := &engineMock{name: "yandex", initialized: false}
srv := NewServerWithOptions("127.0.0.1", 7105, DefaultServerOptions(), google, yandex)
// Prime circuit breaker stats so circuit_state is populated for both engines.
_ = request(t, srv, "/google/search?text=golang")
_ = request(t, srv, "/yandex/search?text=golang")
resp := request(t, srv, "/mega/engines")
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected /mega/engines to return 200, got %d", resp.StatusCode)
}
var payload map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
t.Fatalf("decode /mega/engines response: %v", err)
}
total, ok := payload["total"].(float64)
if !ok {
t.Fatalf("expected numeric total field, got %T", payload["total"])
}
if total != 2 {
t.Fatalf("expected total=2, got %v", total)
}
engines, ok := payload["engines"].([]interface{})
if !ok {
t.Fatalf("expected engines array, got %T", payload["engines"])
}
if len(engines) != 2 {
t.Fatalf("expected 2 engines in payload, got %d", len(engines))
}
byName := map[string]map[string]interface{}{}
for _, entry := range engines {
engineData, ok := entry.(map[string]interface{})
if !ok {
t.Fatalf("expected engine object, got %T", entry)
}
name, _ := engineData["name"].(string)
if name == "" {
t.Fatalf("expected non-empty engine name, got %#v", engineData["name"])
}
if _, ok := engineData["initialized"].(bool); !ok {
t.Fatalf("expected initialized bool for engine %s, got %T", name, engineData["initialized"])
}
state, ok := engineData["circuit_state"].(string)
if !ok || state == "" {
t.Fatalf("expected non-empty circuit_state for engine %s, got %#v", name, engineData["circuit_state"])
}
byName[name] = engineData
}
if _, ok := byName["google"]; !ok {
t.Fatal("expected google engine in response")
}
if _, ok := byName["yandex"]; !ok {
t.Fatal("expected yandex engine in response")
}
}
func TestStatsEndpointStructure(t *testing.T) {
engine := &engineMock{name: "google", initialized: true}
srv := NewServerWithOptions("127.0.0.1", 7106, DefaultServerOptions(), engine)
// Ensure circuit breaker stats are initialized.
_ = request(t, srv, "/google/search?text=golang")
resp := request(t, srv, "/stats")
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected /stats to return 200, got %d", resp.StatusCode)
}
var payload map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
t.Fatalf("decode /stats response: %v", err)
}
cache, ok := payload["cache"].(map[string]interface{})
if !ok {
t.Fatalf("expected cache object, got %T", payload["cache"])
}
if _, ok := cache["status"].(bool); !ok {
t.Fatalf("expected cache.status bool, got %T", cache["status"])
}
if _, ok := cache["entries"].(float64); !ok {
t.Fatalf("expected cache.entries number, got %T", cache["entries"])
}
if _, ok := cache["hits"].(float64); !ok {
t.Fatalf("expected cache.hits number, got %T", cache["hits"])
}
if _, ok := cache["misses"].(float64); !ok {
t.Fatalf("expected cache.misses number, got %T", cache["misses"])
}
if _, ok := cache["bypasses"].(float64); !ok {
t.Fatalf("expected cache.bypasses number, got %T", cache["bypasses"])
}
proxy, ok := payload["proxy"].(map[string]interface{})
if !ok {
t.Fatalf("expected proxy object, got %T", payload["proxy"])
}
if _, ok := proxy["configured_count"].(float64); !ok {
t.Fatalf("expected proxy.configured_count number, got %T", proxy["configured_count"])
}
if _, ok := proxy["healthy_count"].(float64); !ok {
t.Fatalf("expected proxy.healthy_count number, got %T", proxy["healthy_count"])
}
if _, ok := proxy["unhealthy_count"].(float64); !ok {
t.Fatalf("expected proxy.unhealthy_count number, got %T", proxy["unhealthy_count"])
}
if _, ok := proxy["tags"].(map[string]interface{}); !ok {
t.Fatalf("expected proxy.tags object, got %T", proxy["tags"])
}
if _, ok := proxy["entries"].([]interface{}); !ok {
t.Fatalf("expected proxy.entries array, got %T", proxy["entries"])
}
engines, ok := proxy["engines"].(map[string]interface{})
if !ok {
t.Fatalf("expected proxy.engines object, got %T", proxy["engines"])
}
googleStats, ok := engines["google"].(map[string]interface{})
if !ok {
t.Fatalf("expected proxy.engines.google object, got %T", engines["google"])
}
if got := googleStats["selected_proxy"]; got != "direct" {
t.Fatalf("expected proxy.engines.google.selected_proxy=direct, got %#v", got)
}
breakers, ok := payload["circuit_breakers"].([]interface{})
if !ok {
t.Fatalf("expected circuit_breakers array, got %T", payload["circuit_breakers"])
}
if len(breakers) == 0 {
t.Fatal("expected at least one circuit breaker entry")
}
first, ok := breakers[0].(map[string]interface{})
if !ok {
t.Fatalf("expected circuit breaker object, got %T", breakers[0])
}
if _, ok := first["engine"].(string); !ok {
t.Fatalf("expected circuit_breakers[0].engine string, got %T", first["engine"])
}
if _, ok := first["state"].(string); !ok {
t.Fatalf("expected circuit_breakers[0].state string, got %T", first["state"])
}
if _, ok := first["failure_count"].(float64); !ok {
t.Fatalf("expected circuit_breakers[0].failure_count number, got %T", first["failure_count"])
}
}
func TestHealthEndpointStatusSemantics(t *testing.T) {
ready := &engineMock{name: "google", initialized: true, limiter: rate.NewLimiter(rate.Every(time.Second), 1)}
notReady := &engineMock{name: "yandex", initialized: false, limiter: rate.NewLimiter(rate.Every(time.Second), 1)}

View File

@@ -11,30 +11,98 @@ func TestBuildURL(t *testing.T) {
tests := []struct {
name string
query core.Query
page int
wantErr bool
check func(*testing.T, url.Values, string)
}{
{
name: "basic search",
query: core.Query{Text: "golang programming"},
page: 0,
check: func(t *testing.T, params url.Values, host string) {
t.Helper()
if host != "duckduckgo.com" {
t.Fatalf("unexpected host: %s", host)
}
if got := params.Get("q"); got != "golang programming" {
t.Fatalf("unexpected q value: %q", got)
}
if got := params.Get("ia"); got != "web" {
t.Fatalf("unexpected ia value: %q", got)
}
if got := params.Get("t"); got != "h" {
t.Fatalf("unexpected t value: %q", got)
}
if got := params.Get("s"); got != "" {
t.Fatalf("s should be omitted on first page, got %q", got)
}
},
},
{
name: "search with site filter",
query: core.Query{Text: "golang", Site: "github.com"},
name: "combined params with unicode",
query: core.Query{
Text: "поиск",
Site: "github.com",
Filetype: "pdf",
LangCode: "RU-ru",
DateInterval: "20240101..20240131",
},
page: 0,
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("q"); got != "поиск site:github.com filetype:pdf" {
t.Fatalf("unexpected q value: %q", got)
}
if got := params.Get("df"); got != "2024-01-01..2024-01-31" {
t.Fatalf("unexpected df value: %q", got)
}
if got := params.Get("kl"); got != "ru-ru" {
t.Fatalf("unexpected kl value: %q", got)
}
},
},
{
name: "search with filetype",
query: core.Query{Text: "documentation", Filetype: "pdf"},
name: "very large page offset",
query: core.Query{Text: "golang"},
page: 100000,
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("s"); got != "2500000" {
t.Fatalf("unexpected pagination offset: %q", got)
}
},
},
{
name: "empty query",
query: core.Query{Text: ""},
name: "negative page does not add offset",
query: core.Query{Text: "golang"},
page: -1,
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("s"); got != "" {
t.Fatalf("expected empty offset for negative page, got %q", got)
}
},
},
{
name: "invalid date interval returns error",
query: core.Query{
Text: "golang",
DateInterval: "invalid",
},
page: 0,
wantErr: true,
},
{
name: "empty query returns error",
query: core.Query{},
page: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := BuildURL(tt.query, 0)
got, err := BuildURL(tt.query, tt.page)
if (err != nil) != tt.wantErr {
t.Fatalf("BuildURL() error = %v, wantErr %v", err, tt.wantErr)
}
@@ -46,15 +114,8 @@ func TestBuildURL(t *testing.T) {
if err != nil {
t.Fatalf("BuildURL() returned invalid URL: %v", err)
}
params := parsed.Query()
if params.Get("q") == "" {
t.Fatalf("BuildURL() should include q parameter, got %s", got)
}
if params.Get("ia") != "web" {
t.Fatalf("BuildURL() should include ia=web, got %s", got)
}
if params.Get("t") != "h" {
t.Fatalf("BuildURL() should include t=h, got %s", got)
if tt.check != nil {
tt.check(t, parsed.Query(), parsed.Host)
}
})
}
@@ -65,14 +126,47 @@ func TestBuildImageURL(t *testing.T) {
name string
query core.Query
wantErr bool
check func(*testing.T, url.Values, string)
}{
{
name: "basic image search",
query: core.Query{Text: "golang logo"},
name: "combined params with unicode",
query: core.Query{
Text: "горы",
Site: "example.com",
Filetype: "jpg",
LangCode: "RU",
DateInterval: "20240201..20240229",
},
check: func(t *testing.T, params url.Values, host string) {
t.Helper()
if host != "duckduckgo.com" {
t.Fatalf("unexpected host: %s", host)
}
if got := params.Get("q"); got != "горы site:example.com filetype:jpg" {
t.Fatalf("unexpected q value: %q", got)
}
if got := params.Get("iax"); got != "images" || params.Get("ia") != "images" {
t.Fatalf("expected image mode params, got iax=%q ia=%q", params.Get("iax"), params.Get("ia"))
}
if got := params.Get("df"); got != "2024-02-01..2024-02-29" {
t.Fatalf("unexpected df value: %q", got)
}
if got := params.Get("kl"); got != "ru" {
t.Fatalf("unexpected kl value: %q", got)
}
},
},
{
name: "empty query",
query: core.Query{Text: ""},
name: "invalid date interval returns error",
query: core.Query{
Text: "golang",
DateInterval: "202401",
},
wantErr: true,
},
{
name: "empty query returns error",
query: core.Query{},
wantErr: true,
},
}
@@ -91,12 +185,8 @@ func TestBuildImageURL(t *testing.T) {
if err != nil {
t.Fatalf("BuildImageURL() returned invalid URL: %v", err)
}
params := parsed.Query()
if params.Get("q") == "" {
t.Fatalf("BuildImageURL() should include q parameter, got %s", got)
}
if params.Get("iax") != "images" || params.Get("ia") != "images" {
t.Fatalf("BuildImageURL() should target image mode, got %s", got)
if tt.check != nil {
tt.check(t, parsed.Query(), parsed.Host)
}
})
}

View File

@@ -1,11 +1,218 @@
package google
import (
"net/url"
"testing"
"github.com/karust/openserp/core"
)
func TestBuildSearchURL(t *testing.T) {
tests := []struct {
name string
query core.Query
wantErr bool
check func(*testing.T, url.Values, string)
}{
{
name: "combined params with unicode and start zero",
query: core.Query{
Text: "golang тест",
Site: "example.com",
Filetype: "pdf",
DateInterval: "20240101..20240131",
Limit: 50,
Start: 0,
Filter: false,
LangCode: "RU",
},
check: func(t *testing.T, params url.Values, host string) {
t.Helper()
if host != "www.google.ru" {
t.Fatalf("unexpected host: %s", host)
}
if got := params.Get("q"); got != "golang тест site:example.com filetype:pdf" {
t.Fatalf("unexpected q value: %q", got)
}
if got := params.Get("oq"); got != params.Get("q") {
t.Fatalf("oq should match q, got %q vs %q", got, params.Get("q"))
}
if got := params.Get("tbs"); got != "cdr:1,cd_min:20240101,cd_max:20240131" {
t.Fatalf("unexpected tbs: %q", got)
}
if got := params.Get("num"); got != "50" {
t.Fatalf("unexpected num: %q", got)
}
if got := params.Get("start"); got != "" {
t.Fatalf("start should be omitted when Start=0, got %q", got)
}
if got := params.Get("filter"); got != "0" {
t.Fatalf("unexpected filter value: %q", got)
}
if got := params.Get("hl"); got != "RU" {
t.Fatalf("unexpected hl value: %q", got)
}
if got := params.Get("lr"); got != "lang_ru" {
t.Fatalf("unexpected lr value: %q", got)
}
if got := params.Get("pws"); got != "0" {
t.Fatalf("unexpected pws value: %q", got)
}
if got := params.Get("nfpr"); got != "1" {
t.Fatalf("unexpected nfpr value: %q", got)
}
},
},
{
name: "site and filetype without text",
query: core.Query{
Site: "example.com",
Filetype: "txt",
Filter: true,
},
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("q"); got != " site:example.com filetype:txt" {
t.Fatalf("unexpected q value: %q", got)
}
if got := params.Get("filter"); got != "" {
t.Fatalf("filter should be omitted when Filter=true, got %q", got)
}
},
},
{
name: "very large start",
query: core.Query{
Text: "golang",
Start: 2147483647,
Filter: true,
},
check: func(t *testing.T, params url.Values, _ string) {
t.Helper()
if got := params.Get("start"); got != "2147483647" {
t.Fatalf("unexpected start value: %q", got)
}
},
},
{
name: "negative start returns error",
query: core.Query{
Text: "golang",
Start: -1,
},
wantErr: true,
},
{
name: "empty query returns error",
query: core.Query{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := BuildURL(tt.query)
if (err != nil) != tt.wantErr {
t.Fatalf("BuildURL() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
parsed, err := url.Parse(got)
if err != nil {
t.Fatalf("invalid URL returned: %v", err)
}
if tt.check != nil {
tt.check(t, parsed.Query(), parsed.Host)
}
})
}
}
func TestBuildImageSearchURL(t *testing.T) {
tests := []struct {
name string
query core.Query
wantErr bool
check func(*testing.T, url.Values, string)
}{
{
name: "combined params with unicode",
query: core.Query{
Text: "горы",
Site: "example.com",
Filetype: "jpg",
DateInterval: "20240301..20240315",
Limit: 25,
LangCode: "EN",
},
check: func(t *testing.T, params url.Values, host string) {
t.Helper()
if host != "www.google.com" {
t.Fatalf("unexpected host: %s", host)
}
if got := params.Get("tbm"); got != "isch" {
t.Fatalf("unexpected tbm: %q", got)
}
if got := params.Get("q"); got != "горы site:example.com filetype:jpg" {
t.Fatalf("unexpected q value: %q", got)
}
if got := params.Get("oq"); got != params.Get("q") {
t.Fatalf("oq should match q, got %q vs %q", got, params.Get("q"))
}
if got := params.Get("num"); got != "25" {
t.Fatalf("unexpected num value: %q", got)
}
if got := params.Get("tbs"); got != "cdr:1,cd_min:20240301,cd_max:20240315" {
t.Fatalf("unexpected tbs value: %q", got)
}
if got := params.Get("hl"); got != "EN" {
t.Fatalf("unexpected hl value: %q", got)
}
if got := params.Get("lr"); got != "lang_en" {
t.Fatalf("unexpected lr value: %q", got)
}
},
},
{
name: "invalid date interval returns error",
query: core.Query{
Text: "golang",
DateInterval: "20240101",
},
wantErr: true,
},
{
name: "empty query returns error",
query: core.Query{
Text: "",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := BuildImageURL(tt.query)
if (err != nil) != tt.wantErr {
t.Fatalf("BuildImageURL() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErr {
return
}
parsed, err := url.Parse(got)
if err != nil {
t.Fatalf("invalid URL returned: %v", err)
}
if tt.check != nil {
tt.check(t, parsed.Query(), parsed.Host)
}
})
}
}
func TestParseSourceImageURL(t *testing.T) {
//href1 := `/imgres?imgurl=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F2%2F26%2FMarmota_marmota_Alpes2.jpg&tbnid=Be_RycOe8xzlpM&vet=12ahUKEwjkh6WzwIeAAxWV_yoKHRzHC9wQMygAegUIARD0AQ..i&imgrefurl=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FAlpine_marmot&docid=7miWbc2QiSw9uM&w=801&h=599&q=alpine%20marmot&ved=2ahUKEwjkh6WzwIeAAxWV_yoKHRzHC9wQMygAegUIARD0AQ`
href2 := `/imgres?imgurl=https%3A%2F%2Fstatic.wikia.nocookie.net%2Fnaturerules1%2Fimages%2Ff%2Ff2%2F13d79d934ccf6f7919777fcb6dbb6e6c.jpg%2Frevision%2Flatest%3Fcb%3D20210218225522&tbnid=JxC8NUyBjdNbdM&vet=12ahUKEwiHrJnN1YeAAxXvEBAIHfRADAAQMygCegUIARD4AQ..i&imgrefurl=https%3A%2F%2Fnaturerules1.fandom.com%2Fwiki%2FAlpine_Marmot&docid=XXYeDjL67badNM&w=1600&h=1200&q=alpine%20marmot&ved=2ahUKEwiHrJnN1YeAAxXvEBAIHfRADAAQMygCegUIARD4AQ`