考虑获取令牌的请求,例如:
const axios = require('axios');
const FormData = require('form-data');
let data = new FormData();
data.append('client_id', 'SOMEID');
data.append('client_secret', 'SOMESECRER');
data.append('scope', 'ouauth.scope.read');
data.append('grant_type', 'client_credentials');
let config = {
method: 'post',
maxBodyLength: Infinity,
url: 'https://url/oauth2/token',
headers: {
...data.getHeaders()
},
data : data
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
还有一个测试,例如
const mockProvider = new Pact({
consumer: 'some-consumer',
provider: 'fake-provider',
logLevel: 'debug',
})
mockProvider.addInteraction({
uponReceiving: 'a request to get a new token',
withRequest: {
method: 'POST',
path: '/oauth2/token',
body: like({
client_id: config.clientId,
client_secret: config.secret,
grant_type: 'client_credentials',
scope: config.oAuthScope,
}),
headers: {
'Content-Type': like('multipart/form-data'),
},
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: like(expectedToken),
},
})
})
我不断收到错误消息
2023-12-05T12:42:09.017174Z 调试 tokio-runtime-worker pact_mock_server::hyper_server:请求不匹配:请求不匹配 匹配 - HTTP 请求(方法:POST,路径:/oauth2/token,查询:无, 标题:一些({“Content-Type”:[“multipart/form-data”]}),主体: Present(145 bytes) ) 0) $ -> 无法将预期正文解析为 MIME 多部分主体:“内容类型没有边界”
如果我省略了测试可以通过的主体,我将如何绕过边界?计算边界不起作用,因为它似乎会根据每个请求而改变
提前致谢
pact-foundation/pact-js
问题 1140 所示,Pact JS 本身不支持将多部分表单数据与动态生成的边界相匹配。
原始的Axios请求正确发送多部分表单数据,但每个请求动态生成一个新的边界。这使得 Pact 交互中的匹配变得困难,因为 Pact 需要一致的格式来进行验证。
在 HTTP 中,
multipart/form-data
用于作为 POST
请求的一部分高效上传二进制数据或文件。 “边界”分隔符是一个字符串,用于分隔多部分数据的不同部分,以了解一个部分的结束位置和下一个部分的开始位置。multipart/form-data
请求时,Content-Type
标头包含此边界字符串。例如:Content-Type: multipart/form-data; boundary=abc123
。
边界可以由客户端(例如 Web 浏览器或 Axios 等 HTTP 客户端库)“动态”生成。这样,边界字符串就不会意外出现在正在发送的数据中,这可能会破坏请求的格式。这意味着每次您发出 multipart/form-data
请求时,都会生成一个新的、唯一的边界字符串。
Content-Type
标头中的任何边界(如果请求为
multipart/form-data
)。但是......,由于边界是动态的并且随着每个请求而变化,因此在 Pact 测试中精确匹配它变得具有挑战性。因此,出现“no boundary in content-type
”错误。
您可以在测试中使用固定边界字符串。这允许您在 Pact 交互中显式设置并期望相同的边界字符串。或者,如下所示,您可以尝试从 Axios 配置中提取实际的请求正文,其中包括动态生成的边界。通过在 Pact 交互中使用这个精确的主体,您可以避免必须预测或匹配动态边界的问题。
+------------------------+ +-------------------------+ +------------------+
| TypeScript Application | | Axios Request | | Pact Mock Server |
| - Creates FormData | | - Sends multipart form | | - Matches Request |
| - Appends form data | ----> | request with FormData | | with extracted |
| - Sends request | | - Extracts request body | ----> | request body |
| via Axios | | from config | | - Validates |
+------------------------+ +-------------------------+ | interaction |
+------------------+
您的文件(让我们将其命名为
OAuthTokenPactTest.ts
)将是:
import axios from 'axios';
import FormData from 'form-data';
import { Pact, Matchers } from '@pact-foundation/pact';
const { like } = Matchers;
const mockProvider = new Pact({
consumer: 'some-consumer',
provider: 'fake-provider',
logLevel: 'debug',
});
const fetchToken = async () => {
const data = new FormData();
data.append('client_id', 'SOMEID');
data.append('client_secret', 'SOMESECRER');
data.append('scope', 'ouauth.scope.read');
data.append('grant_type', 'client_credentials');
const config = {
method: 'POST',
url: 'https://url/oauth2/token',
headers: {
'Content-Type': 'multipart/form-data',
},
data,
};
// Send the request
const response = await axios.request(config);
// Extract the request body
const requestBody = response.config.data;
mockProvider.addInteraction({
uponReceiving: 'a request to get a new token',
withRequest: {
method: 'POST',
path: '/oauth2/token',
body: requestBody,
headers: {
'Content-Type': like('multipart/form-data'),
},
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: like(/* expectedToken */),
},
});
};
fetchToken();
关键的变化是使用
response.config.data
提取实际的请求正文,而不是尝试在 Pact 交互中匹配多部分表单的动态边界。这绕过了处理边界问题的需要。
Pact 交互是通过实际请求正文设置的。这样,它就与 Axios 发送的内容相匹配,包括多部分表单数据结构和边界。
project-root/
│
├── src/ # Source files for the application
│ ├── app.ts # Main application file
│ └── # Other application-specific files
│
├── tests/ # Test files
│ ├── pact/ # Pact-specific tests
│ │ ├── OAuthTokenPactTest.ts # The modified file for OAuth token Pact test
│ │ └── # Other Pact test files
│ └── # Other test files (unit, integration, etc.)
│
├── node_modules/ # Node.js modules
│
├── package.json # Project metadata and dependencies
├── tsconfig.json # TypeScript configuration
└── ...
tests/pact/
是专门用于Pact相关测试的子目录。这就是
OAuthTokenPactTest.ts
所在的位置。由于其独特的设置和依赖性,最好将 Pact 测试分开。