Can't update variable in default function

Hi everyone,

I have a problem with my code. I will try to explain flow of my performance test.

  1. I have export function setup() where I implemented logging to my application. This function returns => TOKENS = [AUTHORIZATION_TOKEN, REFRESH_TOKEN].
    TOKENS , AUTHORIZATION_TOKEN and REFRESH_TOKEN are declared like that:
var TOKENS = [];
var AUTHORIZATION_TOKEN = "";
var REFRESH_TOKEN = "";

Setup function look like:

export function setup() {
    console.log(`== SETUP BEGIN ===========================================================`)
    const loginRes = http.post(`${HOST}/log-in`, JSON.stringify({
        login: LOGIN,
        password: PASSWORD,
    }), { headers: {
            "Content-type":'application/json',
            "Accept": 'application/json'
        }});
    console.log(`Login response: ${loginRes.status_text}`);
    AUTHORIZATION_TOKEN = loginRes.json('accessToken');
    check(AUTHORIZATION_TOKEN, { 'logged in successfully': () => AUTHORIZATION_TOKEN !== '' });
    console.log('accessToken from setup: ' + AUTHORIZATION_TOKEN);
    REFRESH_TOKEN = loginRes.json('refreshToken');
    console.log('refreshToken from setup: ' + REFRESH_TOKEN);
    TOKENS = [AUTHORIZATION_TOKEN, REFRESH_TOKEN];
    console.log(`== SETUP END =============================================================`);
    return TOKENS;
};

It works OK. It returns correct AUTHORIZATION_TOKEN (Access token) and REFRESH_TOKEN.

  1. Then, my code start to execute export default function(TOKENS) and it works but until access token not expired.
export default function (TOKENS){
    AUTHORIZATION_TOKEN = TOKENS[0];
    HEADERS_TEST = { 
        headers: {
            "Authorization": `Bearer ${AUTHORIZATION_TOKEN}`,
            "Content-type":'application/json',
            "Accept": 'application/json'
        }
    };

    NAME = devices_data[vu.idInTest - 1].name;
    GUID = devices_data[vu.idInTest - 1].guid;
    DESCRIPTION = devices_data[vu.idInTest - 1].description;

    group ('Add app', () => {
        const res_post_add_app_201 = http.post(`${LOCALHOST}/apps`, JSON.stringify({
            name: NAME,
            guid: GUID,
            description: DESCRIPTION
        }), HEADERS_TEST, { myTag: "Add app" });
        check_response(res_post_add_app_201, 201, TOKENS[1]);
    });

    sleep(1);

    group ('Get app', () => {
        const res_get_app_200 = http.get(`${LOCALHOST}/apps/${GUID}`, HEADERS_TEST, { myTag: "Get app" });
        check_response(res_get_app_200, 200, TOKENS[1]);
    });

    sleep(1);

    group ('Remove app', () => {
        const res_delete_app_200 = http.del(`${LOCALHOST}/apps/${GUID}`, "", HEADERS_TEST, { myTag: "Remove app" });
        check_response(res_delete_app_200, 200, TOKENS[1]);
    });

    sleep(1);

    group ('Get app', () => {
        const res_get_app_404 = http.get(`${LOCALHOST}/apps/${GUID}`, HEADERS_TEST, { myTag: "Get app" });
        check_response(res_get_app_404, 404, TOKENS[1]);
    });

    sleep(1);
};
  1. After 30 minutes, I should recevied new access token based on REFRESH_TOKEN.
export function check_response(response, required_status_code, REFRESH_TOKEN) {
    if (response.status === 401) {
        TOKENS = refreshToken(REFRESH_TOKEN);
        AUTHORIZATION_TOKEN = TOKENS[0];
        HEADERS_TEST = {
            headers: {
                "Authorization": `Bearer ${AUTHORIZATION_TOKEN}`,
                "Content-type":'application/json',
                "Accept": 'application/json'
            }
        };
    }
};
export function refreshToken(REFRESH_TOKEN) {
    // Refresh token
    const payload = {
        refreshToken: REFRESH_TOKEN
    };

    const requestConfig = () => ({
        headers: {
          "Content-type":'application/json'
    }});

    const res_post_refresh_token_200 = http.post(`${HOST}/refresh`, JSON.stringify(payload), requestConfig());
    check_response(res_post_refresh_token_200, 200, REFRESH_TOKEN);
    AUTHORIZATION_TOKEN = res_post_refresh_token_200.json('accessToken');
    console.log('accessToken from refreshToken: ' + AUTHORIZATION_TOKEN);
    REFRESH_TOKEN = res_post_refresh_token_200.json('refreshToken');
    console.log('refreshToken from refreshToken: ' + REFRESH_TOKEN_NEW);
    TOKENS = [AUTHORIZATION_TOKEN, REFRESH_TOKEN];
    return TOKENS;
};

After 30 minutes, when I received unauthorized error (401), I want to execute /refresh endpoint to get new access token and save it as a global variable TOKENS, but in default function I still received old access token, which was generated in setup function. How to deal with it? I don’t know if it is limitation of k6 or my coding skills. Thanks for help in advance and any code snippet to resolve this issue.

1 Like

Hi @inetkox

Welcome to the community forum :wave:

You can use the example in Periodically refreshing a token aside a script - #3 by codebien, without waiting for the 401 (which might mess with your test stats). And we have a concrete example in Generated Token every 10 mins and use it during load test - #10 by immavalls2.

Your approach is working only for that iteration, I believe, since AUTHORIZATION_TOKEN = TOKENS[0] will assign the values obtained in the setup function.

I hope this helps.

Cheers!

Thank you for the reply :smiley:

Unfortunately your solution helps me partially because i have the following options:

export const options = {
    setupTimeout: '360s',
    scenarios: {
        default: {
            executor: 'ramping-vus',
            stages: [
                {duration: '2m',  target: 1},    
                {duration: '5m',  target: 1},
                {duration: '2m',  target: 10},   
                {duration: '5m',  target: 10},
                {duration: '2m',  target: 15}, 
                {duration: '5m',  target: 15},
                {duration: '2m',  target: 20},
                {duration: '5m',  target: 20},
                {duration: '10m', target: 0},              
            ],
            gracefulRampDown: '1m'
        },
    },
}

and when stages changed from 1 to NormalLoad (here 10) I recevied Unauthorized error and I need to generate access token after 7 minutes despite that access token is 30 minutes valid. Is it necessary to generate token for each VUs?

Hi @inetkox

Indeed once you are out of the setup function, the code will be executed per VU.

Currently, I believe you have two options.

  • Have each VU refresh its authentication token every 30 minutes
  • Expose a service from where the test can retrieve the token. You can either have an external process update the token every 30 minutes or use a separate k6 process to update the token as Pablo described in Calling Token API every hour - #5 by pablochacin1.

I hope this helps.

Cheers!

2 Likes

Hey @eyeveebe, I appreciate your lengthy answers in a number of these threads. I can see the Periodically refreshing a token aside a script - #3 by codebien - OSS Support - Grafana Labs Community Forums approach by @codebien mentioned a few times as being the best approach.

However, I have tried this and the refreshed variable never gets reassigned, seemingly because it is deemed init code. Is my thinking correct here?

I don’t see how you can have each VU refresh it’s token every 30 minutes without having mutable shared variables accessible within VU code. Could you elaborate on this?