propel_api 0.1.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +59 -0
- data/LICENSE +21 -0
- data/README.md +320 -0
- data/Rakefile +36 -0
- data/lib/generators/propel_api/USAGE +8 -0
- data/lib/generators/propel_api/controller/controller_generator.rb +208 -0
- data/lib/generators/propel_api/core/base.rb +19 -0
- data/lib/generators/propel_api/core/configuration_methods.rb +187 -0
- data/lib/generators/propel_api/core/named_base.rb +457 -0
- data/lib/generators/propel_api/core/path_generation_methods.rb +45 -0
- data/lib/generators/propel_api/core/relationship_inferrer.rb +117 -0
- data/lib/generators/propel_api/install/install_generator.rb +343 -0
- data/lib/generators/propel_api/resource/resource_generator.rb +433 -0
- data/lib/generators/propel_api/templates/config/propel_api.rb.tt +149 -0
- data/lib/generators/propel_api/templates/controllers/api_controller_graphiti.rb +79 -0
- data/lib/generators/propel_api/templates/controllers/api_controller_propel_facets.rb +76 -0
- data/lib/generators/propel_api/templates/controllers/example_controller.rb.tt +96 -0
- data/lib/generators/propel_api/templates/scaffold/facet_controller_template.rb.tt +80 -0
- data/lib/generators/propel_api/templates/scaffold/facet_model_template.rb.tt +141 -0
- data/lib/generators/propel_api/templates/scaffold/graphiti_controller_template.rb.tt +82 -0
- data/lib/generators/propel_api/templates/scaffold/graphiti_model_template.rb.tt +32 -0
- data/lib/generators/propel_api/templates/seeds/seeds_template.rb.tt +493 -0
- data/lib/generators/propel_api/templates/tests/controller_test_template.rb.tt +485 -0
- data/lib/generators/propel_api/templates/tests/fixtures_template.yml.tt +250 -0
- data/lib/generators/propel_api/templates/tests/integration_test_template.rb.tt +487 -0
- data/lib/generators/propel_api/templates/tests/model_test_template.rb.tt +252 -0
- data/lib/generators/propel_api/unpack/unpack_generator.rb +304 -0
- data/lib/propel_api.rb +3 -0
- metadata +95 -0
@@ -0,0 +1,487 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@organization = organizations(:one)
|
9
|
+
@user = users(:one)
|
10
|
+
@<%= singular_table_name %> = <%= table_name %>(:one)
|
11
|
+
@token = @user.generate_jwt_token
|
12
|
+
@auth_headers = { 'Authorization' => "Bearer #{@token}" }
|
13
|
+
|
14
|
+
# Ensure test <%= singular_table_name %> belongs to test user's organization
|
15
|
+
@<%= singular_table_name %>.update!(organization: @organization)
|
16
|
+
end
|
17
|
+
|
18
|
+
# === FULL CRUD WORKFLOW TESTS ===
|
19
|
+
|
20
|
+
test "complete <%= singular_table_name %> lifecycle workflow" do
|
21
|
+
# Step 1: List empty resources
|
22
|
+
get <%= api_route_helper %>_url, headers: @auth_headers
|
23
|
+
assert_response :success
|
24
|
+
|
25
|
+
initial_response = JSON.parse(response.body)
|
26
|
+
initial_count = initial_response['data'].size
|
27
|
+
|
28
|
+
# Step 2: Create new <%= singular_table_name %>
|
29
|
+
new_<%= singular_table_name %>_params = {
|
30
|
+
<% attributes.each_with_index do |attribute, index| -%>
|
31
|
+
<% if attribute.type == :references -%>
|
32
|
+
<%= attribute.name %>_id: @<%= attribute.name %>.id<%= ',' if index < attributes.length - 1 %>
|
33
|
+
<% elsif attribute.type == :string -%>
|
34
|
+
<%= attribute.name %>: "Workflow Test <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
35
|
+
<% elsif attribute.type == :text -%>
|
36
|
+
<%= attribute.name %>: "Workflow test <%= attribute.name.humanize %> content"<%= ',' if index < attributes.length - 1 %>
|
37
|
+
<% elsif attribute.type == :integer -%>
|
38
|
+
<%= attribute.name %>: 100<%= ',' if index < attributes.length - 1 %>
|
39
|
+
<% elsif attribute.type == :boolean -%>
|
40
|
+
<%= attribute.name %>: true<%= ',' if index < attributes.length - 1 %>
|
41
|
+
<% elsif attribute.type == :decimal || attribute.type == :float -%>
|
42
|
+
<%= attribute.name %>: 100.50<%= ',' if index < attributes.length - 1 %>
|
43
|
+
<% else -%>
|
44
|
+
<%= attribute.name %>: "workflow_test"<%= ',' if index < attributes.length - 1 %>
|
45
|
+
<% end -%>
|
46
|
+
<% end -%>
|
47
|
+
}
|
48
|
+
|
49
|
+
post <%= api_route_helper %>_url,
|
50
|
+
params: { data: new_<%= singular_table_name %>_params },
|
51
|
+
headers: @auth_headers
|
52
|
+
|
53
|
+
assert_response :created
|
54
|
+
create_response = JSON.parse(response.body)
|
55
|
+
created_<%= singular_table_name %> = create_response['data']
|
56
|
+
created_id = created_<%= singular_table_name %>['id']
|
57
|
+
|
58
|
+
# Verify created <%= singular_table_name %> data
|
59
|
+
assert_equal @organization.id, created_<%= singular_table_name %>['organization']['id']
|
60
|
+
assert_equal @user.id, created_<%= singular_table_name %>['user']['id']
|
61
|
+
<% attributes.each do |attribute| -%>
|
62
|
+
<% unless attribute.type == :references -%>
|
63
|
+
<% if attribute.type == :string -%>
|
64
|
+
assert_equal "Workflow Test <%= attribute.name.humanize %>", created_<%= singular_table_name %>['<%= attribute.name %>']
|
65
|
+
<% elsif attribute.type == :text -%>
|
66
|
+
assert_equal "Workflow test <%= attribute.name.humanize %> content", created_<%= singular_table_name %>['<%= attribute.name %>']
|
67
|
+
<% elsif attribute.type == :integer -%>
|
68
|
+
assert_equal 100, created_<%= singular_table_name %>['<%= attribute.name %>']
|
69
|
+
<% elsif attribute.type == :boolean -%>
|
70
|
+
assert_equal true, created_<%= singular_table_name %>['<%= attribute.name %>']
|
71
|
+
<% elsif attribute.type == :decimal || attribute.type == :float -%>
|
72
|
+
assert_equal 100.5, created_<%= singular_table_name %>['<%= attribute.name %>']
|
73
|
+
<% else -%>
|
74
|
+
assert_equal "workflow_test", created_<%= singular_table_name %>['<%= attribute.name %>']
|
75
|
+
<% end -%>
|
76
|
+
<% end -%>
|
77
|
+
<% end -%>
|
78
|
+
|
79
|
+
# Step 3: Verify list now includes new <%= singular_table_name %>
|
80
|
+
get <%= api_route_helper %>_url, headers: @auth_headers
|
81
|
+
assert_response :success
|
82
|
+
|
83
|
+
list_response = JSON.parse(response.body)
|
84
|
+
assert_equal initial_count + 1, list_response['data'].size
|
85
|
+
|
86
|
+
<%= table_name %>_ids = list_response['data'].map { |item| item['id'] }
|
87
|
+
assert_includes <%= table_name %>_ids, created_id
|
88
|
+
|
89
|
+
# Step 4: Retrieve specific <%= singular_table_name %>
|
90
|
+
get <%= api_route_helper %>_url(created_id), headers: @auth_headers
|
91
|
+
assert_response :success
|
92
|
+
|
93
|
+
show_response = JSON.parse(response.body)
|
94
|
+
show_<%= singular_table_name %> = show_response['data']
|
95
|
+
assert_equal created_id, show_<%= singular_table_name %>['id']
|
96
|
+
|
97
|
+
# Step 5: Update the <%= singular_table_name %>
|
98
|
+
<% if attributes.any? { |attr| attr.type == :string && attr.name != "organization" && attr.name != "user" } -%>
|
99
|
+
<% string_attr = attributes.find { |attr| attr.type == :string && attr.name != "organization" && attr.name != "user" } -%>
|
100
|
+
update_params = { <%= string_attr.name %>: "Updated Workflow <%= string_attr.name.humanize %>" }
|
101
|
+
|
102
|
+
patch <%= api_route_helper %>_url(created_id),
|
103
|
+
params: { data: update_params },
|
104
|
+
headers: @auth_headers
|
105
|
+
|
106
|
+
assert_response :success
|
107
|
+
|
108
|
+
update_response = JSON.parse(response.body)
|
109
|
+
updated_<%= singular_table_name %> = update_response['data']
|
110
|
+
assert_equal "Updated Workflow <%= string_attr.name.humanize %>", updated_<%= singular_table_name %>['<%= string_attr.name %>']
|
111
|
+
<% else -%>
|
112
|
+
# Update test - customize based on your model's attributes
|
113
|
+
patch <%= api_route_helper %>_url(created_id),
|
114
|
+
params: { data: new_<%= singular_table_name %>_params },
|
115
|
+
headers: @auth_headers
|
116
|
+
|
117
|
+
assert_response :success
|
118
|
+
<% end -%>
|
119
|
+
|
120
|
+
# Step 6: Delete the <%= singular_table_name %>
|
121
|
+
delete <%= api_route_helper %>_url(created_id), headers: @auth_headers
|
122
|
+
assert_response :no_content
|
123
|
+
|
124
|
+
# Step 7: Verify <%= singular_table_name %> is gone
|
125
|
+
get <%= api_route_helper %>_url(created_id), headers: @auth_headers
|
126
|
+
assert_response :not_found
|
127
|
+
|
128
|
+
# Step 8: Verify list count is back to original
|
129
|
+
get <%= api_route_helper %>_url, headers: @auth_headers
|
130
|
+
assert_response :success
|
131
|
+
|
132
|
+
final_response = JSON.parse(response.body)
|
133
|
+
assert_equal initial_count, final_response['data'].size
|
134
|
+
end
|
135
|
+
|
136
|
+
# === PAGINATION WORKFLOW TESTS ===
|
137
|
+
|
138
|
+
test "pagination workflow with multiple <%= table_name %>" do
|
139
|
+
# Create multiple <%= table_name %> for pagination testing
|
140
|
+
<%= table_name %> = []
|
141
|
+
10.times do |i|
|
142
|
+
<%= singular_table_name %> = <%= class_name %>.create!(
|
143
|
+
<% attributes.each_with_index do |attribute, index| -%>
|
144
|
+
<% if attribute.type == :references -%>
|
145
|
+
<%= attribute.name %>: @<%= attribute.name %><%= ',' if index < attributes.length - 1 %>
|
146
|
+
<% elsif attribute.type == :string -%>
|
147
|
+
<%= attribute.name %>: "Pagination Test <%= attribute.name.humanize %> #{i}"<%= ',' if index < attributes.length - 1 %>
|
148
|
+
<% elsif attribute.type == :text -%>
|
149
|
+
<%= attribute.name %>: "Pagination test <%= attribute.name.humanize %> content #{i}"<%= ',' if index < attributes.length - 1 %>
|
150
|
+
<% elsif attribute.type == :integer -%>
|
151
|
+
<%= attribute.name %>: i<%= ',' if index < attributes.length - 1 %>
|
152
|
+
<% elsif attribute.type == :boolean -%>
|
153
|
+
<%= attribute.name %>: i.even?<%= ',' if index < attributes.length - 1 %>
|
154
|
+
<% elsif attribute.type == :decimal || attribute.type == :float -%>
|
155
|
+
<%= attribute.name %>: i * 10.5<%= ',' if index < attributes.length - 1 %>
|
156
|
+
<% else -%>
|
157
|
+
<%= attribute.name %>: "test_#{i}"<%= ',' if index < attributes.length - 1 %>
|
158
|
+
<% end -%>
|
159
|
+
<% end -%>
|
160
|
+
)
|
161
|
+
<%= table_name %> << <%= singular_table_name %>
|
162
|
+
end
|
163
|
+
|
164
|
+
# Test first page
|
165
|
+
get <%= api_route_helper %>_url, params: { page: 1, limit: 5 }, headers: @auth_headers
|
166
|
+
assert_response :success
|
167
|
+
|
168
|
+
page1_response = JSON.parse(response.body)
|
169
|
+
assert_equal 5, page1_response['data'].size
|
170
|
+
|
171
|
+
pagination = page1_response['pagination']
|
172
|
+
assert_equal 1, pagination['page']
|
173
|
+
assert_equal 5, pagination['items']
|
174
|
+
assert pagination['count'] >= 10
|
175
|
+
assert pagination['pages'] >= 2
|
176
|
+
assert_equal false, pagination['first']
|
177
|
+
assert_equal false, pagination['last']
|
178
|
+
|
179
|
+
# Test second page
|
180
|
+
get <%= api_route_helper %>_url, params: { page: 2, limit: 5 }, headers: @auth_headers
|
181
|
+
assert_response :success
|
182
|
+
|
183
|
+
page2_response = JSON.parse(response.body)
|
184
|
+
assert_equal 5, page2_response['data'].size
|
185
|
+
|
186
|
+
pagination2 = page2_response['pagination']
|
187
|
+
assert_equal 2, pagination2['page']
|
188
|
+
assert_equal 5, pagination2['items']
|
189
|
+
|
190
|
+
# Verify different records on different pages
|
191
|
+
page1_ids = page1_response['data'].map { |item| item['id'] }
|
192
|
+
page2_ids = page2_response['data'].map { |item| item['id'] }
|
193
|
+
assert_empty (page1_ids & page2_ids), "Pages should contain different records"
|
194
|
+
end
|
195
|
+
|
196
|
+
# === ERROR HANDLING WORKFLOW TESTS ===
|
197
|
+
|
198
|
+
test "error handling workflow" do
|
199
|
+
# Test 1: Invalid authentication
|
200
|
+
get <%= api_route_helper %>_url, headers: { 'Authorization' => 'Bearer invalid_token' }
|
201
|
+
assert_response :unauthorized
|
202
|
+
|
203
|
+
error_response = JSON.parse(response.body)
|
204
|
+
assert_equal "Invalid token", error_response['error']
|
205
|
+
|
206
|
+
# Test 2: Missing authentication
|
207
|
+
get <%= api_route_helper %>_url
|
208
|
+
assert_response :unauthorized
|
209
|
+
|
210
|
+
error_response = JSON.parse(response.body)
|
211
|
+
assert_equal "No token provided", error_response['error']
|
212
|
+
|
213
|
+
# Test 3: Invalid resource ID
|
214
|
+
get <%= api_route_helper %>_url(99999), headers: @auth_headers
|
215
|
+
assert_response :not_found
|
216
|
+
|
217
|
+
# Test 4: Invalid create params
|
218
|
+
post <%= api_route_helper %>_url,
|
219
|
+
params: { data: { invalid_field: "invalid_value" } },
|
220
|
+
headers: @auth_headers
|
221
|
+
|
222
|
+
assert_response :unprocessable_entity
|
223
|
+
|
224
|
+
error_response = JSON.parse(response.body)
|
225
|
+
assert_includes error_response.keys, 'errors'
|
226
|
+
|
227
|
+
# Test 5: Malformed JSON
|
228
|
+
post <%= api_route_helper %>_url,
|
229
|
+
params: "invalid json",
|
230
|
+
headers: @auth_headers.merge('Content-Type' => 'application/json')
|
231
|
+
|
232
|
+
assert_response :bad_request
|
233
|
+
end
|
234
|
+
|
235
|
+
# === MULTI-TENANCY WORKFLOW TESTS ===
|
236
|
+
|
237
|
+
test "multi-tenancy isolation workflow" do
|
238
|
+
# Create another organization and user
|
239
|
+
other_organization = Organization.create!(name: "Other Organization")
|
240
|
+
other_user = User.create!(
|
241
|
+
email_address: "other@example.com",
|
242
|
+
username: "otheruser",
|
243
|
+
first_name: "Other",
|
244
|
+
last_name: "User",
|
245
|
+
password: "password123",
|
246
|
+
organization: other_organization
|
247
|
+
)
|
248
|
+
other_token = other_user.generate_jwt_token
|
249
|
+
other_headers = { 'Authorization' => "Bearer #{other_token}" }
|
250
|
+
|
251
|
+
# Create <%= singular_table_name %> in each organization
|
252
|
+
org1_<%= singular_table_name %> = <%= class_name %>.create!(
|
253
|
+
<% attributes.each_with_index do |attribute, index| -%>
|
254
|
+
<% if attribute.name == "organization" && attribute.type == :references -%>
|
255
|
+
<%= attribute.name %>: @organization<%= ',' if index < attributes.length - 1 %>
|
256
|
+
<% elsif attribute.name == "user" && attribute.type == :references -%>
|
257
|
+
<%= attribute.name %>: @user<%= ',' if index < attributes.length - 1 %>
|
258
|
+
<% elsif attribute.type == :references -%>
|
259
|
+
<%= attribute.name %>: @<%= attribute.name %><%= ',' if index < attributes.length - 1 %>
|
260
|
+
<% elsif attribute.type == :string -%>
|
261
|
+
<%= attribute.name %>: "Org 1 <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
262
|
+
<% elsif attribute.type == :text -%>
|
263
|
+
<%= attribute.name %>: "Org 1 <%= attribute.name.humanize %> content"<%= ',' if index < attributes.length - 1 %>
|
264
|
+
<% elsif attribute.type == :integer -%>
|
265
|
+
<%= attribute.name %>: 1<%= ',' if index < attributes.length - 1 %>
|
266
|
+
<% elsif attribute.type == :boolean -%>
|
267
|
+
<%= attribute.name %>: true<%= ',' if index < attributes.length - 1 %>
|
268
|
+
<% elsif attribute.type == :decimal || attribute.type == :float -%>
|
269
|
+
<%= attribute.name %>: 11.1<%= ',' if index < attributes.length - 1 %>
|
270
|
+
<% else -%>
|
271
|
+
<%= attribute.name %>: "org1_value"<%= ',' if index < attributes.length - 1 %>
|
272
|
+
<% end -%>
|
273
|
+
<% end -%>
|
274
|
+
)
|
275
|
+
|
276
|
+
org2_<%= singular_table_name %> = <%= class_name %>.create!(
|
277
|
+
<% attributes.each_with_index do |attribute, index| -%>
|
278
|
+
<% if attribute.name == "organization" && attribute.type == :references -%>
|
279
|
+
<%= attribute.name %>: other_organization<%= ',' if index < attributes.length - 1 %>
|
280
|
+
<% elsif attribute.name == "user" && attribute.type == :references -%>
|
281
|
+
<%= attribute.name %>: other_user<%= ',' if index < attributes.length - 1 %>
|
282
|
+
<% elsif attribute.type == :references -%>
|
283
|
+
<%= attribute.name %>: @<%= attribute.name %><%= ',' if index < attributes.length - 1 %>
|
284
|
+
<% elsif attribute.type == :string -%>
|
285
|
+
<%= attribute.name %>: "Org 2 <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
286
|
+
<% elsif attribute.type == :text -%>
|
287
|
+
<%= attribute.name %>: "Org 2 <%= attribute.name.humanize %> content"<%= ',' if index < attributes.length - 1 %>
|
288
|
+
<% elsif attribute.type == :integer -%>
|
289
|
+
<%= attribute.name %>: 2<%= ',' if index < attributes.length - 1 %>
|
290
|
+
<% elsif attribute.type == :boolean -%>
|
291
|
+
<%= attribute.name %>: false<%= ',' if index < attributes.length - 1 %>
|
292
|
+
<% elsif attribute.type == :decimal || attribute.type == :float -%>
|
293
|
+
<%= attribute.name %>: 22.2<%= ',' if index < attributes.length - 1 %>
|
294
|
+
<% else -%>
|
295
|
+
<%= attribute.name %>: "org2_value"<%= ',' if index < attributes.length - 1 %>
|
296
|
+
<% end -%>
|
297
|
+
<% end -%>
|
298
|
+
)
|
299
|
+
|
300
|
+
# Org 1 user should only see Org 1 <%= table_name %>
|
301
|
+
get <%= api_route_helper %>_url, headers: @auth_headers
|
302
|
+
assert_response :success
|
303
|
+
|
304
|
+
org1_response = JSON.parse(response.body)
|
305
|
+
org1_ids = org1_response['data'].map { |item| item['id'] }
|
306
|
+
|
307
|
+
assert_includes org1_ids, org1_<%= singular_table_name %>.id
|
308
|
+
assert_not_includes org1_ids, org2_<%= singular_table_name %>.id
|
309
|
+
|
310
|
+
# Org 2 user should only see Org 2 <%= table_name %>
|
311
|
+
get <%= api_route_helper %>_url, headers: other_headers
|
312
|
+
assert_response :success
|
313
|
+
|
314
|
+
org2_response = JSON.parse(response.body)
|
315
|
+
org2_ids = org2_response['data'].map { |item| item['id'] }
|
316
|
+
|
317
|
+
assert_includes org2_ids, org2_<%= singular_table_name %>.id
|
318
|
+
assert_not_includes org2_ids, org1_<%= singular_table_name %>.id
|
319
|
+
|
320
|
+
# Cross-organization access should be denied
|
321
|
+
get <%= api_route_helper %>_url(org2_<%= singular_table_name %>.id), headers: @auth_headers
|
322
|
+
assert_response :not_found
|
323
|
+
|
324
|
+
get <%= api_route_helper %>_url(org1_<%= singular_table_name %>.id), headers: other_headers
|
325
|
+
assert_response :not_found
|
326
|
+
end
|
327
|
+
|
328
|
+
# === FACET RESPONSE WORKFLOW TESTS ===
|
329
|
+
|
330
|
+
test "facet response format workflow" do
|
331
|
+
# Test index response (short facet)
|
332
|
+
get <%= api_route_helper %>_url, headers: @auth_headers
|
333
|
+
assert_response :success
|
334
|
+
|
335
|
+
index_response = JSON.parse(response.body)
|
336
|
+
|
337
|
+
# Verify pagination structure
|
338
|
+
assert_includes index_response.keys, 'data'
|
339
|
+
assert_includes index_response.keys, 'pagination'
|
340
|
+
assert_kind_of Array, index_response['data']
|
341
|
+
assert_kind_of Hash, index_response['pagination']
|
342
|
+
|
343
|
+
# Verify pagination metadata
|
344
|
+
pagination = index_response['pagination']
|
345
|
+
%w[page items count pages first last next prev].each do |key|
|
346
|
+
assert_includes pagination.keys, key, "Pagination should include #{key}"
|
347
|
+
end
|
348
|
+
|
349
|
+
# Test show response (details facet)
|
350
|
+
get <%= api_route_helper %>_url(@<%= singular_table_name %>.id), headers: @auth_headers
|
351
|
+
assert_response :success
|
352
|
+
|
353
|
+
show_response = JSON.parse(response.body)
|
354
|
+
|
355
|
+
# Verify response structure
|
356
|
+
assert_includes show_response.keys, 'data'
|
357
|
+
assert_kind_of Hash, show_response['data']
|
358
|
+
|
359
|
+
<%= singular_table_name %>_data = show_response['data']
|
360
|
+
|
361
|
+
# Verify core fields are present
|
362
|
+
assert_includes <%= singular_table_name %>_data.keys, 'id'
|
363
|
+
assert_includes <%= singular_table_name %>_data.keys, 'organization'
|
364
|
+
assert_includes <%= singular_table_name %>_data.keys, 'user'
|
365
|
+
|
366
|
+
# Verify associations are properly included
|
367
|
+
assert_kind_of Hash, <%= singular_table_name %>_data['organization']
|
368
|
+
assert_kind_of Hash, <%= singular_table_name %>_data['user']
|
369
|
+
assert_equal @organization.id, <%= singular_table_name %>_data['organization']['id']
|
370
|
+
assert_equal @user.id, <%= singular_table_name %>_data['user']['id']
|
371
|
+
end
|
372
|
+
|
373
|
+
# === CONCURRENT ACCESS WORKFLOW TESTS ===
|
374
|
+
|
375
|
+
test "concurrent modifications workflow" do
|
376
|
+
# Create a <%= singular_table_name %> to modify
|
377
|
+
test_<%= singular_table_name %> = <%= class_name %>.create!(
|
378
|
+
<% attributes.each_with_index do |attribute, index| -%>
|
379
|
+
<% if attribute.type == :references -%>
|
380
|
+
<%= attribute.name %>: @<%= attribute.name %><%= ',' if index < attributes.length - 1 %>
|
381
|
+
<% elsif attribute.type == :string -%>
|
382
|
+
<%= attribute.name %>: "Concurrent Test <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
383
|
+
<% elsif attribute.type == :text -%>
|
384
|
+
<%= attribute.name %>: "Concurrent test <%= attribute.name.humanize %> content"<%= ',' if index < attributes.length - 1 %>
|
385
|
+
<% elsif attribute.type == :integer -%>
|
386
|
+
<%= attribute.name %>: 50<%= ',' if index < attributes.length - 1 %>
|
387
|
+
<% elsif attribute.type == :boolean -%>
|
388
|
+
<%= attribute.name %>: true<%= ',' if index < attributes.length - 1 %>
|
389
|
+
<% elsif attribute.type == :decimal || attribute.type == :float -%>
|
390
|
+
<%= attribute.name %>: 50.5<%= ',' if index < attributes.length - 1 %>
|
391
|
+
<% else -%>
|
392
|
+
<%= attribute.name %>: "concurrent_test"<%= ',' if index < attributes.length - 1 %>
|
393
|
+
<% end -%>
|
394
|
+
<% end -%>
|
395
|
+
)
|
396
|
+
|
397
|
+
# Simulate concurrent read
|
398
|
+
get <%= api_route_helper %>_url(test_<%= singular_table_name %>.id), headers: @auth_headers
|
399
|
+
assert_response :success
|
400
|
+
|
401
|
+
# Simulate concurrent update
|
402
|
+
<% if attributes.any? { |attr| attr.type == :string && attr.name != "organization" && attr.name != "user" } -%>
|
403
|
+
<% string_attr = attributes.find { |attr| attr.type == :string && attr.name != "organization" && attr.name != "user" } -%>
|
404
|
+
patch <%= api_route_helper %>_url(test_<%= singular_table_name %>.id),
|
405
|
+
params: { data: { <%= string_attr.name %>: "Updated Concurrent <%= string_attr.name.humanize %>" } },
|
406
|
+
headers: @auth_headers
|
407
|
+
|
408
|
+
assert_response :success
|
409
|
+
|
410
|
+
# Verify update was successful
|
411
|
+
get <%= api_route_helper %>_url(test_<%= singular_table_name %>.id), headers: @auth_headers
|
412
|
+
assert_response :success
|
413
|
+
|
414
|
+
updated_response = JSON.parse(response.body)
|
415
|
+
assert_equal "Updated Concurrent <%= string_attr.name.humanize %>", updated_response['data']['<%= string_attr.name %>']
|
416
|
+
<% else -%>
|
417
|
+
# Concurrent update test - customize based on your model's attributes
|
418
|
+
patch <%= api_route_helper %>_url(test_<%= singular_table_name %>.id),
|
419
|
+
params: { data: { organization_id: @organization.id } },
|
420
|
+
headers: @auth_headers
|
421
|
+
|
422
|
+
assert_response :success
|
423
|
+
<% end -%>
|
424
|
+
end
|
425
|
+
|
426
|
+
# === BULK OPERATIONS WORKFLOW TESTS ===
|
427
|
+
|
428
|
+
test "bulk operations workflow" do
|
429
|
+
# Create multiple <%= table_name %> for bulk testing
|
430
|
+
bulk_<%= table_name %> = []
|
431
|
+
5.times do |i|
|
432
|
+
<%= singular_table_name %> = <%= class_name %>.create!(
|
433
|
+
<% attributes.each_with_index do |attribute, index| -%>
|
434
|
+
<% if attribute.type == :references -%>
|
435
|
+
<%= attribute.name %>: @<%= attribute.name %><%= ',' if index < attributes.length - 1 %>
|
436
|
+
<% elsif attribute.type == :string -%>
|
437
|
+
<%= attribute.name %>: "Bulk Test <%= attribute.name.humanize %> #{i}"<%= ',' if index < attributes.length - 1 %>
|
438
|
+
<% elsif attribute.type == :text -%>
|
439
|
+
<%= attribute.name %>: "Bulk test <%= attribute.name.humanize %> content #{i}"<%= ',' if index < attributes.length - 1 %>
|
440
|
+
<% elsif attribute.type == :integer -%>
|
441
|
+
<%= attribute.name %>: i * 10<%= ',' if index < attributes.length - 1 %>
|
442
|
+
<% elsif attribute.type == :boolean -%>
|
443
|
+
<%= attribute.name %>: i.even?<%= ',' if index < attributes.length - 1 %>
|
444
|
+
<% elsif attribute.type == :decimal || attribute.type == :float -%>
|
445
|
+
<%= attribute.name %>: i * 10.5<%= ',' if index < attributes.length - 1 %>
|
446
|
+
<% else -%>
|
447
|
+
<%= attribute.name %>: "bulk_#{i}"<%= ',' if index < attributes.length - 1 %>
|
448
|
+
<% end -%>
|
449
|
+
<% end -%>
|
450
|
+
)
|
451
|
+
bulk_<%= table_name %> << <%= singular_table_name %>
|
452
|
+
end
|
453
|
+
|
454
|
+
# Test bulk retrieval
|
455
|
+
get <%= api_route_helper %>_url, headers: @auth_headers
|
456
|
+
assert_response :success
|
457
|
+
|
458
|
+
bulk_response = JSON.parse(response.body)
|
459
|
+
retrieved_ids = bulk_response['data'].map { |item| item['id'] }
|
460
|
+
|
461
|
+
# Verify all bulk <%= table_name %> are included
|
462
|
+
bulk_<%= table_name %>.each do |<%= singular_table_name %>|
|
463
|
+
assert_includes retrieved_ids, <%= singular_table_name %>.id, "Bulk <%= singular_table_name %> should be in results"
|
464
|
+
end
|
465
|
+
|
466
|
+
# Test individual retrieval of bulk items
|
467
|
+
bulk_<%= table_name %>.each do |<%= singular_table_name %>|
|
468
|
+
get <%= api_route_helper %>_url(<%= singular_table_name %>.id), headers: @auth_headers
|
469
|
+
assert_response :success
|
470
|
+
|
471
|
+
item_response = JSON.parse(response.body)
|
472
|
+
assert_equal <%= singular_table_name %>.id, item_response['data']['id']
|
473
|
+
end
|
474
|
+
|
475
|
+
# Test bulk deletion
|
476
|
+
bulk_<%= table_name %>.each do |<%= singular_table_name %>|
|
477
|
+
delete <%= api_route_helper %>_url(<%= singular_table_name %>.id), headers: @auth_headers
|
478
|
+
assert_response :no_content
|
479
|
+
end
|
480
|
+
|
481
|
+
# Verify all items are deleted
|
482
|
+
bulk_<%= table_name %>.each do |<%= singular_table_name %>|
|
483
|
+
get <%= api_route_helper %>_url(<%= singular_table_name %>.id), headers: @auth_headers
|
484
|
+
assert_response :not_found
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|