blob: cdd2b90104e94be22b31877395faebfa52d5c000 [file]
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_shuffle();
no_root_location();
add_block_preprocessor(sub {
my ($block) = @_;
my $http_config = $block->http_config // <<_EOC_;
server {
listen 1979;
location /spec.json {
content_by_lua_block {
local file = io.open("t/spec/spec.json", "r")
local content = file:read("*a")
file:close()
ngx.print(content)
}
}
location /invalid.json {
content_by_lua_block {
ngx.print("not valid json {{{")
}
}
location /not-found.json {
content_by_lua_block {
ngx.status = 404
ngx.print("not found")
}
}
location /spec-with-auth.json {
content_by_lua_block {
local headers = ngx.req.get_headers()
if headers["X-Token"] ~= "my-secret-token" then
ngx.status = 403
ngx.print("forbidden")
return
end
local file = io.open("t/spec/spec.json", "r")
local content = file:read("*a")
file:close()
ngx.print(content)
}
}
}
server {
listen 1970;
location / {
content_by_lua_block {
ngx.say("ok")
}
}
}
_EOC_
$block->set_value("http_config", $http_config);
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->error_log && !$block->no_error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: schema validation -- spec_url is accepted
--- config
location /t {
content_by_lua_block {
local plugin = require("apisix.plugins.oas-validator")
local ok, err = plugin.check_schema({
spec_url = "http://127.0.0.1:1979/spec.json"
})
if not ok then
ngx.say(err)
return
end
ngx.say("done")
}
}
--- response_body
done
=== TEST 2: schema validation -- spec and spec_url are mutually exclusive
--- config
location /t {
content_by_lua_block {
local plugin = require("apisix.plugins.oas-validator")
local ok, err = plugin.check_schema({
spec = "{}",
spec_url = "http://127.0.0.1:1979/spec.json"
})
if not ok then
ngx.say("rejected")
return
end
ngx.say("ok")
}
}
--- response_body
rejected
=== TEST 3: schema validation -- neither spec nor spec_url fails
--- config
location /t {
content_by_lua_block {
local plugin = require("apisix.plugins.oas-validator")
local ok, err = plugin.check_schema({
verbose_errors = true
})
if not ok then
ngx.say("rejected")
return
end
ngx.say("ok")
}
}
--- response_body
rejected
=== TEST 4: schema validation -- spec_url must be http/https
--- config
location /t {
content_by_lua_block {
local plugin = require("apisix.plugins.oas-validator")
local ok, err = plugin.check_schema({
spec_url = "ftp://example.com/spec.json"
})
if not ok then
ngx.say("rejected")
return
end
ngx.say("ok")
}
}
--- response_body
rejected
=== TEST 5: create route with spec_url
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/*",
"plugins": {
"oas-validator": {
"spec_url": "http://127.0.0.1:1979/spec.json"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1970": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 6: request validation works with spec_url
--- request
POST /api/v3/pet
{"id": 10, "name": "doggie", "category": {"id": 1, "name": "Dogs"}, "photoUrls": ["string"], "tags": [{"id": 1, "name": "tag1"}], "status": "available"}
--- more_headers
Content-Type: application/json
--- no_error_log
[error]
=== TEST 7: invalid request body fails validation with spec_url
--- request
POST /api/v3/pet
{"invalid": "body"}
--- more_headers
Content-Type: application/json
--- error_code: 400
--- response_body_like: failed to validate request
--- error_log
error occurred while validating request
=== TEST 8: spec_url returning non-200 triggers error
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/*",
"plugins": {
"oas-validator": {
"spec_url": "http://127.0.0.1:1979/not-found.json"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1970": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: request to route with non-200 spec_url returns 500
--- request
GET /api/v3/pet/1
--- error_code: 500
--- response_body_like: failed to parse openapi spec
--- error_log
spec URL returned status 404
=== TEST 10: spec_url returning invalid JSON triggers error
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/*",
"plugins": {
"oas-validator": {
"spec_url": "http://127.0.0.1:1979/invalid.json"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1970": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: request to route with invalid JSON spec_url returns 500
--- request
GET /api/v3/pet/1
--- error_code: 500
--- response_body_like: failed to parse openapi spec
--- error_log
failed to compile openapi spec fetched from URL
=== TEST 12: spec_url with custom request headers
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/*",
"plugins": {
"oas-validator": {
"spec_url": "http://127.0.0.1:1979/spec-with-auth.json",
"spec_url_request_headers": {
"X-Token": "my-secret-token"
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1970": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 13: request validation works with custom headers spec_url
--- request
POST /api/v3/pet
{"id": 10, "name": "doggie", "category": {"id": 1, "name": "Dogs"}, "photoUrls": ["string"], "tags": [{"id": 1, "name": "tag1"}], "status": "available"}
--- more_headers
Content-Type: application/json
--- no_error_log
[error]
=== TEST 14: spec_url without required auth header fails
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/*",
"plugins": {
"oas-validator": {
"spec_url": "http://127.0.0.1:1979/spec-with-auth.json"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1970": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 15: request to route with missing auth returns 500
--- request
GET /api/v3/pet/1
--- error_code: 500
--- response_body_like: failed to parse openapi spec
--- error_log
spec URL returned status 403
=== TEST 16: metadata schema validation
--- config
location /t {
content_by_lua_block {
local plugin = require("apisix.plugins.oas-validator")
local core = require("apisix.core")
local ok, err = plugin.check_schema({spec_url_ttl = 60}, core.schema.TYPE_METADATA)
if not ok then
ngx.say(err)
return
end
ngx.say("done")
}
}
--- response_body
done
=== TEST 17: metadata schema rejects invalid ttl
--- config
location /t {
content_by_lua_block {
local plugin = require("apisix.plugins.oas-validator")
local core = require("apisix.core")
local ok, err = plugin.check_schema({spec_url_ttl = 0}, core.schema.TYPE_METADATA)
if not ok then
ngx.say("rejected")
return
end
ngx.say("ok")
}
}
--- response_body
rejected
=== TEST 18: set plugin metadata with custom TTL
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/plugin_metadata/oas-validator',
ngx.HTTP_PUT,
[[{
"spec_url_ttl": 2
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 19: create route with spec_url for TTL test
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/*",
"plugins": {
"oas-validator": {
"spec_url": "http://127.0.0.1:1979/spec.json"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1970": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 20: first request fetches and caches spec
--- request
POST /api/v3/pet
{"id": 10, "name": "doggie", "category": {"id": 1, "name": "Dogs"}, "photoUrls": ["string"], "tags": [{"id": 1, "name": "tag1"}], "status": "available"}
--- more_headers
Content-Type: application/json
--- no_error_log
[error]
=== TEST 21: second request uses cached spec (no refetch)
--- request
POST /api/v3/pet
{"id": 10, "name": "doggie", "category": {"id": 1, "name": "Dogs"}, "photoUrls": ["string"], "tags": [{"id": 1, "name": "tag1"}], "status": "available"}
--- more_headers
Content-Type: application/json
--- no_error_log
[error]
=== TEST 22: after TTL expiry, stale validator still works (async refresh)
--- config
location /t {
content_by_lua_block {
ngx.sleep(3)
local http = require("resty.http")
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:" .. ngx.var.server_port .. "/api/v3/pet", {
method = "POST",
body = '{"id": 10, "name": "doggie", "category": {"id": 1, "name": "Dogs"}, "photoUrls": ["string"], "tags": [{"id": 1, "name": "tag1"}], "status": "available"}',
headers = {
["Content-Type"] = "application/json",
}
})
if not res then
ngx.say("request failed: " .. err)
return
end
ngx.say("status: " .. res.status)
}
}
--- response_body
status: 200
--- no_error_log
[error]
=== TEST 23: clean up metadata
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/plugin_metadata/oas-validator',
ngx.HTTP_DELETE
)
ngx.say(body)
}
}
--- response_body
passed