Replace PKI test suites custom runCommands with vault tools runCmd (#25226)

* replace runCommand custom for PKI to vault tools runCmd

* remove duplicate policy command
This commit is contained in:
Angel Garbarino 2024-02-05 18:41:36 -07:00 committed by GitHub
parent edf4caa63f
commit 2f05a362bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 40 additions and 72 deletions

View File

@ -12,7 +12,7 @@ import { v4 as uuidv4 } from 'uuid';
import authPage from 'vault/tests/pages/auth'; import authPage from 'vault/tests/pages/auth';
import logout from 'vault/tests/pages/logout'; import logout from 'vault/tests/pages/logout';
import enablePage from 'vault/tests/pages/settings/mount-secret-backend'; import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
import { runCommands } from 'vault/tests/helpers/pki/pki-run-commands'; import { runCmd } from 'vault/tests/helpers/commands';
import { SELECTORS as S } from 'vault/tests/helpers/pki/workflow'; import { SELECTORS as S } from 'vault/tests/helpers/pki/workflow';
import { issuerPemBundle } from 'vault/tests/helpers/pki/values'; import { issuerPemBundle } from 'vault/tests/helpers/pki/values';
@ -32,7 +32,7 @@ module('Acceptance | pki action forms test', function (hooks) {
await logout.visit(); await logout.visit();
await authPage.login(); await authPage.login();
// Cleanup engine // Cleanup engine
await runCommands([`delete sys/mounts/${this.mountPath}`]); await runCmd([`delete sys/mounts/${this.mountPath}`]);
}); });
module('import', function (hooks) { module('import', function (hooks) {

View File

@ -12,7 +12,7 @@ import { v4 as uuidv4 } from 'uuid';
import authPage from 'vault/tests/pages/auth'; import authPage from 'vault/tests/pages/auth';
import logout from 'vault/tests/pages/logout'; import logout from 'vault/tests/pages/logout';
import enablePage from 'vault/tests/pages/settings/mount-secret-backend'; import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
import { runCommands } from 'vault/tests/helpers/pki/pki-run-commands'; import { runCmd } from 'vault/tests/helpers/commands';
import { SELECTORS } from 'vault/tests/helpers/pki/workflow'; import { SELECTORS } from 'vault/tests/helpers/pki/workflow';
import { issuerPemBundle } from 'vault/tests/helpers/pki/values'; import { issuerPemBundle } from 'vault/tests/helpers/pki/values';
@ -33,7 +33,7 @@ module('Acceptance | pki configuration test', function (hooks) {
await logout.visit(); await logout.visit();
await authPage.login(); await authPage.login();
// Cleanup engine // Cleanup engine
await runCommands([`delete sys/mounts/${this.mountPath}`]); await runCmd([`delete sys/mounts/${this.mountPath}`]);
}); });
module('delete all issuers modal and empty states', function (hooks) { module('delete all issuers modal and empty states', function (hooks) {
@ -157,14 +157,14 @@ module('Acceptance | pki configuration test', function (hooks) {
await fillIn(SELECTORS.configuration.generateRootIssuerNameField, 'issuer-0'); await fillIn(SELECTORS.configuration.generateRootIssuerNameField, 'issuer-0');
await click(SELECTORS.configuration.generateRootSave); await click(SELECTORS.configuration.generateRootSave);
await click(SELECTORS.configuration.doneButton); await click(SELECTORS.configuration.doneButton);
await runCommands([ await runCmd([
`write ${this.mountPath}/roles/some-role \ `write ${this.mountPath}/roles/some-role \
issuer_ref="default" \ issuer_ref="default" \
allowed_domains="example.com" \ allowed_domains="example.com" \
allow_subdomains=true \ allow_subdomains=true \
max_ttl="720h"`, max_ttl="720h"`,
]); ]);
await runCommands([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]); await runCmd([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`); assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`);
await click(SELECTORS.configTab); await click(SELECTORS.configTab);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/configuration`); assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/configuration`);

View File

@ -10,7 +10,7 @@ import { v4 as uuidv4 } from 'uuid';
import authPage from 'vault/tests/pages/auth'; import authPage from 'vault/tests/pages/auth';
import enablePage from 'vault/tests/pages/settings/mount-secret-backend'; import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
import { runCommands } from 'vault/tests/helpers/pki/pki-run-commands'; import { runCmd } from 'vault/tests/helpers/commands';
import { SELECTORS } from 'vault/tests/helpers/pki/pki-issuer-cross-sign'; import { SELECTORS } from 'vault/tests/helpers/pki/pki-issuer-cross-sign';
import { verifyCertificates } from 'vault/utils/parse-pki-cert'; import { verifyCertificates } from 'vault/utils/parse-pki-cert';
module('Acceptance | pki/pki cross sign', function (hooks) { module('Acceptance | pki/pki cross sign', function (hooks) {
@ -27,7 +27,7 @@ module('Acceptance | pki/pki cross sign', function (hooks) {
await enablePage.enable('pki', this.parentMountPath); await enablePage.enable('pki', this.parentMountPath);
await enablePage.enable('pki', this.intMountPath); await enablePage.enable('pki', this.intMountPath);
await runCommands([ await runCmd([
`write "${this.parentMountPath}/root/generate/internal" common_name="Long-Lived Root X1" ttl=8960h issuer_name="${this.oldParentIssuerName}"`, `write "${this.parentMountPath}/root/generate/internal" common_name="Long-Lived Root X1" ttl=8960h issuer_name="${this.oldParentIssuerName}"`,
`write "${this.parentMountPath}/root/generate/internal" common_name="Long-Lived Root X2" ttl=8960h issuer_name="${this.parentIssuerName}"`, `write "${this.parentMountPath}/root/generate/internal" common_name="Long-Lived Root X2" ttl=8960h issuer_name="${this.parentIssuerName}"`,
`write "${this.parentMountPath}/config/issuers" default="${this.parentIssuerName}"`, `write "${this.parentMountPath}/config/issuers" default="${this.parentIssuerName}"`,
@ -36,8 +36,8 @@ module('Acceptance | pki/pki cross sign', function (hooks) {
hooks.afterEach(async function () { hooks.afterEach(async function () {
// Cleanup engine // Cleanup engine
await runCommands([`delete sys/mounts/${this.intMountPath}`]); await runCmd([`delete sys/mounts/${this.intMountPath}`]);
await runCommands([`delete sys/mounts/${this.parentMountPath}`]); await runCmd([`delete sys/mounts/${this.parentMountPath}`]);
}); });
test('it cross-signs an issuer', async function (assert) { test('it cross-signs an issuer', async function (assert) {
@ -88,7 +88,7 @@ module('Acceptance | pki/pki cross sign', function (hooks) {
// verify cross-sign was accurate by creating a role to issue a leaf certificate // verify cross-sign was accurate by creating a role to issue a leaf certificate
const myRole = 'some-role'; const myRole = 'some-role';
await runCommands([ await runCmd([
`write ${this.intMountPath}/roles/${myRole} \ `write ${this.intMountPath}/roles/${myRole} \
issuer_ref=${this.newlySignedIssuer}\ issuer_ref=${this.newlySignedIssuer}\
allow_any_name=true \ allow_any_name=true \

View File

@ -11,7 +11,7 @@ import authPage from 'vault/tests/pages/auth';
import logout from 'vault/tests/pages/logout'; import logout from 'vault/tests/pages/logout';
import enablePage from 'vault/tests/pages/settings/mount-secret-backend'; import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
import { click, currentURL, fillIn, visit } from '@ember/test-helpers'; import { click, currentURL, fillIn, visit } from '@ember/test-helpers';
import { runCommands } from 'vault/tests/helpers/pki/pki-run-commands'; import { runCmd } from 'vault/tests/helpers/commands';
import { SELECTORS } from 'vault/tests/helpers/pki/workflow'; import { SELECTORS } from 'vault/tests/helpers/pki/workflow';
/** /**
@ -34,7 +34,7 @@ module('Acceptance | pki engine route cleanup test', function (hooks) {
await logout.visit(); await logout.visit();
await authPage.login(); await authPage.login();
// Cleanup engine // Cleanup engine
await runCommands([`delete sys/mounts/${this.mountPath}`]); await runCmd([`delete sys/mounts/${this.mountPath}`]);
}); });
module('configuration', function () { module('configuration', function () {

View File

@ -13,7 +13,7 @@ import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
import { click, currentURL, fillIn, find, isSettled, visit } from '@ember/test-helpers'; import { click, currentURL, fillIn, find, isSettled, visit } from '@ember/test-helpers';
import { SELECTORS } from 'vault/tests/helpers/pki/workflow'; import { SELECTORS } from 'vault/tests/helpers/pki/workflow';
import { adminPolicy, readerPolicy, updatePolicy } from 'vault/tests/helpers/policy-generator/pki'; import { adminPolicy, readerPolicy, updatePolicy } from 'vault/tests/helpers/policy-generator/pki';
import { tokenWithPolicy, runCommands, clearRecords } from 'vault/tests/helpers/pki/pki-run-commands'; import { clearRecords } from 'vault/tests/helpers/pki/pki-run-commands';
import { runCmd, tokenWithPolicyCmd } from 'vault/tests/helpers/commands'; import { runCmd, tokenWithPolicyCmd } from 'vault/tests/helpers/commands';
import { unsupportedPem } from 'vault/tests/helpers/pki/values'; import { unsupportedPem } from 'vault/tests/helpers/pki/values';
import { create } from 'ember-cli-page-object'; import { create } from 'ember-cli-page-object';
@ -42,14 +42,14 @@ module('Acceptance | pki workflow', function (hooks) {
await logout.visit(); await logout.visit();
await authPage.login(); await authPage.login();
// Cleanup engine // Cleanup engine
await runCommands([`delete sys/mounts/${this.mountPath}`]); await runCmd([`delete sys/mounts/${this.mountPath}`]);
}); });
module('not configured', function (hooks) { module('not configured', function (hooks) {
hooks.beforeEach(async function () { hooks.beforeEach(async function () {
await authPage.login(); await authPage.login();
const pki_admin_policy = adminPolicy(this.mountPath, 'roles'); const pki_admin_policy = adminPolicy(this.mountPath, 'roles');
this.pkiAdminToken = await tokenWithPolicy(`pki-admin-${this.mountPath}`, pki_admin_policy); this.pkiAdminToken = await runCmd(tokenWithPolicyCmd(`pki-admin-${this.mountPath}`, pki_admin_policy));
await logout.visit(); await logout.visit();
clearRecords(this.store); clearRecords(this.store);
}); });
@ -95,14 +95,14 @@ module('Acceptance | pki workflow', function (hooks) {
hooks.beforeEach(async function () { hooks.beforeEach(async function () {
await authPage.login(); await authPage.login();
// Setup role-specific items // Setup role-specific items
await runCommands([ await runCmd([
`write ${this.mountPath}/roles/some-role \ `write ${this.mountPath}/roles/some-role \
issuer_ref="default" \ issuer_ref="default" \
allowed_domains="example.com" \ allowed_domains="example.com" \
allow_subdomains=true \ allow_subdomains=true \
max_ttl="720h"`, max_ttl="720h"`,
]); ]);
await runCommands([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]); await runCmd([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]);
const pki_admin_policy = adminPolicy(this.mountPath, 'roles'); const pki_admin_policy = adminPolicy(this.mountPath, 'roles');
const pki_reader_policy = readerPolicy(this.mountPath, 'roles'); const pki_reader_policy = readerPolicy(this.mountPath, 'roles');
const pki_editor_policy = updatePolicy(this.mountPath, 'roles'); const pki_editor_policy = updatePolicy(this.mountPath, 'roles');
@ -242,13 +242,13 @@ module('Acceptance | pki workflow', function (hooks) {
hooks.beforeEach(async function () { hooks.beforeEach(async function () {
await authPage.login(); await authPage.login();
// base config pki so empty state doesn't show // base config pki so empty state doesn't show
await runCommands([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]); await runCmd([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]);
const pki_admin_policy = adminPolicy(this.mountPath); const pki_admin_policy = adminPolicy(this.mountPath);
const pki_reader_policy = readerPolicy(this.mountPath, 'keys', true); const pki_reader_policy = readerPolicy(this.mountPath, 'keys', true);
const pki_editor_policy = updatePolicy(this.mountPath, 'keys'); const pki_editor_policy = updatePolicy(this.mountPath, 'keys');
this.pkiKeyReader = await tokenWithPolicy(`pki-reader-${this.mountPath}`, pki_reader_policy); this.pkiKeyReader = await runCmd(tokenWithPolicyCmd(`pki-reader-${this.mountPath}`, pki_reader_policy));
this.pkiKeyEditor = await tokenWithPolicy(`pki-editor-${this.mountPath}`, pki_editor_policy); this.pkiKeyEditor = await runCmd(tokenWithPolicyCmd(`pki-editor-${this.mountPath}`, pki_editor_policy));
this.pkiAdminToken = await tokenWithPolicy(`pki-admin-${this.mountPath}`, pki_admin_policy); this.pkiAdminToken = await runCmd(tokenWithPolicyCmd(`pki-admin-${this.mountPath}`, pki_admin_policy));
await logout.visit(); await logout.visit();
clearRecords(this.store); clearRecords(this.store);
}); });
@ -365,9 +365,9 @@ module('Acceptance | pki workflow', function (hooks) {
hooks.beforeEach(async function () { hooks.beforeEach(async function () {
await authPage.login(); await authPage.login();
const pki_admin_policy = adminPolicy(this.mountPath); const pki_admin_policy = adminPolicy(this.mountPath);
this.pkiAdminToken = await tokenWithPolicy(`pki-admin-${this.mountPath}`, pki_admin_policy); this.pkiAdminToken = await runCmd(tokenWithPolicyCmd(`pki-admin-${this.mountPath}`, pki_admin_policy));
// Configure engine with a default issuer // Configure engine with a default issuer
await runCommands([ await runCmd([
`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test" name="Hashicorp Test"`, `write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test" name="Hashicorp Test"`,
]); ]);
await logout.visit(); await logout.visit();
@ -401,9 +401,8 @@ module('Acceptance | pki workflow', function (hooks) {
capabilities = ["deny"] capabilities = ["deny"]
} }
`; `;
this.token = await tokenWithPolicy( this.token = await runCmd(
`pki-issuer-denied-policy-${this.mountPath}`, tokenWithPolicyCmd(`pki-issuer-denied-policy-${this.mountPath}`, pki_issuer_denied_policy)
pki_issuer_denied_policy
); );
await logout.visit(); await logout.visit();
await authPage.login(this.token); await authPage.login(this.token);
@ -479,7 +478,7 @@ module('Acceptance | pki workflow', function (hooks) {
module('rotate', function (hooks) { module('rotate', function (hooks) {
hooks.beforeEach(async function () { hooks.beforeEach(async function () {
await authPage.login(); await authPage.login();
await runCommands([`write ${this.mountPath}/root/generate/internal issuer_name="existing-issuer"`]); await runCmd([`write ${this.mountPath}/root/generate/internal issuer_name="existing-issuer"`]);
await logout.visit(); await logout.visit();
}); });
test('it renders a warning banner when parent issuer has unsupported OIDs', async function (assert) { test('it renders a warning banner when parent issuer has unsupported OIDs', async function (assert) {
@ -513,7 +512,7 @@ module('Acceptance | pki workflow', function (hooks) {
module('config', function (hooks) { module('config', function (hooks) {
hooks.beforeEach(async function () { hooks.beforeEach(async function () {
await authPage.login(); await authPage.login();
await runCommands([`write ${this.mountPath}/root/generate/internal issuer_name="existing-issuer"`]); await runCmd([`write ${this.mountPath}/root/generate/internal issuer_name="existing-issuer"`]);
const mixed_config_policy = ` const mixed_config_policy = `
${adminPolicy(this.mountPath)} ${adminPolicy(this.mountPath)}
${readerPolicy(this.mountPath, 'config/cluster')} ${readerPolicy(this.mountPath, 'config/cluster')}

View File

@ -12,7 +12,8 @@ import logout from 'vault/tests/pages/logout';
import enablePage from 'vault/tests/pages/settings/mount-secret-backend'; import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
import { click, currentURL, currentRouteName, visit } from '@ember/test-helpers'; import { click, currentURL, currentRouteName, visit } from '@ember/test-helpers';
import { SELECTORS } from 'vault/tests/helpers/pki/overview'; import { SELECTORS } from 'vault/tests/helpers/pki/overview';
import { tokenWithPolicy, runCommands, clearRecords } from 'vault/tests/helpers/pki/pki-run-commands'; import { clearRecords } from 'vault/tests/helpers/pki/pki-run-commands';
import { runCmd, tokenWithPolicyCmd } from 'vault/tests/helpers/commands';
module('Acceptance | pki overview', function (hooks) { module('Acceptance | pki overview', function (hooks) {
setupApplicationTest(hooks); setupApplicationTest(hooks);
@ -24,7 +25,7 @@ module('Acceptance | pki overview', function (hooks) {
const mountPath = `pki-${uuidv4()}`; const mountPath = `pki-${uuidv4()}`;
await enablePage.enable('pki', mountPath); await enablePage.enable('pki', mountPath);
this.mountPath = mountPath; this.mountPath = mountPath;
await runCommands([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]); await runCmd([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]);
const pki_admin_policy = ` const pki_admin_policy = `
path "${this.mountPath}/*" { path "${this.mountPath}/*" {
capabilities = ["create", "read", "update", "delete", "list"] capabilities = ["create", "read", "update", "delete", "list"]
@ -41,9 +42,9 @@ module('Acceptance | pki overview', function (hooks) {
}, },
`; `;
this.pkiRolesList = await tokenWithPolicy('pki-roles-list', pki_roles_list_policy); this.pkiRolesList = await runCmd(tokenWithPolicyCmd('pki-roles-list', pki_roles_list_policy));
this.pkiIssuersList = await tokenWithPolicy('pki-issuers-list', pki_issuers_list_policy); this.pkiIssuersList = await runCmd(tokenWithPolicyCmd('pki-issuers-list', pki_issuers_list_policy));
this.pkiAdminToken = await tokenWithPolicy('pki-admin', pki_admin_policy); this.pkiAdminToken = await runCmd(tokenWithPolicyCmd('pki-admin', pki_admin_policy));
await logout.visit(); await logout.visit();
clearRecords(this.store); clearRecords(this.store);
}); });
@ -52,7 +53,7 @@ module('Acceptance | pki overview', function (hooks) {
await logout.visit(); await logout.visit();
await authPage.login(); await authPage.login();
// Cleanup engine // Cleanup engine
await runCommands([`delete sys/mounts/${this.mountPath}`]); await runCmd([`delete sys/mounts/${this.mountPath}`]);
}); });
test('navigates to view issuers when link is clicked on issuer card', async function (assert) { test('navigates to view issuers when link is clicked on issuer card', async function (assert) {
@ -72,7 +73,7 @@ module('Acceptance | pki overview', function (hooks) {
assert.dom(SELECTORS.rolesCardOverviewNum).hasText('0'); assert.dom(SELECTORS.rolesCardOverviewNum).hasText('0');
await click(SELECTORS.rolesCardLink); await click(SELECTORS.rolesCardLink);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/roles`); assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/roles`);
await runCommands([ await runCmd([
`write ${this.mountPath}/roles/some-role \ `write ${this.mountPath}/roles/some-role \
issuer_ref="default" \ issuer_ref="default" \
allowed_domains="example.com" \ allowed_domains="example.com" \
@ -92,7 +93,7 @@ module('Acceptance | pki overview', function (hooks) {
test('navigates to generate certificate page for Issue Certificates card', async function (assert) { test('navigates to generate certificate page for Issue Certificates card', async function (assert) {
await authPage.login(this.pkiAdminToken); await authPage.login(this.pkiAdminToken);
await runCommands([ await runCmd([
`write ${this.mountPath}/roles/some-role \ `write ${this.mountPath}/roles/some-role \
issuer_ref="default" \ issuer_ref="default" \
allowed_domains="example.com" \ allowed_domains="example.com" \

View File

@ -13,7 +13,7 @@ import { v4 as uuidv4 } from 'uuid';
import authPage from 'vault/tests/pages/auth'; import authPage from 'vault/tests/pages/auth';
import logout from 'vault/tests/pages/logout'; import logout from 'vault/tests/pages/logout';
import enablePage from 'vault/tests/pages/settings/mount-secret-backend'; import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
import { runCommands } from 'vault/tests/helpers/pki/pki-run-commands'; import { runCmd } from 'vault/tests/helpers/commands';
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-tidy'; import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-tidy';
module('Acceptance | pki tidy', function (hooks) { module('Acceptance | pki tidy', function (hooks) {
@ -26,7 +26,7 @@ module('Acceptance | pki tidy', function (hooks) {
const mountPath = `pki-workflow-${uuidv4()}`; const mountPath = `pki-workflow-${uuidv4()}`;
await enablePage.enable('pki', mountPath); await enablePage.enable('pki', mountPath);
this.mountPath = mountPath; this.mountPath = mountPath;
await runCommands([ await runCmd([
`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test" name="Hashicorp Test"`, `write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test" name="Hashicorp Test"`,
]); ]);
await logout.visit(); await logout.visit();
@ -36,7 +36,7 @@ module('Acceptance | pki tidy', function (hooks) {
await logout.visit(); await logout.visit();
await authPage.login(); await authPage.login();
// Cleanup engine // Cleanup engine
await runCommands([`delete sys/mounts/${this.mountPath}`]); await runCmd([`delete sys/mounts/${this.mountPath}`]);
}); });
test('it configures a manual tidy operation and shows its details and tidy states', async function (assert) { test('it configures a manual tidy operation and shows its details and tidy states', async function (assert) {

View File

@ -3,38 +3,6 @@
* SPDX-License-Identifier: BUSL-1.1 * SPDX-License-Identifier: BUSL-1.1
*/ */
import consoleClass from 'vault/tests/pages/components/console/ui-panel';
import { create } from 'ember-cli-page-object';
const consoleComponent = create(consoleClass);
export const tokenWithPolicy = async function (name, policy) {
await consoleComponent.runCommands([
`write sys/policies/acl/${name} policy=${btoa(policy)}`,
`write -field=client_token auth/token/create policies=${name}`,
]);
return consoleComponent.lastLogOutput;
};
export const runCommands = async function (commands) {
try {
await consoleComponent.runCommands(commands);
const res = consoleComponent.lastLogOutput;
if (res.includes('Error')) {
throw new Error(res);
}
return res;
} catch (error) {
// eslint-disable-next-line no-console
console.error(
`The following occurred when trying to run the command(s):\n ${commands.join('\n')} \n\n ${
consoleComponent.lastLogOutput
}`
);
throw error;
}
};
// Clears pki-related data and capabilities so that admin // Clears pki-related data and capabilities so that admin
// capabilities from setup don't rollover // capabilities from setup don't rollover
export function clearRecords(store) { export function clearRecords(store) {