github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/cmd/search_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"os"
    10  	"testing"
    11  
    12  	"github.com/qri-io/ioes"
    13  	"github.com/qri-io/qri/errors"
    14  	"github.com/qri-io/qri/lib"
    15  	"github.com/qri-io/qri/registry/regclient"
    16  )
    17  
    18  func TestSearchComplete(t *testing.T) {
    19  	run := NewTestRunner(t, "test_peer_search_complete", "qri_test_search_complete")
    20  	defer run.Delete()
    21  
    22  	ctx, cancel := context.WithCancel(context.Background())
    23  	defer cancel()
    24  
    25  	f, err := NewTestFactory(ctx)
    26  	if err != nil {
    27  		t.Errorf("error creating new test factory: %s", err)
    28  		return
    29  	}
    30  
    31  	cases := []struct {
    32  		args   []string
    33  		expect string
    34  		err    string
    35  	}{
    36  		{[]string{}, "", ""},
    37  		{[]string{"test"}, "test", ""},
    38  		{[]string{"test", "test2"}, "test", ""},
    39  	}
    40  
    41  	for i, c := range cases {
    42  		opt := &SearchOptions{
    43  			IOStreams: run.Streams,
    44  		}
    45  
    46  		opt.Complete(f, c.args)
    47  
    48  		if c.err != run.ErrStream.String() {
    49  			t.Errorf("case %d, error mismatch. Expected: '%s', Got: '%s'", i, c.err, run.ErrStream.String())
    50  			run.IOReset()
    51  			continue
    52  		}
    53  
    54  		if c.expect != opt.Query {
    55  			t.Errorf("case %d, opt.Ref not set correctly. Expected: '%s', Got: '%s'", i, c.expect, opt.Query)
    56  			run.IOReset()
    57  			continue
    58  		}
    59  
    60  		run.IOReset()
    61  	}
    62  }
    63  
    64  func TestSearchValidate(t *testing.T) {
    65  	cases := []struct {
    66  		query string
    67  		err   string
    68  		msg   string
    69  	}{
    70  		{"test", "", ""},
    71  		{"", lib.ErrBadArgs.Error(), "please provide search parameters, for example:\n    $ qri search census\n    $ qri search 'census 2018'\nsee `qri search --help` for more information"},
    72  	}
    73  	for i, c := range cases {
    74  		opt := &SearchOptions{
    75  			Query: c.query,
    76  		}
    77  
    78  		err := opt.Validate()
    79  		if (err == nil && c.err != "") || (err != nil && c.err != err.Error()) {
    80  			t.Errorf("case %d, mismatched error. Expected: %s, Got: %s", i, c.err, err)
    81  			continue
    82  		}
    83  		if libErr, ok := err.(errors.Error); ok {
    84  			if libErr.Message() != c.msg {
    85  				t.Errorf("case %d, mismatched user-friendly message. Expected: '%s', Got: '%s'", i, c.msg, libErr.Message())
    86  				continue
    87  			}
    88  		} else if c.msg != "" {
    89  			t.Errorf("case %d, mismatched user-friendly message. Expected: '%s', Got: ''", i, c.msg)
    90  			continue
    91  		}
    92  	}
    93  }
    94  
    95  // SearchTestRunner holds state used by the search test
    96  // TODO(dustmop): Compose this with TestRunner instead
    97  type SearchTestRunner struct {
    98  	Pwd       string
    99  	RootPath  string
   100  	Teardown  func()
   101  	Streams   ioes.IOStreams
   102  	InStream  *bytes.Buffer
   103  	OutStream *bytes.Buffer
   104  	ErrStream *bytes.Buffer
   105  }
   106  
   107  // NewSearchTestRunner sets up state needed for the search test
   108  // TODO (b5) - add an explicit RepoPath to the SearchTestRunner. Tests are
   109  // relying on the "RootPath" property, which should be configurable per-test
   110  func NewSearchTestRunner(t *testing.T) *SearchTestRunner {
   111  	run := SearchTestRunner{}
   112  
   113  	// Set IOStreams
   114  	run.Streams, run.InStream, run.OutStream, run.ErrStream = ioes.NewTestIOStreams()
   115  
   116  	// Get current directory
   117  	var err error
   118  	run.Pwd, err = os.Getwd()
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  
   123  	// Create temporary directory to run the test in
   124  	run.RootPath, err = ioutil.TempDir("", "")
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	os.Chdir(run.RootPath)
   129  
   130  	// Clean up function
   131  	run.Teardown = func() {
   132  		os.Chdir(run.Pwd)
   133  		os.RemoveAll(run.RootPath)
   134  	}
   135  	return &run
   136  }
   137  
   138  // Close tears down the test
   139  func (r *SearchTestRunner) Close() {
   140  	r.Teardown()
   141  }
   142  
   143  // IOReset resets the io streams
   144  func (r *SearchTestRunner) IOReset() {
   145  	r.InStream.Reset()
   146  	r.OutStream.Reset()
   147  	r.ErrStream.Reset()
   148  }
   149  
   150  func TestSearchRun(t *testing.T) {
   151  	run := NewSearchTestRunner(t)
   152  	defer run.Close()
   153  
   154  	ctx, cancel := context.WithCancel(context.Background())
   155  	defer cancel()
   156  	setNoColor(true)
   157  
   158  	// mock registry server that returns cached response data
   159  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   160  		w.Write(mockResponse)
   161  	}))
   162  	rc := regclient.NewClient(&regclient.Config{Location: server.URL})
   163  
   164  	f, err := NewTestFactoryInstanceOptions(ctx, run.RootPath, lib.OptRegistryClient(rc))
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	inst, err := f.Instance()
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  
   173  	cases := []struct {
   174  		query    string
   175  		format   string
   176  		expected string
   177  		err      string
   178  		msg      string
   179  	}{
   180  		{"test", "", textSearchResponse, "", ""},
   181  		{"test", "json", jsonSearchResponse, "", ""},
   182  	}
   183  
   184  	for i, c := range cases {
   185  		opt := &SearchOptions{
   186  			IOStreams: run.Streams,
   187  			Query:     c.query,
   188  			Format:    c.format,
   189  			Instance:  inst,
   190  		}
   191  
   192  		err = opt.Run()
   193  
   194  		if (err == nil && c.err != "") || (err != nil && c.err != err.Error()) {
   195  			t.Errorf("case %d, mismatched error. Expected: '%s', Got: '%v'", i, c.err, err)
   196  			run.IOReset()
   197  			continue
   198  		}
   199  
   200  		if libErr, ok := err.(errors.Error); ok {
   201  			if libErr.Message() != c.msg {
   202  				t.Errorf("case %d, mismatched user-friendly message. Expected: '%s', Got: '%s'", i, c.msg, libErr.Message())
   203  				run.IOReset()
   204  				continue
   205  			}
   206  		} else if c.msg != "" {
   207  			t.Errorf("case %d, mismatched user-friendly message. Expected: '%s', Got: ''", i, c.msg)
   208  			run.IOReset()
   209  			continue
   210  		}
   211  
   212  		if c.expected != run.OutStream.String() {
   213  			t.Errorf("case %d, output mismatch. Expected: '%s', Got: '%s'", i, c.expected, run.OutStream.String())
   214  			run.IOReset()
   215  			continue
   216  		}
   217  		run.IOReset()
   218  	}
   219  }
   220  
   221  var mockResponse = []byte(`{"data":[
   222  	{
   223        "type": "dataset",
   224        "id": "/ipfs/QmZEnjt3Y5RxXsoZyufJfFzcogicBEwfaimJSyDuC7nySA",
   225        "value": {
   226          "commit": {
   227            "qri": "cm:0",
   228            "timestamp": "2019-08-31T12:07:56.212858Z",
   229            "title": "change to 10"
   230          },
   231          "meta": {
   232            "keywords": [
   233              "joke"
   234            ],
   235            "qri": "md:0",
   236            "title": "this is a d"
   237          },
   238          "name": "nuun",
   239          "path": "/ipfs/QmZEnjt3Y5RxXsoZyufJfFzcogicBEwfaimJSyDuC7nySA",
   240          "peername": "nuun",
   241          "qri": "ds:0",
   242          "structure": {
   243            "entries": 3,
   244            "format": "csv",
   245            "length": 36,
   246            "qri": "st:0"
   247          }
   248        }
   249      }
   250  ],"meta":{"code":200}}`)
   251  
   252  var textSearchResponse = `showing 1 results for 'test'
   253  1   nuun/nuun
   254      /ipfs/QmZEnjt3Y5RxXsoZyufJfFzcogicBEwfaimJSyDuC7nySA
   255      this is a d
   256      36 B, 3 entries, 0 errors
   257  
   258  `
   259  
   260  var jsonSearchResponse = `[
   261    {
   262      "type": "dataset",
   263      "id": "/ipfs/QmZEnjt3Y5RxXsoZyufJfFzcogicBEwfaimJSyDuC7nySA",
   264      "url": "",
   265      "value": {
   266        "commit": {
   267          "qri": "cm:0",
   268          "timestamp": "2019-08-31T12:07:56.212858Z",
   269          "title": "change to 10"
   270        },
   271        "meta": {
   272          "keywords": [
   273            "joke"
   274          ],
   275          "qri": "md:0",
   276          "title": "this is a d"
   277        },
   278        "name": "nuun",
   279        "path": "/ipfs/QmZEnjt3Y5RxXsoZyufJfFzcogicBEwfaimJSyDuC7nySA",
   280        "peername": "nuun",
   281        "qri": "ds:0",
   282        "structure": {
   283          "entries": 3,
   284          "format": "csv",
   285          "length": 36,
   286          "qri": "st:0"
   287        }
   288      }
   289    }
   290  ]`