fake_dynamo 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,10 +30,6 @@ module FakeDynamo
30
30
  raise UnknownOperationException, "Unknown operation: #{operation}" unless available_operations.include? operation
31
31
  end
32
32
 
33
- def available_operations
34
- api_config[:operations].keys
35
- end
36
-
37
33
  def validate_input(operation, data)
38
34
  api_input_spec(operation).each do |attribute, spec|
39
35
  validate_spec(attribute, data[attribute], spec, [])
@@ -71,7 +67,7 @@ module FakeDynamo
71
67
  when :within
72
68
  range = constrain[:within]
73
69
  unless range.include? data.size
74
- add_errors("The parameter '#{param(attribute, parents)}' value '#{data}' should be within #{range} characters")
70
+ add_errors("The parameter '#{param(attribute, parents)}' value '#{data}' should be within #{range}")
75
71
  end
76
72
  when :enum
77
73
  enum = constrain[:enum]
@@ -81,7 +77,7 @@ module FakeDynamo
81
77
  when :structure
82
78
  structure = constrain[:structure]
83
79
  structure.each do |attribute, spec|
84
- validate_spec(attribute, data[attribute], spec, new_parents)
80
+ validate_spec(attribute, data[attribute], spec, parents + ["member"])
85
81
  end
86
82
  when :map
87
83
  map = constrain[:map]
@@ -92,8 +88,8 @@ module FakeDynamo
92
88
  end
93
89
  when :list
94
90
  raise "#{param(attribute, parents)} must be a Array" unless data.kind_of? Array
95
- data.each do |element|
96
- validate_spec(element, element, constrain[:list], new_parents)
91
+ data.each_with_index do |element, i|
92
+ validate_spec(element, element, constrain[:list], new_parents + [(i+1).to_s])
97
93
  end
98
94
  else
99
95
  raise "Unhandled constraint #{constrain}"
@@ -111,7 +107,11 @@ module FakeDynamo
111
107
  end
112
108
 
113
109
  def api_input_spec(operation)
114
- api_config[:operations][operation][:input]
110
+ api_config[:operations].find { |spec| spec[:name] == operation }[:inputs]
111
+ end
112
+
113
+ def available_operations
114
+ @available_operations ||= api_config[:operations].map { |spec| spec[:name] }
115
115
  end
116
116
 
117
117
  def api_config
@@ -119,7 +119,7 @@ module FakeDynamo
119
119
  end
120
120
 
121
121
  def api_config_path
122
- File.join File.expand_path(File.dirname(__FILE__)), 'api.yml'
122
+ File.join File.expand_path(File.dirname(__FILE__)), 'api_2012-08-10.yml'
123
123
  end
124
124
 
125
125
  def validate_type(value, attribute)
@@ -144,21 +144,73 @@ module FakeDynamo
144
144
  end
145
145
 
146
146
  def validate_key_data(data, key_schema)
147
- validate_type(data['HashKeyElement'], key_schema.hash_key)
147
+ hash_key = data[key_schema.hash_key.name] or key_schema_mismatch
148
+ validate_type(hash_key, key_schema.hash_key)
148
149
 
149
150
  if key_schema.range_key
150
- range_key = data['RangeKeyElement'] or raise ValidationException, "Missing the key RangeKeyElement in the Key"
151
+ range_key = data[key_schema.range_key.name] or key_schema_mismatch
151
152
  validate_type(range_key, key_schema.range_key)
152
- elsif data['RangeKeyElement']
153
- raise ValidationException, "RangeKeyElement is not present in the schema"
153
+ key_schema_mismatch if data.size != 2
154
+ else
155
+ key_schema_mismatch if data.size != 1
154
156
  end
155
157
  end
156
158
 
159
+ def key_schema_mismatch
160
+ raise ValidationException, "The provided key element does not match the schema"
161
+ end
162
+
157
163
  def validate_request_size(data)
158
164
  if data.to_s.bytesize > 1 * 1024 * 1024
159
165
  raise ValidationException, "Request size can't exceed 1 mb"
160
166
  end
161
167
  end
162
168
 
169
+ def validate_range_key(key_schema)
170
+ unless key_schema.range_key
171
+ raise ValidationException, 'Table KeySchema does not have a range key'
172
+ end
173
+ end
174
+
175
+ def validate_hash_key(index, table)
176
+ if index.hash_key != table.hash_key
177
+ raise ValidationException, "Index KeySchema does not have the same leading hash key as table KeySchema for index"
178
+ end
179
+ end
180
+
181
+ def validate_projection(projection)
182
+ if projection.type == 'INCLUDE'
183
+ unless projection.non_key_attributes
184
+ raise ValidationException, "ProjectionType is #{projection.type}, but NonKeyAttributes is not specified"
185
+ end
186
+ else
187
+ if projection.non_key_attributes
188
+ raise ValidationException, "ProjectionType is #{projection.type}, but NonKeyAttributes is specified"
189
+ end
190
+ end
191
+ end
192
+
193
+ def validate_index_names(indexes)
194
+ names = indexes.map(&:name)
195
+ if names.uniq.size != names.size
196
+ raise ValidationException, "Duplicate index name: #{names.find { |n| names.count(n) > 1 }}"
197
+ end
198
+ end
199
+
200
+ def validate_hash_condition(condition)
201
+ unless condition
202
+ raise ValidationException, "Query condition missed key schema element #{key_schema.hash_key.name}"
203
+ end
204
+
205
+ if condition['ComparisonOperator'] != 'EQ'
206
+ raise ValidationException, "Query key condition not supported"
207
+ end
208
+ end
209
+
210
+ def validate_range_condition(condition, schema)
211
+ unless condition.has_key?(schema.range_key.name)
212
+ raise ValidationException, "Query condition missed key schema element #{schema.range_key.name}"
213
+ end
214
+ end
163
215
  end
164
216
  end
@@ -1,3 +1,3 @@
1
1
  module FakeDynamo
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/fake_dynamo.rb CHANGED
@@ -6,6 +6,8 @@ require 'active_support/core_ext/class/attribute'
6
6
  require 'fake_dynamo/exceptions'
7
7
  require 'fake_dynamo/validation'
8
8
  require 'fake_dynamo/filter'
9
+ require 'fake_dynamo/local_secondary_index'
10
+ require 'fake_dynamo/projection'
9
11
  require 'fake_dynamo/attribute'
10
12
  require 'fake_dynamo/key_schema'
11
13
  require 'fake_dynamo/item'
@@ -5,24 +5,107 @@ module FakeDynamo
5
5
  let(:data) do
6
6
  {
7
7
  "TableName" => "Table1",
8
+ "AttributeDefinitions" =>
9
+ [{"AttributeName" => "AttributeName1","AttributeType" => "S"},
10
+ {"AttributeName" => "AttributeName2","AttributeType" => "N"}],
8
11
  "KeySchema" =>
9
- {"HashKeyElement" => {"AttributeName" => "AttributeName1","AttributeType" => "S"},
10
- "RangeKeyElement" => {"AttributeName" => "AttributeName2","AttributeType" => "N"}},
12
+ [{"AttributeName" => "AttributeName1","KeyType" => "HASH"},
13
+ {"AttributeName" => "AttributeName2","KeyType" => "RANGE"}],
11
14
  "ProvisionedThroughput" => {"ReadCapacityUnits" => 5,"WriteCapacityUnits" => 10}
12
15
  }
13
16
  end
14
17
 
15
18
  let(:user_table) do
16
19
  {"TableName" => "User",
20
+ "AttributeDefinitions" =>
21
+ [{"AttributeName" => "id","AttributeType" => "S"}],
17
22
  "KeySchema" =>
18
- {"HashKeyElement" => {"AttributeName" => "id","AttributeType" => "S"}},
23
+ [{"AttributeName" => "id","KeyType" => "HASH"}],
19
24
  "ProvisionedThroughput" => {"ReadCapacityUnits" => 5,"WriteCapacityUnits" => 10}
20
25
  }
21
26
  end
22
27
 
23
- it 'should not allow to create duplicate tables' do
24
- subject.create_table(data)
25
- expect { subject.create_table(data) }.to raise_error(ResourceInUseException, /duplicate/i)
28
+ let(:user_table_a) do
29
+ {"TableName" => "User",
30
+ "AttributeDefinitions" =>
31
+ [{"AttributeName" => "id","AttributeType" => "S"},
32
+ {"AttributeName" => "age","AttributeType" => "S"},
33
+ {"AttributeName" => "name","AttributeType" => "S"}],
34
+ "KeySchema" =>
35
+ [{"AttributeName" => "id","KeyType" => "HASH"},
36
+ {"AttributeName" => "age", "KeyType" => "RANGE"}],
37
+ "LocalSecondaryIndexes" =>
38
+ [{"IndexName" => "age",
39
+ "KeySchema" =>
40
+ [{"AttributeName" => "id", "KeyType" => "HASH"},
41
+ {"AttributeName" => "name", "KeyType" => "RANGE"}],
42
+ "Projection" => {
43
+ "ProjectionType" => "INCLUDE",
44
+ "NonKeyAttributes" => ["name", "gender"]
45
+ }
46
+ }],
47
+ "ProvisionedThroughput" => {"ReadCapacityUnits" => 5,"WriteCapacityUnits" => 10}
48
+ }
49
+ end
50
+
51
+ context 'CreateTable' do
52
+ it 'should not allow to create duplicate tables' do
53
+ subject.create_table(data)
54
+ expect { subject.create_table(data) }.to raise_error(ResourceInUseException, /duplicate/i)
55
+ end
56
+
57
+ it 'should allow to create table with secondary indexes' do
58
+ subject.create_table(user_table_a)
59
+ end
60
+
61
+ it 'should fail on extra attribute' do
62
+ user_table_a['AttributeDefinitions'] << {"AttributeName" => "gender","AttributeType" => "S"}
63
+ expect { subject.create_table(user_table_a) }.to raise_error(ValidationException, /some attributedefinitions.*not.*used/i)
64
+ end
65
+
66
+ it 'should fail on missing attribute' do
67
+ user_table_a['AttributeDefinitions'].delete_at(1)
68
+ expect { subject.create_table(user_table_a) }.to raise_error(ValidationException, /some.*attributes.*not.*defined/i)
69
+ end
70
+
71
+ context 'LocalSecondaryIndex' do
72
+ let(:lsi) { user_table_a['LocalSecondaryIndexes'][0] }
73
+
74
+ it 'should fail on invalid KeyType' do
75
+ lsi["KeySchema"][0]['KeyType'] = 'invalid'
76
+ expect { subject.process('CreateTable', user_table_a) }.to raise_error(ValidationException, /invalid.*enum/)
77
+ end
78
+
79
+ it 'should fail if range key is missing' do
80
+ lsi['KeySchema'].delete_at(1)
81
+ expect { subject.create_table(user_table_a) }.to raise_error(ValidationException, /not.*range.*key/i)
82
+ end
83
+
84
+ it 'should fail on duplicate index names' do
85
+ duplicate = lsi.clone()
86
+ user_table_a['LocalSecondaryIndexes'] << duplicate
87
+ expect { subject.create_table(user_table_a) }.to raise_error(ValidationException, /duplicate index/i)
88
+ end
89
+
90
+ it 'should fail on different hash key' do
91
+ lsi['KeySchema'][0]['AttributeName'] = 'age'
92
+ expect { subject.create_table(user_table_a) }.to raise_error(ValidationException, /not have.*same.*hash key/i)
93
+ end
94
+
95
+ context 'projection' do
96
+ let(:projection) { user_table_a['LocalSecondaryIndexes'][0]['Projection'] }
97
+
98
+ it 'should fail if non key attributes are specified unnecessarily' do
99
+ projection['ProjectionType'] = 'KEYS_ONLY'
100
+ expect { subject.create_table(user_table_a) }.to raise_error(ValidationException, /NonKeyAttributes.*specified/i)
101
+ end
102
+
103
+ it 'should fail if non key attributes are not specified' do
104
+ projection.delete('NonKeyAttributes')
105
+ expect { subject.create_table(user_table_a) }.to raise_error(ValidationException, /NonKeyAttributes.*not.*specified/i)
106
+ end
107
+ end
108
+ end
26
109
  end
27
110
 
28
111
  it 'should fail on unknown operation' do
@@ -33,7 +116,7 @@ module FakeDynamo
33
116
  it 'should describe table' do
34
117
  table = subject.create_table(data)
35
118
  description = subject.describe_table({'TableName' => 'Table1'})
36
- description.should include({
119
+ description['Table'].should include({
37
120
  "ItemCount"=>0,
38
121
  "TableSizeBytes"=>0})
39
122
  end
@@ -134,22 +217,22 @@ module FakeDynamo
134
217
  subject.process('GetItem', {
135
218
  'TableName' => 'Table1',
136
219
  'Key' => {
137
- 'HashKeyElement' => { 'S' => 'test' },
138
- 'RangeKeyElement' => { 'N' => '11' }
220
+ 'AttributeName1' => { 'S' => 'test' },
221
+ 'AttributeName2' => { 'N' => '11' }
139
222
  },
140
223
  'AttributesToGet' => ['AttributeName3']
141
224
  })
142
225
  subject.process('DeleteItem', {
143
226
  'TableName' => 'Table1',
144
227
  'Key' => {
145
- 'HashKeyElement' => { 'S' => 'test' },
146
- 'RangeKeyElement' => { 'N' => '11' }
228
+ 'AttributeName1' => { 'S' => 'test' },
229
+ 'AttributeName2' => { 'N' => '11' }
147
230
  }})
148
231
  subject.process('UpdateItem', {
149
232
  'TableName' => 'Table1',
150
233
  'Key' => {
151
- 'HashKeyElement' => { 'S' => 'test' },
152
- 'RangeKeyElement' => { 'N' => '11' }
234
+ 'AttributeName1' => { 'S' => 'test' },
235
+ 'AttributeName2' => { 'N' => '11' }
153
236
  },
154
237
  'AttributeUpdates' =>
155
238
  {'AttributeName3' =>
@@ -163,10 +246,15 @@ module FakeDynamo
163
246
  'TableName' => 'Table1',
164
247
  'Limit' => 5,
165
248
  'Count' => true,
166
- 'HashKeyValue' => {'S' => 'att1'},
167
- 'RangeKeyCondition' => {
168
- 'AttributeValueList' => [{'N' => '1'}],
169
- 'ComparisonOperator' => 'GT'
249
+ 'KeyConditions' => {
250
+ 'AttributeName1' => {
251
+ 'AttributeValueList' => [{'S' => 'att1'}],
252
+ 'ComparisonOperator' => 'EQ'
253
+ },
254
+ 'AttributeName2' => {
255
+ 'AttributeValueList' => [{'N' => '1'}],
256
+ 'ComparisonOperator' => 'GT'
257
+ }
170
258
  },
171
259
  'ScanIndexForward' => true
172
260
  })
@@ -205,25 +293,22 @@ module FakeDynamo
205
293
  response = subject.process('BatchGetItem', { 'RequestItems' =>
206
294
  {
207
295
  'User' => {
208
- 'Keys' => [{ 'HashKeyElement' => { 'S' => '1' }},
209
- { 'HashKeyElement' => { 'S' => '2' }}]
296
+ 'Keys' => [{ 'id' => { 'S' => '1' }},
297
+ { 'id' => { 'S' => '2' }}]
210
298
  },
211
299
  'Table1' => {
212
- 'Keys' => [{'HashKeyElement' => { 'S' => 'test' },
213
- 'RangeKeyElement' => { 'N' => '11' }}],
300
+ 'Keys' => [{'AttributeName1' => { 'S' => 'test' },
301
+ 'AttributeName2' => { 'N' => '11' }}],
214
302
  'AttributesToGet' => ['AttributeName1', 'AttributeName2']
215
303
  }
216
304
  }})
217
305
 
218
306
  response.should eq({"Responses"=>
219
307
  {"User"=>
220
- {"ConsumedCapacityUnits"=>1,
221
- "Items"=>[{"id"=>{"S"=>"1"}}, {"id"=>{"S"=>"2"}}]},
308
+ [{"id"=>{"S"=>"1"}}, {"id"=>{"S"=>"2"}}],
222
309
  "Table1"=>
223
- {"ConsumedCapacityUnits"=>1,
224
- "Items"=>
225
- [{"AttributeName1"=>{"S"=>"test"},
226
- "AttributeName2"=>{"N"=>"11"}}]}},
310
+ [{"AttributeName1"=>{"S"=>"test"},
311
+ "AttributeName2"=>{"N"=>"11"}}]},
227
312
  "UnprocessedKeys"=>{}})
228
313
  end
229
314
 
@@ -231,15 +316,15 @@ module FakeDynamo
231
316
  response = subject.process('BatchGetItem', { 'RequestItems' =>
232
317
  {
233
318
  'User' => {
234
- 'Keys' => [{ 'HashKeyElement' => { 'S' => '1' }},
235
- { 'HashKeyElement' => { 'S' => 'asd' }}]
319
+ 'Keys' => [{ 'id' => { 'S' => '1' }},
320
+ { 'id' => { 'S' => 'asd' }}]
236
321
  }
237
- }})
322
+ },
323
+ 'ReturnConsumedCapacity' => 'TOTAL'})
238
324
  response.should eq({"Responses"=>
239
- {"User"=>
240
- {"ConsumedCapacityUnits"=>1,
241
- "Items"=>[{"id"=>{"S"=>"1"}}]}},
242
- "UnprocessedKeys"=>{}})
325
+ {"User"=> [{"id"=>{"S"=>"1"}}]},
326
+ "UnprocessedKeys"=>{},
327
+ "ConsumedCapacity" => ['CapacityUnits' => 1, 'TableName' => 'User']})
243
328
  end
244
329
 
245
330
  it 'should fail if table not found' do
@@ -247,8 +332,8 @@ module FakeDynamo
247
332
  subject.process('BatchGetItem', { 'RequestItems' =>
248
333
  {
249
334
  'xxx' => {
250
- 'Keys' => [{ 'HashKeyElement' => { 'S' => '1' }},
251
- { 'HashKeyElement' => { 'S' => 'asd' }}]}
335
+ 'Keys' => [{ 'AttributeName1' => { 'S' => '1' }},
336
+ { 'AttributeName1' => { 'S' => 'asd' }}]}
252
337
  }})
253
338
  }.to raise_error(FakeDynamo::ResourceNotFoundException)
254
339
  end
@@ -261,6 +346,8 @@ module FakeDynamo
261
346
  db
262
347
  end
263
348
 
349
+ let(:consumed_capacity) { {'ConsumedCapacity' => { 'CapacityUnits' => 1, 'TableName' => 'User' }} }
350
+
264
351
  it 'should validate payload' do
265
352
  expect {
266
353
  subject.process('BatchWriteItem', {})
@@ -271,7 +358,7 @@ module FakeDynamo
271
358
  expect {
272
359
  subject.process('BatchWriteItem', {
273
360
  'RequestItems' => {
274
- 'xxx' => ['DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}]
361
+ 'xxx' => ['DeleteRequest' => { 'Key' => { 'AttributeName1' => { 'S' => 'ananth' }}}]
275
362
  }
276
363
  })
277
364
  }.to raise_error(FakeDynamo::ResourceNotFoundException, /table.*not.*found/i)
@@ -281,8 +368,8 @@ module FakeDynamo
281
368
  expect {
282
369
  subject.process('BatchWriteItem', {
283
370
  'RequestItems' => {
284
- 'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}},
285
- { 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}}]
371
+ 'User' => [{ 'DeleteRequest' => { 'Key' => { 'id' => { 'S' => 'ananth' }}}},
372
+ { 'DeleteRequest' => { 'Key' => { 'id' => { 'S' => 'ananth' }}}}]
286
373
  }
287
374
  })
288
375
  }.to raise_error(FakeDynamo::ValidationException, /duplicate/i)
@@ -290,7 +377,7 @@ module FakeDynamo
290
377
  expect {
291
378
  subject.process('BatchWriteItem', {
292
379
  'RequestItems' => {
293
- 'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}},
380
+ 'User' => [{ 'DeleteRequest' => { 'Key' => { 'id' => { 'S' => 'ananth' }}}},
294
381
  {'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}}]
295
382
  }
296
383
  })
@@ -310,31 +397,38 @@ module FakeDynamo
310
397
  response = subject.process('BatchWriteItem', {
311
398
  'RequestItems' => {
312
399
  'User' => [{'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}}]
313
- }
400
+ },
401
+ 'ReturnConsumedCapacity' => 'TOTAL'
314
402
  })
315
-
316
- response['Responses'].should eq('User' => { 'ConsumedCapacityUnits' => 1 })
403
+ response['ItemCollectionMetrics'].should be_nil
404
+ response.should eq('ConsumedCapacity' => [consumed_capacity['ConsumedCapacity']],
405
+ 'UnprocessedItems' => {})
317
406
 
318
407
  response = subject.get_item({'TableName' => 'User',
319
- 'Key' => {'HashKeyElement' => { 'S' => 'ananth'}}})
408
+ 'Key' => {'id' => { 'S' => 'ananth'}}})
320
409
 
321
410
  response['Item']['id'].should eq('S' => 'ananth')
322
411
 
323
- subject.process('BatchWriteItem', {
324
- 'RequestItems' => {
325
- 'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}}]
326
- }
327
- })
412
+ response = subject.process('BatchWriteItem', {
413
+ 'RequestItems' => {
414
+ 'User' => [{ 'DeleteRequest' => { 'Key' => { 'id' => { 'S' => 'ananth' }}}}]
415
+ },
416
+ 'ReturnItemCollectionMetrics' => 'SIZE'
417
+ })
418
+
419
+ response['ItemCollectionMetrics'].should_not be_nil
328
420
 
329
421
  response = subject.get_item({'TableName' => 'User',
330
- 'Key' => {'HashKeyElement' => { 'S' => 'ananth'}}})
422
+ 'Key' => {'id' => { 'S' => 'ananth'}},
423
+ 'ReturnConsumedCapacity' => 'TOTAL'})
424
+
425
+ response.should eq(consumed_capacity)
331
426
 
332
- response.should eq({"ConsumedCapacityUnits"=>1})
333
427
  end
334
428
 
335
429
  it 'fails it the requested operation is more than 25' do
336
430
  expect {
337
- requests = (1..26).map { |i| { 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => "ananth#{i}" }}}} }
431
+ requests = (1..26).map { |i| { 'DeleteRequest' => { 'Key' => { 'id' => { 'S' => "ananth#{i}" }}}} }
338
432
 
339
433
  subject.process('BatchWriteItem', {
340
434
  'RequestItems' => {
@@ -342,7 +436,7 @@ module FakeDynamo
342
436
  }
343
437
  })
344
438
 
345
- }.to raise_error(FakeDynamo::ValidationException, /too many items/i)
439
+ }.to raise_error(FakeDynamo::ValidationException, /within.*25/i)
346
440
  end
347
441
 
348
442
  it 'should fail on request size greater than 1 mb' do
@@ -7,9 +7,12 @@ module FakeDynamo
7
7
  let(:data) do
8
8
  {
9
9
  "TableName" => "Table1",
10
+ "AttributeDefinitions" =>
11
+ [{"AttributeName" => "AttributeName1","AttributeType" => "S"},
12
+ {"AttributeName" => "AttributeName2","AttributeType" => "N"}],
10
13
  "KeySchema" =>
11
- {"HashKeyElement" => {"AttributeName" => "AttributeName1","AttributeType" => "S"},
12
- "RangeKeyElement" => {"AttributeName" => "AttributeName2","AttributeType" => "N"}},
14
+ [{"AttributeName" => "AttributeName1","KeyType" => "HASH"},
15
+ {"AttributeName" => "AttributeName2","KeyType" => "RANGE"}],
13
16
  "ProvisionedThroughput" => {"ReadCapacityUnits" => 5,"WriteCapacityUnits" => 10}
14
17
  }
15
18
  end
@@ -6,8 +6,10 @@ module FakeDynamo
6
6
 
7
7
  let(:table) do
8
8
  {"TableName" => "User",
9
+ "AttributeDefinitions" =>
10
+ [{"AttributeName" => "id","AttributeType" => "S"}],
9
11
  "KeySchema" =>
10
- {"HashKeyElement" => {"AttributeName" => "id","AttributeType" => "S"}},
12
+ [{"AttributeName" => "id","KeyType" => "HASH"}],
11
13
  "ProvisionedThroughput" => {"ReadCapacityUnits" => 5,"WriteCapacityUnits" => 10}
12
14
  }
13
15
  end