RSpec.describe ROM::HTTP::Relation do let(:relation_klass) do Class.new(ROM::HTTP::Relation) do schema do attribute :id, ROM::Types::Strict::Int attribute :name, ROM::Types::Strict::String end end end let(:relation) { relation_klass.new(dataset) } let(:dataset) { ROM::HTTP::Dataset.new({ name: 'test' }, {}) } let(:data) do [ { id: 1, name: 'John' }, { id: 2, name: 'Jill' } ] end before do allow(dataset).to receive(:response).and_return(data) end describe '#primary_key' do subject { relation.primary_key } context 'with no primary key defined in schema' do it 'defaults to :id' do is_expected.to eq(:id) end end context 'with primary key defined in schema' do context 'without alias' do let(:relation_klass) do Class.new(ROM::HTTP::Relation) do schema do attribute :id, ROM::Types::Strict::Int attribute :name, ROM::Types::Strict::String.meta(primary_key: true) end end end it 'returns the attribute name of the primary key' do is_expected.to eq(:name) end end context 'with alias' do let(:relation_klass) do Class.new(ROM::HTTP::Relation) do schema do attribute :id, ROM::Types::Strict::Int.meta(primary_key: true, alias: :ident) attribute :name, ROM::Types::Strict::String end end end it 'returns the attribute name of the primary key' do is_expected.to eq(:ident) end end end end describe '#project' do subject { relation.project(:id).to_a } it 'returns the projected data' do is_expected.to match_array([ { id: 1 }, { id: 2 } ]) end end describe '#exclude' do subject { relation.exclude(:id).to_a } it 'returns the data with specified keys excluded' do is_expected.to match_array([ { name: 'John' }, { name: 'Jill' } ]) end end describe '#rename' do subject { relation.rename(id: :identity).to_a } it 'returns the data with keys renamed according to mapping' do is_expected.to match_array([ { name: 'John', identity: 1 }, { name: 'Jill', identity: 2 } ]) end end describe '#prefix' do subject { relation.prefix('user').to_a } it 'returns the data with prefixed keys' do is_expected.to match_array([ { user_id: 1, user_name: 'John' }, { user_id: 2, user_name: 'Jill' } ]) end end describe '#wrap' do context 'when called without a prefix' do subject { relation.wrap.to_a } it 'returns the data with keys prefixed by dataset name' do is_expected.to match_array([ { test_id: 1, test_name: 'John' }, { test_id: 2, test_name: 'Jill' } ]) end end context 'when called with a prefix' do subject { relation.wrap('user').to_a } it 'returns the data with keys prefixed by the given prefix' do is_expected.to match_array([ { user_id: 1, user_name: 'John' }, { user_id: 2, user_name: 'Jill' } ]) end end end describe '#to_a' do subject { relation.to_a } context 'with standard schema' do let(:relation_klass) do Class.new(ROM::HTTP::Relation) do schema do attribute :id, ROM::Types::Strict::Int end end end it 'applies the schema and returns the materialized results' do is_expected.to match_array([ { id: 1 }, { id: 2 } ]) end end context 'with aliased schema' do let(:relation_klass) do Class.new(ROM::HTTP::Relation) do schema do attribute :id, ROM::Types::Strict::Int attribute :name, ROM::Types::Strict::String.meta(alias: :username) end end end it 'applies the schema and returns the materialized results' do is_expected.to match_array([ { id: 1, username: 'John' }, { id: 2, username: 'Jill' } ]) end end end %i[insert update].each do |method_name| describe "##{method_name}" do subject { relation.send(method_name, name: 'John') } before do allow(dataset).to receive(method_name).and_return(data) end context 'with standard schema' do let(:relation_klass) do Class.new(ROM::HTTP::Relation) do schema do attribute :id, ROM::Types::Strict::Int end end end context 'when respond with single tuple' do let(:data) { { id: 1, name: 'John' } } it 'applies the schema and returns the materialized results' do is_expected.to eq(id: 1) end end context 'when respond with multiple tuples' do let(:data) do [ { id: 1, name: 'John' }, { id: 2, name: 'Jill' } ] end it 'applies the schema and returns the materialized results' do is_expected.to match_array([ { id: 1 }, { id: 2 } ]) end end end context 'with aliased schema' do let(:relation_klass) do Class.new(ROM::HTTP::Relation) do schema do attribute :id, ROM::Types::Strict::Int attribute :name, ROM::Types::Strict::String.meta(alias: :username) end end end context 'when respond with single tuple' do let(:data) { { id: 1, name: 'John' } } it 'applies the schema and returns the materialized results' do is_expected.to eq(id: 1, username: 'John') end end context 'when respond with multiple tuples' do let(:data) do [ { id: 1, name: 'John' }, { id: 2, name: 'Jill' } ] end it 'applies the schema and returns the materialized results' do is_expected.to match_array([ { id: 1, username: 'John' }, { id: 2, username: 'Jill' } ]) end end end end end end