mirror of
https://github.com/alibaba/higress.git
synced 2026-02-26 05:30:50 +08:00
704 lines
21 KiB
Go
704 lines
21 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"testing"
|
||
|
||
"github.com/alibaba/higress/plugins/wasm-go/extensions/response-cache/config"
|
||
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
|
||
"github.com/higress-group/wasm-go/pkg/test"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
// 测试配置:使用header提取key
|
||
var configWithHeaderKey = func() json.RawMessage {
|
||
data, _ := json.Marshal(map[string]interface{}{
|
||
"cache": map[string]interface{}{
|
||
"type": "redis",
|
||
"serviceName": "redis.static",
|
||
"servicePort": 6379,
|
||
"timeout": 10000,
|
||
},
|
||
"cacheKeyFromHeader": "x-user-id",
|
||
"cacheValueFromBody": "data",
|
||
"cacheValueFromBodyType": "application/json",
|
||
"cacheResponseCode": []int{200},
|
||
})
|
||
return data
|
||
}()
|
||
|
||
// 测试配置:使用body提取key
|
||
var configWithBodyKey = func() json.RawMessage {
|
||
data, _ := json.Marshal(map[string]interface{}{
|
||
"cache": map[string]interface{}{
|
||
"type": "redis",
|
||
"serviceName": "redis.static",
|
||
"servicePort": 6379,
|
||
"timeout": 10000,
|
||
},
|
||
"cacheKeyFromBody": "user_id",
|
||
"cacheValueFromBody": "message.content",
|
||
"cacheValueFromBodyType": "application/json",
|
||
"cacheResponseCode": []int{200},
|
||
})
|
||
return data
|
||
}()
|
||
|
||
// 测试配置:使用整个body作为key
|
||
var configWithBodyAsKey = func() json.RawMessage {
|
||
data, _ := json.Marshal(map[string]interface{}{
|
||
"cache": map[string]interface{}{
|
||
"type": "redis",
|
||
"serviceName": "redis.static",
|
||
"servicePort": 6379,
|
||
"timeout": 10000,
|
||
},
|
||
"cacheKeyFromBody": "",
|
||
"cacheValueFromBody": "",
|
||
"cacheValueFromBodyType": "application/json",
|
||
"cacheResponseCode": []int{200},
|
||
})
|
||
return data
|
||
}()
|
||
|
||
// 测试配置:配置冲突(同时设置header和body key)
|
||
var configConflict = func() json.RawMessage {
|
||
data, _ := json.Marshal(map[string]interface{}{
|
||
"cache": map[string]interface{}{
|
||
"type": "redis",
|
||
"serviceName": "redis.static",
|
||
"servicePort": 6379,
|
||
},
|
||
"cacheKeyFromHeader": "x-user-id",
|
||
"cacheKeyFromBody": "user_id",
|
||
})
|
||
return data
|
||
}()
|
||
|
||
// 测试配置:最小配置
|
||
var minimalConfig = func() json.RawMessage {
|
||
data, _ := json.Marshal(map[string]interface{}{
|
||
"cache": map[string]interface{}{
|
||
"type": "redis",
|
||
"serviceName": "redis.static",
|
||
"servicePort": 6379,
|
||
},
|
||
})
|
||
return data
|
||
}()
|
||
|
||
// 测试配置:缺少cache provider
|
||
var configMissingCache = func() json.RawMessage {
|
||
data, _ := json.Marshal(map[string]interface{}{
|
||
"cacheKeyFromHeader": "x-user-id",
|
||
})
|
||
return data
|
||
}()
|
||
|
||
func TestParseConfig(t *testing.T) {
|
||
test.RunGoTest(t, func(t *testing.T) {
|
||
// 测试header key配置
|
||
t.Run("config with header key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
configRaw, err := host.GetMatchConfig()
|
||
require.NoError(t, err)
|
||
require.NotNil(t, configRaw)
|
||
|
||
cfg, ok := configRaw.(*config.PluginConfig)
|
||
require.True(t, ok)
|
||
require.Equal(t, "x-user-id", cfg.CacheKeyFromHeader)
|
||
require.Equal(t, "", cfg.CacheKeyFromBody)
|
||
require.Equal(t, "data", cfg.CacheValueFromBody)
|
||
require.Equal(t, []int32{200}, cfg.CacheResponseCode)
|
||
})
|
||
|
||
// 测试body key配置
|
||
t.Run("config with body key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
configRaw, err := host.GetMatchConfig()
|
||
require.NoError(t, err)
|
||
require.NotNil(t, configRaw)
|
||
|
||
cfg, ok := configRaw.(*config.PluginConfig)
|
||
require.True(t, ok)
|
||
require.Equal(t, "", cfg.CacheKeyFromHeader)
|
||
require.Equal(t, "user_id", cfg.CacheKeyFromBody)
|
||
require.Equal(t, "message.content", cfg.CacheValueFromBody)
|
||
})
|
||
|
||
// 测试整个body作为key
|
||
t.Run("config with body as key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyAsKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
configRaw, err := host.GetMatchConfig()
|
||
require.NoError(t, err)
|
||
require.NotNil(t, configRaw)
|
||
|
||
cfg, ok := configRaw.(*config.PluginConfig)
|
||
require.True(t, ok)
|
||
require.Equal(t, "", cfg.CacheKeyFromHeader)
|
||
require.Equal(t, "", cfg.CacheKeyFromBody)
|
||
})
|
||
|
||
// 测试配置冲突
|
||
t.Run("conflict config", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configConflict)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusFailed, status)
|
||
})
|
||
|
||
// 测试缺少cache provider
|
||
t.Run("missing cache provider", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configMissingCache)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusFailed, status)
|
||
})
|
||
|
||
// 测试最小配置
|
||
t.Run("minimal config", func(t *testing.T) {
|
||
host, status := test.NewTestHost(minimalConfig)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
configRaw, err := host.GetMatchConfig()
|
||
require.NoError(t, err)
|
||
require.NotNil(t, configRaw)
|
||
|
||
cfg, ok := configRaw.(*config.PluginConfig)
|
||
require.True(t, ok)
|
||
require.Equal(t, []int32{200}, cfg.CacheResponseCode)
|
||
})
|
||
})
|
||
}
|
||
|
||
func TestOnHttpRequestHeaders(t *testing.T) {
|
||
test.RunTest(t, func(t *testing.T) {
|
||
// 测试使用header key的请求头处理
|
||
t.Run("request headers with header key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头,包含cache key
|
||
action := host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
})
|
||
|
||
// 应该返回ActionContinue,因为从header提取key后继续处理
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试header key为空
|
||
t.Run("request headers with empty header key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头,不包含x-user-id
|
||
action := host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
})
|
||
|
||
// 应该返回ActionContinue,跳过缓存
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试skip cache header
|
||
t.Run("skip cache header", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置跳过缓存的请求头
|
||
action := host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
{"x-higress-skip-response-cache", "on"},
|
||
})
|
||
|
||
// 应该返回ActionContinue
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试使用body key的content-type检查
|
||
t.Run("request headers for body key with content type", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头,包含application/json content-type
|
||
action := host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 应该返回HeaderStopIteration,等待读取body
|
||
require.Equal(t, types.HeaderStopIteration, action)
|
||
})
|
||
|
||
// 测试content-type不匹配
|
||
t.Run("request headers with non-json content type", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头,content-type不是json
|
||
action := host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
{"content-type", "text/plain"},
|
||
})
|
||
|
||
// 应该返回ActionContinue,跳过缓存
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试无content-type
|
||
t.Run("request headers without content type", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头,无content-type
|
||
action := host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
})
|
||
|
||
// 应该返回ActionContinue,跳过缓存
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
})
|
||
}
|
||
|
||
func TestOnHttpRequestBody(t *testing.T) {
|
||
test.RunTest(t, func(t *testing.T) {
|
||
// 测试从body提取key
|
||
t.Run("request body with key extraction", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 构造请求体
|
||
requestBody := `{"user_id": "user123", "data": "test"}`
|
||
action := host.CallOnHttpRequestBody([]byte(requestBody))
|
||
|
||
// 应该返回ActionPause,等待缓存检查结果
|
||
require.Equal(t, types.ActionPause, action)
|
||
})
|
||
|
||
// 测试从body提取key失败(key为空)
|
||
t.Run("request body with empty key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 构造请求体,不包含user_id字段
|
||
requestBody := `{"data": "test"}`
|
||
action := host.CallOnHttpRequestBody([]byte(requestBody))
|
||
|
||
// 应该返回ActionContinue,跳过缓存
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试整个body作为key
|
||
t.Run("request body as key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyAsKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 构造请求体
|
||
requestBody := `{"data": "test"}`
|
||
action := host.CallOnHttpRequestBody([]byte(requestBody))
|
||
|
||
// 应该返回ActionPause
|
||
require.Equal(t, types.ActionPause, action)
|
||
})
|
||
})
|
||
}
|
||
|
||
func TestOnHttpResponseHeaders(t *testing.T) {
|
||
test.RunTest(t, func(t *testing.T) {
|
||
// 测试响应头处理 - 状态码200
|
||
t.Run("response headers with 200 status", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
})
|
||
|
||
// 设置响应头
|
||
action := host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "200"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 应该返回ActionContinue
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试响应头处理 - 状态码500(不支持缓存)
|
||
t.Run("response headers with 500 status", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
})
|
||
|
||
// 设置响应头,状态码500
|
||
action := host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "500"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 应该返回ActionContinue,但跳过缓存
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试skip cache header的处理
|
||
t.Run("response headers with skip cache", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头,包含skip cache标志
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
{"x-higress-skip-response-cache", "on"},
|
||
})
|
||
|
||
// 设置响应头
|
||
action := host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "200"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 应该返回ActionContinue
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
})
|
||
}
|
||
|
||
func TestOnHttpResponseBody(t *testing.T) {
|
||
test.RunTest(t, func(t *testing.T) {
|
||
// 测试响应体处理 - 提取特定字段
|
||
t.Run("response body with value extraction", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
})
|
||
|
||
// 设置响应头
|
||
host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "200"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 构造响应体
|
||
responseBody := `{"data": "cached value", "other": "ignored"}`
|
||
action := host.CallOnHttpResponseBody([]byte(responseBody))
|
||
|
||
// 应该返回ActionContinue
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试响应体处理 - 整个body作为value
|
||
t.Run("response body as value", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyAsKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 设置请求体
|
||
host.CallOnHttpRequestBody([]byte(`{"test": "data"}`))
|
||
|
||
// 设置响应头
|
||
host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "200"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 构造响应体
|
||
responseBody := `{"data": "full response"}`
|
||
action := host.CallOnHttpResponseBody([]byte(responseBody))
|
||
|
||
// 应该返回ActionContinue
|
||
require.Equal(t, types.ActionContinue, action)
|
||
})
|
||
|
||
// 测试无key的响应体处理
|
||
t.Run("response body without key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置响应头,不经过请求处理
|
||
host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "200"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 构造响应体
|
||
responseBody := `{"data": "test"}`
|
||
host.CallOnHttpResponseBody([]byte(responseBody))
|
||
})
|
||
})
|
||
}
|
||
|
||
// 测试缓存命中流程
|
||
func TestCacheHitFlow(t *testing.T) {
|
||
test.RunTest(t, func(t *testing.T) {
|
||
// 测试完整的缓存命中流程
|
||
t.Run("complete cache hit flow with header key", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
})
|
||
|
||
// 模拟Redis缓存命中 - 返回之前缓存的data字段值
|
||
cacheHitResp := test.CreateRedisRespString("cached value")
|
||
host.CallOnRedisCall(0, cacheHitResp)
|
||
|
||
// 完成HTTP请求
|
||
host.CompleteHttp()
|
||
|
||
// 验证缓存命中的响应
|
||
localResp := host.GetLocalResponse()
|
||
require.Equal(t, uint32(200), localResp.StatusCode)
|
||
require.Equal(t, "cached value", string(localResp.Data))
|
||
require.True(t, test.HasHeaderWithValue(localResp.Headers, "content-type", "application/json"))
|
||
require.True(t, test.HasHeaderWithValue(localResp.Headers, "x-cache-status", "hit"))
|
||
})
|
||
|
||
// 测试缓存未命中然后存储的流程
|
||
t.Run("cache miss and store flow", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
})
|
||
|
||
// 模拟Redis缓存未命中(返回null)
|
||
cacheMissResp := test.CreateRedisRespNull()
|
||
host.CallOnRedisCall(0, cacheMissResp)
|
||
|
||
// 设置响应头
|
||
host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "200"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 设置响应体
|
||
responseBody := `{"data": "new data", "other": "ignored"}`
|
||
action := host.CallOnHttpResponseBody([]byte(responseBody))
|
||
require.Equal(t, types.ActionContinue, action)
|
||
|
||
// 模拟Redis存储操作(SET操作返回OK)
|
||
storeResp := test.CreateRedisRespArray([]interface{}{"OK"})
|
||
host.CallOnRedisCall(0, storeResp)
|
||
|
||
// 完成HTTP请求
|
||
host.CompleteHttp()
|
||
})
|
||
|
||
// 测试两次请求:第一次miss,第二次hit
|
||
t.Run("first request miss then second request hit", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithHeaderKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// ========== 第一次请求:缓存未命中 ==========
|
||
// 设置请求头
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"},
|
||
})
|
||
|
||
// 模拟Redis缓存未命中(第一次查询返回null)
|
||
cacheMissResp := test.CreateRedisRespNull()
|
||
host.CallOnRedisCall(0, cacheMissResp)
|
||
|
||
// 设置响应头
|
||
host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "200"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
// 设置响应体
|
||
responseBody := `{"data": "first response"}`
|
||
action := host.CallOnHttpResponseBody([]byte(responseBody))
|
||
require.Equal(t, types.ActionContinue, action)
|
||
|
||
// 模拟Redis SET操作(第一次请求后将数据存入缓存)
|
||
storeResp := test.CreateRedisRespArray([]interface{}{"OK"})
|
||
host.CallOnRedisCall(0, storeResp)
|
||
host.CompleteHttp()
|
||
|
||
// 设置请求头(相同的x-user-id)
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "GET"},
|
||
{"x-user-id", "user123"}, // 相同的user ID
|
||
})
|
||
|
||
// 模拟Redis缓存命中(第二次查询返回缓存的数据)
|
||
cacheHitResp := test.CreateRedisRespString("first response")
|
||
host.CallOnRedisCall(0, cacheHitResp)
|
||
|
||
// 完成HTTP请求
|
||
host.CompleteHttp()
|
||
|
||
// 验证第二次请求返回的是缓存的数据
|
||
localResp := host.GetLocalResponse()
|
||
require.Equal(t, uint32(200), localResp.StatusCode)
|
||
require.Equal(t, "first response", string(localResp.Data))
|
||
require.True(t, test.HasHeaderWithValue(localResp.Headers, "x-cache-status", "hit"))
|
||
require.True(t, test.HasHeaderWithValue(localResp.Headers, "content-type", "application/json"))
|
||
})
|
||
|
||
// 测试body key的两次请求流程
|
||
t.Run("body key first miss then second hit", func(t *testing.T) {
|
||
host, status := test.NewTestHost(configWithBodyKey)
|
||
defer host.Reset()
|
||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||
|
||
// 第一次请求
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
requestBody := `{"user_id": "user123"}`
|
||
host.CallOnHttpRequestBody([]byte(requestBody))
|
||
|
||
// Redis缓存未命中
|
||
cacheMissResp := test.CreateRedisRespNull()
|
||
host.CallOnRedisCall(0, cacheMissResp)
|
||
|
||
// 响应
|
||
host.CallOnHttpResponseHeaders([][2]string{
|
||
{":status", "200"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
|
||
responseBody := `{"message": {"content": "hello world"}}`
|
||
host.CallOnHttpResponseBody([]byte(responseBody))
|
||
|
||
// 存储到Redis
|
||
storeResp := test.CreateRedisRespArray([]interface{}{"OK"})
|
||
host.CallOnRedisCall(0, storeResp)
|
||
host.CompleteHttp()
|
||
|
||
host.CallOnHttpRequestHeaders([][2]string{
|
||
{":authority", "example.com"},
|
||
{":path", "/api/data"},
|
||
{":method", "POST"},
|
||
{"content-type", "application/json"},
|
||
})
|
||
host.CallOnHttpRequestBody([]byte(`{"user_id": "user123"}`))
|
||
|
||
// 缓存命中
|
||
cacheHitResp := test.CreateRedisRespString("hello world")
|
||
host.CallOnRedisCall(0, cacheHitResp)
|
||
host.CompleteHttp()
|
||
|
||
// 验证第二次请求返回的是缓存的数据
|
||
localResp := host.GetLocalResponse()
|
||
require.Equal(t, uint32(200), localResp.StatusCode)
|
||
require.Equal(t, "hello world", string(localResp.Data))
|
||
require.True(t, test.HasHeaderWithValue(localResp.Headers, "x-cache-status", "hit"))
|
||
require.True(t, test.HasHeaderWithValue(localResp.Headers, "content-type", "application/json"))
|
||
})
|
||
})
|
||
}
|