diff --git a/packages/k6-tests/data/test1.docx b/packages/k6-tests/data/test1.docx new file mode 100644 index 0000000..4c5c0eb Binary files /dev/null and b/packages/k6-tests/data/test1.docx differ diff --git a/packages/k6-tests/tests/oc/upload-search/default.k6.ts b/packages/k6-tests/tests/oc/upload-search/default.k6.ts new file mode 100644 index 0000000..de67954 --- /dev/null +++ b/packages/k6-tests/tests/oc/upload-search/default.k6.ts @@ -0,0 +1,109 @@ +import { ENV, queryJson, queryXml, randomString, store, check } from '@ownclouders/k6-tdk/lib/utils' +import { Permission, ShareType } from '@ownclouders/k6-tdk/lib/values' +import { randomBytes } from 'k6/crypto' +import exec from 'k6/execution' +import { Options } from 'k6/options' +import { sleep } from 'k6' +import { times } from 'lodash' + +import { clientFor } from '@/shortcuts' +import { envValues } from '@/values' + +// eslint-disable-next-line no-restricted-globals +const t1docX = open('../data/test1.docx', 'b') + +export interface Environment { + adminData: { + adminRoot: string; + }; + actorData: { + actorLogin: string; + actorPassword: string; + actorRoot: string; + }[]; +} + +/**/ +export const settings = { + ...envValues(), + sleepTime: parseInt(ENV('SLEEP_TIME', '10'), 10) +} +/**/ + +export const options: Options = { + vus: 1, + insecureSkipTLSVerify: true, + setupTimeout: '3m' +} + +export function setup(): Environment { + const adminClient = clientFor({ userLogin: settings.admin.login, userPassword: settings.admin.password }) + const getMyDrivesResponseAdmin = adminClient.me.getMyDrives({ params: { $filter: "driveType eq 'personal'" } }) + const [adminRoot = settings.admin.login] = queryJson("$.value[?(@.driveType === 'personal')].id", getMyDrivesResponseAdmin?.body) + + adminClient.resource.createResource({ root: adminRoot, resourcePath: settings.testFolder }) + + const actorData = times(options.vus || 1, () => { + const [actorLogin, actorPassword] = [randomString(), randomString()] + adminClient.user.createUser({ userLogin: actorLogin, userPassword: actorPassword }) + adminClient.user.enableUser({ userLogin: actorLogin }) + + const actorClient = clientFor({ userLogin: actorLogin, userPassword: actorPassword }) + const getMyDrivesResponseActor = actorClient.me.getMyDrives({ params: { $filter: "driveType eq 'personal'" } }) + const [actorRoot = actorLogin] = queryJson("$.value[?(@.driveType === 'personal')].id", getMyDrivesResponseActor?.body) + + actorClient.resource.createResource({ root: actorRoot, resourcePath: "initial" }) + + actorClient.resource.uploadResource({ + root: actorRoot, + resourcePath: ["initial", "file.docx"].join('/'), + resourceBytes: t1docX + }) + + return { + actorLogin, + actorPassword, + actorRoot + } + }) + + // give time for the search extractor to index the file + sleep(settings.sleepTime) + + return { + adminData: { + adminRoot + }, + actorData + } +} + + +export default async function actor({ actorData }: Environment): Promise { + const { actorLogin, actorPassword, actorRoot } = actorData[exec.vu.idInTest - 1] + const actorStore = store(actorLogin) + + const actorClient = await actorStore.setOrGet('client', async () => { + return clientFor({ userLogin: actorLogin, userPassword: actorPassword }) + }) + + const resp = actorClient.search.searchForResources({ + root: actorRoot, + searchQuery: "(name:\"*automatically*\" OR content:\"automatically\")", + }) + check({val: resp}, { + 'search -> only one result found': ({ body }) => { + const props = queryXml("$..['d:prop']", body) + return props.length === 1 && props[0]['oc:name'] === 'file.docx' + }, + }) +} + +export function teardown({ adminData, actorData }: Environment): void { + const adminClient = clientFor({ userLogin: settings.admin.login, userPassword: settings.admin.password }) + + adminClient.resource.deleteResource({ root: adminData.adminRoot, resourcePath: settings.testFolder }) + actorData.forEach(({ actorLogin }) => { + adminClient.user.deleteUser({ userLogin: actorLogin }) + }) +} diff --git a/packages/k6-tests/tests/oc/upload-search/default.md b/packages/k6-tests/tests/oc/upload-search/default.md new file mode 100644 index 0000000..d6639f3 --- /dev/null +++ b/packages/k6-tests/tests/oc/upload-search/default.md @@ -0,0 +1,36 @@ +# Description + +The `upload search` test mimics a typical end user resource search scenario. + +Each user uploads a file and searches a particular word in that file. + +**NOTE**: A search extractor is required to be configured in the oCIS instance in order to extract the word from the file, otherwise the search won't find the word and the test will fail. + + +## Procedure + +* `admin` creates `N` users. + * `N` can be set with the `--vus` option. + * by default, it set to 1. +* each `user` logs into the system individually. +* each `user` uploads a particular file containing the `automatically` word to the `initial/file.docx` path. This happens during the setup phase. +* each `user` searches for the `automatically` word inside his root folder. +* `admin` deletes the created users. + +Note that the setup phase will wait for 10 seconds (configurable) to ensure that the file is uploaded and indexed before the search is executed. + +The iterations are shared among the users, so if you define `--iterations 5` and `--vus 2`, the test will run 5 times in total, within the 2 users running in parallel. +In other words, 2 users will run the tests in parallel until the 5 iterations are completed. + + +## Available options + +* [Shared options](/k6-tests/src/values/env) +* `SLEEP_TIME`: time to wait after the setup to allow indexing the file. Not giving enough time can cause the tests to fail. + * default value: `10` (seconds) + * `export SLEEP_TIME=12` + + +## How to run the test + +please read [here](/k6-tests/docs/run) how the test can be executed, only the script is different diff --git a/packages/k6-tests/tests/oc/upload-search/indexing.k6.ts b/packages/k6-tests/tests/oc/upload-search/indexing.k6.ts new file mode 100644 index 0000000..5616847 --- /dev/null +++ b/packages/k6-tests/tests/oc/upload-search/indexing.k6.ts @@ -0,0 +1,106 @@ +import { ENV, queryJson, queryXml, randomString, store, check } from '@ownclouders/k6-tdk/lib/utils' +import { Permission, ShareType } from '@ownclouders/k6-tdk/lib/values' +import { randomBytes } from 'k6/crypto' +import exec from 'k6/execution' +import { Options } from 'k6/options' +import { sleep } from 'k6' +import { times } from 'lodash' + +import { clientFor } from '@/shortcuts' +import { envValues } from '@/values' + +// eslint-disable-next-line no-restricted-globals +const t1docX = open('../data/test1.docx', 'b') + +export interface Environment { + adminData: { + adminRoot: string; + }; + actorData: { + actorLogin: string; + actorPassword: string; + actorRoot: string; + }[]; +} + +/**/ +export const settings = { + ...envValues(), +} +/**/ + +export const options: Options = { + vus: 1, + insecureSkipTLSVerify: true, +} + +export function setup(): Environment { + const adminClient = clientFor({ userLogin: settings.admin.login, userPassword: settings.admin.password }) + const getMyDrivesResponseAdmin = adminClient.me.getMyDrives({ params: { $filter: "driveType eq 'personal'" } }) + const [adminRoot = settings.admin.login] = queryJson("$.value[?(@.driveType === 'personal')].id", getMyDrivesResponseAdmin?.body) + + adminClient.resource.createResource({ root: adminRoot, resourcePath: settings.testFolder }) + + const actorData = times(options.vus || 1, () => { + const [actorLogin, actorPassword] = [randomString(), randomString()] + adminClient.user.createUser({ userLogin: actorLogin, userPassword: actorPassword }) + adminClient.user.enableUser({ userLogin: actorLogin }) + + const actorClient = clientFor({ userLogin: actorLogin, userPassword: actorPassword }) + const getMyDrivesResponseActor = actorClient.me.getMyDrives({ params: { $filter: "driveType eq 'personal'" } }) + const [actorRoot = actorLogin] = queryJson("$.value[?(@.driveType === 'personal')].id", getMyDrivesResponseActor?.body) + + actorClient.resource.createResource({ root: actorRoot, resourcePath: "initial" }) + + return { + actorLogin, + actorPassword, + actorRoot + } + }) + + return { + adminData: { + adminRoot + }, + actorData + } +} + + +export default async function actor({ actorData }: Environment): Promise { + const { actorLogin, actorPassword, actorRoot } = actorData[exec.vu.idInTest - 1] + const actorStore = store(actorLogin) + + const actorClient = await actorStore.setOrGet('client', async () => { + return clientFor({ userLogin: actorLogin, userPassword: actorPassword }) + }) + + + const filename = [randomString(), exec.scenario.iterationInTest, '.docx'].join('') + actorClient.resource.uploadResource({ + root: actorRoot, + resourcePath: ["initial", filename].join('/'), + resourceBytes: t1docX + }) + + // We're focused on response times so we don't care about the result, just the status code. + // No need to wait for indexing. + + const resp = actorClient.search.searchForResources({ + root: actorRoot, + searchQuery: `(name:\"*${filename}*\" OR content:\"${filename}\")`, + }) + check({val: resp}, { + 'search -> status': ({ status }) => status === 207, + }) +} + +export function teardown({ adminData, actorData }: Environment): void { + const adminClient = clientFor({ userLogin: settings.admin.login, userPassword: settings.admin.password }) + + adminClient.resource.deleteResource({ root: adminData.adminRoot, resourcePath: settings.testFolder }) + actorData.forEach(({ actorLogin }) => { + adminClient.user.deleteUser({ userLogin: actorLogin }) + }) +} diff --git a/packages/k6-tests/tests/oc/upload-search/indexing.md b/packages/k6-tests/tests/oc/upload-search/indexing.md new file mode 100644 index 0000000..150814e --- /dev/null +++ b/packages/k6-tests/tests/oc/upload-search/indexing.md @@ -0,0 +1,31 @@ +# Description + +The `upload search` test mimics a typical end user resource search scenario. + +While the `default` test just focuses on searching, this test also includes the upload part. The test will just upload a file and search it, but it will only check the status code of the search request. This means that the search will likely will return empty results (not indexed yet) and the test will pass. + +The test is expected to perform worse than the `default` test, as it includes the upload part. There are also additional write locks on the index, due to the indexing operation, that are expected to slow down the test. Note that we won't wait for the indexing to finish in order to send the search request. + + +## Procedure + +* `admin` creates `N` users. + * `N` can be set with the `--vus` option. + * by default, it set to 1. +* each `user` logs into the system individually. +* each `user` uploads a particular file in the `initial` folder. The file is uploaded with a random name. +* each `user` searches for the filename inside his root folder. +* `admin` deletes the created users. + +The iterations are shared among the users, so if you define `--iterations 5` and `--vus 2`, the test will run 5 times in total, within the 2 users running in parallel. +In other words, 2 users will run the tests in parallel until the 5 iterations are completed. + + +## Available options + +* [Shared options](/k6-tests/src/values/env) + + +## How to run the test + +please read [here](/k6-tests/docs/run) how the test can be executed, only the script is different