Postman test script - how to call an api twice to simulate 409 error - postman

I am trying to run a few automated testing using the Postman tool. For regular scenarios, I understand how to write pre-test and test scripts. What I do not know (and trying to understand) is, how to write scripts for checking 409 error (let us call it duplicate resource check).
I want to run a create resource api like below, then run it again and ensure that the 2nd invocation really returns 409 error.
POST /myservice/books
Is there a way to run the same api twice and check the return value for 2nd invocation. If yes, how do I do that. One crude way of achieving this could be to create a dependency between two tests, where the first one creates a resource, and the second one uses the same payload once again to create the same resource. I am looking for a single test to do an end-to-end testing.

Postman doesn't really provide a standard way, but is still flexible. I realized that we have to write javascript code in the pre-request tab, to do our own http request (using sendRequest method) and store the resulting data into env vars for use by the main api call.
Here is a sample:
var phone = pm.variables.replaceIn("{{$randomPhoneNumber}}");
console.log("phone:", phone)
var baseURL = pm.variables.replaceIn("{{ROG_SERVER}}:{{ROG_PORT}}{{ROG_BASE_URL}}")
var usersURL = pm.variables.replaceIn("{{ROG_SERVICE}}/users")
var otpURL = `${baseURL}/${phone}/_otp_x`
// Payload for partner creation
const payload = {
"name": pm.variables.replaceIn("{{username}}"),
"phone":phone,
"password": pm.variables.replaceIn("{{$randomPassword}}"),
}
console.log("user payload:", payload)
function getOTP (a, callback) {
// Get an OTP
pm.sendRequest(otpURL, function(err, response) {
if (err) throw err
var jsonDaata = response.json()
pm.expect(jsonDaata).to.haveOwnProperty('otp')
pm.environment.set("otp", jsonDaata.otp)
pm.environment.set("phone", phone);
pm.environment.set("username", "{{$randomUserName}}")
if (callback) callback(jsonDaata.otp)
})
}
// Get an OTP
getOTP("a", otp => {
console.log("OTP received:", otp)
payload.partnerRef = pm.variables.replaceIn("{{$randomPassword}}")
payload.otp = otp
//create a partner user with the otp.
let reqOpts = {
url: usersURL,
method: 'POST',
headers: { 'Content-Type': 'application/json'},
body: JSON.stringify(payload)
}
pm.sendRequest(reqOpts, (err, response) => {
console.log("response?", response)
pm.expect(response).to.have.property('code', 201)
})
// Get a new OTP for the main request to be executed.
getOTP()
})

I did it in my test block. Create your normal request as you would send it, then in your tests, validate the original works, and then you can send the second command and validate the response.
You can also use the pre and post scripting to do something similar, or have one test after the other in the file (they run sequentially) to do the same testing.
For instance, I sent an API call here to create records. As I need the Key_ to delete them, I can make a call to GET /foo at my API
pm.test("Response should be 200", function () {
pm.response.to.be.ok;
pm.response.to.have.status(200);
});
pm.test("Parse Key_ values and send DELETE from original request response", function () {
var jsonData = JSON.parse(responseBody);
jsonData.forEach(function (TimeEntryRecord) {
console.log(TimeEntryRecord.Key_);
const DeleteURL = pm.variables.get('APIHost') + '/bar/' + TimeEntryRecord.Key_;
pm.sendRequest({
url: DeleteURL,
method: 'DELETE',
header: { 'Content-Type': 'application/json' },
body: { TimeEntryRecord }
}, function (err, res) {
console.log("Sent Delete: " + DeleteURL );
});
});
});

Related

How to run a GET and a POST request from pre request tab of another request in postman?

I am in a situation where I need to run a request (lets say request C) based on the responses of request A and B. Here A is a GET & B is a POST request. Now I have tried to use pm.sendRequest twice in pre-request tab of C. But the problem I am facing mainly is, B is running ahead of A all the time. That means POST is running before GET. As a result I am unable to successfully run request C.
Here is a sample of my pre request script:
const getOTP = {
method: 'GET',
url: `${pm.environment.get('base_url')}/${pm.environment.get('common_path')}/otp-login?msisdn=${pm.environment.get('msisdnWhite')}`,
header: {
'User-Agent': 'something..',
'Accept-Language' : 'en'
},
};
setTimeout(15000)
const postOTP={
method : 'POST',
url : `${pm.environment.get('base_url')}/${pm.environment.get('common_path')}/otp-login`,
header: {
'Content-Type':'application/json',
'Accept-Language':'en'
},
body:{
mode : 'application/json',
raw: JSON.stringify(
{
"msisdn":"123456789",
"otp":"0000"
} )
}
};
pm.sendRequest(getOTP, (err, response) => {
const jsonResponse = response.json();
pm.environment.set("GETOTP",jsonResponse.result)
console.log(jsonResponse);
});
pm.sendRequest(postOTP, (err, response) => {
const jsonData = response.json();
pm.environment.set("access_token",jsonData.access_token)
});
It seems you need to run request A, wait for the response, then run request B, and wait for the response again.
To do this you could trigger the sendRequest() for B in the callback of the A request.
For example:
// send GET request A
pm.sendRequest (getOTP, (err, response) => {
// here you are in the callback of the request A
// this runs after the response of the request A
const jsonResponse = response.json ();
pm.environment.set ("GETOTP", jsonResponse.result)
console.log (jsonResponse);
// send POST request B
pm.sendRequest (postOTP, (err, response) => {
// here you are in the callback of the request B
const jsonData = response.json ();
pm.environment.set ("access_token", jsonData.access_token)
});
});
I saw you set a setTimeout(15000), I guess it's to wait for the answer of A. You shouldn't use setTimeout for this purpose, so please delete it.
That said you need to know that if you wanted the setTimeout() to be useful you need it between the two sendRequest() functions. It doesn't do much right now, between the two variables declarations.

Nuxt Vuex Helper not sending Client Cookies to API

Okay, I have the bad feeling that I'm missing a key concept in what I'm doing. Hope someone can help me out with a hint.
I'm using Nuxt and Vuex Store Modules. Every fetch a Module Action does is wrapped in a helper Function (saveFetch) that I imported to decrease repetitive code, like this:
export const actions = {
async sampleAction(context, data){
...
await saveFetch(context, 'POST', '/pages', data)
...
}
}
The helper simple checks if the users accessToken is still valid, refreshes it if not and then sends the request:
export const saveFetch = async (context, method = 'POST', path, data) => {
const accessTokenExpiry = context.rootGetters['auth/getAccessTokenExpiry']
let accessToken = context.rootGetters['auth/getAccessToken']
// If the client provides an accessToken and the accessToken is expired,
// refresh the token before making the "real" fetch.
if (accessToken && accessTokenExpiry < new Date() && path !== '/auth/refresh-token') {
if (process.client) {
// Works fine
await window.$nuxt.$store.dispatch('auth/refreshToken')
} else {
// This is where the trouble starts
await context.dispatch('auth/refreshToken', null, { root: true })
}
accessToken = rootGetters['auth/getAccessToken']
}
return fetch(path, {
method,
headers: { ... },
body: JSON.stringify(data),
}
}
If the accessToken is expired the helper function dispatches a Vuex Action to refresh it. This works well on the client side, but not if that process happens on the server side.
The Problem that's coming up on the server side is, that the user has to provide a refreshToken to get a refreshed accessToken from the API. This refreshToken is stored as a HttpOnly Cookie in the Client. When logging the Nuxt request details on the API side of things I noticed, that Nuxt is not sending that cookie.
My current workaround looks like this:
export const actions = {
async refreshToken(context){
...
let refreshToken
if (process?.server && this?.app?.context?.req?.headers?.cookie) {
const parsedCookies = cookie.parse(
this.app.context.req.headers.cookie
)
refreshToken = parsedCookies?.refreshToken
}
const response = await saveFetch(context, 'POST', '/auth/refresh-token', {
refreshToken,
})
...
}
...
}
If on server side, access the req object, get the cookies from it and send the refreshToken Cookie Content in the requests body.
This looks clearly bad to me and I would love to get some feedback on how to do this better. Did I maybe miss some key concepts that would help me not get into this problem in the first place?

Postman - Cant run pm.sendRequest() in collection level pre-request script

Im using newman to run api tests after build in travis.
Im trying to limit the duplication of pre-request scripts so checked out some workarounds on how can I have pre-request-scripts at collection level.
My problem is that I dont want to run them on every request, only the ones where I need them.
Example: Im trying to run a login script to use the returned token on private endpoints.
My code looks like:
Collection level pre-request script definiton:
Object.prototype.login = function() {
const request = {
url: 'somthing',
method: 'GET',
header: 'Content-Type:application/json',
body: {
mode: 'application/json',
raw: JSON.stringify(
{
email: pm.environment.get('someenv'),
password: pm.environment.get('someenv')
})
}
};
pm.sendRequest(request, function (err, res) {
var response = res.json();
pm.environment.set("token", response.token);
});
}
Request level pre-request script definiton:
_.login();
Can someone help me out why I cant run pm.sendRequest in this scope?
pm.environment.get('someenv') works like a charm, so Im not sure what to do here.
It runs fine when called from Collection level pre-request script without using the Object, but if I just put the whole request there, it will run before every request what I want to avoid in the first place.
I have tried to log some stuff out using console.log(), but it seems that the callback in pm.sendRequest() never runs.
So I have found a workaround for the issue, I hope its going to help out someone in the future :)
So its easy to setup a collection level pre-request that runs before every single request.
But to optimize this a little bit because you dont need to run every script for every request you make in a collection. You can use my solution here. :)
The issue I think is caused by:
PM object used in a different scope is not going to affect the PM object in global scope, so first you should pass global PM object as parameter for function call.
The collection level request should look like this:
login = function (pm) {
const request = {
url: pm.environment.get('base_url') + '/login',
method: 'POST',
header: {
'Content-Type': 'application/json',
},
body: {
mode: 'application/json',
raw: JSON.stringify({
email: pm.environment.get('email'),
password:pm.environment.get('passwd')
})
}
};
pm.sendRequest(request, (err, res) => {
var response = res.json();
pm.expect(err).to.be.a('null');
pm.expect(response).to.have.property('token')
.and.to.not.be.empty;
pm.globals.set("token", response.token);
});
};
And for the exact request where you want to call auth first and use the token for the request call:
login(pm);

How to unit test file upload with Supertest -and- send a token?

How can I test a file upload with a token being sent? I'm getting back "0" instead of a confirmation of upload.
This is a failed test:
var chai = require('chai');
var expect = chai.expect;
var config = require("../config"); // contains call to supertest and token info
describe('Upload Endpoint', function (){
it('Attach photos - should return 200 response & accepted text', function (done){
this.timeout(15000);
setTimeout(done, 15000);
config.api.post('/customer/upload')
.set('Accept', 'application.json')
.send({"token": config.token})
.field('vehicle_vin', "randomVIN")
.attach('file', '/Users/moi/Desktop/unit_test_extravaganza/hardwork.jpg')
.end(function(err, res) {
expect(res.body.ok).to.equal(true);
expect(res.body.result[0].web_link).to.exist;
done();
});
});
});
This is a Working test:
describe('Upload Endpoint - FL token ', function (){
this.timeout(15000);
it('Press Send w/out attaching photos returns error message', function (done){
config.api.post('/customer/upload')
.set('Accept', 'application.json')
.send({"token": config.token })
.expect(200)
.end(function(err, res) {
expect(res.body.ok).to.equal(false);
done();
});
});
Any suggestions are appreciated!
With supertest 4.0.2 I was able to set the token and attach the file:
import * as request from 'supertest';
return request(server)
.post('/route')
.set('Authorization', 'bearer ' + token)
.attach('name', 'file/path/name.txt');
And even better, per the docs, you can make a Buffer object to attach:
const buffer = Buffer.from('some data');
return request(server)
.post('/route')
.set('Authorization', 'bearer ' + token)
.attach('name', buffer, 'custom_file_name.txt');
It seems like the token field is overrided when attaching a file. My workaround is to add token to URL query parameter:
describe('Upload Endpoint - FL token ', function (){
this.timeout(15000);
it('Press Send w/out attaching photos returns error message', function (done){
config.api.post('/customer/upload/?token='+config.token)
.attach('file', '/Users/moi/Desktop/unit_test_extravaganza/hardwork.jpg')
.expect(200)
.end(function(err, res) {
expect(res.body.ok).to.equal(false);
done();
});
});
Your authentication middleware must be set to extract the JWT from URL query parameter. Passport-JWT performs this extraction on my server.
The official document states
When you use .field() or .attach() you can't use .send() and you must not set Content-Type (the correct type will be set for you).
So replace
.send({"token": config.token})
with
.field("token", config.token)

Parse Cloud Code with facebook API not working properly

I want to get a location Id from facebook API (that is already in my DB) and than use this to get the events from that location.
So, i'm first running a query to get this info and than adding this result as a parameter in my url. The fact is that the query is returning the result properly but when calling the httpRequest this is failling. Its important to say that my httpRequest works when I use the locationId hard coded.
I guess this problem is occuring because of the response calls but i cant figure out how to fix it. I'm also looking on a better way to design this code. Any ideas?
Parse.Cloud.define("hello", function(request, response) {
var query = new Parse.Query("Location");
query.find({
success: function(results) {
locationId = results[0].get("locationFbId");
console.log(locationId);
},
error: function() {
response.error("Failed on getting locationId");
}
});
Parse.Cloud.httpRequest({
url: 'https://graph.facebook.com/v2.2/'+locationId+'/events?access_token='+accessToken,
success: function(httpResponse) {
console.log(httpResponse.data);
response.success("result");
},
error:function(httpResponse){
console.error(httpResponse.message);
response.error("Failed to get events");
}
});
});
Adolfosrs, your problem here is that your two requests are running asynchronously on different threads. Therefore, your first request isn't returning until after your second request has been called. I would suggest chaining the requests as below so that your second request will be initialized with the data retrieved from the first request.
Parse.Cloud.define("hello", function(request, response) {
var query = new Parse.Query("Location");
query.find({
success: function(results) {
locationId = results[0].get("locationFbId");
console.log(locationId);
Parse.Cloud.httpRequest({
url: 'https://graph.facebook.com/v2.2/'+locationId+'/events?access_token='+accessToken,
success: function(httpResponse) {
console.log(httpResponse.data);
response.success("result");
},
error:function(httpResponse){
console.error(httpResponse.message);
response.error("Failed to get events");
}
});
},
error: function() {
response.error("Failed on getting locationId");
}
});
});