From 7378beffa004fe0321b1c23d9cdac5741518356c Mon Sep 17 00:00:00 2001 From: Nate Date: Sat, 30 Jan 2016 20:08:07 -0500 Subject: [PATCH 1/3] add GroupsController#index and acceptance specs for Group --- app/controllers/groups_controller.rb | 15 +++ app/interactors/index_group.rb | 9 ++ app/interactors/update_group.rb | 2 +- spec/acceptance/groups_spec.rb | 145 +++++++++++++++++++++ spec/controllers/groups_controller_spec.rb | 79 +++++++++++ spec/interactors/index_group_spec.rb | 35 +++++ 6 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 app/interactors/index_group.rb create mode 100644 spec/acceptance/groups_spec.rb create mode 100644 spec/interactors/index_group_spec.rb diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 0768700..f780c62 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,4 +1,5 @@ class GroupsController < AuthenticationController + # POST /groups def create result = CreateGroup.call(user: current_user, group_params: group_params) @@ -9,6 +10,7 @@ def create end end + # GET /groups/:id def show result = ShowGroup.call(id: params[:id]) @@ -19,6 +21,7 @@ def show end end + # PATCH /groups/:id def update result = UpdateGroup.call(id: params[:id], group_params: group_params) @@ -29,6 +32,18 @@ def update end end + # GET /groups + def index + result = IndexGroup.call + + if result.success? + render json: result.groups, status: :ok + else + render json: result.errors, status: :internal_server_error + end + end + + # DELETE /groups/:id def destroy result = DeleteGroup.call(id: params[:id]) diff --git a/app/interactors/index_group.rb b/app/interactors/index_group.rb new file mode 100644 index 0000000..59d2cf1 --- /dev/null +++ b/app/interactors/index_group.rb @@ -0,0 +1,9 @@ +class IndexGroup < StandardInteraction + def execute + context.groups = Group.all + end + + def validate_output + context.fail!(errors: "internal server error") unless context.groups + end +end diff --git a/app/interactors/update_group.rb b/app/interactors/update_group.rb index be2d6dc..1543baf 100644 --- a/app/interactors/update_group.rb +++ b/app/interactors/update_group.rb @@ -10,7 +10,7 @@ def execute private def update_group - Group.update(context.id, context.group_params) + context.group = Group.update(context.id, context.group_params) end def valid_input? diff --git a/spec/acceptance/groups_spec.rb b/spec/acceptance/groups_spec.rb new file mode 100644 index 0000000..a0f22ed --- /dev/null +++ b/spec/acceptance/groups_spec.rb @@ -0,0 +1,145 @@ +require "rspec_api_documentation/dsl" + +RSpec.resource "Groups" do + let(:authenticated_user) do + create(:confirmed_user) + end + + let(:auth_token) do + Knock::AuthToken.new(payload: { sub: authenticated_user.id }).token + end + + def schema_path + "./config/schema/schemata/group.json" + end + + shared_context "group parameters" do + parameter "name", <<-DESC, scope: :group, required: true + The name of the group. + DESC + parameter "city", <<-DESC, scope: :group, required: true + The city where the group is located. + DESC + parameter "state", <<-DESC, scope: :group, required: true + The state where the group is located. + DESC + parameter "country", <<-DESC, scope: :group, required: true + The country where the group is located. + DESC + parameter "facebook", <<-DESC, scope: :group, required: true + The facebook address for the group. + DESC + parameter "twitter", <<-DESC, scope: :group, required: true + The twitter address for the group. + DESC + end + + post "/groups" do + let(:schema_path) { "#{Rails.root}/config/schema/api.json" } + let(:last_response) { response } + let(:last_request) { request } + + include_context "group parameters" + + let(:name) { "Waaggh" } + let(:city) { "Washington" } + let(:state) { "DC" } + let(:country) { "USA" } + let(:facebook) { "facebook.com/waaaggh" } + let(:twitter) { "@waaggh" } + + header "Authorization", :auth_token + + example_request "POST /groups" do + expect(status).to eq 201 + + json_response = JSON.parse(response_body) + + expect(json_response["data"]["attributes"]["name"]). + to eq public_send(:name) + end + end + + patch "/groups/:id" do + include_context "group parameters" + + let!(:group) { create(:group) } + let(:id) { group.id } + + parameter "id", <<-DESC, required: true + The id of the group. + DESC + + let(:name) { "Zombicide Tuesdays" } + let(:city) { "Washington" } + let(:state) { "DC" } + let(:country) { "USA" } + let(:facebook) { "facebook.com/zombie" } + let(:twitter) { "@zombie" } + + header "Authorization", :auth_token + + example_request "PATCH /groups/:id" do + expect(status).to eq 200 + + group = JSON.parse(response_body) + + expect(group["data"]["attributes"]["name"]). + to eq public_send(:name) + end + end + + get "/groups/:id" do + let!(:group) { create(:group) } + let(:id) { group.id } + let(:name) { group.name } + + parameter "id", <<-DESC, required: true + The id of the group. + DESC + + header "Authorization", :auth_token + + example_request "GET /groups/:id" do + expect(status).to eq 200 + + group = JSON.parse(response_body) + + expect(group["data"]["attributes"]["name"]). + to eq public_send(:name) + end + end + + get "/groups/" do + let!(:group1) { create(:group) } + let!(:group2) { create(:group) } + let!(:group3) { create(:group) } + + header "Authorization", :auth_token + + example_request "GET /groups/" do + expect(status).to eq 200 + + list = JSON.parse(response_body) + + expect(list["data"][0].value?(group1.id.to_s)).to be_truthy + expect(list["data"][1].value?(group2.id.to_s)).to be_truthy + expect(list["data"][2].value?(group3.id.to_s)).to be_truthy + end + end + + delete "/groups/:id" do + let!(:group) { create(:group) } + let(:id) { group.id } + + header "Authorization", :auth_token + + parameter "id", <<-DESC, required: true + The id of the group. + DESC + + example_request "DELETE /groups/:id" do + expect(status).to eq 204 + end + end +end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index ee4a48f..99b1442 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -1,4 +1,10 @@ +include Committee::Test::Methods + RSpec.describe GroupsController do + let(:schema_path) { "#{Rails.root}/config/schema/api.json" } + let(:last_response) { response } + let(:last_request) { request } + let(:user) { create(:confirmed_user) } let(:group) { create(:group) } @@ -39,6 +45,11 @@ post :create, params expect(serialize(group)).to eq(response.body) end + + it "conforms to JSON schema" do + post :create, params + assert_schema_conform + end end context "when CreateGroup is a failure" do @@ -90,6 +101,11 @@ get :show, params expect(serialize(group)).to eq(response.body) end + + it "conforms to JSON schema" do + get :show, params + assert_schema_conform + end end context "when ShowGroup is a failure" do @@ -147,6 +163,11 @@ patch :update, params expect(serialize(group)).to eq(response.body) end + + it "conforms to JSON schema" do + patch :update, params + assert_schema_conform + end end context "when UpdateGroup is a failure" do @@ -167,6 +188,64 @@ end end + describe "GET #index" do + let(:group1) { create(:group) } + let(:group2) { create(:group) } + let(:group3) { create(:group) } + + let(:index_group_context) do + Interactor::Context.new(errors: :val, groups: [group1, group2, group3]) + end + + before(:example) do + allow(IndexGroup).to receive(:call).and_return(index_group_context) + end + + before do + authenticate + end + + context "when succesful" do + it "calls the IndexGroup interactor" do + expect(IndexGroup).to receive(:call) + get :index + end + + it "returns HTTP status 200" do + get :index + expect(response).to have_http_status(200) + end + + it "render the groups as JSON" do + get :index + expect(json["data"][0].value?(group1.id.to_s)).to be_truthy + expect(json["data"][1].value?(group2.id.to_s)).to be_truthy + expect(json["data"][2].value?(group3.id.to_s)).to be_truthy + end + + it "conforms to JSON schema" do + get :index + assert_schema_conform + end + end + + context "when IndexGroup fails" do + let(:index_group_context) do + double(:context, errors: "internal server error", success?: false) + end + + it "returns HTTP status 500" do + get :index + expect(response).to have_http_status(500) + end + + it "renders an error" do + get :index + expect(response.body).to eq("internal server error") + end + end + end + describe "DELETE #destroy" do let(:params) do { id: group.id } diff --git a/spec/interactors/index_group_spec.rb b/spec/interactors/index_group_spec.rb new file mode 100644 index 0000000..3a8e39d --- /dev/null +++ b/spec/interactors/index_group_spec.rb @@ -0,0 +1,35 @@ +RSpec.describe IndexGroup do + describe ".call" do + subject do + described_class.call + end + + let!(:group1) { create(:group) } + let!(:group2) { create(:group) } + let!(:group3) { create(:group) } + + context "when successful" do + it "returns a successful context" do + is_expected.to be_a_success + end + + it "sets profiles to an array of groups" do + expect(subject.groups).to eq([group1, group2, group3]) + end + end + + context "when unsuccessful" do + before do + allow(Group).to receive(:all).and_return(nil) + end + + it "fails" do + is_expected.to be_a_failure + end + + it "returns an error" do + expect(subject.errors).to eq("internal server error") + end + end + end +end From 088c6ef0889535c6196d3acf728aef857114af1e Mon Sep 17 00:00:00 2001 From: Nate Date: Sat, 30 Jan 2016 20:13:01 -0500 Subject: [PATCH 2/3] CreateGroup now validates all input correctly --- app/interactors/create_group.rb | 8 +++++++- spec/interactors/create_group_spec.rb | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/interactors/create_group.rb b/app/interactors/create_group.rb index a227321..de5a0b8 100644 --- a/app/interactors/create_group.rb +++ b/app/interactors/create_group.rb @@ -1,6 +1,6 @@ class CreateGroup < StandardInteraction def validate_input - context.fail!(errors: "invalid input") unless context.group_params + context.fail!(errors: "invalid input") unless valid_input? end def execute @@ -10,4 +10,10 @@ def execute def validate_output context.fail!(errors: context.group.errors) unless context.group.persisted? end + + private + + def valid_input? + context.group_params && context.user + end end diff --git a/spec/interactors/create_group_spec.rb b/spec/interactors/create_group_spec.rb index bde4426..b85c950 100644 --- a/spec/interactors/create_group_spec.rb +++ b/spec/interactors/create_group_spec.rb @@ -43,7 +43,7 @@ end end - context "when invalid input is provided" do + context "when group_params are not provided" do subject do described_class.call(group_params: nil) end @@ -56,5 +56,19 @@ expect(subject.errors).to eq("invalid input") end end + + context "when user is not provided" do + subject do + described_class.call(group_params: group_params, user: nil) + end + + it "fails" do + is_expected.to be_a_failure + end + + it "returns an error" do + expect(subject.errors).to eq("invalid input") + end + end end end From a67be0ea3427cc724621137e3c3944c11306295e Mon Sep 17 00:00:00 2001 From: Nate Date: Sat, 30 Jan 2016 20:25:16 -0500 Subject: [PATCH 3/3] remove old schema_path --- spec/acceptance/profiles_spec.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/acceptance/profiles_spec.rb b/spec/acceptance/profiles_spec.rb index 311be31..7e09344 100644 --- a/spec/acceptance/profiles_spec.rb +++ b/spec/acceptance/profiles_spec.rb @@ -9,10 +9,6 @@ Knock::AuthToken.new(payload: { sub: authenticated_user.id }).token end - def schema_path - "./config/schema/api.json" - end - shared_context "user parameters" do parameter "first_name", <<-DESC, scope: :user, required: true The first name of the user.