hensei-api/spec/requests/parties_controller_spec.rb
Justin Edmund d6300f7aeb
Add first round of tests (#178)
* Install Rspec

* Create .aidigestignore

* Update rails_helper

- Added sections and comments
- Add support for loading via canonical.rb
- Add FactoryBot syntax methods
- Disable SQL logging in test environment

* Move gems around

* Add canonical.rb and test env CSVs

We load these CSVs via canonical.rb when we run tests as a data source for canonical objects.

* Remove RBS for now

This is too much and we need to find the right solution

* Refactor GridSummonsController and add tests

* Create GridSummon factory

* Refactor GridSummon and add documentation and tests

* Create have_error_on.rb

* Update .aidigestignore

* Fix warnings

* Add GridWeapons and Parties factories

* Refactor GridWeapon and add documentation and tests

* Create .rubocop.yml

* Create no_weapon_provided_error.rb

* Refactor GridWeaponsController

- Refactors controller
- Adds YARD documentation
- Adds Rspec tests

* Refactor GridSummonsController

- Refactors controller
- Adds YARD documentation
- Adds Rspec tests

* Enable shoulda/matchers

* Update User factory

* Update party.rb

We moved updating the party's element and extra flag to inside the party. We use an after_commit hook to minimize the amount of queries we're running to do this.

* Update party.rb

We change setting the edit key to use the conditional assignment operator so that it doesn't get overridden when we're running tests. This shouldn't have an effect in production.

* Update api_controller.rb

Change render_unprocessable_entity_response to render the errors hash instead of the exception so that we get more helpful errors.

* Add new errors

Added NoCharacterProvidedError and NoSummonProvidedError

* Add tests and docs to GridCharacter

We added a factory, spec and documentation to the GridCharacter model

* Ensure numericality

* Move enums into GranblueEnums

We don't use these yet, but it gives us a structured place to pull them from.

* Refactor GridCharactersController

- Refactors controller
- Adds YARD documentation
- Adds Rspec tests

* Add debug hook and other small changes

* Update grid_characters_controller.rb

Removes logs

* Update .gitignore

* Update .aidigestignore

* Refactored PartiesController

- Split PartiesController into three concerns
- Implemented testing for PartiesController and two concerns
- Implemented fixes across other files to ensure PartiesController tests pass
- Added Favorites factory

* Implement SimpleCov

* Refactor Party model

- Refactors Party model
- Adds tests
- Adds documentation

* Update granblue_enums.rb

Remove included block
2025-02-12 02:42:30 -08:00

205 lines
7.6 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Parties API', type: :request do
let(:user) { create(:user) }
let(:access_token) do
Doorkeeper::AccessToken.create!(resource_owner_id: user.id, expires_in: 30.days, scopes: 'public')
end
let(:headers) do
{ 'Authorization' => "Bearer #{access_token.token}", 'Content-Type' => 'application/json' }
end
describe 'POST /api/v1/parties' do
context 'with valid attributes' do
let(:valid_attributes) do
{
party: {
name: 'Test Party',
description: 'A party for testing',
raid_id: nil,
visibility: 1,
full_auto: false,
auto_guard: false,
charge_attack: true,
clear_time: 500,
button_count: 3,
turn_count: 4,
chain_count: 2
}
}
end
it 'creates a new party and returns status created' do
expect do
post '/api/v1/parties', params: valid_attributes.to_json, headers: headers
end.to change(Party, :count).by(1)
expect(response).to have_http_status(:created)
json = JSON.parse(response.body)
expect(json['party']['name']).to eq('Test Party')
end
end
end
describe 'GET /api/v1/parties/:id' do
let!(:party) { create(:party, user: user, name: 'Visible Party', visibility: 1) }
context 'when the party is public or owned' do
it 'returns the party details' do
get "/api/v1/parties/#{party.shortcode}", headers: headers
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json['party']['name']).to eq('Visible Party')
end
end
context 'when the party is private and not owned' do
let!(:private_party) { create(:party, user: create(:user), visibility: 3, name: 'Private Party') }
it 'returns unauthorized' do
get "/api/v1/parties/#{private_party.shortcode}", headers: headers
expect(response).to have_http_status(:unauthorized)
end
end
end
describe 'PUT /api/v1/parties/:id' do
let!(:party) { create(:party, user: user, name: 'Old Name') }
let(:update_attributes) do
{ party: { name: 'New Name', description: 'Updated description' } }
end
it 'updates the party and returns the updated party' do
put "/api/v1/parties/#{party.id}", params: update_attributes.to_json, headers: headers
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json['party']['name']).to eq('New Name')
expect(json['party']['description']).to eq('Updated description')
end
end
describe 'DELETE /api/v1/parties/:id' do
let!(:party) { create(:party, user: user) }
it 'destroys the party and returns the destroyed party view' do
delete "/api/v1/parties/#{party.id}", headers: headers
expect(response).to have_http_status(:ok)
expect { party.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe 'POST /api/v1/parties/:id/remix' do
let!(:party) { create(:party, user: user, name: 'Original Party') }
let(:remix_params) { { party: { local_id: party.local_id } } }
it 'creates a remixed copy of the party' do
post "/api/v1/parties/#{party.shortcode}/remix", params: remix_params.to_json, headers: headers
expect(response).to have_http_status(:created)
json = JSON.parse(response.body)
expect(json['party']['source_party']['id']).to eq(party.id)
end
end
describe 'GET /api/v1/parties' do
before { create_list(:party, 3, user: user, visibility: 1) }
it 'lists parties with pagination' do
get '/api/v1/parties', headers: headers
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json['results']).to be_an(Array)
expect(json['meta']).to have_key('count')
end
end
describe 'GET /api/v1/parties/favorites' do
let(:other_user) { create(:user) }
let!(:party) { create(:party, user: other_user, visibility: 1) }
before do
# Create associated records so that the party meets the default filtering minimums:
# - At least 3 characters,
# - At least 5 weapons,
# - At least 2 summons.
create_list(:grid_character, 3, party: party)
create_list(:grid_weapon, 5, party: party)
create_list(:grid_summon, 2, party: party)
party.reload # Reload to update counter caches.
ap "DEBUG: Party counts - characters: #{party.characters_count}, weapons: #{party.weapons_count}, summons: #{party.summons_count}"
create(:favorite, user: user, party: party)
end
before { create(:favorite, user: user, party: party) }
it 'lists parties favorited by the current user' do
# Debug: print IDs returned by the join query (this code can be removed later)
favorite_ids = Party.joins(:favorites).where(favorites: { user_id: user.id }).distinct.pluck(:id)
ap "DEBUG: Created party id: #{party.id}"
ap "DEBUG: Favorite party ids: #{favorite_ids.inspect}"
get '/api/v1/parties/favorites', headers: headers
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json['results']).not_to be_empty
expect(json['results'].first).to include('favorited' => true)
end
end
describe 'Preview Management Endpoints' do
let(:user) { create(:user) }
let!(:party) { create(:party, user: user, shortcode: 'PREV01', element: 'default') }
let(:headers) do
{ 'Authorization' => "Bearer #{Doorkeeper::AccessToken.create!(resource_owner_id: user.id, expires_in: 30.days, scopes: 'public').token}",
'Content-Type' => 'application/json' }
end
describe 'GET /api/v1/parties/:id/preview' do
before do
# Stub send_file on the correctly namespaced controller.
allow_any_instance_of(Api::V1::PartiesController).to receive(:send_file) do |instance, *args|
instance.render plain: 'dummy image content', content_type: 'image/png', status: 200
end
end
it 'serves the preview image (returns 200)' do
get "/api/v1/parties/#{party.shortcode}/preview", headers: headers
expect(response).to have_http_status(200)
expect(response.content_type).to eq('image/png; charset=utf-8')
expect(response.body).to eq('dummy image content')
end
end
describe 'GET /api/v1/parties/:id/preview_status' do
it 'returns the preview status of the party' do
get "/api/v1/parties/#{party.shortcode}/preview_status", headers: headers
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json).to have_key('state')
end
end
describe 'POST /api/v1/parties/:id/regenerate_preview' do
it 'forces preview regeneration when requested by the owner' do
post "/api/v1/parties/#{party.shortcode}/regenerate_preview", headers: headers
expect(response.status).to(satisfy { |s| [200, 422].include?(s) })
end
end
end
# Debug block: prints debug info if an example fails.
after(:each) do |example|
if example.exception && defined?(response) && response.present?
error_message = begin
JSON.parse(response.body)['exception']
rescue JSON::ParserError
response.body
end
puts "\nDEBUG: Error Message for '#{example.full_description}': #{error_message}"
# Parse once and grab the trace safely
parsed_body = JSON.parse(response.body)
trace = parsed_body.dig('traces', 'Application Trace')
ap trace if trace # Only print if trace is not nil
end
end
end