mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-17 20:17:00 +02:00
* Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License. Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUS-1.1 * Fix test that expected exact offset on hcl file --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Co-authored-by: Sarah Thompson <sthompson@hashicorp.com> Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
356 lines
14 KiB
JavaScript
356 lines
14 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import { module, test } from 'qunit';
|
|
import { setupRenderingTest } from 'ember-qunit';
|
|
import { setupEngine } from 'ember-engines/test-support';
|
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
|
import { render, click, fillIn } from '@ember/test-helpers';
|
|
import hbs from 'htmlbars-inline-precompile';
|
|
import sinon from 'sinon';
|
|
|
|
module('Integration | Component | kubernetes | Page::Role::CreateAndEdit', function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
setupEngine(hooks, 'kubernetes');
|
|
setupMirage(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
const router = this.owner.lookup('service:router');
|
|
const routerStub = sinon.stub(router, 'transitionTo');
|
|
this.transitionCalledWith = (routeName, name) => {
|
|
const route = `vault.cluster.secrets.backend.kubernetes.${routeName}`;
|
|
const args = name ? [route, name] : [route];
|
|
return routerStub.calledWith(...args);
|
|
};
|
|
|
|
const store = this.owner.lookup('service:store');
|
|
this.getRole = (trait) => {
|
|
const role = this.server.create('kubernetes-role', trait);
|
|
store.pushPayload('kubernetes/role', {
|
|
modelName: 'kubernetes/role',
|
|
backend: 'kubernetes-test',
|
|
...role,
|
|
});
|
|
return store.peekRecord('kubernetes/role', role.name);
|
|
};
|
|
|
|
this.newModel = store.createRecord('kubernetes/role', { backend: 'kubernetes-test' });
|
|
this.breadcrumbs = [
|
|
{ label: this.newModel.backend, route: 'overview' },
|
|
{ label: 'roles', route: 'roles' },
|
|
{ label: 'create' },
|
|
];
|
|
});
|
|
|
|
test('it should display placeholder when generation preference is not selected', async function (assert) {
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}} />`,
|
|
{ owner: this.engine }
|
|
);
|
|
assert
|
|
.dom('[data-test-empty-state-title]')
|
|
.hasText('Choose an option above', 'Empty state title renders');
|
|
assert
|
|
.dom('[data-test-empty-state-message]')
|
|
.hasText(
|
|
'To configure a Vault role, choose what should be generated in Kubernetes by Vault.',
|
|
'Empty state message renders'
|
|
);
|
|
assert.dom('[data-test-save]').isDisabled('Save button is disabled');
|
|
});
|
|
|
|
test('it should display different form fields based on generation preference selection', async function (assert) {
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}} />`,
|
|
{ owner: this.engine }
|
|
);
|
|
const commonFields = [
|
|
'name',
|
|
'allowedKubernetesNamespaces',
|
|
'tokenMaxTtl',
|
|
'tokenDefaultTtl',
|
|
'annotations',
|
|
];
|
|
|
|
await click('[data-test-radio-card="basic"]');
|
|
['serviceAccountName', ...commonFields].forEach((field) => {
|
|
assert.dom(`[data-test-field="${field}"]`).exists(`${field} field renders`);
|
|
});
|
|
|
|
await click('[data-test-radio-card="expanded"]');
|
|
['kubernetesRoleType', 'kubernetesRoleName', 'nameTemplate', ...commonFields].forEach((field) => {
|
|
assert.dom(`[data-test-field="${field}"]`).exists(`${field} field renders`);
|
|
});
|
|
|
|
await click('[data-test-radio-card="full"]');
|
|
['kubernetesRoleType', 'nameTemplate', ...commonFields].forEach((field) => {
|
|
assert.dom(`[data-test-field="${field}"]`).exists(`${field} field renders`);
|
|
});
|
|
assert.dom('[data-test-generated-role-rules]').exists('Generated role rules section renders');
|
|
});
|
|
|
|
test('it should clear specific form fields when switching generation preference', async function (assert) {
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}} />`,
|
|
{ owner: this.engine }
|
|
);
|
|
|
|
await click('[data-test-radio-card="basic"]');
|
|
await fillIn('[data-test-input="serviceAccountName"]', 'test');
|
|
await click('[data-test-radio-card="expanded"]');
|
|
assert.strictEqual(
|
|
this.newModel.serviceAccountName,
|
|
null,
|
|
'Service account name cleared when switching from basic to expanded'
|
|
);
|
|
|
|
await fillIn('[data-test-input="kubernetesRoleName"]', 'test');
|
|
await click('[data-test-radio-card="full"]');
|
|
assert.strictEqual(
|
|
this.newModel.kubernetesRoleName,
|
|
null,
|
|
'Kubernetes role name cleared when switching from expanded to full'
|
|
);
|
|
|
|
await click('[data-test-input="kubernetesRoleType"] input');
|
|
await click('[data-test-toggle-input="show-nameTemplate"]');
|
|
await fillIn('[data-test-input="nameTemplate"]', 'bar');
|
|
await fillIn('[data-test-select-template]', '6');
|
|
await click('[data-test-radio-card="expanded"]');
|
|
assert.strictEqual(
|
|
this.newModel.generatedRoleRules,
|
|
null,
|
|
'Role rules cleared when switching from full to expanded'
|
|
);
|
|
|
|
await click('[data-test-radio-card="basic"]');
|
|
assert.strictEqual(
|
|
this.newModel.kubernetesRoleType,
|
|
null,
|
|
'Kubernetes role type cleared when switching from expanded to basic'
|
|
);
|
|
assert.strictEqual(
|
|
this.newModel.kubernetesRoleName,
|
|
null,
|
|
'Kubernetes role name cleared when switching from expanded to basic'
|
|
);
|
|
assert.strictEqual(
|
|
this.newModel.nameTemplate,
|
|
null,
|
|
'Name template cleared when switching from expanded to basic'
|
|
);
|
|
});
|
|
|
|
test('it should create new role', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
this.server.post('/kubernetes-test/roles/role-1', () => assert.ok('POST request made to save role'));
|
|
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}} />`,
|
|
{ owner: this.engine }
|
|
);
|
|
await click('[data-test-radio-card="basic"]');
|
|
await click('[data-test-save]');
|
|
assert.dom('[data-test-inline-error-message]').hasText('Name is required', 'Validation error renders');
|
|
await fillIn('[data-test-input="name"]', 'role-1');
|
|
await fillIn('[data-test-input="serviceAccountName"]', 'default');
|
|
await click('[data-test-save]');
|
|
assert.ok(
|
|
this.transitionCalledWith('roles.role.details', this.newModel.name),
|
|
'Transitions to details route on save'
|
|
);
|
|
});
|
|
|
|
test('it should populate fields when editing role', async function (assert) {
|
|
assert.expect(15);
|
|
|
|
this.server.post('/kubernetes-test/roles/:name', () => assert.ok('POST request made to save role'));
|
|
|
|
for (const pref of ['basic', 'expanded', 'full']) {
|
|
const trait = { expanded: 'withRoleName', full: 'withRoleRules' }[pref];
|
|
this.role = this.getRole(trait);
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.role}} @breadcrumbs={{this.breadcrumbs}} />`,
|
|
{ owner: this.engine }
|
|
);
|
|
assert.dom(`[data-test-radio-card="${pref}"] input`).isChecked('Correct radio card is checked');
|
|
assert.dom('[data-test-input="name"]').hasValue(this.role.name, 'Role name is populated');
|
|
const selector = {
|
|
basic: { name: '[data-test-input="serviceAccountName"]', method: 'hasValue', value: 'default' },
|
|
expanded: {
|
|
name: '[data-test-input="kubernetesRoleName"]',
|
|
method: 'hasValue',
|
|
value: 'vault-k8s-secrets-role',
|
|
},
|
|
full: {
|
|
name: '[data-test-select-template]',
|
|
method: 'hasValue',
|
|
value: '5',
|
|
},
|
|
}[pref];
|
|
assert.dom(selector.name)[selector.method](selector.value);
|
|
await click('[data-test-save]');
|
|
assert.ok(
|
|
this.transitionCalledWith('roles.role.details', this.role.name),
|
|
'Transitions to details route on save'
|
|
);
|
|
}
|
|
});
|
|
|
|
test('it should show and hide annotations and labels', async function (assert) {
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}} />`,
|
|
{ owner: this.engine }
|
|
);
|
|
await click('[data-test-radio-card="basic"]');
|
|
assert.dom('[data-test-annotations]').doesNotExist('Annotations and labels are hidden');
|
|
|
|
await click('[data-test-field="annotations"]');
|
|
await fillIn('[data-test-kv="annotations"] [data-test-kv-key]', 'foo');
|
|
await fillIn('[data-test-kv="annotations"] [data-test-kv-value]', 'bar');
|
|
await click('[data-test-kv="annotations"] [data-test-kv-add-row]');
|
|
assert.deepEqual(this.newModel.extraAnnotations, { foo: 'bar' }, 'Annotations set');
|
|
|
|
await fillIn('[data-test-kv="labels"] [data-test-kv-key]', 'bar');
|
|
await fillIn('[data-test-kv="labels"] [data-test-kv-value]', 'baz');
|
|
await click('[data-test-kv="labels"] [data-test-kv-add-row]');
|
|
assert.deepEqual(this.newModel.extraLabels, { bar: 'baz' }, 'Labels set');
|
|
});
|
|
|
|
test('it should expand annotations and labels when editing if they were populated', async function (assert) {
|
|
this.role = this.getRole();
|
|
await render(hbs`<Page::Role::CreateAndEdit @model={{this.role}} @breadcrumbs={{this.breadcrumbs}}/>`, {
|
|
owner: this.engine,
|
|
});
|
|
assert
|
|
.dom('[data-test-annotations]')
|
|
.doesNotExist('Annotations and labels are collapsed initially when not defined');
|
|
this.role = this.getRole('withRoleRules');
|
|
await render(hbs`<Page::Role::CreateAndEdit @model={{this.role}} @breadcrumbs={{this.breadcrumbs}}/>`, {
|
|
owner: this.engine,
|
|
});
|
|
assert
|
|
.dom('[data-test-annotations]')
|
|
.exists('Annotations and labels are expanded initially when defined');
|
|
});
|
|
|
|
test('it should restore role rule example', async function (assert) {
|
|
this.role = this.getRole('withRoleRules');
|
|
await render(hbs`<Page::Role::CreateAndEdit @model={{this.role}} @breadcrumbs={{this.breadcrumbs}}/>`, {
|
|
owner: this.engine,
|
|
});
|
|
const addedText = 'this will be add to the start of the first line in the JsonEditor';
|
|
await fillIn('[data-test-component="code-mirror-modifier"] textarea', addedText);
|
|
await click('[data-test-restore-example]');
|
|
assert.dom('.CodeMirror-code').doesNotContainText(addedText, 'Role rules example restored');
|
|
});
|
|
|
|
test('it should set generatedRoleRoles model prop on save', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
this.server.post('/kubernetes-test/roles/role-1', (schema, req) => {
|
|
const payload = JSON.parse(req.requestBody);
|
|
const role = this.server.create('kubernetes-role', 'withRoleRules');
|
|
assert.strictEqual(
|
|
payload.generated_role_rules,
|
|
role.generated_role_rules,
|
|
'Generated roles rules are passed in save request'
|
|
);
|
|
});
|
|
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}}/>`,
|
|
{ owner: this.engine }
|
|
);
|
|
await click('[data-test-radio-card="full"]');
|
|
await fillIn('[data-test-input="name"]', 'role-1');
|
|
await fillIn('[data-test-select-template]', '5');
|
|
await click('[data-test-save]');
|
|
});
|
|
|
|
test('it should unset selectedTemplateId when switching from full generation preference', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
this.server.post('/kubernetes-test/roles/role-1', (schema, req) => {
|
|
const payload = JSON.parse(req.requestBody);
|
|
assert.strictEqual(payload.generated_role_rules, null, 'Generated roles rules are not set');
|
|
});
|
|
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}}/>`,
|
|
{ owner: this.engine }
|
|
);
|
|
await click('[data-test-radio-card="full"]');
|
|
await fillIn('[data-test-input="name"]', 'role-1');
|
|
await fillIn('[data-test-select-template]', '5');
|
|
await click('[data-test-radio-card="basic"]');
|
|
await fillIn('[data-test-input="serviceAccountName"]', 'default');
|
|
await click('[data-test-save]');
|
|
});
|
|
|
|
test('it should go back to list route and clean up model', async function (assert) {
|
|
const unloadSpy = sinon.spy(this.newModel, 'unloadRecord');
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}}/>`,
|
|
{ owner: this.engine }
|
|
);
|
|
await click('[data-test-cancel]');
|
|
assert.ok(unloadSpy.calledOnce, 'New model is unloaded on cancel');
|
|
assert.ok(this.transitionCalledWith('roles'), 'Transitions to roles list on cancel');
|
|
|
|
this.role = this.getRole();
|
|
const rollbackSpy = sinon.spy(this.role, 'rollbackAttributes');
|
|
await render(hbs`<Page::Role::CreateAndEdit @model={{this.role}} @breadcrumbs={{this.breadcrumbs}} />`, {
|
|
owner: this.engine,
|
|
});
|
|
await click('[data-test-cancel]');
|
|
assert.ok(rollbackSpy.calledOnce, 'Attributes are rolled back for existing model on cancel');
|
|
assert.ok(this.transitionCalledWith('roles'), 'Transitions to roles list on cancel');
|
|
});
|
|
|
|
test('it should check for form errors', async function (assert) {
|
|
await render(
|
|
hbs`<Page::Role::CreateAndEdit @model={{this.newModel}} @breadcrumbs={{this.breadcrumbs}}/>`,
|
|
{ owner: this.engine }
|
|
);
|
|
await click('[data-test-radio-card="basic"]');
|
|
await click('[data-test-save]');
|
|
assert
|
|
.dom('[data-test-input="name"]')
|
|
.hasClass('has-error-border', 'shows border error on input with error');
|
|
assert.dom('[data-test-inline-error-message]').hasText('Name is required');
|
|
assert
|
|
.dom('[data-test-invalid-form-alert] [data-test-inline-error-message]')
|
|
.hasText('There is an error with this form.');
|
|
});
|
|
|
|
test('it should save edited role with correct properties', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
this.role = this.getRole();
|
|
|
|
this.server.post('/kubernetes-test/roles/:name', (schema, req) => {
|
|
const data = JSON.parse(req.requestBody);
|
|
const expected = {
|
|
name: 'role-0',
|
|
service_account_name: 'demo',
|
|
kubernetes_role_type: 'Role',
|
|
allowed_kubernetes_namespaces: '*',
|
|
token_max_ttl: 86400,
|
|
token_default_ttl: 600,
|
|
};
|
|
assert.deepEqual(expected, data, 'POST request made to save role with correct properties');
|
|
});
|
|
|
|
await render(hbs`<Page::Role::CreateAndEdit @model={{this.role}} @breadcrumbs={{this.breadcrumbs}} />`, {
|
|
owner: this.engine,
|
|
});
|
|
|
|
await fillIn('[data-test-input="serviceAccountName"]', 'demo');
|
|
await click('[data-test-save]');
|
|
});
|
|
});
|