gems/aws-sdk-dynamodb/spec/client_spec.rb (217 lines of code) (raw):
# frozen_string_literal: true
require_relative 'spec_helper'
require 'zlib'
module Aws
module DynamoDB
describe Client do
let(:opts) {{
credentials: Credentials.new('akid', 'secret'),
region: 'us-west-2',
}}
let(:ddb) { Client.new(opts) }
describe ':simple_attributes' do
it 'simple attributes mode is enabled by default' do
expect(Client.new(opts).config.simple_attributes).to be(true)
end
it 'can be disabled' do
ddb = Client.new(opts.merge(simple_attributes: false))
expect(ddb.config.simple_attributes).to be(false)
end
it 'marshals given attribute values' do
ddb.handle(step: :send) do |context|
Seahorse::Client::Response.new(context: context)
end
resp = ddb.put_item(table_name: 'aws-sdk', item: { 'id' => 'guid' })
expect(resp.context.params).to eq(
table_name: 'aws-sdk',
item: {"id"=>{:s=>"guid"}}
)
end
it 'unmarshals returned attribute values' do
ddb.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body: '{ "Item": { "id": { "S": "guid" } } }'
)
Seahorse::Client::Response.new(context: context)
end
resp = ddb.get_item(table_name: 'aws-sdk', key: { 'id' => 'guid' })
expect(resp.data.item).to eq('id' => 'guid')
end
it 'unmarshals attribute values in errors' do
ddb.handle(step: :send) do |context|
context.http_response.signal_headers(400, {})
context.http_response.signal_data(<<-JSON)
{
"__type": "com.amazonaws.dynamodb.v20120810#ConditionalCheckFailedException",
"Message": "The conditional request failed.",
"Item": { "id": { "S": "guid" } }
}
JSON
context.http_response.signal_done
Seahorse::Client::Response.new(context: context)
end
expectation = proc do |error|
expect(error).to be_a(Errors::ConditionalCheckFailedException)
expect(error.data.item).to eq('id' => 'guid')
end
expect do
ddb.put_item(table_name: 'aws-sdk', item: { 'id' => 'guid' })
end.to raise_error(&expectation)
end
it 'avoids double-marshaling of structs' do
batch = {
"TableName" => [
{
put_request: {
item: {"attr-1-name" => "attr-1-value"}
}
}
]
}
stub = DynamoDB::Client.new(
stub_responses: {
batch_write_item: { unprocessed_items: batch }
}
)
wr = DynamoDB::Types::WriteRequest.new
pr = DynamoDB::Types::PutRequest.new
pr.item = { "attr-1-name" => "attr-1-value" }
wr.put_request = pr
expected = {
"TableName"=>[wr]
}
resp1 = stub.batch_write_item(request_items: batch)
expect(resp1.unprocessed_items).to eq(expected)
stub.batch_write_item(request_items: resp1.unprocessed_items)
# Expect no mutation.
expect(resp1.unprocessed_items).to eq(expected)
end
end
describe 'CRC32' do
it 'rejects responses with an invalid crc32 header' do
opts[:retry_limit] = 0
ddb.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: { 'x-amz-crc32' => 'invalid' },
body: '{ "Item": { "id": { "S": "guid" } } }'
)
Seahorse::Client::Response.new(context: context)
end
expect {
ddb.get_item(table_name: 'aws-sdk', key: { 'id' => 'guid' })
}.to raise_error(Errors::CRC32CheckFailed)
end
it 'accepts responses with a valid crc32 header' do
ddb.handle(step: :send) do |context|
context.http_response.status_code = 200
body = <<-JSON
{ "Item": { "id": { "S": "guid" } } }
JSON
context.http_response.body = body
context.http_response.headers['x-amz-crc32'] = Zlib.crc32(body)
Seahorse::Client::Response.new(context: context)
end
ddb.get_item(table_name: 'aws-sdk', key: { 'id' => 'guid' })
end
it 'retries crc32 failures' do
ddb.handle(step: :send) do |context|
context.http_response.status_code = 200
body = <<-JSON
{ "Item": { "id": { "S": "guid" } } }
JSON
context.http_response.body = body
if context[:already_failed]
context.http_response.headers['x-amz-crc32'] = Zlib.crc32(body)
else
context.http_response.headers['x-amz-crc32'] = 'invalid'
context[:already_failed] = true
end
Seahorse::Client::Response.new(context: context)
end
ddb.get_item(table_name: 'aws-sdk', key: { 'id' => 'guid' })
end
end
describe '#stub_responses' do
it 'accepts the simplified attribute format' do
client = Client.new(stub_responses: true)
client.stub_responses(:get_item, item: {'id' => 'value', })
resp = client.get_item(table_name:'table', key: {'id' => 'value' })
expect(resp.item).to eq('id' => 'value')
end
it 'observes the :simple_attributes configuration option' do
client = Client.new(stub_responses: true, simple_attributes: false)
client.stub_responses(:get_item, item: { 'id' => 'value' })
expect {
client.get_item(table_name: 'table', key: { 'id' => { s: 'value' }})
}.to raise_error(ArgumentError)
end
end
describe '#enumerable responses' do
it 'can round trip paging params that contain item attributes' do
client = Client.new(stub_responses: {
query: {
:items=> [
{"indexhash"=>"hash", "id"=>"id-1", "indexrange"=>"range-1"},
{"indexhash"=>"hash", "id"=>"id-2", "indexrange"=>"range-2"},
],
:count=>2,
:scanned_count=>2,
:last_evaluated_key=>{
"indexhash"=>"hash", "id"=>"id-160", "indexrange"=>"range-160"
}
}
})
result = client.query(
table_name: 'table-name',
limit: 2,
index_name: 'indexname',
key_condition_expression: 'indexhash = :value1',
expression_attribute_values: { ':value1' => 'hash'}
)
result2 = result.next_page
expect(result2.context.params[:expression_attribute_values]).to eq(
':value1' => { s:'hash'}
)
end
end
describe '#stub_data' do
it 'accepts and returns simple attributes' do
client = Client.new(stub_responses: true)
data = client.stub_data(:get_item, item: { 'id' => 'value' })
expect(data.item).to eq({ 'id' => 'value' })
end
it 'observes the :simple_attributes configuration option' do
client = Client.new(stub_responses: true, simple_attributes: false)
expect {
client.stub_data(:get_item, item: { 'id' => 'value' })
}.to raise_error(ArgumentError)
data = client.stub_data(:get_item, item: { 'id' => { s: 'value' }})
expect(data.to_h[:item]).to eq({ 'id' => { s: 'value' }})
end
it 'can be called without data' do
expect {
Client.new(stub_responses:true).stub_data(:get_item)
}.not_to raise_error
end
it 'parses errors from DynamoDB __type' do
client = Client.new(stub_responses: true)
client.handle(step: :send) do |context|
context.http_response.signal_headers(400, {})
context.http_response.signal_data(<<-JSON)
{
"__type": "com.amazonaws.dynamodb.v20120810#ResourceNotFoundException",
"Message": "Requested resource not found: Table: abc not found"
}
JSON
context.http_response.signal_done
Seahorse::Client::Response.new(context: context)
end
expect {
client.describe_table(table_name: 'abc')
}.to raise_error(Errors::ResourceNotFoundException)
end
end
end
end
end