am very new to Pact-js and contract testing I'll try my best to explain my issue.
for now, I am only trying to generate the consumer contract
here is my pact provider:
export const provider = new Pact({
consumer: 'Users',
provider: 'UsersService',
port: 1234,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
pactfileWriteMode: 'overwrite',
logLevel: "DEBUG",
dir: path.resolve(process.cwd(), 'pacts'),
});
and here is my test:
jest.mock("axios");
const EXPECTED_BODY = {...}
describe("Pact tests axios", () => {
describe("/GET login", () => {
beforeAll(() => provider.setup());
afterEach(()=> provider.verify())
afterAll(() => provider.finalize());
it("should login user and response with user object", async () => {
await provider.addInteraction({
state: 'user logged', uponReceiving: 'request logged user', withRequest: {
method: 'GET',
path: '/users/login',
body: {username: "test", password: "11223344"},
}, willRespondWith: {
status: 200, headers: {
'Content-Type': 'application/json',
}, body: eachLike(EXPECTED_BODY),
},
});
axios.get.mockResolvedValueOnce(EXPECTED_BODY);
const loggedUser = await loginUser("test", "11223344")
expect(axios.get).toHaveBeenCalledTimes(1)
expect(axios.get).toHaveBeenLastCalledWith("http://localhost:8080/users/login", {"headers": {"Content-Type": "application/json"}, "params": {"password": "11223344", "username": "test"}})
expect(loggedUser).toEqual(EXPECTED_BODY)
})
});
})
I should say that my original request takes two parameters username and password and returns an object containing all that user's information of course the user exists if not it returns null
here is the API call function if needed:
export default async function loginUser(username, password) {
try{
return await axios.get(("http://localhost:8080/users/login"), {
headers: {
"Content-Type": "application/json"
},
params: {
username: username,
password: password
}
})
}catch (e){
return null
}
}
Pact expects you to actually make the call to the endpoint you're mocking in Pact.
Missing requests: GET /users/login
This error says "you said you'd make a GET call to /users/login but I didn't receive it".
jest.mock("axios");
This looks like you're mocking the HTTP client Axios. Now you have a mock for the thing that needs to send requests to the Pact Mock.
In a Pact test, think of it as a unit tests for your API client code. The actual request needs to be sent to the Pact Mock and Pact will check the correct request was made, and return back the mocked response.
So the solution is simple:
Remove all of the axios mocking
Provide a way to modify the API target for loginUser
Configure your API client to send the request to localhost:1234 instead of the real thing before running the tests
(NOTE: you can have pact find a free port dynamically by not setting the port option, and take the host from the response from the setup() call)
Related
I am developing a web application using a React frontend and a Node.js backend. The frontend sends a POST request to the backend using Axios like this:
Register.js
...
handleSubmit = (e) => {
e.preventDefault();
const { email, password, name, dateofbirth } = this.state;
const user = { email, password, name, dateofbirth };
const url = "http://localhost:9000/register";
axios
.post(url, user, {
headers: {
"Content-Type": "application/json",
},
})
.then((response) => console.log(response))
.catch((error) => {
console.error("You have made a big error. " + error);
console.log(user);
});
};
...
While the backend receives the request like this:
./routes/register.js
...
router.post("/register", async (req, res) => {
console.log("Inside Home Login");
res.writeHead(200, {
"Content-Type": "application/json",
});
console.log("Users : ", JSON.stringify(users));
res.end(JSON.stringify(users));
})
...
However I get the error "POST http://localhost:9000/register 404 (Not Found)" upon trying to send anything.
My guess would be that you are routing in your index.js. If you can provide a code sample to figure it out.
If so, the thing is defining a routing like,
app.use('/register', yourImportedVariable);
does define a route at http://localhost:9000/register.
So, if in your routes/register.js file you define a GET endpoint with '/register' your front-end call must be http://localhost:9000/register/register
To fix it, either rename your route as '/', or fix your front-end call with the above url.
I am very new to the Google OAUth2.0 authentication and thus my question sounds like dumb. However, I am stuck with this problem quite a time and need your input to solve it.
I was integrating the Globus login within my app. Globus login using Google OAuth-2 protocol for authentication. According to the Globus Auth developer guide, I successfully redirect the app to their authorization service, the user can put their credential to authenticate, and the app receives the code returned from the Globus Auth server upon successful authentication. Next step is sending the code to the Token endpoint to get the access token. I used the following code:
var querystring = require('querystring');
export const logInGlobus = (payload) => {
let tokenUri = encodeURIComponent(payload.redirect_uri);
let client_id = 'out app client id'
let client_secret = 'client secret generated for authentication'
let cred = btoa(client_secret);
return axios.post('https://auth.globus.org/v2/oauth2/token',
querystring.stringify({
grant_type: 'authorization_code',
code: payload.code,
redirect_uri: tokenUri,
client_id: client_id
}),
{
headers:{
Authorization: 'Basic '+ cred,
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(response => {
return{
res: response,
success: true
}
})
.catch(err => {
return{
res: err,
success: false
}
})
}
I am getting 401 {"error":"invalid_client"} code for this post request from the server. What am I missing?
N.B: I have tried without client secret, client id, not encoding redirect URL. No luck so far>
I would really appreciate your effort if you show me some light. Thanks for your time.
====Edited====
The error from the console at the browser is attached
I solved the problem. I had to put the client secret at the body of the post request. The following code resolves my problem.
var querystring = require('querystring');
export const logInGlobus = (payload) => {
let client_id = 'app client id'
let client_secret = 'client secret generated for authentication'
return axios.post('https://auth.globus.org/v2/oauth2/token',
querystring.stringify({
grant_type: 'authorization_code',
code: payload.code,
redirect_uri: payload.redirect_uri,
client_id: client_id,
client_secret: client_secret
}),
{
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(response => {
return{
res: response,
success: true
}
})
.catch(err => {
return{
res: err,
success: false
}
})
}
I am converting a code that connected AWS Lambda to nanoexpress. My VueJS frontend was not passing data correctly so I started to do changes and now I cannot reach the backend at all and I have no idea why.
The backend is trivial:
const nanoexpress = require('nanoexpress');
const app = nanoexpress();
app.post('/v1/authorizeUser', async (req) => {
console.log(req);
const { email, password } = req.body;
console.log(email);
return { status: 'Ok', body: req.body };
});
app.listen(3000).then(r => console.log('started'));
This is the original store.js code. I am able to send the request from Vue app in Chrome but it is not parsed correctly. Probably because of missing or incorrect content type
const axiosResponse = await axios.post(`${API_ENDPOINT}/authorizeUser`, JSON.stringify({
email: payload.email,
password: payload.password,
}));
and the server log:
body: [Object: null prototype] {
'{"email":"literak#seznam.cz","password":"centrum"}': ''
}
undefined
When I added Axios options, the chrome shows an error for this request but there is nothing on server.
const options = {
headers: {
'Content-Type': 'application/json',
},
};
const axiosResponse = await axios.post(`${API_ENDPOINT}/authorizeUser`, JSON.stringify({
email: payload.email,
password: payload.password,
}), options);
Chrome request looks strange, there is no response section, request headers are very different and I can see in the Timing tab, that the request stalled.
If I remove stringify function, it behaves the same:
const axiosResponse = await axios.post(`${API_ENDPOINT}/authorizeUser`, {
email: payload.email,
password: payload.password,
}, options);
And same trouble without stringify and custom options:
const axiosResponse = await axios.post(`${API_ENDPOINT}/authorizeUser`, {
email: payload.email,
password: payload.password,
}, options);
Only the first variant with stringify and without options reaches the server. I use Postman the request is processed by the server.
logs:
body: { email: 'literak#seznam.cz', password: 'centrum' }
}
literak#seznam.cz
Axios is the latest 0.19.2 version. Chrome is up to date as well. I have no idea why the other combinations do not reach the server. It looks like Chrome received a correct request. What can be wrong? It drives me crazy.
Update: console
Error: Network Error
at createError (createError.js?2d83:16)
at XMLHttpRequest.handleError (xhr.js?b50d:83)
CORS in nanoexpress is apparently known bug:
const corsPerRoute = cors();
app.options('/my-route', corsPerRoute, () => {});
app.get('/my-route', corsPerRoute, (req, res) => {
res.send('this route protected by your cors per-route config');
});
I want to automate the OAuth 2.0 token automatically via javascript. Is there any way I can do that and obtain the token to use it in the artillery scrtips.
For the OAuth token generation I have below details:
Auth URL
Client ID
Scope
It is done by client authentication credentials.
Below is the sample code I am using to generate the token:
var ClientOAuth2 = require('client-oauth2')
var Auth = new ClientOAuth2({
clientId: 'ClientID',
accessTokenUri: 'https://Auth_URL/v2.0/token',
authorizationUri: 'https://Auth_URL/v2.0/authorize',
redirectUri: 'https://Auth_URL/',
scope: 'api://Scope/access_as_user'
})
Auth.owner.getToken('Username', 'password')
.then(async (user) => {
await console.log(user) //=> { accessToken: '...', tokenType: 'bearer', ... }
}).catch((e) => { console.log('error show',e); })
.finally( () => console.log('end'));
You can declare your custom JS files which will be triggered every time before the request:
Your YAML file can be like here:
config:
target: "https://baseUrl.com"
phases:
- duration: 60
arrivalRate: 100
processor: "./customFile.js"
scenarios:
- flow:
- post:
url: "/pathInYourApi"
headers:
Content-Type: "application/json"
Accept: application/json
json: {}
beforeRequest: "beforeRequest"
and then your customFile.js script:
module.exports = {
beforeRequest: beforeRequest,
};
function beforeRequest(requestParams, context, ee, next) {
// Call your OAuth client, and after you obtain token you can assign it to requestParams Authorization header
// eg. requestParams.headers.Authorization = `Bearer + ${token}`
return next(); // MUST be called for the scenario to continue
}
I have a contact form on my front end react application that I want to post to an Lambda function that is behind a API Gateway which in turn has a custom domain on top of it.
My front end runs on domain dev.example.com:3000
My API Gateway is on contact.example.com
Further more, I have created my Lambda function with serverless and in my YAML file, have enabled CORS as so:
# serverless.yml
service: contact-form-api
custom:
secrets: ${file(secrets.json)}
provider:
name: aws
runtime: nodejs8.10
stage: ${self:custom.secrets.NODE_ENV}
region: us-east-1
environment:
NODE_ENV: ${self:custom.secrets.NODE_ENV}
EMAIL: ${self:custom.secrets.EMAIL}
DOMAIN: ${self:custom.secrets.DOMAIN}
iamRoleStatements:
- Effect: "Allow"
Action:
- "ses:SendEmail"
Resource: "*"
functions:
send:
handler: handler.send
events:
- http:
path: email/send
method: post
cors: true
I am using AXIOS to make my post request which happens client side:
const data = await axios.post(
"https://contact.example.com/email/send",
formData,
{
"Content-Type": "application/json",
}
)
And the error I get is:
Access to XMLHttpRequest at 'https://contact.example.com/email/send' from origin 'http://dev.example.com:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains the invalid value 'example.com'.
I would have thought having the front end and API on the same domain would get around any cors errors (although I am spoofing dev.example.com) locally in order to test). I would also think the cors setting in my YAML file would get around it.
Anybody know why I might still be getting this CORS error?
Edit: Showing handler code that runs in Lambda function
// handler.js
const aws = require('aws-sdk')
const ses = new aws.SES()
const myEmail = process.env.EMAIL
const myDomain = process.env.DOMAIN
function generateResponse (code, payload) {
return {
statusCode: code,
headers: {
'Access-Control-Allow-Origin': "*",
'Access-Control-Allow-Headers': 'x-requested-with',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify(payload)
}
}
function generateError (code, err) {
console.log(err)
return {
statusCode: code,
headers: {
'Access-Control-Allow-Origin': "*",
'Access-Control-Allow-Headers': 'x-requested-with',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify(err.message)
}
}
function generateEmailParams (body) {
const { email, name, content } = JSON.parse(body)
console.log(email, name, content)
if (!(email && name && content)) {
throw new Error('Missing parameters! Make sure to add parameters \'email\', \'name\', \'content\'.')
}
return {
Source: myEmail,
Destination: { ToAddresses: [myEmail] },
ReplyToAddresses: [email],
Message: {
Body: {
Text: {
Charset: 'UTF-8',
Data: `Message sent from email ${email} by ${name} \nContent: ${content}`
}
},
Subject: {
Charset: 'UTF-8',
Data: `You received a message from ${myDomain}!`
}
}
}
}
module.exports.send = async (event) => {
try {
const emailParams = generateEmailParams(event.body)
const data = await ses.sendEmail(emailParams).promise()
return generateResponse(200, data)
} catch (err) {
return generateError(500, err)
}
}
You should add an HTTP request header as 'Content-type':
Now, go to Integration requests, and change the mapping template as follows:
Hope, it helps. and don't forgot to deploy the API before testing.