Path: blob/master/lib/remote/googlecse_scanner_test.go
988 views
package remote12import (3"errors"4"github.com/stretchr/testify/assert"5"github.com/sundowndev/phoneinfoga/v2/lib/filter"6"github.com/sundowndev/phoneinfoga/v2/lib/number"7"github.com/sundowndev/phoneinfoga/v2/test"8"google.golang.org/api/customsearch/v1"9"google.golang.org/api/googleapi"10"gopkg.in/h2non/gock.v1"11"net/http"12"os"13"testing"14)1516func TestGoogleCSEScanner_Metadata(t *testing.T) {17scanner := NewGoogleCSEScanner(&http.Client{})18assert.Equal(t, GoogleCSE, scanner.Name())19assert.NotEmpty(t, scanner.Description())20}2122func TestGoogleCSEScanner_Scan_Success(t *testing.T) {23testcases := []struct {24name string25number *number.Number26opts ScannerOptions27expected map[string]interface{}28wantErrors map[string]error29mocks func()30}{31{32name: "test with no results",33number: test.NewFakeUSNumber(),34expected: map[string]interface{}{35"googlecse": GoogleCSEScannerResponse{36Homepage: "https://cse.google.com/cse?cx=fake_search_engine_id",37ResultCount: 0,38TotalResultCount: 0,39TotalRequestCount: 2,40Items: nil,41},42},43wantErrors: map[string]error{},44mocks: func() {45gock.New("https://customsearch.googleapis.com").46Get("/customsearch/v1").47MatchParam("cx", "fake_search_engine_id").48// TODO: the matcher below doesn't work for some reason49//MatchParam("q", "intext:\"14152229670\" OR intext:\"+14152229670\" OR intext:\"4152229670\" OR intext:\"(415) 222-9670\"").50MatchParam("start", "0").51Reply(200).52JSON(&customsearch.Search{53ServerResponse: googleapi.ServerResponse{54Header: http.Header{},55HTTPStatusCode: 200,56},57SearchInformation: &customsearch.SearchSearchInformation{58FormattedSearchTime: "0",59FormattedTotalResults: "0",60SearchTime: 0,61TotalResults: "0",62ForceSendFields: nil,63NullFields: nil,64},65Items: []*customsearch.Result{},66})6768gock.New("https://customsearch.googleapis.com").69Get("/customsearch/v1").70MatchParam("cx", "fake_search_engine_id").71// TODO: the matcher below doesn't work for some reason72//MatchParam("q", "(ext:doc OR ext:docx OR ext:odt OR ext:pdf OR ext:rtf OR ext:sxw OR ext:psw OR ext:ppt OR ext:pptx OR ext:pps OR ext:csv OR ext:txt OR ext:xls) intext:\"14152229670\" OR intext:\"+14152229670\" OR intext:\"4152229670\" OR intext:\"(415)+222-9670\"").73MatchParam("start", "0").74Reply(200).75JSON(&customsearch.Search{76ServerResponse: googleapi.ServerResponse{77Header: http.Header{},78HTTPStatusCode: 200,79},80SearchInformation: &customsearch.SearchSearchInformation{81FormattedSearchTime: "0",82FormattedTotalResults: "0",83SearchTime: 0,84TotalResults: "0",85ForceSendFields: nil,86NullFields: nil,87},88Items: []*customsearch.Result{},89})90},91},92{93name: "test with options and no results",94number: test.NewFakeUSNumber(),95opts: ScannerOptions{96"GOOGLECSE_CX": "custom_cx",97"GOOGLE_API_KEY": "secret",98},99expected: map[string]interface{}{100"googlecse": GoogleCSEScannerResponse{101Homepage: "https://cse.google.com/cse?cx=custom_cx",102ResultCount: 0,103TotalResultCount: 0,104TotalRequestCount: 2,105Items: nil,106},107},108wantErrors: map[string]error{},109mocks: func() {110gock.New("https://customsearch.googleapis.com").111Get("/customsearch/v1").112MatchParam("cx", "custom_cx").113// TODO: ensure that custom api key is used114// MatchHeader("Authorization", "secret").115// TODO: the matcher below doesn't work for some reason116//MatchParam("q", "intext:\"14152229670\" OR intext:\"+14152229670\" OR intext:\"4152229670\" OR intext:\"(415) 222-9670\"").117MatchParam("start", "0").118Reply(200).119JSON(&customsearch.Search{120ServerResponse: googleapi.ServerResponse{121Header: http.Header{},122HTTPStatusCode: 200,123},124SearchInformation: &customsearch.SearchSearchInformation{125FormattedSearchTime: "0",126FormattedTotalResults: "0",127SearchTime: 0,128TotalResults: "0",129ForceSendFields: nil,130NullFields: nil,131},132Items: []*customsearch.Result{},133})134135gock.New("https://customsearch.googleapis.com").136Get("/customsearch/v1").137MatchParam("cx", "custom_cx").138// TODO: ensure that custom api key is used139// MatchHeader("Authorization", "secret").140// TODO: the matcher below doesn't work for some reason141//MatchParam("q", "(ext:doc OR ext:docx OR ext:odt OR ext:pdf OR ext:rtf OR ext:sxw OR ext:psw OR ext:ppt OR ext:pptx OR ext:pps OR ext:csv OR ext:txt OR ext:xls) intext:\"14152229670\" OR intext:\"+14152229670\" OR intext:\"4152229670\" OR intext:\"(415)+222-9670\"").142MatchParam("start", "0").143Reply(200).144JSON(&customsearch.Search{145ServerResponse: googleapi.ServerResponse{146Header: http.Header{},147HTTPStatusCode: 200,148},149SearchInformation: &customsearch.SearchSearchInformation{150FormattedSearchTime: "0",151FormattedTotalResults: "0",152SearchTime: 0,153TotalResults: "0",154ForceSendFields: nil,155NullFields: nil,156},157Items: []*customsearch.Result{},158})159},160},161{162name: "test with results",163number: test.NewFakeUSNumber(),164expected: map[string]interface{}{165"googlecse": GoogleCSEScannerResponse{166Homepage: "https://cse.google.com/cse?cx=fake_search_engine_id",167ResultCount: 2,168TotalResultCount: 2,169TotalRequestCount: 2,170Items: []ResultItem{171{172Title: "Result 1",173URL: "https://result1.com",174},175{176Title: "Result 2",177URL: "https://result2.com",178},179},180},181},182wantErrors: map[string]error{},183mocks: func() {184gock.New("https://customsearch.googleapis.com").185Get("/customsearch/v1").186MatchParam("cx", "fake_search_engine_id").187// TODO: the matcher below doesn't work for some reason188//MatchParam("q", "intext:\"14152229670\" OR intext:\"+14152229670\" OR intext:\"4152229670\" OR intext:\"(415) 222-9670\"").189MatchParam("start", "0").190Reply(200).191JSON(&customsearch.Search{192ServerResponse: googleapi.ServerResponse{193Header: http.Header{},194HTTPStatusCode: 200,195},196SearchInformation: &customsearch.SearchSearchInformation{197FormattedSearchTime: "0",198FormattedTotalResults: "2",199SearchTime: 0,200TotalResults: "2",201ForceSendFields: nil,202NullFields: nil,203},204Items: []*customsearch.Result{205{206Title: "Result 1",207Link: "https://result1.com",208},209{210Title: "Result 2",211Link: "https://result2.com",212},213},214})215216gock.New("https://customsearch.googleapis.com").217Get("/customsearch/v1").218MatchParam("cx", "fake_search_engine_id").219// TODO: the matcher below doesn't work for some reason220//MatchParam("q", "(ext:doc OR ext:docx OR ext:odt OR ext:pdf OR ext:rtf OR ext:sxw OR ext:psw OR ext:ppt OR ext:pptx OR ext:pps OR ext:csv OR ext:txt OR ext:xls) intext:\"14152229670\" OR intext:\"+14152229670\" OR intext:\"4152229670\" OR intext:\"(415)+222-9670\"").221MatchParam("start", "0").222Reply(200).223JSON(&customsearch.Search{224ServerResponse: googleapi.ServerResponse{225Header: http.Header{},226HTTPStatusCode: 200,227},228SearchInformation: &customsearch.SearchSearchInformation{229FormattedSearchTime: "0",230FormattedTotalResults: "0",231SearchTime: 0,232TotalResults: "0",233ForceSendFields: nil,234NullFields: nil,235},236Items: []*customsearch.Result{},237})238},239},240{241name: "test with rate limit error",242number: test.NewFakeUSNumber(),243expected: map[string]interface{}{},244wantErrors: map[string]error{245"googlecse": errors.New("rate limit exceeded, see https://developers.google.com/custom-search/v1/overview#pricing"),246},247mocks: func() {248gock.New("https://customsearch.googleapis.com").249Get("/customsearch/v1").250MatchParam("cx", "fake_search_engine_id").251MatchParam("start", "0").252Reply(429).253JSON(&googleapi.Error{254Code: 429,255Message: "rate limit exceeded",256Details: nil,257Body: "rate limit exceeded",258Header: http.Header{},259Errors: []googleapi.ErrorItem{},260})261},262},263{264name: "test with basic error",265number: test.NewFakeUSNumber(),266expected: map[string]interface{}{},267wantErrors: map[string]error{268"googlecse": &googleapi.Error{269Code: 403,270Message: "",271Details: nil,272Body: "{\"code\":403,\"message\":\"dummy error\",\"details\":null,\"Body\":\"dummy error\",\"Header\":{},\"Errors\":[]}\n",273Header: http.Header{274"Content-Type": []string{"application/json"},275},276Errors: nil,277},278},279mocks: func() {280gock.New("https://customsearch.googleapis.com").281Get("/customsearch/v1").282MatchParam("cx", "fake_search_engine_id").283MatchParam("start", "0").284Reply(403).285JSON(&googleapi.Error{286Code: 403,287Message: "dummy error",288Details: nil,289Body: "dummy error",290Header: http.Header{},291Errors: []googleapi.ErrorItem{},292})293},294},295}296297for _, tt := range testcases {298t.Run(tt.name, func(t *testing.T) {299_ = os.Setenv("GOOGLECSE_CX", "fake_search_engine_id")300_ = os.Setenv("GOOGLE_API_KEY", "fake_api_key")301defer os.Unsetenv("GOOGLECSE_CX")302defer os.Unsetenv("GOOGLE_API_KEY")303304tt.mocks()305defer gock.Off() // Flush pending mocks after test execution306307scanner := NewGoogleCSEScanner(&http.Client{})308remote := NewLibrary(filter.NewEngine())309remote.AddScanner(scanner)310311if scanner.DryRun(*tt.number, tt.opts) != nil {312t.Fatal("DryRun() should return nil")313}314315got, errs := remote.Scan(tt.number, tt.opts)316if len(tt.wantErrors) > 0 {317assert.Equal(t, tt.wantErrors, errs)318} else {319assert.Len(t, errs, 0)320}321assert.Equal(t, tt.expected, got)322})323}324}325326func TestGoogleCSEScanner_DryRun(t *testing.T) {327_ = os.Setenv("GOOGLECSE_CX", "abc")328_ = os.Setenv("GOOGLE_API_KEY", "abc")329defer os.Unsetenv("GOOGLECSE_CX")330defer os.Unsetenv("GOOGLE_API_KEY")331scanner := NewGoogleCSEScanner(&http.Client{})332assert.Nil(t, scanner.DryRun(*test.NewFakeUSNumber(), ScannerOptions{}))333}334335func TestGoogleCSEScanner_DryRunWithOptions(t *testing.T) {336errStr := "search engine ID and/or API key is not defined"337338scanner := NewGoogleCSEScanner(&http.Client{})339assert.Nil(t, scanner.DryRun(*test.NewFakeUSNumber(), ScannerOptions{"GOOGLECSE_CX": "test", "GOOGLE_API_KEY": "secret"}))340assert.EqualError(t, scanner.DryRun(*test.NewFakeUSNumber(), ScannerOptions{"GOOGLECSE_CX": "", "GOOGLE_API_KEY": ""}), errStr)341assert.EqualError(t, scanner.DryRun(*test.NewFakeUSNumber(), ScannerOptions{"GOOGLECSE_CX": "test"}), errStr)342assert.EqualError(t, scanner.DryRun(*test.NewFakeUSNumber(), ScannerOptions{"GOOGLE_API_KEY": "test"}), errStr)343}344345func TestGoogleCSEScanner_DryRun_Error(t *testing.T) {346scanner := NewGoogleCSEScanner(&http.Client{})347assert.EqualError(t, scanner.DryRun(*test.NewFakeUSNumber(), ScannerOptions{}), "search engine ID and/or API key is not defined")348}349350func TestGoogleCSEScanner_MaxResults(t *testing.T) {351testcases := []struct {352value string353expected int64354}{355{356value: "",357expected: 10,358},359{360value: "20",361expected: 20,362},363{364value: "120",365expected: 100,366},367}368369defer os.Clearenv()370371for _, tt := range testcases {372_ = os.Setenv("GOOGLECSE_MAX_RESULTS", tt.value)373374scanner := NewGoogleCSEScanner(nil)375376assert.Equal(t, tt.expected, scanner.(*googleCSEScanner).MaxResults)377}378}379380381