Merge remote-tracking branch 'upstream/develop' into fix-prameter-name-of-accounts...
[akkoma] / test / web / mastodon_api / mastodon_api_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Ecto.Changeset
9 alias Pleroma.Config
10 alias Pleroma.Notification
11 alias Pleroma.Object
12 alias Pleroma.Repo
13 alias Pleroma.Tests.ObanHelpers
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.OAuth.App
18 alias Pleroma.Web.Push
19
20 import ExUnit.CaptureLog
21 import Pleroma.Factory
22 import Swoosh.TestAssertions
23 import Tesla.Mock
24
25 setup do
26 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
27 :ok
28 end
29
30 clear_config([:instance, :public])
31 clear_config([:rich_media, :enabled])
32
33 test "apps/verify_credentials", %{conn: conn} do
34 token = insert(:oauth_token)
35
36 conn =
37 conn
38 |> assign(:user, token.user)
39 |> assign(:token, token)
40 |> get("/api/v1/apps/verify_credentials")
41
42 app = Repo.preload(token, :app).app
43
44 expected = %{
45 "name" => app.client_name,
46 "website" => app.website,
47 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
48 }
49
50 assert expected == json_response(conn, 200)
51 end
52
53 test "creates an oauth app", %{conn: conn} do
54 user = insert(:user)
55 app_attrs = build(:oauth_app)
56
57 conn =
58 conn
59 |> assign(:user, user)
60 |> post("/api/v1/apps", %{
61 client_name: app_attrs.client_name,
62 redirect_uris: app_attrs.redirect_uris
63 })
64
65 [app] = Repo.all(App)
66
67 expected = %{
68 "name" => app.client_name,
69 "website" => app.website,
70 "client_id" => app.client_id,
71 "client_secret" => app.client_secret,
72 "id" => app.id |> to_string(),
73 "redirect_uri" => app.redirect_uris,
74 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
75 }
76
77 assert expected == json_response(conn, 200)
78 end
79
80 describe "media upload" do
81 setup do
82 user = insert(:user)
83
84 conn =
85 build_conn()
86 |> assign(:user, user)
87
88 image = %Plug.Upload{
89 content_type: "image/jpg",
90 path: Path.absname("test/fixtures/image.jpg"),
91 filename: "an_image.jpg"
92 }
93
94 [conn: conn, image: image]
95 end
96
97 clear_config([:media_proxy])
98 clear_config([Pleroma.Upload])
99
100 test "returns uploaded image", %{conn: conn, image: image} do
101 desc = "Description of the image"
102
103 media =
104 conn
105 |> post("/api/v1/media", %{"file" => image, "description" => desc})
106 |> json_response(:ok)
107
108 assert media["type"] == "image"
109 assert media["description"] == desc
110 assert media["id"]
111
112 object = Repo.get(Object, media["id"])
113 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
114 end
115 end
116
117 test "getting a list of mutes", %{conn: conn} do
118 user = insert(:user)
119 other_user = insert(:user)
120
121 {:ok, user} = User.mute(user, other_user)
122
123 conn =
124 conn
125 |> assign(:user, user)
126 |> get("/api/v1/mutes")
127
128 other_user_id = to_string(other_user.id)
129 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
130 end
131
132 test "getting a list of blocks", %{conn: conn} do
133 user = insert(:user)
134 other_user = insert(:user)
135
136 {:ok, user} = User.block(user, other_user)
137
138 conn =
139 conn
140 |> assign(:user, user)
141 |> get("/api/v1/blocks")
142
143 other_user_id = to_string(other_user.id)
144 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
145 end
146
147 test "unimplemented follow_requests, blocks, domain blocks" do
148 user = insert(:user)
149
150 ["blocks", "domain_blocks", "follow_requests"]
151 |> Enum.each(fn endpoint ->
152 conn =
153 build_conn()
154 |> assign(:user, user)
155 |> get("/api/v1/#{endpoint}")
156
157 assert [] = json_response(conn, 200)
158 end)
159 end
160
161 test "returns the favorites of a user", %{conn: conn} do
162 user = insert(:user)
163 other_user = insert(:user)
164
165 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
166 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
167
168 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
169
170 first_conn =
171 conn
172 |> assign(:user, user)
173 |> get("/api/v1/favourites")
174
175 assert [status] = json_response(first_conn, 200)
176 assert status["id"] == to_string(activity.id)
177
178 assert [{"link", _link_header}] =
179 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
180
181 # Honours query params
182 {:ok, second_activity} =
183 CommonAPI.post(other_user, %{
184 "status" =>
185 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
186 })
187
188 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
189
190 last_like = status["id"]
191
192 second_conn =
193 conn
194 |> assign(:user, user)
195 |> get("/api/v1/favourites?since_id=#{last_like}")
196
197 assert [second_status] = json_response(second_conn, 200)
198 assert second_status["id"] == to_string(second_activity.id)
199
200 third_conn =
201 conn
202 |> assign(:user, user)
203 |> get("/api/v1/favourites?limit=0")
204
205 assert [] = json_response(third_conn, 200)
206 end
207
208 test "get instance information", %{conn: conn} do
209 conn = get(conn, "/api/v1/instance")
210 assert result = json_response(conn, 200)
211
212 email = Config.get([:instance, :email])
213 # Note: not checking for "max_toot_chars" since it's optional
214 assert %{
215 "uri" => _,
216 "title" => _,
217 "description" => _,
218 "version" => _,
219 "email" => from_config_email,
220 "urls" => %{
221 "streaming_api" => _
222 },
223 "stats" => _,
224 "thumbnail" => _,
225 "languages" => _,
226 "registrations" => _,
227 "poll_limits" => _
228 } = result
229
230 assert email == from_config_email
231 end
232
233 test "get instance stats", %{conn: conn} do
234 user = insert(:user, %{local: true})
235
236 user2 = insert(:user, %{local: true})
237 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
238
239 insert(:user, %{local: false, nickname: "u@peer1.com"})
240 insert(:user, %{local: false, nickname: "u@peer2.com"})
241
242 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
243
244 # Stats should count users with missing or nil `info.deactivated` value
245
246 {:ok, _user} =
247 user.id
248 |> User.get_cached_by_id()
249 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
250
251 Pleroma.Stats.force_update()
252
253 conn = get(conn, "/api/v1/instance")
254
255 assert result = json_response(conn, 200)
256
257 stats = result["stats"]
258
259 assert stats
260 assert stats["user_count"] == 1
261 assert stats["status_count"] == 1
262 assert stats["domain_count"] == 2
263 end
264
265 test "get peers", %{conn: conn} do
266 insert(:user, %{local: false, nickname: "u@peer1.com"})
267 insert(:user, %{local: false, nickname: "u@peer2.com"})
268
269 Pleroma.Stats.force_update()
270
271 conn = get(conn, "/api/v1/instance/peers")
272
273 assert result = json_response(conn, 200)
274
275 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
276 end
277
278 test "put settings", %{conn: conn} do
279 user = insert(:user)
280
281 conn =
282 conn
283 |> assign(:user, user)
284 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
285
286 assert _result = json_response(conn, 200)
287
288 user = User.get_cached_by_ap_id(user.ap_id)
289 assert user.info.settings == %{"programming" => "socks"}
290 end
291
292 describe "link headers" do
293 test "preserves parameters in link headers", %{conn: conn} do
294 user = insert(:user)
295 other_user = insert(:user)
296
297 {:ok, activity1} =
298 CommonAPI.post(other_user, %{
299 "status" => "hi @#{user.nickname}",
300 "visibility" => "public"
301 })
302
303 {:ok, activity2} =
304 CommonAPI.post(other_user, %{
305 "status" => "hi @#{user.nickname}",
306 "visibility" => "public"
307 })
308
309 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
310 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
311
312 conn =
313 conn
314 |> assign(:user, user)
315 |> get("/api/v1/notifications", %{media_only: true})
316
317 assert [link_header] = get_resp_header(conn, "link")
318 assert link_header =~ ~r/media_only=true/
319 assert link_header =~ ~r/min_id=#{notification2.id}/
320 assert link_header =~ ~r/max_id=#{notification1.id}/
321 end
322 end
323
324 describe "custom emoji" do
325 test "with tags", %{conn: conn} do
326 [emoji | _body] =
327 conn
328 |> get("/api/v1/custom_emojis")
329 |> json_response(200)
330
331 assert Map.has_key?(emoji, "shortcode")
332 assert Map.has_key?(emoji, "static_url")
333 assert Map.has_key?(emoji, "tags")
334 assert is_list(emoji["tags"])
335 assert Map.has_key?(emoji, "category")
336 assert Map.has_key?(emoji, "url")
337 assert Map.has_key?(emoji, "visible_in_picker")
338 end
339 end
340
341 describe "index/2 redirections" do
342 setup %{conn: conn} do
343 session_opts = [
344 store: :cookie,
345 key: "_test",
346 signing_salt: "cooldude"
347 ]
348
349 conn =
350 conn
351 |> Plug.Session.call(Plug.Session.init(session_opts))
352 |> fetch_session()
353
354 test_path = "/web/statuses/test"
355 %{conn: conn, path: test_path}
356 end
357
358 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
359 conn = get(conn, path)
360
361 assert conn.status == 302
362 assert redirected_to(conn) == "/web/login"
363 end
364
365 test "redirects not logged-in users to the login page on private instances", %{
366 conn: conn,
367 path: path
368 } do
369 Config.put([:instance, :public], false)
370
371 conn = get(conn, path)
372
373 assert conn.status == 302
374 assert redirected_to(conn) == "/web/login"
375 end
376
377 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
378 token = insert(:oauth_token)
379
380 conn =
381 conn
382 |> assign(:user, token.user)
383 |> put_session(:oauth_token, token.token)
384 |> get(path)
385
386 assert conn.status == 200
387 end
388
389 test "saves referer path to session", %{conn: conn, path: path} do
390 conn = get(conn, path)
391 return_to = Plug.Conn.get_session(conn, :return_to)
392
393 assert return_to == path
394 end
395
396 test "redirects to the saved path after log in", %{conn: conn, path: path} do
397 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
398 auth = insert(:oauth_authorization, app: app)
399
400 conn =
401 conn
402 |> put_session(:return_to, path)
403 |> get("/web/login", %{code: auth.token})
404
405 assert conn.status == 302
406 assert redirected_to(conn) == path
407 end
408
409 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
410 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
411 auth = insert(:oauth_authorization, app: app)
412
413 conn = get(conn, "/web/login", %{code: auth.token})
414
415 assert conn.status == 302
416 assert redirected_to(conn) == "/web/getting-started"
417 end
418 end
419
420 describe "GET /api/v1/polls/:id" do
421 test "returns poll entity for object id", %{conn: conn} do
422 user = insert(:user)
423
424 {:ok, activity} =
425 CommonAPI.post(user, %{
426 "status" => "Pleroma does",
427 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
428 })
429
430 object = Object.normalize(activity)
431
432 conn =
433 conn
434 |> assign(:user, user)
435 |> get("/api/v1/polls/#{object.id}")
436
437 response = json_response(conn, 200)
438 id = to_string(object.id)
439 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
440 end
441
442 test "does not expose polls for private statuses", %{conn: conn} do
443 user = insert(:user)
444 other_user = insert(:user)
445
446 {:ok, activity} =
447 CommonAPI.post(user, %{
448 "status" => "Pleroma does",
449 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
450 "visibility" => "private"
451 })
452
453 object = Object.normalize(activity)
454
455 conn =
456 conn
457 |> assign(:user, other_user)
458 |> get("/api/v1/polls/#{object.id}")
459
460 assert json_response(conn, 404)
461 end
462 end
463
464 describe "POST /api/v1/polls/:id/votes" do
465 test "votes are added to the poll", %{conn: conn} do
466 user = insert(:user)
467 other_user = insert(:user)
468
469 {:ok, activity} =
470 CommonAPI.post(user, %{
471 "status" => "A very delicious sandwich",
472 "poll" => %{
473 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
474 "expires_in" => 20,
475 "multiple" => true
476 }
477 })
478
479 object = Object.normalize(activity)
480
481 conn =
482 conn
483 |> assign(:user, other_user)
484 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
485
486 assert json_response(conn, 200)
487 object = Object.get_by_id(object.id)
488
489 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
490 total_items == 1
491 end)
492 end
493
494 test "author can't vote", %{conn: conn} do
495 user = insert(:user)
496
497 {:ok, activity} =
498 CommonAPI.post(user, %{
499 "status" => "Am I cute?",
500 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
501 })
502
503 object = Object.normalize(activity)
504
505 assert conn
506 |> assign(:user, user)
507 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
508 |> json_response(422) == %{"error" => "Poll's author can't vote"}
509
510 object = Object.get_by_id(object.id)
511
512 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
513 end
514
515 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
516 user = insert(:user)
517 other_user = insert(:user)
518
519 {:ok, activity} =
520 CommonAPI.post(user, %{
521 "status" => "The glass is",
522 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
523 })
524
525 object = Object.normalize(activity)
526
527 assert conn
528 |> assign(:user, other_user)
529 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
530 |> json_response(422) == %{"error" => "Too many choices"}
531
532 object = Object.get_by_id(object.id)
533
534 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
535 total_items == 1
536 end)
537 end
538
539 test "does not allow choice index to be greater than options count", %{conn: conn} do
540 user = insert(:user)
541 other_user = insert(:user)
542
543 {:ok, activity} =
544 CommonAPI.post(user, %{
545 "status" => "Am I cute?",
546 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
547 })
548
549 object = Object.normalize(activity)
550
551 conn =
552 conn
553 |> assign(:user, other_user)
554 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
555
556 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
557 end
558
559 test "returns 404 error when object is not exist", %{conn: conn} do
560 user = insert(:user)
561
562 conn =
563 conn
564 |> assign(:user, user)
565 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
566
567 assert json_response(conn, 404) == %{"error" => "Record not found"}
568 end
569
570 test "returns 404 when poll is private and not available for user", %{conn: conn} do
571 user = insert(:user)
572 other_user = insert(:user)
573
574 {:ok, activity} =
575 CommonAPI.post(user, %{
576 "status" => "Am I cute?",
577 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
578 "visibility" => "private"
579 })
580
581 object = Object.normalize(activity)
582
583 conn =
584 conn
585 |> assign(:user, other_user)
586 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
587
588 assert json_response(conn, 404) == %{"error" => "Record not found"}
589 end
590 end
591
592 describe "POST /auth/password, with valid parameters" do
593 setup %{conn: conn} do
594 user = insert(:user)
595 conn = post(conn, "/auth/password?email=#{user.email}")
596 %{conn: conn, user: user}
597 end
598
599 test "it returns 204", %{conn: conn} do
600 assert json_response(conn, :no_content)
601 end
602
603 test "it creates a PasswordResetToken record for user", %{user: user} do
604 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
605 assert token_record
606 end
607
608 test "it sends an email to user", %{user: user} do
609 ObanHelpers.perform_all()
610 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
611
612 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
613 notify_email = Config.get([:instance, :notify_email])
614 instance_name = Config.get([:instance, :name])
615
616 assert_email_sent(
617 from: {instance_name, notify_email},
618 to: {user.name, user.email},
619 html_body: email.html_body
620 )
621 end
622 end
623
624 describe "POST /auth/password, with invalid parameters" do
625 setup do
626 user = insert(:user)
627 {:ok, user: user}
628 end
629
630 test "it returns 404 when user is not found", %{conn: conn, user: user} do
631 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
632 assert conn.status == 404
633 assert conn.resp_body == ""
634 end
635
636 test "it returns 400 when user is not local", %{conn: conn, user: user} do
637 {:ok, user} = Repo.update(Changeset.change(user, local: false))
638 conn = post(conn, "/auth/password?email=#{user.email}")
639 assert conn.status == 400
640 assert conn.resp_body == ""
641 end
642 end
643
644 describe "GET /api/v1/suggestions" do
645 setup do
646 user = insert(:user)
647 other_user = insert(:user)
648 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
649 url500 = "http://test500?#{host}&#{user.nickname}"
650 url200 = "http://test200?#{host}&#{user.nickname}"
651
652 mock(fn
653 %{method: :get, url: ^url500} ->
654 %Tesla.Env{status: 500, body: "bad request"}
655
656 %{method: :get, url: ^url200} ->
657 %Tesla.Env{
658 status: 200,
659 body:
660 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
661 other_user.ap_id
662 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
663 }
664 end)
665
666 [user: user, other_user: other_user]
667 end
668
669 clear_config(:suggestions)
670
671 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
672 Config.put([:suggestions, :enabled], false)
673
674 res =
675 conn
676 |> assign(:user, user)
677 |> get("/api/v1/suggestions")
678 |> json_response(200)
679
680 assert res == []
681 end
682
683 test "returns error", %{conn: conn, user: user} do
684 Config.put([:suggestions, :enabled], true)
685 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
686
687 assert capture_log(fn ->
688 res =
689 conn
690 |> assign(:user, user)
691 |> get("/api/v1/suggestions")
692 |> json_response(500)
693
694 assert res == "Something went wrong"
695 end) =~ "Could not retrieve suggestions"
696 end
697
698 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
699 Config.put([:suggestions, :enabled], true)
700 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
701
702 res =
703 conn
704 |> assign(:user, user)
705 |> get("/api/v1/suggestions")
706 |> json_response(200)
707
708 assert res == [
709 %{
710 "acct" => "yj455",
711 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
712 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
713 "id" => 0
714 },
715 %{
716 "acct" => other_user.ap_id,
717 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
718 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
719 "id" => other_user.id
720 }
721 ]
722 end
723 end
724
725 describe "PUT /api/v1/media/:id" do
726 setup do
727 actor = insert(:user)
728
729 file = %Plug.Upload{
730 content_type: "image/jpg",
731 path: Path.absname("test/fixtures/image.jpg"),
732 filename: "an_image.jpg"
733 }
734
735 {:ok, %Object{} = object} =
736 ActivityPub.upload(
737 file,
738 actor: User.ap_id(actor),
739 description: "test-m"
740 )
741
742 [actor: actor, object: object]
743 end
744
745 test "updates name of media", %{conn: conn, actor: actor, object: object} do
746 media =
747 conn
748 |> assign(:user, actor)
749 |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
750 |> json_response(:ok)
751
752 assert media["description"] == "test-media"
753 assert refresh_record(object).data["name"] == "test-media"
754 end
755
756 test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do
757 media =
758 conn
759 |> assign(:user, actor)
760 |> put("/api/v1/media/#{object.id}", %{})
761 |> json_response(400)
762
763 assert media == %{"error" => "bad_request"}
764 end
765 end
766
767 describe "DELETE /auth/sign_out" do
768 test "redirect to root page", %{conn: conn} do
769 user = insert(:user)
770
771 conn =
772 conn
773 |> assign(:user, user)
774 |> delete("/auth/sign_out")
775
776 assert conn.status == 302
777 assert redirected_to(conn) == "/"
778 end
779 end
780
781 describe "empty_array, stubs for mastodon api" do
782 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
783 user = insert(:user)
784
785 res =
786 conn
787 |> assign(:user, user)
788 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
789 |> json_response(200)
790
791 assert res == []
792 end
793
794 test "GET /api/v1/endorsements", %{conn: conn} do
795 user = insert(:user)
796
797 res =
798 conn
799 |> assign(:user, user)
800 |> get("/api/v1/endorsements")
801 |> json_response(200)
802
803 assert res == []
804 end
805
806 test "GET /api/v1/trends", %{conn: conn} do
807 user = insert(:user)
808
809 res =
810 conn
811 |> assign(:user, user)
812 |> get("/api/v1/trends")
813 |> json_response(200)
814
815 assert res == []
816 end
817 end
818 end