This is an automated email from the ASF dual-hosted git repository.

shreemaanabhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new d6e288b0f feat(limit-count): support configuring multiple rules 
(#12977)
d6e288b0f is described below

commit d6e288b0f3941f559415cf600ec00a03f826626c
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Tue Feb 10 11:25:07 2026 +0545

    feat(limit-count): support configuring multiple rules (#12977)
---
 apisix/plugins/limit-count/init.lua   | 176 ++++++++++++----
 docs/en/latest/plugins/limit-count.md |   8 +-
 docs/zh/latest/plugins/limit-count.md |   8 +-
 t/admin/consumer-group.t              |   2 +-
 t/admin/plugin-configs.t              |   2 +-
 t/plugin/limit-count-rules.t          | 380 ++++++++++++++++++++++++++++++++++
 t/plugin/limit-count-variable.t       |   2 -
 t/plugin/limit-count.t                |   4 +-
 t/plugin/workflow.t                   |   4 +-
 9 files changed, 533 insertions(+), 53 deletions(-)

diff --git a/apisix/plugins/limit-count/init.lua 
b/apisix/plugins/limit-count/init.lua
index 7d5fe7ca9..1a1427e10 100644
--- a/apisix/plugins/limit-count/init.lua
+++ b/apisix/plugins/limit-count/init.lua
@@ -25,6 +25,7 @@ local get_phase = ngx.get_phase
 local tonumber = tonumber
 local type = type
 local tostring = tostring
+local str_format = string.format
 
 local limit_redis_cluster_new
 local limit_redis_new
@@ -82,6 +83,28 @@ local schema = {
                 {type = "string"},
             },
         },
+        rules = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    count = {
+                        oneOf = {
+                            {type = "integer", exclusiveMinimum = 0},
+                            {type = "string"},
+                        },
+                    },
+                    time_window = {
+                        oneOf = {
+                            {type = "integer", exclusiveMinimum = 0},
+                            {type = "string"},
+                        },
+                    },
+                    key = {type = "string"},
+                },
+                required = {"count", "time_window", "key"},
+            },
+        },
         group = {type = "string"},
         key = {type = "string", default = "remote_addr"},
         key_type = {type = "string",
@@ -102,7 +125,14 @@ local schema = {
         allow_degradation = {type = "boolean", default = false},
         show_limit_quota_header = {type = "boolean", default = true}
     },
-    required = {"count", "time_window"},
+    oneOf = {
+        {
+            required = {"count", "time_window"},
+        },
+        {
+            required = {"rules"},
+        }
+    },
     ["if"] = {
         properties = {
             policy = {
@@ -180,51 +210,34 @@ function _M.check_schema(conf, schema_type)
         end
     end
 
-    return true
-end
-
-
-local function create_limit_obj(conf, ctx, plugin_name)
-    core.log.info("create new " .. plugin_name .. " plugin instance")
-
-    local count = conf.count
-    if type(count) == "string" then
-        local err, _
-        count, err, _ = core.utils.resolve_var(count, ctx.var)
-        if err then
-            return nil, "could not resolve vars in count: " .. err
-        end
-        count = tonumber(count)
-        if not count then
-            return nil, "resolved count is not a number: " .. tostring(count)
+    local keys = {}
+    for _, rule in ipairs(conf.rules or {}) do
+        if keys[rule.key] then
+            return false, str_format("duplicate key '%s' in rules", rule.key)
         end
+        keys[rule.key] = true
     end
 
-    local time_window = conf.time_window
-    if type(time_window) == "string" then
-        local err, _
-        time_window, err, _ = core.utils.resolve_var(time_window, ctx.var)
-        if err then
-            return nil, "could not resolve vars in time_window: " .. err
-        end
-        time_window = tonumber(time_window)
-        if not time_window then
-            return nil, "resolved time_window is not a number: " .. 
tostring(time_window)
-        end
-    end
+    return true
+end
+
 
-    core.log.info("limit count: ", count, ", time_window: ", time_window)
+local function create_limit_obj(conf, rule, plugin_name)
+    core.log.info("create new ", plugin_name, " plugin instance",
+        ", rule: ", core.json.delay_encode(rule, true))
 
     if not conf.policy or conf.policy == "local" then
-        return limit_local_new("plugin-" .. plugin_name, count, time_window)
+        return limit_local_new("plugin-" .. plugin_name, rule.count,
+                               rule.time_window)
     end
 
     if conf.policy == "redis" then
-        return limit_redis_new("plugin-" .. plugin_name, count, time_window, 
conf)
+        return limit_redis_new("plugin-" .. plugin_name, rule.count, 
rule.time_window, conf)
     end
 
     if conf.policy == "redis-cluster" then
-        return limit_redis_cluster_new("plugin-" .. plugin_name, count, 
time_window, conf)
+        return limit_redis_cluster_new("plugin-" .. plugin_name, rule.count,
+                                       rule.time_window, conf)
     end
 
     return nil
@@ -258,11 +271,71 @@ local function gen_limit_key(conf, ctx, key)
 end
 
 
-function _M.rate_limit(conf, ctx, name, cost, dry_run)
-    core.log.info("ver: ", ctx.conf_version)
-    core.log.info("conf: ", core.json.delay_encode(conf, true))
+local function resolve_var(ctx, value)
+    if type(value) == "string" then
+        local err, _
+        value, err, _ = core.utils.resolve_var(value, ctx.var)
+        if err then
+            return nil, "could not resolve var for value: " .. value .. ", 
err: " .. err
+        end
+        value = tonumber(value)
+        if not value then
+            return nil, "resolved value is not a number: " .. tostring(value)
+        end
+    end
+    return value
+end
 
-    local lim, err = create_limit_obj(conf, ctx, name)
+
+local function get_rules(ctx, conf)
+    if not conf.rules then
+        local count, err = resolve_var(ctx, conf.count)
+        if err then
+            return nil, err
+        end
+        local time_window, err2 = resolve_var(ctx, conf.time_window)
+        if err2 then
+            return nil, err2
+        end
+        return {
+            {
+                count = count,
+                time_window = time_window,
+                key = conf.key,
+                key_type = conf.key_type,
+            }
+        }
+    end
+
+    local rules = {}
+    for _, rule in ipairs(conf.rules) do
+        local count, err = resolve_var(ctx, rule.count)
+        if err then
+            goto CONTINUE
+        end
+        local time_window, err2 = resolve_var(ctx, rule.time_window)
+        if err2 then
+            goto CONTINUE
+        end
+        local key, _, n_resolved = core.utils.resolve_var(rule.key, ctx.var)
+        if n_resolved == 0 then
+            goto CONTINUE
+        end
+        core.table.insert(rules, {
+            count = count,
+            time_window = time_window,
+            key_type = "constant",
+            key = key,
+        })
+
+        ::CONTINUE::
+    end
+    return rules
+end
+
+
+local function run_rate_limit(conf, rule, ctx, name, cost, dry_run)
+    local lim, err = create_limit_obj(conf, rule, name)
 
     if not lim then
         core.log.error("failed to fetch limit.count object: ", err)
@@ -272,9 +345,9 @@ function _M.rate_limit(conf, ctx, name, cost, dry_run)
         return 500
     end
 
-    local conf_key = conf.key
+    local conf_key = rule.key
     local key
-    if conf.key_type == "var_combination" then
+    if rule.key_type == "var_combination" then
         local err, n_resolved
         key, err, n_resolved = core.utils.resolve_var(conf_key, ctx.var)
         if err then
@@ -284,7 +357,7 @@ function _M.rate_limit(conf, ctx, name, cost, dry_run)
         if n_resolved == 0 then
             key = nil
         end
-    elseif conf.key_type == "constant" then
+    elseif rule.key_type == "constant" then
         key = conf_key
     else
         key = ctx.var[conf_key]
@@ -353,4 +426,25 @@ function _M.rate_limit(conf, ctx, name, cost, dry_run)
 end
 
 
+function _M.rate_limit(conf, ctx, name, cost, dry_run)
+    core.log.info("ver: ", ctx.conf_version)
+
+    local rules, err = get_rules(ctx, conf)
+    if not rules or #rules == 0 then
+        core.log.error("failed to get rate limit rules: ", err)
+        if conf.allow_degradation then
+            return
+        end
+        return 500
+    end
+
+    for _, rule in ipairs(rules) do
+        local code, msg = run_rate_limit(conf, rule, ctx, name, cost, dry_run)
+        if code then
+            return code, msg
+        end
+    end
+end
+
+
 return _M
diff --git a/docs/en/latest/plugins/limit-count.md 
b/docs/en/latest/plugins/limit-count.md
index 3f872c215..cba8f8d6a 100644
--- a/docs/en/latest/plugins/limit-count.md
+++ b/docs/en/latest/plugins/limit-count.md
@@ -44,8 +44,12 @@ You may see the following rate limiting headers in the 
response:
 
 | Name                    | Type    | Required                                 
 | Default       | Valid values                           | Description         
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
 | ----------------------- | ------- | 
----------------------------------------- | ------------- | 
-------------------------------------- | 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
-| count                   | integer | True                                     
 |               | > 0                              | The maximum number of 
requests allowed within a given time interval.                                  
                                                                                
                                                                                
                                                                                
                  [...]
-| time_window             | integer | True                                     
 |               | > 0                        | The time interval corresponding 
to the rate limiting `count` in seconds.                                        
                                                                                
                                                                                
                                                                                
              [...]
+| count                   | integer | False                                    
 |               | > 0                              | The maximum number of 
requests allowed within a given time interval. Required if `rules` is not 
configured.                                                                     
                                                                                
                                                                                
                        [...]
+| time_window             | integer | False                                    
 |               | > 0                        | The time interval corresponding 
to the rate limiting `count` in seconds. Required if `rules` is not configured. 
                                                                                
                                                                                
                                                                                
              [...]
+| rules                   | array[object] | False                              
 |               |                            | A list of rate limiting rules. 
Each rule is an object containing `count`, `time_window`, and `key`.            
                                                                                
                                                                                
                                    |
+| rules.count             | integer | True                                     
 |               | > 0                        | The maximum number of requests 
allowed within a given time interval.                                           
                                                                                
                                                                                
                                  |
+| rules.time_window       | integer | True                                     
 |               | > 0                        | The time interval corresponding 
to the rate limiting `count` in seconds.                                        
                                                                                
                                                                                
                                 |
+| rules.key               | string  | True                                     
 |               |                            | The key to count requests by. 
If the configured key does not exist, the rule will not be executed. The `key` 
is interpreted as a combination of variables, for example: `$http_custom_a 
$http_custom_b`.                                                                
                                                                                
                      [...]
 | key_type                | string  | False                                    
 | var         | ["var","var_combination","constant"] | The type of key. If the 
`key_type` is `var`, the `key` is interpreted a variable. If the `key_type` is 
`var_combination`, the `key` is interpreted as a combination of variables. If 
the `key_type` is `constant`, the `key` is interpreted as a constant.           
       |
 | key                     | string  | False                                    
 | remote_addr |                                        | The key to count 
requests by. If the `key_type` is `var`, the `key` is interpreted a variable. 
The variable does not need to be prefixed by a dollar sign (`$`). If the 
`key_type` is `var_combination`, the `key` is interpreted as a combination of 
variables. All variables should be prefixed by dollar signs (`$`). For example, 
to configure the `key` to use [...]
 | rejected_code           | integer | False                                    
 | 503           | [200,...,599]                          | The HTTP status 
code returned when a request is rejected for exceeding the threshold.           
                                                                                
                                                                                
                                                                                
                  [...]
diff --git a/docs/zh/latest/plugins/limit-count.md 
b/docs/zh/latest/plugins/limit-count.md
index 44fe97970..df4acfa71 100644
--- a/docs/zh/latest/plugins/limit-count.md
+++ b/docs/zh/latest/plugins/limit-count.md
@@ -45,8 +45,12 @@ description: limit-count 插件使用固定窗口算法,通过给定时间间
 
 | 名称                | 类型    | 必选项      | 默认值        | 有效值                      
             | 描述                                                               
                                                                                
                                                                                
  |
 | ------------------- | ------- | ---------- | ------------- | 
--------------------------------------- | 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 |
-| count | integer | 是 | | > 0 | 给定时间间隔内允许的最大请求数。 |
-| time_window | integer | 是 | | > 0 | 速率限制 `count` 对应的时间间隔(以秒为单位)。 |
+| count | integer | 否 | | > 0 | 给定时间间隔内允许的最大请求数。如果未配置 `rules`,则此项必填。 |
+| time_window | integer | 否 | | > 0 | 速率限制 `count` 对应的时间间隔(以秒为单位)。如果未配置 
`rules`,则此项必填。 |
+| rules | array[object] | 否 | | | 速率限制规则列表。每个规则是一个包含 `count`、`time_window` 和 
`key` 的对象。如果配置了 `rules`,则顶层的 `count` 和 `time_window` 将被忽略。 |
+| rules.count | integer | 是 | | > 0 | 给定时间间隔内允许的最大请求数。 |
+| rules.time_window | integer | 是 | | > 0 | 速率限制 `count` 对应的时间间隔(以秒为单位)。 |
+| rules.key | string | 是 | | | 用于统计请求的键。如果配置的键不存在,则不会执行该规则。`key` 
被解释为变量的组合,例如:`$http_custom_a $http_custom_b`。|
 | key_type | string | 否 | var | ["var","var_combination","constant"] | key 
的类型。如果`key_type` 为 `var`,则 `key` 将被解释为变量。如果 `key_type` 为 `var_combination`,则 
`key` 将被解释为变量的组合。如果 `key_type` 为 `constant`,则 `key` 将被解释为常量。 |
 | key | string | 否 | remote_addr | | 用于计数请求的 key。如果 `key_type` 为 `var`,则 `key` 
将被解释为变量。变量不需要以美元符号(`$`)为前缀。如果 `key_type` 为 `var_combination`,则 `key` 
会被解释为变量的组合。所有变量都应该以美元符号 (`$`) 为前缀。例如,要配置 `key` 使用两个请求头 `custom-a` 和 `custom-b` 
的组合,则 `key` 应该配置为 `$http_custom_a $http_custom_b`。如果 `key_type` 为 `constant`,则 
`key` 会被解释为常量值。|
 | rejection_code | integer | 否 | 503 | [200,...,599] | 请求因超出阈值而被拒绝时返回的 HTTP 
状态代码。|
diff --git a/t/admin/consumer-group.t b/t/admin/consumer-group.t
index 305afe48f..fd28570f1 100644
--- a/t/admin/consumer-group.t
+++ b/t/admin/consumer-group.t
@@ -287,7 +287,7 @@ passed
         }
     }
 --- response_body
-{"error_msg":"failed to check the configuration of plugin limit-count err: 
property \"count\" is required"}
+{"error_msg":"failed to check the configuration of plugin limit-count err: 
value should match only one schema, but matches none"}
 --- error_code: 400
 
 
diff --git a/t/admin/plugin-configs.t b/t/admin/plugin-configs.t
index ab6d2592b..4b72bc5c9 100644
--- a/t/admin/plugin-configs.t
+++ b/t/admin/plugin-configs.t
@@ -287,7 +287,7 @@ passed
         }
     }
 --- response_body
-{"error_msg":"failed to check the configuration of plugin limit-count err: 
property \"count\" is required"}
+{"error_msg":"failed to check the configuration of plugin limit-count err: 
value should match only one schema, but matches none"}
 --- error_code: 400
 
 
diff --git a/t/plugin/limit-count-rules.t b/t/plugin/limit-count-rules.t
new file mode 100644
index 000000000..cda541e65
--- /dev/null
+++ b/t/plugin/limit-count-rules.t
@@ -0,0 +1,380 @@
+#
+# 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';
+
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level('info');
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    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: configure count/time_window and rules at same time
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "plugins": {
+                            "limit-count": {
+                                "count": 2,
+                                "time_window": 5,
+                                "rejected_code": 503,
+                                "key_type": "var",
+                                "key": "remote_addr",
+                                "rules": [
+                                    {
+                                        "count": 1,
+                                        "time_window": 10,
+                                        "key": "${http_company}"
+                                    }
+                                ]
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.print(body)
+        }
+    }
+--- error_code: 400
+--- response_body
+{"error_msg":"failed to check the configuration of plugin limit-count err: 
value should match only one schema, but matches both schemas 1 and 2"}
+
+
+
+=== TEST 2: configure multiple rules with same key
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "plugins": {
+                            "limit-count": {
+                                "rejected_code": 503,
+                                "rules": [
+                                    {
+                                        "count": 5,
+                                        "time_window": 10,
+                                        "key": "${http_company}"
+                                    },
+                                    {
+                                        "count": 8,
+                                        "time_window": 20,
+                                        "key": "${http_company}"
+                                    }
+                                ]
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.print(body)
+        }
+    }
+--- error_code: 400
+--- response_body
+{"error_msg":"failed to check the configuration of plugin limit-count err: 
duplicate key '${http_company}' in rules"}
+
+
+
+=== TEST 3: setup route with rules
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "plugins": {
+                            "limit-count": {
+                                "rejected_code": 503,
+                                "rejected_msg" : "rejected",
+                                "rules": [
+                                    {
+                                        "key": "${http_user}",
+                                        "count": "${http_jack_count}",
+                                        "time_window": 60
+                                    },
+                                    {
+                                        "key": "${http_project}",
+                                        "count": "${http_apisix_count}",
+                                        "time_window": 60
+                                    }
+                                ]
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 4: no any rule matched
+--- request
+GET /hello
+--- error_code: 500
+--- error_log
+failed to get rate limit rules
+
+
+
+=== TEST 5: match user rule
+--- pipelined_requests eval
+["GET /hello", "GET /hello", "GET /hello"]
+--- more_headers
+user: jack
+jack-count: 2
+--- error_code eval
+[200, 200, 503]
+--- response_body eval
+["hello world\n", "hello world\n", "{\"error_msg\":\"rejected\"}\n"]
+
+
+
+=== TEST 6: match project rule
+--- pipelined_requests eval
+["GET /hello", "GET /hello"]
+--- more_headers
+project: apisix
+apisix-count: 3
+--- error_code eval
+[200, 200, 200, 503]
+--- response_body eval
+["hello world\n", "hello world\n", "hello world\n", 
"{\"error_msg\":\"rejected\"}\n"]
+
+
+
+=== TEST 7: setup route with rules with variables with default values
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "plugins": {
+                            "limit-count": {
+                                "rejected_code": 503,
+                                "rejected_msg" : "rejected",
+                                "rules": [
+                                    {
+                                        "count": "${http_count ?? 2}",
+                                        "time_window": "${http_tw ?? 5}",
+                                        "key": "${remote_addr}"
+                                    }
+                                ]
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 8: rules with variables in count - default value
+--- pipelined_requests eval
+["GET /hello", "GET /hello", "GET /hello"]
+--- error_code eval
+[200, 200, 503]
+--- response_body eval
+["hello world\n", "hello world\n", "{\"error_msg\":\"rejected\"}\n"]
+
+
+
+=== TEST 9: rules with variables in count - with header
+--- setup
+    ngx.sleep(5)
+--- pipelined_requests eval
+["GET /hello", "GET /hello"]
+--- more_headers
+count: 1
+--- error_code eval
+[200, 503]
+--- response_body eval
+["hello world\n", "{\"error_msg\":\"rejected\"}\n"]
+
+
+
+=== TEST 10: rules with same key and custom headers
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "plugins": {
+                            "limit-count": {
+                                "rejected_code": 503,
+                                "rejected_msg" : "rejected",
+                                "show_limit_quota_header": true,
+                                "rules": [
+                                    {
+                                        "count": 2,
+                                        "time_window": 2,
+                                        "key": "${remote_addr}_2s"
+                                    },
+                                    {
+                                        "count": 3,
+                                        "time_window": 5,
+                                        "key": "${remote_addr}_5s"
+                                    }
+                                ]
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 11: test rules with same key
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require("resty.http")
+            local httpc = http.new()
+            local uri = "http://127.0.0.1:"; .. ngx.var.server_port .. "/hello"
+
+            for i = 1, 2, 1 do
+                local res = httpc:request_uri(uri)
+                if res.status ~= 200 then
+                    ngx.say("first two requests failed, status: " .. 
res.status)
+                    return
+                end
+            end
+
+            -- req 3, rejected by rule 1
+            res = httpc:request_uri(uri)
+            if res.status ~= 503 then
+                ngx.say("req 3 should be rejected by rule 1, but got status: 
", res.status)
+                return
+            end
+
+            ngx.sleep(2)
+
+            -- req 4, after sleep
+            res = httpc:request_uri(uri)
+            if res.status ~= 200 then
+                ngx.say("req 4 failed, status: ", res.status)
+                return
+            end
+
+            -- req 5, rejected by rule 2
+            res = httpc:request_uri(uri)
+            if res.status ~= 503 then
+                ngx.say("req 5 should be rejected by rule 2, but got status: 
", res.status)
+                return
+            end
+
+            ngx.say("passed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
diff --git a/t/plugin/limit-count-variable.t b/t/plugin/limit-count-variable.t
index ad021c2d5..c26a858fa 100644
--- a/t/plugin/limit-count-variable.t
+++ b/t/plugin/limit-count-variable.t
@@ -141,5 +141,3 @@ GET /t
 --- timeout: 10
 --- response_body
 passed
---- error_log
-limit count: 3, time_window: 2
diff --git a/t/plugin/limit-count.t b/t/plugin/limit-count.t
index 402c93dd2..a1c96d93d 100644
--- a/t/plugin/limit-count.t
+++ b/t/plugin/limit-count.t
@@ -215,7 +215,7 @@ passed
     }
 --- error_code: 400
 --- response_body
-{"error_msg":"failed to check the configuration of plugin limit-count err: 
property \"count\" is required"}
+{"error_msg":"failed to check the configuration of plugin limit-count err: 
value should match only one schema, but matches none"}
 
 
 
@@ -326,7 +326,7 @@ passed
     }
 --- error_code: 400
 --- response_body
-{"error_msg":"failed to check the configuration of plugin limit-count err: 
property \"count\" is required"}
+{"error_msg":"failed to check the configuration of plugin limit-count err: 
value should match only one schema, but matches none"}
 
 
 
diff --git a/t/plugin/workflow.t b/t/plugin/workflow.t
index 022aa4851..a8e8e2d57 100644
--- a/t/plugin/workflow.t
+++ b/t/plugin/workflow.t
@@ -541,8 +541,8 @@ hello1 world
     }
 --- response_body
 done
-failed to validate the 'limit-count' action: property "time_window" is required
-failed to validate the 'limit-count' action: property "count" is required
+failed to validate the 'limit-count' action: value should match only one 
schema, but matches none
+failed to validate the 'limit-count' action: value should match only one 
schema, but matches none
 failed to validate the 'limit-count' action: group is not supported
 
 

Reply via email to