# frozen_string_literal: true require 'swagger_helper' RSpec.describe 'Places API', type: :request do path '/api/v1/places' do get 'Retrieves all places for the authenticated user' do tags 'Places' produces 'application/json' parameter name: :api_key, in: :query, type: :string, required: true, description: 'API key for authentication' parameter name: :tag_ids, in: :query, type: :array, items: { type: :integer }, required: false, description: 'Filter places by tag IDs' response '200', 'places found' do schema type: :array, items: { type: :object, properties: { id: { type: :integer }, name: { type: :string }, latitude: { type: :number, format: :float }, longitude: { type: :number, format: :float }, source: { type: :string }, icon: { type: :string, nullable: true }, color: { type: :string, nullable: true }, visits_count: { type: :integer }, created_at: { type: :string, format: 'date-time' }, tags: { type: :array, items: { type: :object, properties: { id: { type: :integer }, name: { type: :string }, icon: { type: :string }, color: { type: :string } } } } }, required: %w[id name latitude longitude] } let(:user) { create(:user) } let(:api_key) { user.api_key } let!(:place) { create(:place, user: user) } run_test! do |response| data = JSON.parse(response.body) expect(data).to be_an(Array) expect(data.first['id']).to eq(place.id) end end response '401', 'unauthorized' do let(:api_key) { 'invalid' } run_test! end end post 'Creates a place' do tags 'Places' consumes 'application/json' produces 'application/json' parameter name: :api_key, in: :query, type: :string, required: true, description: 'API key for authentication' parameter name: :place, in: :body, schema: { type: :object, properties: { name: { type: :string }, latitude: { type: :number, format: :float }, longitude: { type: :number, format: :float }, source: { type: :string }, tag_ids: { type: :array, items: { type: :integer } } }, required: %w[name latitude longitude] } response '201', 'place created' do schema type: :object, properties: { id: { type: :integer }, name: { type: :string }, latitude: { type: :number, format: :float }, longitude: { type: :number, format: :float }, source: { type: :string }, icon: { type: :string, nullable: true }, color: { type: :string, nullable: true }, visits_count: { type: :integer }, created_at: { type: :string, format: 'date-time' }, tags: { type: :array } } let(:user) { create(:user) } let(:tag) { create(:tag, user: user) } let(:api_key) { user.api_key } let(:place) do { name: 'Coffee Shop', latitude: 40.7589, longitude: -73.9851, source: 'manual', tag_ids: [tag.id] } end run_test! do |response| data = JSON.parse(response.body) expect(data['name']).to eq('Coffee Shop') # Note: tags array is expected to be in the response schema but may be empty initially # Tags can be added separately via the update endpoint expect(data).to have_key('tags') end end response '422', 'invalid request' do let(:user) { create(:user) } let(:api_key) { user.api_key } let(:place) { { name: '' } } run_test! end response '401', 'unauthorized' do let(:api_key) { 'invalid' } let(:place) { { name: 'Test', latitude: 40.0, longitude: -73.0 } } run_test! end end end path '/api/v1/places/nearby' do get 'Searches for nearby places using Photon geocoding API' do tags 'Places' produces 'application/json' parameter name: :api_key, in: :query, type: :string, required: true, description: 'API key for authentication' parameter name: :latitude, in: :query, type: :number, format: :float, required: true, description: 'Latitude coordinate' parameter name: :longitude, in: :query, type: :number, format: :float, required: true, description: 'Longitude coordinate' parameter name: :radius, in: :query, type: :number, format: :float, required: false, description: 'Search radius in kilometers (default: 0.5)' parameter name: :limit, in: :query, type: :integer, required: false, description: 'Maximum number of results (default: 10)' response '200', 'nearby places found' do schema type: :object, properties: { places: { type: :array, items: { type: :object, properties: { name: { type: :string }, latitude: { type: :number, format: :float }, longitude: { type: :number, format: :float }, distance: { type: :number, format: :float }, type: { type: :string } } } } } let(:user) { create(:user) } let(:api_key) { user.api_key } let(:latitude) { 40.7589 } let(:longitude) { -73.9851 } let(:radius) { 1.0 } let(:limit) { 5 } run_test! do |response| data = JSON.parse(response.body) expect(data).to have_key('places') expect(data['places']).to be_an(Array) end end response '401', 'unauthorized' do let(:api_key) { 'invalid' } let(:latitude) { 40.7589 } let(:longitude) { -73.9851 } run_test! end end end path '/api/v1/places/{id}' do parameter name: :id, in: :path, type: :integer, description: 'Place ID' get 'Retrieves a specific place' do tags 'Places' produces 'application/json' parameter name: :api_key, in: :query, type: :string, required: true, description: 'API key for authentication' response '200', 'place found' do schema type: :object, properties: { id: { type: :integer }, name: { type: :string }, latitude: { type: :number, format: :float }, longitude: { type: :number, format: :float }, source: { type: :string }, icon: { type: :string, nullable: true }, color: { type: :string, nullable: true }, visits_count: { type: :integer }, created_at: { type: :string, format: 'date-time' }, tags: { type: :array } } let(:user) { create(:user) } let(:api_key) { user.api_key } let(:place) { create(:place, user: user) } let(:id) { place.id } run_test! do |response| data = JSON.parse(response.body) expect(data['id']).to eq(place.id) end end response '404', 'place not found' do let(:user) { create(:user) } let(:api_key) { user.api_key } let(:id) { 'invalid' } run_test! end response '401', 'unauthorized' do let(:api_key) { 'invalid' } let(:place) { create(:place) } let(:id) { place.id } run_test! end end patch 'Updates a place' do tags 'Places' consumes 'application/json' produces 'application/json' parameter name: :api_key, in: :query, type: :string, required: true, description: 'API key for authentication' parameter name: :place, in: :body, schema: { type: :object, properties: { name: { type: :string }, latitude: { type: :number, format: :float }, longitude: { type: :number, format: :float }, tag_ids: { type: :array, items: { type: :integer } } } } response '200', 'place updated' do schema type: :object, properties: { id: { type: :integer }, name: { type: :string }, latitude: { type: :number, format: :float }, longitude: { type: :number, format: :float }, tags: { type: :array } } let(:user) { create(:user) } let(:api_key) { user.api_key } let(:existing_place) { create(:place, user: user) } let(:id) { existing_place.id } let(:place) { { name: 'Updated Name' } } run_test! do |response| data = JSON.parse(response.body) expect(data['name']).to eq('Updated Name') end end response '404', 'place not found' do let(:user) { create(:user) } let(:api_key) { user.api_key } let(:id) { 'invalid' } let(:place) { { name: 'Updated' } } run_test! end response '422', 'invalid request' do let(:user) { create(:user) } let(:api_key) { user.api_key } let(:existing_place) { create(:place, user: user) } let(:id) { existing_place.id } let(:place) { { name: '' } } run_test! end response '401', 'unauthorized' do let(:api_key) { 'invalid' } let(:existing_place) { create(:place) } let(:id) { existing_place.id } let(:place) { { name: 'Updated' } } run_test! end end delete 'Deletes a place' do tags 'Places' produces 'application/json' parameter name: :api_key, in: :query, type: :string, required: true, description: 'API key for authentication' response '204', 'place deleted' do let(:user) { create(:user) } let(:api_key) { user.api_key } let(:place) { create(:place, user: user) } let(:id) { place.id } run_test! end response '404', 'place not found' do let(:user) { create(:user) } let(:api_key) { user.api_key } let(:id) { 'invalid' } run_test! end response '401', 'unauthorized' do let(:api_key) { 'invalid' } let(:place) { create(:place) } let(:id) { place.id } run_test! end end end end