reqcord 0.1.0 → 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 +4 -4
- data/.gitignore +8 -0
- data/CHANGELOG.md +77 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +171 -0
- data/LICENSE.txt +21 -0
- data/README.md +239 -95
- data/Rakefile +13 -0
- data/docs/configuration.md +319 -0
- data/examples/complex-spec-app/.rspec +1 -0
- data/examples/complex-spec-app/README.md +21 -0
- data/examples/complex-spec-app/app.rb +363 -0
- data/examples/complex-spec-app/docs/api/README.md +104 -0
- data/examples/complex-spec-app/docs/api/api/v1/admin/products/create.md +117 -0
- data/examples/complex-spec-app/docs/api/api/v1/admin/products/destroy.md +51 -0
- data/examples/complex-spec-app/docs/api/api/v1/admin/products/index.md +11 -0
- data/examples/complex-spec-app/docs/api/api/v1/admin/products/list.md +106 -0
- data/examples/complex-spec-app/docs/api/api/v1/auth/index.md +9 -0
- data/examples/complex-spec-app/docs/api/api/v1/auth/login.md +76 -0
- data/examples/complex-spec-app/docs/api/api/v1/cart-items/create.md +91 -0
- data/examples/complex-spec-app/docs/api/api/v1/cart-items/destroy.md +51 -0
- data/examples/complex-spec-app/docs/api/api/v1/cart-items/index.md +10 -0
- data/examples/complex-spec-app/docs/api/api/v1/carts/checkout.md +102 -0
- data/examples/complex-spec-app/docs/api/api/v1/carts/index.md +10 -0
- data/examples/complex-spec-app/docs/api/api/v1/carts/show.md +73 -0
- data/examples/complex-spec-app/docs/api/api/v1/home/index.md +9 -0
- data/examples/complex-spec-app/docs/api/api/v1/home/list.md +47 -0
- data/examples/complex-spec-app/docs/api/api/v1/notes/create.md +94 -0
- data/examples/complex-spec-app/docs/api/api/v1/notes/index.md +10 -0
- data/examples/complex-spec-app/docs/api/api/v1/notes/list.md +61 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/cancel.md +69 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/create.md +150 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/destroy.md +37 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/index.md +13 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/list.md +78 -0
- data/examples/complex-spec-app/docs/api/api/v1/orders/show.md +83 -0
- data/examples/complex-spec-app/docs/api/api/v1/products/index.md +11 -0
- data/examples/complex-spec-app/docs/api/api/v1/products/list.md +83 -0
- data/examples/complex-spec-app/docs/api/api/v1/products/search.md +88 -0
- data/examples/complex-spec-app/docs/api/api/v1/products/show.md +71 -0
- data/examples/complex-spec-app/docs/api/api/v1/profiles/index.md +10 -0
- data/examples/complex-spec-app/docs/api/api/v1/profiles/show.md +61 -0
- data/examples/complex-spec-app/docs/api/api/v1/profiles/update.md +91 -0
- data/examples/complex-spec-app/docs/api/api/v2/products/index.md +9 -0
- data/examples/complex-spec-app/docs/api/api/v2/products/list.md +77 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/create.sh +18 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/destroy.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/admin/products/list.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/auth/login.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/cart-items/create.sh +16 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/cart-items/destroy.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/carts/checkout.sh +13 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/carts/show.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/home/list.sh +8 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/notes/create.sh +15 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/notes/list.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/cancel.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/create.sh +29 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/destroy.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/list.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/orders/show.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/products/list.sh +8 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/products/search.sh +8 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/products/show.sh +8 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/profiles/show.sh +9 -0
- data/examples/complex-spec-app/docs/api/curl/api/v1/profiles/update.sh +16 -0
- data/examples/complex-spec-app/docs/api/curl/api/v2/products/list.sh +8 -0
- data/examples/complex-spec-app/docs/api/dataset.json +5116 -0
- data/examples/complex-spec-app/docs/api/postman/collection.json +2519 -0
- data/examples/complex-spec-app/generate.rb +14 -0
- data/examples/complex-spec-app/reqcord.yml +28 -0
- data/examples/complex-spec-app/spec/requests/admin_spec.rb +67 -0
- data/examples/complex-spec-app/spec/requests/cart_spec.rb +61 -0
- data/examples/complex-spec-app/spec/requests/home_and_auth_spec.rb +49 -0
- data/examples/complex-spec-app/spec/requests/orders_spec.rb +124 -0
- data/examples/complex-spec-app/spec/requests/products_spec.rb +65 -0
- data/examples/complex-spec-app/spec/spec_helper.rb +17 -0
- data/examples/complex-test-app/README.md +40 -0
- data/examples/complex-test-app/app.rb +384 -0
- data/examples/complex-test-app/docs/api/README.md +104 -0
- data/examples/complex-test-app/docs/api/api/v1/admin/products/create.md +117 -0
- data/examples/complex-test-app/docs/api/api/v1/admin/products/destroy.md +51 -0
- data/examples/complex-test-app/docs/api/api/v1/admin/products/index.md +11 -0
- data/examples/complex-test-app/docs/api/api/v1/admin/products/list.md +106 -0
- data/examples/complex-test-app/docs/api/api/v1/auth/index.md +9 -0
- data/examples/complex-test-app/docs/api/api/v1/auth/login.md +76 -0
- data/examples/complex-test-app/docs/api/api/v1/cart-items/create.md +91 -0
- data/examples/complex-test-app/docs/api/api/v1/cart-items/destroy.md +51 -0
- data/examples/complex-test-app/docs/api/api/v1/cart-items/index.md +10 -0
- data/examples/complex-test-app/docs/api/api/v1/carts/checkout.md +102 -0
- data/examples/complex-test-app/docs/api/api/v1/carts/index.md +10 -0
- data/examples/complex-test-app/docs/api/api/v1/carts/show.md +73 -0
- data/examples/complex-test-app/docs/api/api/v1/home/index.md +9 -0
- data/examples/complex-test-app/docs/api/api/v1/home/list.md +47 -0
- data/examples/complex-test-app/docs/api/api/v1/notes/create.md +94 -0
- data/examples/complex-test-app/docs/api/api/v1/notes/index.md +10 -0
- data/examples/complex-test-app/docs/api/api/v1/notes/list.md +61 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/cancel.md +69 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/create.md +150 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/destroy.md +37 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/index.md +13 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/list.md +71 -0
- data/examples/complex-test-app/docs/api/api/v1/orders/show.md +83 -0
- data/examples/complex-test-app/docs/api/api/v1/products/index.md +11 -0
- data/examples/complex-test-app/docs/api/api/v1/products/list.md +83 -0
- data/examples/complex-test-app/docs/api/api/v1/products/search.md +88 -0
- data/examples/complex-test-app/docs/api/api/v1/products/show.md +71 -0
- data/examples/complex-test-app/docs/api/api/v1/profiles/index.md +10 -0
- data/examples/complex-test-app/docs/api/api/v1/profiles/show.md +61 -0
- data/examples/complex-test-app/docs/api/api/v1/profiles/update.md +91 -0
- data/examples/complex-test-app/docs/api/api/v2/products/index.md +9 -0
- data/examples/complex-test-app/docs/api/api/v2/products/list.md +77 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/create.sh +18 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/destroy.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/admin/products/list.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/auth/login.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/cart-items/create.sh +16 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/cart-items/destroy.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/carts/checkout.sh +13 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/carts/show.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/home/list.sh +8 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/notes/create.sh +15 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/notes/list.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/cancel.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/create.sh +29 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/destroy.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/list.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/orders/show.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/products/list.sh +8 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/products/search.sh +8 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/products/show.sh +8 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/profiles/show.sh +9 -0
- data/examples/complex-test-app/docs/api/curl/api/v1/profiles/update.sh +16 -0
- data/examples/complex-test-app/docs/api/curl/api/v2/products/list.sh +8 -0
- data/examples/complex-test-app/docs/api/dataset.json +5109 -0
- data/examples/complex-test-app/docs/api/postman/collection.json +2568 -0
- data/examples/complex-test-app/generate.rb +14 -0
- data/examples/complex-test-app/reqcord.yml +28 -0
- data/examples/complex-test-app/test/integration/admin_test.rb +70 -0
- data/examples/complex-test-app/test/integration/cart_test.rb +64 -0
- data/examples/complex-test-app/test/integration/home_and_auth_test.rb +53 -0
- data/examples/complex-test-app/test/integration/orders_test.rb +127 -0
- data/examples/complex-test-app/test/integration/products_test.rb +66 -0
- data/examples/reqcord.yml +58 -0
- data/examples/spec-app/.rspec +1 -0
- data/examples/spec-app/README.md +36 -0
- data/examples/spec-app/app.rb +209 -0
- data/examples/spec-app/docs/api/README.md +45 -0
- data/examples/spec-app/docs/api/api/v1/customers/create.md +95 -0
- data/examples/spec-app/docs/api/api/v1/customers/index.md +11 -0
- data/examples/spec-app/docs/api/api/v1/customers/list.md +67 -0
- data/examples/spec-app/docs/api/api/v1/customers/show.md +65 -0
- data/examples/spec-app/docs/api/api/v1/tasks/complete.md +51 -0
- data/examples/spec-app/docs/api/api/v1/tasks/create.md +90 -0
- data/examples/spec-app/docs/api/api/v1/tasks/destroy.md +35 -0
- data/examples/spec-app/docs/api/api/v1/tasks/index.md +14 -0
- data/examples/spec-app/docs/api/api/v1/tasks/list.md +65 -0
- data/examples/spec-app/docs/api/api/v1/tasks/show.md +65 -0
- data/examples/spec-app/docs/api/api/v1/tasks/update.md +74 -0
- data/examples/spec-app/docs/api/api/v1/users/create.md +95 -0
- data/examples/spec-app/docs/api/api/v1/users/index.md +11 -0
- data/examples/spec-app/docs/api/api/v1/users/list.md +67 -0
- data/examples/spec-app/docs/api/api/v1/users/show.md +65 -0
- data/examples/spec-app/docs/api/curl/api/v1/customers/create.sh +17 -0
- data/examples/spec-app/docs/api/curl/api/v1/customers/list.sh +9 -0
- data/examples/spec-app/docs/api/curl/api/v1/customers/show.sh +9 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/complete.sh +8 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/create.sh +15 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/destroy.sh +8 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/list.sh +8 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/show.sh +8 -0
- data/examples/spec-app/docs/api/curl/api/v1/tasks/update.sh +14 -0
- data/examples/spec-app/docs/api/curl/api/v1/users/create.sh +17 -0
- data/examples/spec-app/docs/api/curl/api/v1/users/list.sh +9 -0
- data/examples/spec-app/docs/api/curl/api/v1/users/show.sh +9 -0
- data/examples/spec-app/docs/api/dataset.json +2402 -0
- data/examples/spec-app/docs/api/postman/collection.json +1290 -0
- data/examples/spec-app/generate.rb +14 -0
- data/examples/spec-app/reqcord.yml +27 -0
- data/examples/spec-app/spec/requests/customers_spec.rb +71 -0
- data/examples/spec-app/spec/requests/tasks_spec.rb +78 -0
- data/examples/spec-app/spec/requests/users_spec.rb +68 -0
- data/examples/spec-app/spec/spec_helper.rb +17 -0
- data/examples/test-app/README.md +57 -0
- data/examples/test-app/app.rb +212 -0
- data/examples/test-app/docs/api/README.md +45 -0
- data/examples/test-app/docs/api/api/v1/customers/create.md +95 -0
- data/examples/test-app/docs/api/api/v1/customers/index.md +11 -0
- data/examples/test-app/docs/api/api/v1/customers/list.md +67 -0
- data/examples/test-app/docs/api/api/v1/customers/show.md +65 -0
- data/examples/test-app/docs/api/api/v1/tasks/complete.md +51 -0
- data/examples/test-app/docs/api/api/v1/tasks/create.md +90 -0
- data/examples/test-app/docs/api/api/v1/tasks/destroy.md +35 -0
- data/examples/test-app/docs/api/api/v1/tasks/index.md +14 -0
- data/examples/test-app/docs/api/api/v1/tasks/list.md +65 -0
- data/examples/test-app/docs/api/api/v1/tasks/show.md +65 -0
- data/examples/test-app/docs/api/api/v1/tasks/update.md +74 -0
- data/examples/test-app/docs/api/api/v1/users/create.md +95 -0
- data/examples/test-app/docs/api/api/v1/users/index.md +11 -0
- data/examples/test-app/docs/api/api/v1/users/list.md +67 -0
- data/examples/test-app/docs/api/api/v1/users/show.md +65 -0
- data/examples/test-app/docs/api/curl/api/v1/customers/create.sh +17 -0
- data/examples/test-app/docs/api/curl/api/v1/customers/list.sh +9 -0
- data/examples/test-app/docs/api/curl/api/v1/customers/show.sh +9 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/complete.sh +8 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/create.sh +15 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/destroy.sh +8 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/list.sh +8 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/show.sh +8 -0
- data/examples/test-app/docs/api/curl/api/v1/tasks/update.sh +14 -0
- data/examples/test-app/docs/api/curl/api/v1/users/create.sh +17 -0
- data/examples/test-app/docs/api/curl/api/v1/users/list.sh +9 -0
- data/examples/test-app/docs/api/curl/api/v1/users/show.sh +9 -0
- data/examples/test-app/docs/api/dataset.json +2402 -0
- data/examples/test-app/docs/api/postman/collection.json +1290 -0
- data/examples/test-app/generate.rb +14 -0
- data/examples/test-app/reqcord.yml +28 -0
- data/examples/test-app/test/integration/customers_test.rb +72 -0
- data/examples/test-app/test/integration/tasks_test.rb +80 -0
- data/examples/test-app/test/integration/users_test.rb +72 -0
- data/lib/reqcord/capture/collector.rb +31 -0
- data/lib/reqcord/capture/integration_patch.rb +209 -0
- data/lib/reqcord/capture/minitest_context.rb +34 -0
- data/lib/reqcord/capture/rspec_context.rb +42 -0
- data/lib/reqcord/capture/test_context.rb +25 -0
- data/lib/reqcord/capture.rb +19 -0
- data/lib/reqcord/configuration.rb +198 -0
- data/lib/reqcord/dataset.rb +176 -0
- data/lib/reqcord/endpoint.rb +263 -0
- data/lib/reqcord/errors.rb +9 -0
- data/lib/reqcord/exporters/curl.rb +68 -0
- data/lib/reqcord/exporters/markdown.rb +295 -0
- data/lib/reqcord/exporters/postman.rb +206 -0
- data/lib/reqcord/exporters.rb +32 -0
- data/lib/reqcord/generator.rb +364 -0
- data/lib/reqcord/railtie.rb +51 -0
- data/lib/reqcord/renderers/curl.rb +56 -0
- data/lib/reqcord/renderers/payload.rb +69 -0
- data/lib/reqcord/request_example.rb +104 -0
- data/lib/reqcord/response_example.rb +72 -0
- data/lib/reqcord/route_collector.rb +242 -0
- data/lib/reqcord/sanitizers/sanitizer.rb +140 -0
- data/lib/reqcord/schema.rb +187 -0
- data/lib/reqcord/support.rb +58 -0
- data/lib/reqcord/version.rb +5 -0
- data/lib/reqcord.rb +78 -0
- data/lib/tasks/reqcord.rake +99 -0
- data/reqcord.gemspec +46 -0
- metadata +317 -3
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Stands in for `bin/rails reqcord:generate`, which this single file example
|
|
4
|
+
# has no `bin/rails` to run.
|
|
5
|
+
require_relative "app"
|
|
6
|
+
|
|
7
|
+
dataset = Reqcord::Generator.call(
|
|
8
|
+
resources: ENV.fetch("RESOURCE", "").split(",").map(&:strip).reject(&:empty?),
|
|
9
|
+
version: ENV["VERSION"]
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
puts
|
|
13
|
+
puts "Endpoints: #{dataset.endpoints.size} (#{dataset.documented_endpoints.size} covered by tests)"
|
|
14
|
+
puts "Output: #{Reqcord.configuration.output_directory}"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
|
|
3
|
+
test:
|
|
4
|
+
framework: rspec
|
|
5
|
+
paths:
|
|
6
|
+
- spec/requests
|
|
7
|
+
|
|
8
|
+
routes:
|
|
9
|
+
prefix: /api
|
|
10
|
+
|
|
11
|
+
output:
|
|
12
|
+
directory: docs/api
|
|
13
|
+
|
|
14
|
+
exporters:
|
|
15
|
+
- curl
|
|
16
|
+
- markdown
|
|
17
|
+
- postman
|
|
18
|
+
|
|
19
|
+
variables:
|
|
20
|
+
base_url: http://localhost:3000
|
|
21
|
+
|
|
22
|
+
sanitize:
|
|
23
|
+
headers:
|
|
24
|
+
Authorization: "Bearer {{token}}"
|
|
25
|
+
X-Api-Key: "{{api_key}}"
|
|
26
|
+
body:
|
|
27
|
+
password: "{{password}}"
|
|
28
|
+
token: "{{token}}"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Admin and v2", type: :request do
|
|
6
|
+
let(:api_key) { { "X-Api-Key" => "sk_live_super-secret-key" } }
|
|
7
|
+
|
|
8
|
+
it "admin lists products with costs" do
|
|
9
|
+
get "/api/v1/admin/products", headers: api_key, as: :json
|
|
10
|
+
|
|
11
|
+
expect(response.status).to eq(200)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "admin requires an api key" do
|
|
15
|
+
get "/api/v1/admin/products", as: :json
|
|
16
|
+
|
|
17
|
+
expect(response.status).to eq(401)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "admin creates a product" do
|
|
21
|
+
post "/api/v1/admin/products",
|
|
22
|
+
params: { product: { sku: "TEA-003", name: "Jasmine", category: "tea", price_cents: 1400 } },
|
|
23
|
+
headers: api_key,
|
|
24
|
+
as: :json
|
|
25
|
+
|
|
26
|
+
expect(response.status).to eq(201)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "admin rejects a product without a sku" do
|
|
30
|
+
post "/api/v1/admin/products", params: { product: { sku: "", name: "Nameless" } }, headers: api_key, as: :json
|
|
31
|
+
|
|
32
|
+
expect(response.status).to eq(422)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "admin rejects a duplicate sku" do
|
|
36
|
+
post "/api/v1/admin/products",
|
|
37
|
+
params: { product: { sku: "TEA-001", name: "Earl Grey again", category: "tea", price_cents: 1200 } },
|
|
38
|
+
headers: api_key,
|
|
39
|
+
as: :json
|
|
40
|
+
|
|
41
|
+
expect(response.status).to eq(409)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "admin deletes a product" do
|
|
45
|
+
delete "/api/v1/admin/products/4", headers: api_key, as: :json
|
|
46
|
+
|
|
47
|
+
expect(response.status).to eq(204)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "admin cannot delete an unknown product" do
|
|
51
|
+
delete "/api/v1/admin/products/999", headers: api_key, as: :json
|
|
52
|
+
|
|
53
|
+
expect(response.status).to eq(404)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "v2 lists products" do
|
|
57
|
+
get "/api/v2/products", as: :json
|
|
58
|
+
|
|
59
|
+
expect(response.status).to eq(200)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "v2 follows a cursor" do
|
|
63
|
+
get "/api/v2/products", params: { cursor: "eyJpZCI6Mn0" }, as: :json
|
|
64
|
+
|
|
65
|
+
expect(response.status).to eq(200)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Cart", type: :request do
|
|
6
|
+
let(:auth) { { "Authorization" => "Bearer eyJhbGciOiJIUzI1NiJ9.super-secret" } }
|
|
7
|
+
|
|
8
|
+
it "shows the cart" do
|
|
9
|
+
get "/api/v1/cart", headers: auth, as: :json
|
|
10
|
+
|
|
11
|
+
expect(response.status).to eq(200)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "adds an item to the cart" do
|
|
15
|
+
post "/api/v1/cart/items", params: { item: { sku: "TEA-002", quantity: 3 } }, headers: auth, as: :json
|
|
16
|
+
|
|
17
|
+
expect(response.status).to eq(201)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "rejects an unknown sku" do
|
|
21
|
+
post "/api/v1/cart/items", params: { item: { sku: "TEA-999", quantity: 1 } }, headers: auth, as: :json
|
|
22
|
+
|
|
23
|
+
expect(response.status).to eq(422)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "removes an item from the cart" do
|
|
27
|
+
delete "/api/v1/cart/items/TEA-001", headers: auth, as: :json
|
|
28
|
+
|
|
29
|
+
expect(response.status).to eq(204)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "cannot remove an item that is not in the cart" do
|
|
33
|
+
delete "/api/v1/cart/items/MUG-002", headers: auth, as: :json
|
|
34
|
+
|
|
35
|
+
expect(response.status).to eq(404)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "checks out with a card" do
|
|
39
|
+
post "/api/v1/cart/checkout", params: { payment_method: "card" }, headers: auth, as: :json
|
|
40
|
+
|
|
41
|
+
expect(response.status).to eq(201)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "checks out with a bank transfer" do
|
|
45
|
+
post "/api/v1/cart/checkout", params: { payment_method: "bank_transfer" }, headers: auth, as: :json
|
|
46
|
+
|
|
47
|
+
expect(response.status).to eq(201)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "rejects an unknown payment method" do
|
|
51
|
+
post "/api/v1/cart/checkout", params: { payment_method: "cash" }, headers: auth, as: :json
|
|
52
|
+
|
|
53
|
+
expect(response.status).to eq(422)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "cart requires authentication" do
|
|
57
|
+
get "/api/v1/cart", as: :json
|
|
58
|
+
|
|
59
|
+
expect(response.status).to eq(401)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Home and auth", type: :request do
|
|
6
|
+
let(:auth) { { "Authorization" => "Bearer eyJhbGciOiJIUzI1NiJ9.super-secret" } }
|
|
7
|
+
|
|
8
|
+
it "root describes the api" do
|
|
9
|
+
get "/api/v1", as: :json
|
|
10
|
+
|
|
11
|
+
expect(response.status).to eq(200)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "logs in with a form" do
|
|
15
|
+
post "/api/v1/auth/login", params: { email: "ada@example.com", password: "correct-horse-battery" }
|
|
16
|
+
|
|
17
|
+
expect(response.status).to eq(200)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "rejects a wrong password" do
|
|
21
|
+
post "/api/v1/auth/login", params: { email: "ada@example.com", password: "wrong" }
|
|
22
|
+
|
|
23
|
+
expect(response.status).to eq(401)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "shows the profile" do
|
|
27
|
+
get "/api/v1/profile", headers: auth, as: :json
|
|
28
|
+
|
|
29
|
+
expect(response.status).to eq(200)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "updates the profile" do
|
|
33
|
+
patch "/api/v1/profile", params: { profile: { name: "Ada L.", locale: "tr" } }, headers: auth, as: :json
|
|
34
|
+
|
|
35
|
+
expect(response.status).to eq(200)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "rejects an unknown locale" do
|
|
39
|
+
patch "/api/v1/profile", params: { profile: { locale: "xx" } }, headers: auth, as: :json
|
|
40
|
+
|
|
41
|
+
expect(response.status).to eq(422)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "profile requires authentication" do
|
|
45
|
+
get "/api/v1/profile", as: :json
|
|
46
|
+
|
|
47
|
+
expect(response.status).to eq(401)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Orders", type: :request do
|
|
6
|
+
let(:auth) { { "Authorization" => "Bearer eyJhbGciOiJIUzI1NiJ9.super-secret" } }
|
|
7
|
+
|
|
8
|
+
let(:order_payload) do
|
|
9
|
+
{
|
|
10
|
+
order: {
|
|
11
|
+
line_items: [
|
|
12
|
+
{ sku: "TEA-001", quantity: 2 },
|
|
13
|
+
{ sku: "MUG-001", quantity: 1 }
|
|
14
|
+
],
|
|
15
|
+
shipping_address: { line1: "1 Analytical Engine Way", city: "London", country: "GB" }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "lists orders" do
|
|
21
|
+
get "/api/v1/orders", headers: auth, as: :json
|
|
22
|
+
|
|
23
|
+
expect(response.status).to eq(200)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "lists pending orders" do
|
|
27
|
+
get "/api/v1/orders", params: { status: "pending" }, headers: auth, as: :json
|
|
28
|
+
|
|
29
|
+
expect(response.status).to eq(200)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "lists shipped orders" do
|
|
33
|
+
get "/api/v1/orders", params: { status: "shipped" }, headers: auth, as: :json
|
|
34
|
+
|
|
35
|
+
expect(response.status).to eq(200)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "shows an order" do
|
|
39
|
+
get "/api/v1/orders/1", headers: auth, as: :json
|
|
40
|
+
|
|
41
|
+
expect(response.status).to eq(200)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "hides another customer's order" do
|
|
45
|
+
get "/api/v1/orders/3", headers: auth, as: :json
|
|
46
|
+
|
|
47
|
+
expect(response.status).to eq(403)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "returns not found for an unknown order" do
|
|
51
|
+
get "/api/v1/orders/999", headers: auth, as: :json
|
|
52
|
+
|
|
53
|
+
expect(response.status).to eq(404)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "creates an order" do
|
|
57
|
+
post "/api/v1/orders", params: order_payload, headers: auth, as: :json
|
|
58
|
+
|
|
59
|
+
expect(response.status).to eq(201)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "creates a single item order" do
|
|
63
|
+
post "/api/v1/orders",
|
|
64
|
+
params: { order: { line_items: [{ sku: "MUG-002", quantity: 1 }], shipping_address: { line1: "Piazza", city: "Rome", country: "IT" } } },
|
|
65
|
+
headers: auth,
|
|
66
|
+
as: :json
|
|
67
|
+
|
|
68
|
+
expect(response.status).to eq(201)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "rejects an order without line items" do
|
|
72
|
+
post "/api/v1/orders", params: { order: { line_items: [] } }, headers: auth, as: :json
|
|
73
|
+
|
|
74
|
+
expect(response.status).to eq(422)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "rejects an unknown sku" do
|
|
78
|
+
post "/api/v1/orders", params: { order: { line_items: [{ sku: "TEA-999", quantity: 1 }] } }, headers: auth, as: :json
|
|
79
|
+
|
|
80
|
+
expect(response.status).to eq(422)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "cancels a pending order" do
|
|
84
|
+
post "/api/v1/orders/1/cancel", headers: auth, as: :json
|
|
85
|
+
|
|
86
|
+
expect(response.status).to eq(200)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "cannot cancel a shipped order" do
|
|
90
|
+
post "/api/v1/orders/2/cancel", headers: auth, as: :json
|
|
91
|
+
|
|
92
|
+
expect(response.status).to eq(409)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "deletes an order" do
|
|
96
|
+
delete "/api/v1/orders/1", headers: auth, as: :json
|
|
97
|
+
|
|
98
|
+
expect(response.status).to eq(204)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "orders require authentication" do
|
|
102
|
+
get "/api/v1/orders", as: :json
|
|
103
|
+
|
|
104
|
+
expect(response.status).to eq(401)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "lists the notes of an order" do
|
|
108
|
+
get "/api/v1/orders/1/notes", headers: auth, as: :json
|
|
109
|
+
|
|
110
|
+
expect(response.status).to eq(200)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "adds a note to an order" do
|
|
114
|
+
post "/api/v1/orders/1/notes", params: { note: { body: "Ring the bell twice" } }, headers: auth, as: :json
|
|
115
|
+
|
|
116
|
+
expect(response.status).to eq(201)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "rejects an empty note" do
|
|
120
|
+
post "/api/v1/orders/1/notes", params: { note: { body: "" } }, headers: auth, as: :json
|
|
121
|
+
|
|
122
|
+
expect(response.status).to eq(422)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Products", type: :request do
|
|
6
|
+
it "lists products" do
|
|
7
|
+
get "/api/v1/products", as: :json
|
|
8
|
+
|
|
9
|
+
expect(response.status).to eq(200)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "filters products by category" do
|
|
13
|
+
get "/api/v1/products", params: { filter: { category: "tea" } }, as: :json
|
|
14
|
+
|
|
15
|
+
expect(response.status).to eq(200)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "filters mugs" do
|
|
19
|
+
get "/api/v1/products", params: { filter: { category: "mugs" } }, as: :json
|
|
20
|
+
|
|
21
|
+
expect(response.status).to eq(200)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "sorts by price ascending" do
|
|
25
|
+
get "/api/v1/products", params: { sort: "price_asc" }, as: :json
|
|
26
|
+
|
|
27
|
+
expect(response.status).to eq(200)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "sorts by price descending" do
|
|
31
|
+
get "/api/v1/products", params: { sort: "price_desc" }, as: :json
|
|
32
|
+
|
|
33
|
+
expect(response.status).to eq(200)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "paginates" do
|
|
37
|
+
get "/api/v1/products", params: { page: 2, per_page: 2 }, as: :json
|
|
38
|
+
|
|
39
|
+
expect(response.status).to eq(200)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "shows a product" do
|
|
43
|
+
get "/api/v1/products/3", as: :json
|
|
44
|
+
|
|
45
|
+
expect(response.status).to eq(200)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "returns not found for an unknown product" do
|
|
49
|
+
get "/api/v1/products/999", as: :json
|
|
50
|
+
|
|
51
|
+
expect(response.status).to eq(404)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "searches products" do
|
|
55
|
+
get "/api/v1/products/search", params: { q: "mug" }, as: :json
|
|
56
|
+
|
|
57
|
+
expect(response.status).to eq(200)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "search needs a query" do
|
|
61
|
+
get "/api/v1/products/search", as: :json
|
|
62
|
+
|
|
63
|
+
expect(response.status).to eq(400)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../app"
|
|
4
|
+
|
|
5
|
+
require "rspec/core"
|
|
6
|
+
require "action_dispatch/testing/integration"
|
|
7
|
+
|
|
8
|
+
RSpec.configure do |config|
|
|
9
|
+
# What rspec-rails would give you as `type: :request`; spelled out here so
|
|
10
|
+
# the example runs with plain rspec-core.
|
|
11
|
+
config.include ActionDispatch::Integration::Runner, type: :request
|
|
12
|
+
config.include ActionDispatch::IntegrationTest::Behavior, type: :request
|
|
13
|
+
|
|
14
|
+
config.before(:each, type: :request) { integration_session }
|
|
15
|
+
|
|
16
|
+
config.expect_with(:rspec) { |expectations| expectations.syntax = :expect }
|
|
17
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# complex-test-app (Minitest)
|
|
2
|
+
|
|
3
|
+
A store API in one file, with ordinary Minitest integration tests, chosen to
|
|
4
|
+
show the harder things Reqcord documents. Start with [test-app](../test-app)
|
|
5
|
+
if you want the gentle version.
|
|
6
|
+
|
|
7
|
+
| Endpoint | What it shows in the output |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `GET /api/v1` | `root` inside a namespace, titled "Home" |
|
|
10
|
+
| `POST /api/v1/auth/login` | a **form** post (`urlencoded` in Postman, `--data 'email=…'` in cURL); the `token` in the response is sanitized to `{{token}}` |
|
|
11
|
+
| `GET` / `PATCH /api/v1/profile` | singular `resource`; `PATCH` and `PUT` folded into one page |
|
|
12
|
+
| `GET /api/v1/products` | nested query `filter[category]`, an optional `sort` with a closed set (`"price_asc" \| "price_desc"`), `page` / `per_page` |
|
|
13
|
+
| `GET /api/v1/products/search` | collection action, `400` without `q` |
|
|
14
|
+
| `POST /api/v1/orders` | an array body: `order.line_items[].sku`, `order.shipping_address.city` |
|
|
15
|
+
| `GET /api/v1/orders/:id` | `403` for another customer's order, `404` |
|
|
16
|
+
| `POST /api/v1/orders/:id/cancel` | member action, `409` once shipped |
|
|
17
|
+
| `GET /api/v1/orders/:order_id/notes` | nested resource, `order_id` as a path parameter |
|
|
18
|
+
| `GET /api/v1/cart` | singular `resource :cart` — one record, no id |
|
|
19
|
+
| `POST` / `DELETE /api/v1/cart/items/:sku` | items nested under the cart, addressed by `sku` (`param: :sku`) |
|
|
20
|
+
| `POST /api/v1/cart/checkout` | custom action on a singular resource ("Checkout Cart"), `payment_method` as `"bank_transfer" \| "card"` |
|
|
21
|
+
| `/api/v1/admin/products` | a second credential (`X-Api-Key` → `{{api_key}}`), its own `admin` folder, `409` on a duplicate sku |
|
|
22
|
+
| `GET /api/v2/products` | a second API version next to the first: `VERSION=v2 ruby generate.rb` |
|
|
23
|
+
|
|
24
|
+
Every route is covered by a test, so the report ends in
|
|
25
|
+
`routes: 22 = 22 documented + 0 uncovered + 0 skipped`.
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
app.rb the application
|
|
29
|
+
test/integration/*_test.rb ordinary integration tests
|
|
30
|
+
reqcord.yml paths, prefix, two sanitized headers and two body keys
|
|
31
|
+
docs/api/ generated Markdown, cURL scripts, Postman collection
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Regenerate
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ruby generate.rb
|
|
38
|
+
RESOURCE=orders,notes ruby generate.rb
|
|
39
|
+
VERSION=v2 ruby generate.rb
|
|
40
|
+
```
|