From 9b8f10358a0f0505e1ed7733cd25c4bdf60c2e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonne=20Ha=C3=9F?= Date: Sat, 27 Apr 2019 16:01:54 +0200 Subject: [PATCH] Introduce JSON Schema for API responses and validate the responses against it --- lib/schemas/api_v1.json | 414 ++++++++++++++++++ .../api/aspects_controller_spec.rb | 4 + .../api/comments_controller_spec.rb | 2 + .../api/contacts_controller_spec.rb | 2 + .../api/conversations_controller_spec.rb | 4 + spec/integration/api/likes_controller_spec.rb | 2 + .../api/messages_controller_spec.rb | 6 +- .../api/notifications_controller_spec.rb | 10 +- .../integration/api/photos_controller_spec.rb | 14 +- spec/integration/api/posts_controller_spec.rb | 6 + .../api/reshares_controller_spec.rb | 2 + .../integration/api/search_controller_spec.rb | 8 + .../api/streams_controller_spec.rb | 79 +++- .../api/tag_followings_controller_spec.rb | 2 + spec/integration/api/users_controller_spec.rb | 14 + spec/spec_helper.rb | 1 + 16 files changed, 555 insertions(+), 15 deletions(-) create mode 100644 lib/schemas/api_v1.json diff --git a/lib/schemas/api_v1.json b/lib/schemas/api_v1.json new file mode 100644 index 000000000..3bbb4b6cd --- /dev/null +++ b/lib/schemas/api_v1.json @@ -0,0 +1,414 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://diaspora.software/api/v1/schema.json", + "oneOf": [ + {"$ref": "#/definitions/aspects"}, + {"$ref": "#/definitions/aspect"}, + {"$ref": "#/definitions/comments_or_messages"}, + {"$ref": "#/definitions/users"}, + {"$ref": "#/definitions/conversations"}, + {"$ref": "#/definitions/conversation"}, + {"$ref": "#/definitions/authored_content_references"}, + {"$ref": "#/definitions/likes"}, + {"$ref": "#/definitions/notifications"}, + {"$ref": "#/definitions/notification"}, + {"$ref": "#/definitions/photos"}, + {"$ref": "#/definitions/photo"}, + {"$ref": "#/definitions/post"}, + {"$ref": "#/definitions/posts"}, + {"$ref": "#/definitions/tags"}, + {"$ref": "#/definitions/own_user"}, + {"$ref": "#/definitions/user"} + ], + + "definitions": { + "guid": { + "type": "string", + "minLength": 16, + "maxLength": 255 + }, + + "timestamp": { + "type": "string", + "format": "date-time" + }, + + "short_profile": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "diaspora_id": { "type": "string" }, + "name": { "type": "string" }, + "avatar": { "type": "string" } + }, + "required": ["guid", "diaspora_id", "name"], + "additionalProperties": false + }, + + "aspects": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "order": { + "type": "integer" + } + }, + "required": ["id", "name", "order"], + "additionalProperties": false + } + }, + + "aspect": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "chat_enabled": { + "type": "boolean" + } + }, + "required": ["id", "name", "order", "chat_enabled"], + "additionalProperties": false + }, + + "comments_or_messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "created_at": { "$ref": "#/definitions/timestamp" }, + "author": { "$ref": "#/definitions/short_profile" }, + "body": { "type": "string" } + }, + "required": ["guid", "created_at", "author", "body"], + "additionalProperties": false + } + }, + + "users": { + "type": "array", + "items": { "$ref": "#/definitions/short_profile" } + }, + + "conversations": { + "type": "array", + "items": { "$ref": "#/definitions/conversation" } + }, + + "conversation": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "subject": { "type": "string" }, + "created_at": { "$ref": "#/definitions/timestamp" }, + "read": { "type": "boolean" }, + "participants": { + "type": "array", + "items": { "$ref": "#/definitions/short_profile" } + } + }, + "required": ["subject", "created_at", "read", "participants"], + "additionalProperties": false + }, + + "authored_content_reference": { + "type": "object", + "created_at": { "$ref": "#/definitions/timestamp" }, + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "author": { "$ref": "#/definitions/short_profile" } + }, + "required": ["guid", "created_at", "author"], + "additionalProperties": false + }, + + "authored_content_references": { + "type": "array", + "items": { "$ref": "#/definitions/authored_content_reference" } + }, + + "likes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "author": { "$ref": "#/definitions/short_profile" } + }, + "required": ["guid", "author"], + "additionalProperties": false + } + }, + + "notifications": { + "type": "array", + "items": { "$ref": "#/definitions/notification" } + }, + + "notification": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "type": { + "enum": [ + "also_commented", + "comment_on_post", + "liked", + "mentioned", + "mentioned_in_comment", + "reshared", + "started_sharing", + "contacts_birthday" + ] + }, + "read": { "type": "boolean" }, + "created_at": { "$ref": "#/definitions/timestamp" }, + "target": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "author": { "$ref": "#/definitions/short_profile" } + }, + "required": ["guid"] + }, + "event_creators": { + "type": "array", + "items": { "$ref": "#/definitions/short_profile" } + } + }, + "required": ["guid", "type", "read", "created_at", "target"], + "additionalProperties": false + }, + + "photos": { + "type": "array", + "items": { "$ref": "#/definitions/photo"} + }, + + "photo_sizes": { + "type": "object", + "properties": { + "large": { "type": "string" }, + "medium": { "type": "string" }, + "small": { "type": "string" } + }, + "required": ["large", "medium", "small"], + "additionalProperties": true + }, + + "photo_dimensions": { + "type": "object", + "properties": { + "width": { "type": "integer" }, + "height": { "type": "integer" } + }, + "required": ["width", "height"] + }, + + "photo": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "post": { "$ref": "#/definitions/guid" }, + "created_at": { "$ref": "#/definitions/timestamp" }, + "dimensions": { "$ref": "#/definitions/photo_dimensions" }, + "sizes": { "$ref": "#/definitions/photo_sizes" } + }, + "required": ["guid", "created_at", "dimensions", "sizes"], + "additionalProperties": false + }, + + "post_common": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "created_at": { "$ref": "#/definitions/timestamp" }, + "title": { "type": "string" }, + "body": { "type": "string" }, + "provider_display_name": { "type": "string" }, + "public": { "type": "boolean" }, + "nsfw": { "type": "boolean" }, + "author": { "$ref": "#/definitions/short_profile" }, + "interaction_counters": { + "type": "object", + "properties": { + "comments": { "type": "integer" }, + "likes": { "type": "integer" }, + "reshares":{ "type": "integer" } + }, + "required": ["comments", "likes", "reshares"], + "additionalProperties": false + }, + "mentioned_people": { + "type": "array", + "items": { "$ref": "#/definitions/short_profile" } + }, + "photos": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimensions": { "$ref": "#/definitions/photo_dimensions" }, + "sizes": { "$ref": "#/definitions/photo_sizes" } + }, + "required": ["dimensions", "sizes"] + } + }, + "poll": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "participation_count": { "type": "integer" }, + "already_participated": { "type": "boolean" }, + "question": { "type": "string" }, + "poll_answers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "answer": { "type": "string" }, + "vote_count": { "type": "integer" } + }, + "required": ["id", "answer", "vote_count"], + "additionalProperties": false + } + } + }, + "required": ["guid", "participation_count", "already_participated", "question", "poll_answers"], + "additionalProperties": false + }, + "location": { + "type": "object", + "properties": { + "address": { "type": "string" }, + "lat": { "type": "number" }, + "lng": { "type": "number" } + }, + "required": ["address", "lat", "lng"], + "additionalProperties": false + } + }, + "required": ["guid", "created_at", "title", "body", "public", "nsfw", "author", "interaction_counters", "mentioned_people", "photos"] + }, + + "post": { + "anyOf": [ + { + "allOf": [ + { "$ref": "#/definitions/post_common" }, + { + "properties": { + "post_type": { "type": "string", "format": "^StatusMessage$" } + }, + "required": ["post_type"] + } + ] + }, + { + "allOf": [ + { "$ref": "#/definitions/post_common" }, + { + "properties": { + "post_type": { "type": "string", "format": "^Reshare$" }, + "root": { "$ref": "#/definitions/authored_content_reference" } + }, + "required": ["post_type", "root"] + } + ] + } + ] + }, + + "posts": { + "type": "array", + "items": { "$ref": "#/definitions/post" } + }, + + "tags": { + "type": "array", + "items": { "type": "string", "pattern": "^[^#]" } + }, + + "birthday": { "type": "string", "pattern": "^\\d\\d\\d\\d-\\d\\d-\\d\\d$" }, + + "user_data": { + "type": "object", + "properties": { + "guid": { "$ref": "#/definitions/guid" }, + "diaspora_id": { "type": "string" }, + "name": { "type": "string" }, + "birthday": { "$ref": "#/definitions/birthday" }, + "gender": { "type": "string" }, + "location": { "type": "string" }, + "bio": { "type": "string" }, + "avatar": { "$ref": "#/definitions/photo_sizes" }, + "tags": { "$ref": "#/definitions/tags" } + }, + "required": ["guid", "diaspora_id", "tags"] + }, + + "own_user": { + "allOf": [ + { "$ref": "#/definitions/user_data" }, + { + "type": "object", + "properties": { + "searchable": { "type": "boolean" }, + "show_profile_info": { "type": "boolean" } + }, + "required": ["searchable", "show_profile_info"] + } + ] + }, + + "user": { + "allOf": [ + { "$ref": "#/definitions/user_data" }, + { + "type": "object", + "properties": { + "blocked": { "type": "boolean" }, + "relationship": { + "type": "object", + "properties": { + "receiving": { "type": "boolean" }, + "sharing": { "type": "boolean" } + }, + "required": ["receiving", "sharing"], + "additionalProperties": false + }, + "aspects": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "name": { "type": "string" } + }, + "required": ["id", "name"], + "additionalProperties": false + } + } + }, + "required": ["blocked", "relationship", "aspects"] + } + ] + } + } +} diff --git a/spec/integration/api/aspects_controller_spec.rb b/spec/integration/api/aspects_controller_spec.rb index 094e591f1..2a948b2c4 100644 --- a/spec/integration/api/aspects_controller_spec.rb +++ b/spec/integration/api/aspects_controller_spec.rb @@ -45,6 +45,8 @@ describe Api::V1::AspectsController do expect(aspect["name"]).to eq(found_aspect.name) expect(aspect["order"]).to eq(found_aspect.order_id) end + + expect(aspects.to_json).to match_json_schema(:api_v1_schema) end context "without impromper credentials" do @@ -79,6 +81,8 @@ describe Api::V1::AspectsController do expect(aspect["name"]).to eq(@aspect2.name) expect(aspect["order"]).to eq(@aspect2.order_id) expect(aspect["chat_enabled"]).to eq(@aspect2.chat_enabled) + + expect(aspect.to_json).to match_json_schema(:api_v1_schema) end end diff --git a/spec/integration/api/comments_controller_spec.rb b/spec/integration/api/comments_controller_spec.rb index cdae54439..5e3881126 100644 --- a/spec/integration/api/comments_controller_spec.rb +++ b/spec/integration/api/comments_controller_spec.rb @@ -139,6 +139,8 @@ describe Api::V1::CommentsController do expect(comments.length).to eq(2) confirm_comment_format(comments[0], auth.user, @comment_text1) confirm_comment_format(comments[1], auth.user, @comment_text2) + + expect(comments.to_json).to match_json_schema(:api_v1_schema) end end diff --git a/spec/integration/api/contacts_controller_spec.rb b/spec/integration/api/contacts_controller_spec.rb index fc48b7294..dcee30523 100644 --- a/spec/integration/api/contacts_controller_spec.rb +++ b/spec/integration/api/contacts_controller_spec.rb @@ -56,6 +56,8 @@ describe Api::V1::ContactsController do expect(response.status).to eq(200) contacts = response_body_data(response) expect(contacts.length).to eq(@aspect1.contacts.length) + + expect(contacts.to_json).to match_json_schema(:api_v1_schema) end end diff --git a/spec/integration/api/conversations_controller_spec.rb b/spec/integration/api/conversations_controller_spec.rb index 7f26e84b9..313d580fe 100644 --- a/spec/integration/api/conversations_controller_spec.rb +++ b/spec/integration/api/conversations_controller_spec.rb @@ -166,6 +166,8 @@ describe Api::V1::ConversationsController do expect(returned_conversations.length).to eq(3) actual_conversation = returned_conversations.select {|c| c["guid"] == @read_conversation_guid }[0] confirm_conversation_format(actual_conversation, @read_conversation, [auth.user, alice]) + + expect(returned_conversations.to_json).to match_json_schema(:api_v1_schema) end it "returns all the user unread conversations" do @@ -221,6 +223,8 @@ describe Api::V1::ConversationsController do expect(response.status).to eq(200) conversation = response_body(response) confirm_conversation_format(conversation, @conversation, [auth.user, alice]) + + expect(conversation.to_json).to match_json_schema(:api_v1_schema) end end diff --git a/spec/integration/api/likes_controller_spec.rb b/spec/integration/api/likes_controller_spec.rb index 9a17934ed..62790229d 100644 --- a/spec/integration/api/likes_controller_spec.rb +++ b/spec/integration/api/likes_controller_spec.rb @@ -70,6 +70,8 @@ describe Api::V1::LikesController do confirm_like_format(likes, alice) confirm_like_format(likes, bob) confirm_like_format(likes, auth.user) + + expect(likes.to_json).to match_json_schema(:api_v1_schema) end end diff --git a/spec/integration/api/messages_controller_spec.rb b/spec/integration/api/messages_controller_spec.rb index e64735cda..7e621f3a1 100644 --- a/spec/integration/api/messages_controller_spec.rb +++ b/spec/integration/api/messages_controller_spec.rb @@ -37,7 +37,7 @@ describe Api::V1::MessagesController do @message_text = "reply to first message" end - describe "#create " do + describe "#create" do before do post api_v1_conversations_path, params: @conversation @conversation_guid = JSON.parse(response.body)["guid"] @@ -114,7 +114,7 @@ describe Api::V1::MessagesController do end end - describe "#index " do + describe "#index" do before do post api_v1_conversations_path, params: @conversation @conversation_guid = JSON.parse(response.body)["guid"] @@ -130,6 +130,8 @@ describe Api::V1::MessagesController do messages = response_body_data(response) expect(messages.length).to eq(1) + expect(messages.to_json).to match_json_schema(:api_v1_schema) + confirm_message_format(messages[0], "first message", auth.user) conversation = get_conversation(@conversation_guid) expect(conversation[:read]).to be_truthy diff --git a/spec/integration/api/notifications_controller_spec.rb b/spec/integration/api/notifications_controller_spec.rb index ee63f3bb9..999c6515b 100644 --- a/spec/integration/api/notifications_controller_spec.rb +++ b/spec/integration/api/notifications_controller_spec.rb @@ -37,9 +37,11 @@ describe Api::V1::NotificationsController do params: {access_token: access_token} ) expect(response.status).to eq(200) - notification = response_body_data(response) - expect(notification.length).to eq(2) - confirm_notification_format(notification[1], @notification, "also_commented", nil) + notifications = response_body_data(response) + expect(notifications.length).to eq(2) + confirm_notification_format(notifications[1], @notification, "also_commented", nil) + + expect(notifications.to_json).to match_json_schema(:api_v1_schema) end it "with proper credentials and unread only" do @@ -117,6 +119,8 @@ describe Api::V1::NotificationsController do expect(response.status).to eq(200) notification = JSON.parse(response.body) confirm_notification_format(notification, @notification, "also_commented", @post) + + expect(notification.to_json).to match_json_schema(:api_v1_schema) end end diff --git a/spec/integration/api/photos_controller_spec.rb b/spec/integration/api/photos_controller_spec.rb index dc3ddf68e..db8beff53 100644 --- a/spec/integration/api/photos_controller_spec.rb +++ b/spec/integration/api/photos_controller_spec.rb @@ -76,7 +76,9 @@ describe Api::V1::PhotosController do expect(response.status).to eq(200) photo = response_body(response) expect(photo.has_key?("post")).to be_falsey - confirm_photo_format(photo, @user_photo1, auth.user) + confirm_photo_format(photo, @user_photo1) + + expect(photo.to_json).to match_json_schema(:api_v1_schema) end it "with correct GUID user's photo used in post and access token" do @@ -87,7 +89,7 @@ describe Api::V1::PhotosController do expect(response.status).to eq(200) photo = response_body(response) expect(photo.has_key?("post")).to be_truthy - confirm_photo_format(photo, @user_photo2, auth.user) + confirm_photo_format(photo, @user_photo2) end it "with correct GUID of other user's public photo and access token" do @@ -97,7 +99,7 @@ describe Api::V1::PhotosController do ) expect(response.status).to eq(200) photo = response_body(response) - confirm_photo_format(photo, @alice_public_photo, alice) + confirm_photo_format(photo, @alice_public_photo) end end @@ -149,6 +151,8 @@ describe Api::V1::PhotosController do expect(response.status).to eq(200) photos = response_body_data(response) expect(photos.length).to eq(2) + + expect(photos.to_json).to match_json_schema(:api_v1_schema) end end @@ -198,7 +202,7 @@ describe Api::V1::PhotosController do photo = response_body(response) ref_photo = auth.user.photos.reload.find_by(guid: photo["guid"]) expect(ref_photo.pending).to be_falsey - confirm_photo_format(photo, ref_photo, auth.user) + confirm_photo_format(photo, ref_photo) end it "with valid encoded file set as pending" do @@ -363,7 +367,7 @@ describe Api::V1::PhotosController do end # rubocop:disable Metrics/AbcSize - def confirm_photo_format(photo, ref_photo, ref_user) + def confirm_photo_format(photo, ref_photo) expect(photo["guid"]).to eq(ref_photo.guid) if ref_photo.status_message_guid expect(photo["post"]).to eq(ref_photo.status_message_guid) diff --git a/spec/integration/api/posts_controller_spec.rb b/spec/integration/api/posts_controller_spec.rb index 3463a3f46..a9142c3bd 100644 --- a/spec/integration/api/posts_controller_spec.rb +++ b/spec/integration/api/posts_controller_spec.rb @@ -68,6 +68,8 @@ describe Api::V1::PostsController do expect(response.status).to eq(200) post = response_body(response) confirm_post_format(post, alice, @status, [bob, eve]) + + expect(post.to_json).to match_json_schema(:api_v1_schema) end end @@ -90,6 +92,8 @@ describe Api::V1::PostsController do expect(response.status).to eq(200) post = response_body(response) confirm_post_format(post, alice, status_message) + + expect(post.to_json).to match_json_schema(:api_v1_schema) end end @@ -105,6 +109,8 @@ describe Api::V1::PostsController do expect(response.status).to eq(200) post = response_body(response) confirm_reshare_format(post, @status, alice) + + expect(post.to_json).to match_json_schema(:api_v1_schema) end end diff --git a/spec/integration/api/reshares_controller_spec.rb b/spec/integration/api/reshares_controller_spec.rb index f605e749d..a50450261 100644 --- a/spec/integration/api/reshares_controller_spec.rb +++ b/spec/integration/api/reshares_controller_spec.rb @@ -52,6 +52,8 @@ describe Api::V1::ResharesController do reshare = reshares[0] expect(reshare["guid"]).not_to be_nil confirm_person_format(reshare["author"], alice) + + expect(reshares.to_json).to match_json_schema(:api_v1_schema) end it "succeeds but empty with private post it can see" do diff --git a/spec/integration/api/search_controller_spec.rb b/spec/integration/api/search_controller_spec.rb index 252d3478e..f8301637f 100644 --- a/spec/integration/api/search_controller_spec.rb +++ b/spec/integration/api/search_controller_spec.rb @@ -62,6 +62,8 @@ describe Api::V1::SearchController do expect(response.status).to eq(200) users = response_body_data(response) expect(users.length).to eq(15) + + expect(users.to_json).to match_json_schema(:api_v1_schema) end it "succeeds by name" do @@ -72,6 +74,8 @@ describe Api::V1::SearchController do expect(response.status).to eq(200) users = response_body_data(response) expect(users.length).to eq(1) + + expect(users.to_json).to match_json_schema(:api_v1_schema) end it "succeeds by handle" do @@ -82,6 +86,8 @@ describe Api::V1::SearchController do expect(response.status).to eq(200) users = response_body_data(response) expect(users.length).to eq(1) + + expect(users.to_json).to match_json_schema(:api_v1_schema) end it "doesn't return closed accounts" do @@ -177,6 +183,8 @@ describe Api::V1::SearchController do expect(response.status).to eq(200) posts = response_body_data(response) expect(posts.length).to eq(2) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) end it "only returns public posts without private scope" do diff --git a/spec/integration/api/streams_controller_spec.rb b/spec/integration/api/streams_controller_spec.rb index 883ca2c9f..2b60751b3 100644 --- a/spec/integration/api/streams_controller_spec.rb +++ b/spec/integration/api/streams_controller_spec.rb @@ -72,15 +72,27 @@ describe Api::V1::StreamsController do end describe "#aspect" do + it "returns a valid schema" do + get( + api_v1_aspects_stream_path(aspect_ids: JSON.generate([@aspect.id])), + params: {access_token: access_token_read_only} + ) + expect(response.status).to eq(200) + + posts = response_body_data(response) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) + end + it "contains expected aspect message" do get( api_v1_aspects_stream_path(aspect_ids: JSON.generate([@aspect.id])), params: {access_token: access_token_read_only} ) expect(response.status).to eq(200) - post = response_body_data(response) - expect(post.length).to eq 3 - json_post = post.select {|p| p["guid"] == @status.guid }.first + posts = response_body_data(response) + expect(posts.length).to eq 3 + json_post = posts.select {|p| p["guid"] == @status.guid }.first confirm_post_format(json_post, auth_read_only.user, @status) end @@ -128,8 +140,10 @@ describe Api::V1::StreamsController do params: {access_token: access_token_read_only} ) expect(response.status).to eq(200) - post = response_body_data(response) - expect(post.length).to eq(3) + posts = response_body_data(response) + expect(posts.length).to eq(3) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) end it "public posts only tags expected" do @@ -160,6 +174,17 @@ describe Api::V1::StreamsController do end describe "#activity" do + it "returns a valid schema" do + get( + api_v1_activity_stream_path, + params: {access_token: access_token_read_only} + ) + expect(response.status).to eq(200) + posts = response_body_data(response) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) + end + it "contains activity message" do get( api_v1_activity_stream_path, @@ -192,6 +217,17 @@ describe Api::V1::StreamsController do end describe "#main" do + it "returns a valid schema" do + get( + api_v1_stream_path, + params: {access_token: access_token_read_only} + ) + expect(response.status).to eq(200) + posts = response_body_data(response) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) + end + it "contains main message" do get( api_v1_stream_path, @@ -224,6 +260,17 @@ describe Api::V1::StreamsController do end describe "#commented" do + it "returns a valid schema" do + get( + api_v1_commented_stream_path, + params: {access_token: access_token_read_only} + ) + expect(response.status).to eq(200) + posts = response_body_data(response) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) + end + it "contains commented message" do get( api_v1_commented_stream_path, @@ -254,6 +301,17 @@ describe Api::V1::StreamsController do end describe "#mentions" do + it "returns a valid schema" do + get( + api_v1_mentions_stream_path, + params: {access_token: access_token_read_only} + ) + expect(response.status).to eq(200) + posts = response_body_data(response) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) + end + it "contains mentions message" do get( api_v1_mentions_stream_path, @@ -284,6 +342,17 @@ describe Api::V1::StreamsController do end describe "#liked" do + it "returns a valid schema" do + get( + api_v1_liked_stream_path, + params: {access_token: access_token_read_only} + ) + expect(response.status).to eq(200) + posts = response_body_data(response) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) + end + it "contains liked message" do get( api_v1_liked_stream_path, diff --git a/spec/integration/api/tag_followings_controller_spec.rb b/spec/integration/api/tag_followings_controller_spec.rb index 9f6e3ebb9..bcb066db5 100755 --- a/spec/integration/api/tag_followings_controller_spec.rb +++ b/spec/integration/api/tag_followings_controller_spec.rb @@ -103,6 +103,8 @@ describe Api::V1::TagFollowingsController do items = JSON.parse(response.body) expect(items.length).to eq(@expected_tags.length) @expected_tags.each {|tag| expect(items.find(tag)).to be_truthy } + + expect(items.to_json).to match_json_schema(:api_v1_schema) end end diff --git a/spec/integration/api/users_controller_spec.rb b/spec/integration/api/users_controller_spec.rb index ccca37bd1..4d6e5cef2 100644 --- a/spec/integration/api/users_controller_spec.rb +++ b/spec/integration/api/users_controller_spec.rb @@ -58,6 +58,8 @@ describe Api::V1::UsersController do expect(response.status).to eq(200) expect(user["guid"]).to eq(auth.user.guid) confirm_self_data_format(user) + + expect(user.to_json).to match_json_schema(:api_v1_schema) end it "fails if invalid token" do @@ -81,6 +83,8 @@ describe Api::V1::UsersController do expect(response.status).to eq(200) expect(user["guid"]).to eq(alice.person.guid) confirm_public_profile_hash(user) + + expect(user.to_json).to match_json_schema(:api_v1_schema) end it "succeeds with in Aspect valid user" do @@ -96,6 +100,8 @@ describe Api::V1::UsersController do expect(response.status).to eq(200) expect(user["guid"]).to eq(alice.person.guid) confirm_public_profile_hash(user) + + expect(user.to_json).to match_json_schema(:api_v1_schema) end it "succeeds with limited data on non-public/not shared" do @@ -120,6 +126,8 @@ describe Api::V1::UsersController do expect(response.status).to eq(200) expect(user["guid"]).to eq(eve.person.guid) confirm_public_profile_hash(user) + + expect(user.to_json).to match_json_schema(:api_v1_schema) end it "fails if invalid token" do @@ -315,6 +323,8 @@ describe Api::V1::UsersController do contacts = response_body_data(response) expect(contacts.length).to eq(1) confirm_person_format(contacts[0], alice) + + expect(contacts.to_json).to match_json_schema(:api_v1_schema) end it "fails with invalid GUID" do @@ -381,6 +391,8 @@ describe Api::V1::UsersController do expect(guids).to include(@public_photo1.guid, @public_photo2.guid, @shared_photo1.guid) expect(guids).not_to include(@private_photo1.guid) confirm_photos(photos) + + expect(photos.to_json).to match_json_schema(:api_v1_schema) end it "returns only public photos of other user without private:read scope in token" do @@ -456,6 +468,8 @@ describe Api::V1::UsersController do expect(guids).not_to include(@private_post1.guid) post = posts.select {|p| p["guid"] == @public_post1.guid } confirm_post_format(post[0], alice, @public_post1) + + expect(posts.to_json).to match_json_schema(:api_v1_schema) end it "returns logged in user's posts" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e75902bef..05d631160 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -146,6 +146,7 @@ RSpec.configure do |config| config.include JSON::SchemaMatchers config.json_schemas[:archive_schema] = "lib/schemas/archive-format.json" + config.json_schemas[:api_v1_schema] = "lib/schemas/api_v1.json" JSON::Validator.add_schema( JSON::Schema.new(