Skip to content

Commit

Permalink
test(load): add K6 load test workflow with example test
Browse files Browse the repository at this point in the history
This commit adds a GitHub Actions workflow (you have to *manually*
trigger it) that uses the K6 load testing utillity to run tests on a
given environment.

I had to add the `K6_API_TOKEN` secret to GitHub to allow running the
tests in K6's cloud (VSF has a paid plan). You can also run the test on
GitHub's agent though.

M2-899

fix: use js, not ts file

chore: partially revert #1107

This reverts commit 9b8de85.

This change was made because K6 recorded tests are gigantic when
customQuery sends the GraphQL AST in the request body. After this
change, this the request is sent in string form, but is converted into
GraphQL AST on the middleware side (because Apollo GraphQL client
expects AST (DocumentNode TS type), not string)

test(load): improve gql requests

refactor: fix newlines
  • Loading branch information
sethidden committed Jun 30, 2022
1 parent 79c87ce commit 54ff69e
Show file tree
Hide file tree
Showing 17 changed files with 438 additions and 44 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Expand Up @@ -30,6 +30,9 @@ module.exports = {
'@vue-storefront/eslint-config-vue',
'@vue-storefront/eslint-config-jest',
],
globals: {
"__ENV": "readonly",
},
rules: {
"@typescript-eslint/no-floating-promises": "off",
"jest/expect-expect": [
Expand Down
52 changes: 52 additions & 0 deletions .github/workflows/run-k6-load-test.yml
@@ -0,0 +1,52 @@
name: Run K6 test file
on:
workflow_dispatch:
inputs:
cloud:
description: True = run in K6 cloud; False = run on the test on GitHub Actions agent
required: true
default: false
type: boolean

filename:
description: The K6 test file to run relative to repository root
required: true
default: packages/theme/tests/load/searchProduct.js
type: string

environment:
description: The full URL of the environment on which load tests will be ran
required: true
default: https://demo-magento2-canary.europe-west1.gcp.storefrontcloud.io
type: choice
options:
- https://demo-magento2-canary.europe-west1.gcp.storefrontcloud.io
- https://demo-magento2-dev.europe-west1.gcp.storefrontcloud.io
- https://demo-magento2.europe-west1.gcp.storefrontcloud.cloud
- https://demo-magento2-enterprise.europe-west1.gcp.storefrontcloud.io

flags:
description: Additional argument and flags to provide to the k6 CLI. See https://k6.io/docs/using-k6/options for details.
required: false
default: ''
type: string

jobs:
build:
name: Run k6 cloud test
runs-on: ubuntu-latest
environment: test

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Run k6 cloud test
uses: grafana/k6-action@v0.2.0
with:
cloud: ${{ inputs.cloud }}
token: ${{ secrets.K6_CLOUD_API_TOKEN }}
filename: ${{ inputs.filename }}
flags: ${{ inputs.flags }}
env:
BASE_URL: ${{ inputs.environment }}
6 changes: 3 additions & 3 deletions packages/api-client/src/api/customMutation/index.ts
@@ -1,5 +1,5 @@
import gql from 'graphql-tag';
import { FetchPolicy, FetchResult } from '@apollo/client/core';
import { DocumentNode } from 'graphql';
import { Context } from '../../types/context';
import getHeaders from '../getHeaders';

Expand All @@ -10,12 +10,12 @@ export default async <MUTATION = any, MUTATION_VARIABLES = any>(
mutationVariables,
fetchPolicy,
}: {
mutation: DocumentNode,
mutation: string,
mutationVariables: MUTATION_VARIABLES,
fetchPolicy?: Extract<FetchPolicy, 'network-only' | 'no-cache'>,
},
): Promise<FetchResult<MUTATION>> => context.client.mutate<MUTATION, MUTATION_VARIABLES>({
mutation,
mutation: gql`${mutation}`,
variables: { ...mutationVariables },
fetchPolicy: fetchPolicy || 'no-cache',
context: {
Expand Down
6 changes: 3 additions & 3 deletions packages/api-client/src/api/customQuery/index.ts
@@ -1,5 +1,5 @@
import gql from 'graphql-tag';
import { ApolloQueryResult, FetchPolicy } from '@apollo/client/core';
import { DocumentNode } from 'graphql';
import { Context } from '../../types/context';
import getHeaders from '../getHeaders';

Expand All @@ -10,12 +10,12 @@ export default async <QUERY = any, QUERY_VARIABLES = any>(
queryVariables,
fetchPolicy,
}: {
query: DocumentNode,
query: string,
queryVariables?: QUERY_VARIABLES,
fetchPolicy?: FetchPolicy,
},
): Promise<ApolloQueryResult<QUERY>> => context.client.query<QUERY, QUERY_VARIABLES>({
query,
query: gql`${query}`,
variables: { ...queryVariables },
fetchPolicy: fetchPolicy || 'no-cache',
context: {
Expand Down
6 changes: 3 additions & 3 deletions packages/api-client/src/types/API.ts
@@ -1,5 +1,5 @@
import { ApolloQueryResult, FetchPolicy, FetchResult } from '@apollo/client/core';
import { DocumentNode, ExecutionResult } from 'graphql';
import { ExecutionResult } from 'graphql';
import { CustomQuery } from '@vue-storefront/core';
import {
AddConfigurableProductsToCartInput,
Expand Down Expand Up @@ -278,13 +278,13 @@ export interface MagentoApiMethods {
): Promise<ApolloQueryResult<CustomerOrdersQuery>>;

customQuery<QUERY, QUERY_VARIABLES = any>(params: {
query: DocumentNode,
query: string,
queryVariables?: QUERY_VARIABLES,
fetchPolicy?: FetchPolicy,
}): Promise<ApolloQueryResult<QUERY>>;

customMutation<MUTATION, MUTATION_VARIABLES = any>(params: {
mutation: DocumentNode,
mutation: string,
mutationVariables: MUTATION_VARIABLES,
fetchPolicy?: Extract<FetchPolicy, 'network-only' | 'no-cache'>,
}): Promise<FetchResult<MUTATION>>;
Expand Down
@@ -1,6 +1,4 @@
import gql from 'graphql-tag';

export default gql`
export default `
query getStoresAndCurrencies {
availableStores {
store_code
Expand Down
11 changes: 3 additions & 8 deletions packages/theme/composables/useApi/index.ts
@@ -1,5 +1,4 @@
import { useContext } from '@nuxtjs/composition-api';
import type { DocumentNode } from 'graphql';
import { Logger } from '~/helpers/logger';

export type FetchPolicy = 'cache-first' | 'network-only' | 'cache-only' | 'no-cache' | 'standby';
Expand All @@ -19,7 +18,7 @@ export type Error = {
};

export type Request = <DATA, VARIABLES extends Variables = Variables>(
request: DocumentNode,
request: string,
variables?: VARIABLES,
fetchPolicy?: FetchPolicy,
) => Promise<{ data: DATA, errors: Error[] }>;
Expand Down Expand Up @@ -75,10 +74,6 @@ export interface UseApiInterface {
mutate: Request;
}

function getGqlString(doc: DocumentNode) {
return doc.loc && doc.loc.source.body;
}

/**
* Allows executing arbitrary GraphQL queries and mutations.
*
Expand All @@ -93,7 +88,7 @@ export function useApi(): UseApiInterface {
variables,
) => {
const reqID = `id${Math.random().toString(16).slice(2)}`;
Logger.debug(`customQuery/request/${reqID}`, getGqlString(request));
Logger.debug(`customQuery/request/${reqID}`, request);
const { data, errors } = await context.app.$vsf.$magento.api.customQuery({ query: request, queryVariables: variables });
Logger.debug(`customQuery/result/${reqID}`, { data, errors });

Expand All @@ -106,7 +101,7 @@ export function useApi(): UseApiInterface {
variables,
) => {
const reqID = `id${Math.random().toString(16).slice(2)}`;
Logger.debug(`customQuery/request/${reqID}`, getGqlString(request));
Logger.debug(`customQuery/request/${reqID}`, request);
const { data, errors } = await context.app.$vsf.$magento.api.customMutation({ mutation: request, mutationVariables: variables });
Logger.debug(`customQuery/result/${reqID}`, { data, errors });

Expand Down
@@ -1,6 +1,4 @@
import gql from 'graphql-tag';

export default gql`
export default `
query getCategoryContentData($filters: CategoryFilterInput) {
categoryList(filters: $filters) {
uid
Expand Down
@@ -1,6 +1,4 @@
import gql from 'graphql-tag';

export default gql`
export default `
query getProductFiltersByCategory($categoryIdFilter: FilterEqualTypeInput!) {
products(filter: { category_uid: $categoryIdFilter }) {
aggregations {
Expand Down
@@ -1,10 +1,8 @@
import gql from 'graphql-tag';

/**
* GraphQL Query that fetches products using received search term and the params
* for filter, sort and pagination.
*/
export default gql`
export default `
query getFacetData($search: String = "", $filter: ProductAttributeFilterInput, $pageSize: Int = 10, $currentPage: Int = 1, $sort: ProductAttributeSortInput) {
products(search: $search, filter: $filter, pageSize: $pageSize, currentPage: $currentPage, sort: $sort) {
items {
Expand Down
@@ -1,6 +1,4 @@
import gql from 'graphql-tag';

const fragmentCategory = gql`
const fragmentCategory = `
fragment CategoryFields on CategoryTree {
is_anchor
name
Expand All @@ -13,7 +11,7 @@ const fragmentCategory = gql`
}
`;

export default gql`
export default `
query categoryList {
categories {
items {
Expand Down
4 changes: 1 addition & 3 deletions packages/theme/modules/catalog/pricing/getPricesQuery.gql.ts
@@ -1,6 +1,4 @@
import gql from 'graphql-tag';

export default gql`
export default `
query productsList($search: String = "", $filter: ProductAttributeFilterInput, $pageSize: Int = 20, $currentPage: Int = 1, $sort: ProductAttributeSortInput) {
products(search: $search, filter: $filter, pageSize: $pageSize, currentPage: $currentPage, sort: $sort) {
items {
Expand Down
@@ -1,6 +1,4 @@
import gql from 'graphql-tag';

const fragmentPriceRangeFields = gql`
const fragmentPriceRangeFields = `
fragment PriceRangeFields on PriceRange {
maximum_price {
final_price {
Expand All @@ -25,7 +23,7 @@ const fragmentPriceRangeFields = gql`
}
`;

export default gql`
export default `
query getProductPriceBySku($sku: String) {
products(filter: {sku: {eq: $sku}}) {
items {
Expand Down
2 changes: 1 addition & 1 deletion packages/theme/package.json
Expand Up @@ -47,7 +47,6 @@
"cookie-universal-nuxt": "^2.1.5",
"deepdash": "^5.3.9",
"express": "4.17.3",
"graphql-tag": "^2.12.6",
"is-https": "^4.0.0",
"isomorphic-dompurify": "^0.18.0",
"lodash-es": "^4.17.21",
Expand All @@ -69,6 +68,7 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/user-event": "^14.2.0",
"@testing-library/vue": "^5.8.3",
"@types/k6": "^0.37.0",
"@types/lodash-es": "^4.17.6",
"@vue/test-utils": "^1.3.0",
"babel-core": "7.0.0-bridge.0",
Expand Down
4 changes: 1 addition & 3 deletions packages/theme/plugins/query/StoreConfig.gql.ts
@@ -1,7 +1,5 @@
import gql from 'graphql-tag';

/** GraphQL Query that fetches store configuration from the API */
export const StoreConfigQuery = gql`
export const StoreConfigQuery = `
query storeConfig {
storeConfig {
store_code,
Expand Down

0 comments on commit 54ff69e

Please sign in to comment.