github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/lib/render_test.go (about) 1 package lib 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/google/go-cmp/cmp" 10 "github.com/qri-io/dataset" 11 "github.com/qri-io/qri/base" 12 "github.com/qri-io/qri/base/dsfs" 13 testcfg "github.com/qri-io/qri/config/test" 14 "github.com/qri-io/qri/dsref" 15 "github.com/qri-io/qri/event" 16 "github.com/qri-io/qri/p2p" 17 "github.com/qri-io/qri/repo" 18 testrepo "github.com/qri-io/qri/repo/test" 19 ) 20 21 // renderTestRunner holds state to make it easier to run tests 22 type renderTestRunner struct { 23 Node *p2p.QriNode 24 Repo repo.Repo 25 Instance *Instance 26 Context context.Context 27 ContextDone func() 28 TsFunc func() time.Time 29 } 30 31 // newRenderTestRunner returns a test runner for render 32 func newRenderTestRunner(t *testing.T, testName string) *renderTestRunner { 33 ctx, done := context.WithCancel(context.Background()) 34 defer done() 35 36 r := renderTestRunner{} 37 r.Context, r.ContextDone = context.WithCancel(context.Background()) 38 39 // To keep hashes consistent, artificially specify the timestamp by overriding 40 // the dsfs.Timestamp func 41 r.TsFunc = dsfs.Timestamp 42 dsfs.Timestamp = func() time.Time { return time.Time{} } 43 44 var err error 45 r.Repo, err = testrepo.NewTestRepo() 46 if err != nil { 47 panic(err) 48 } 49 50 r.Node, err = p2p.NewQriNode(r.Repo, testcfg.DefaultP2PForTesting(), event.NilBus, nil) 51 if err != nil { 52 panic(err) 53 } 54 r.Instance = NewInstanceFromConfigAndNode(ctx, testcfg.DefaultConfigForTesting(), r.Node) 55 56 return &r 57 } 58 59 // Delete cleans up after the test is done 60 func (r *renderTestRunner) Delete() { 61 r.ContextDone() 62 dsfs.Timestamp = r.TsFunc 63 } 64 65 // Save saves a version of the dataset with a body 66 func (r *renderTestRunner) Save(ref string, ds *dataset.Dataset, bodyPath string) { 67 params := SaveParams{ 68 Ref: ref, 69 Dataset: ds, 70 BodyPath: bodyPath, 71 } 72 _, err := r.Instance.Dataset().Save(r.Context, ¶ms) 73 if err != nil { 74 panic(err) 75 } 76 } 77 78 func TestRenderViz(t *testing.T) { 79 ctx, done := context.WithCancel(context.Background()) 80 defer done() 81 82 // set Default Template to something easier to work with, then 83 // cleanup when test completes 84 prevDefaultTemplate := base.DefaultTemplate 85 base.DefaultTemplate = `<html><h1>{{.Peername}}/{{.Name}}</h1></html>` 86 defer func() { base.DefaultTemplate = prevDefaultTemplate }() 87 88 cases := []struct { 89 description string 90 params *RenderParams 91 expect []byte 92 err string 93 }{ 94 {"invalid ref", 95 &RenderParams{ 96 Ref: "foo/invalid_ref", 97 Selector: "viz", 98 }, nil, "reference not found"}, 99 {"template override just title", 100 &RenderParams{ 101 Ref: "me/movies", 102 Template: []byte("{{ .Meta.Title }}"), 103 Selector: "viz", 104 }, []byte("example movie data"), ""}, 105 {"override with invalid template", 106 &RenderParams{ 107 Ref: "me/movies", 108 Template: []byte("{{ .BadTemplate }}"), 109 Selector: "viz", 110 }, nil, `template: index.html:1:3: executing "index.html" at <.BadTemplate>: can't evaluate field BadTemplate in type *dataset.Dataset`}, 111 {"override with corrupt template", 112 &RenderParams{ 113 Ref: "me/movies", 114 Template: []byte("{{ .BadTemplateBooPlzFail"), 115 Selector: "viz", 116 }, nil, `parsing template: template: index.html:1: unclosed action`}, 117 {"default template", 118 &RenderParams{ 119 Ref: "me/movies", 120 Selector: "viz", 121 }, []byte("<html><h1>peer/movies</h1></html>"), ""}, 122 {"alternate dataset default template", 123 &RenderParams{ 124 Ref: "me/sitemap", 125 Selector: "viz", 126 }, []byte("<html><h1>peer/sitemap</h1></html>"), ""}, 127 } 128 129 tr, err := testrepo.NewTestRepo() 130 if err != nil { 131 t.Errorf("error allocating test repo: %s", err.Error()) 132 return 133 } 134 node, err := p2p.NewQriNode(tr, testcfg.DefaultP2PForTesting(), event.NilBus, nil) 135 if err != nil { 136 t.Fatal(err.Error()) 137 } 138 139 inst := NewInstanceFromConfigAndNode(ctx, testcfg.DefaultConfigForTesting(), node) 140 141 for i, c := range cases { 142 got, err := inst.Dataset().Render(ctx, c.params) 143 if !(err == nil && c.err == "" || err != nil && err.Error() == c.err) { 144 t.Errorf("case %d %s error mismatch. expected: '%s', got: '%s'", i, c.description, c.err, err) 145 return 146 } 147 148 if diff := cmp.Diff(string(got), string(c.expect)); diff != "" { 149 t.Errorf("case %d result mismatch. (-want +got):\n%s", i, c.description) 150 } 151 } 152 } 153 154 // Test that render with a readme returns an html string 155 func TestRenderReadme(t *testing.T) { 156 runner := newRenderTestRunner(t, "render_readme") 157 defer runner.Delete() 158 159 ctx := context.TODO() 160 161 runner.Save( 162 "me/my_dataset", 163 &dataset.Dataset{ 164 Readme: &dataset.Readme{ 165 Text: "# hi\n\nhello\n", 166 }, 167 }, 168 "testdata/jobs_by_automation/body.csv") 169 170 params := RenderParams{ 171 Ref: "peer/my_dataset", 172 Format: "html", 173 Selector: "readme", 174 } 175 text, err := runner.Instance.Dataset().Render(ctx, ¶ms) 176 if err != nil { 177 t.Fatal(err) 178 } 179 180 expect := "<h1>hi</h1>\n\n<p>hello</p>\n" 181 if diff := cmp.Diff(expect, string(text)); diff != "" { 182 t.Errorf("response mismatch (-want +got):\n%s", diff) 183 } 184 185 params = RenderParams{ 186 Dataset: &dataset.Dataset{ 187 Readme: &dataset.Readme{ 188 Text: "# hi\n\nhello", 189 }, 190 }, 191 Selector: "readme", 192 } 193 text, err = runner.Instance.Dataset().Render(ctx, ¶ms) 194 if err != nil { 195 t.Errorf("dynamic dataset render error: %s", err) 196 } 197 198 if diff := cmp.Diff(expect, string(text)); diff != "" { 199 t.Errorf("dynamic dataset render response mismatch (-want +got):\n%s", diff) 200 } 201 202 params = RenderParams{ 203 Ref: "foo/bar", 204 Dataset: &dataset.Dataset{ 205 Readme: &dataset.Readme{ 206 Text: "# hi\n\nhello", 207 }, 208 }, 209 Selector: "readme", 210 } 211 text, err = runner.Instance.Dataset().Render(ctx, ¶ms) 212 if err == nil { 213 t.Errorf("expected RenderReadme with both ref & dataset to error") 214 } 215 } 216 217 func TestRenderValidationFailure(t *testing.T) { 218 runner := newRenderTestRunner(t, "render_readme") 219 defer runner.Delete() 220 221 params := RenderParams{ 222 Ref: "peer/my_dataset", 223 Dataset: &dataset.Dataset{}, 224 Format: "html", 225 Selector: "viz", 226 } 227 _, err := runner.Instance.Dataset().Render(runner.Context, ¶ms) 228 if err == nil { 229 t.Fatal("expected error, got nil") 230 } 231 232 expect := "cannot provide both a reference and a dataset to render" 233 if diff := cmp.Diff(expect, err.Error()); diff != "" { 234 t.Errorf("err mismatch (-want +got):\n%s", diff) 235 } 236 237 params = RenderParams{} 238 _, err = runner.Instance.Dataset().Render(runner.Context, ¶ms) 239 if !errors.Is(dsref.ErrEmptyRef, err) { 240 t.Errorf("err mismatch, expected %q, got %q", dsref.ErrEmptyRef, err) 241 } 242 243 params = RenderParams{ 244 Ref: "peer/my_dataset", 245 } 246 _, err = runner.Instance.Dataset().Render(runner.Context, ¶ms) 247 if err == nil { 248 t.Fatal("expected error, got nil") 249 } 250 expect = "selector must be one of 'viz' or 'readme'" 251 if diff := cmp.Diff(expect, err.Error()); diff != "" { 252 t.Errorf("err mismatch (-want +got):\n%s", diff) 253 } 254 }