/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { click, fillIn, find, render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupMirage } from 'ember-cli-mirage/test-support';
import recoveryHandler from 'vault/mirage/handlers/recovery';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
const SELECTORS = {
badge: (name) => `[data-test-badge="${name}"]`,
};
module('Integration | Component | recovery/snapshots/snapshot-manage', function (hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);
hooks.beforeEach(async function () {
recoveryHandler(this.server);
const snapshot = this.server.create('snapshot');
const api = this.owner.lookup('service:api');
let namespaces = [];
try {
const response = await api.sys.internalUiListNamespaces();
namespaces = response.keys ?? [];
} catch {
namespaces = [];
}
this.model = {
snapshot,
namespaces,
};
this.version = this.owner.lookup('service:version');
this.version.type = 'enterprise';
});
test('it displays loaded snapshot card', async function (assert) {
await render(hbs``);
assert.dom(SELECTORS.badge('status')).hasText('Ready', 'status badge renders');
});
test('it displays namespace selector for root namespace', async function (assert) {
await render(hbs``);
assert.dom(GENERAL.selectByAttr('namespace')).exists('namespace selector is visible in root namespace');
});
test('it validates form fields before read/recover operations', async function (assert) {
await render(hbs``);
// Try to read without selecting mount or resource path
await click(GENERAL.button('read'));
assert.dom(GENERAL.validationErrorByAttr('mount')).hasText('Please select a secret mount');
assert.dom(GENERAL.validationErrorByAttr('resourcePath')).hasText('Please enter a resource path');
});
test('it clears form selections', async function (assert) {
await render(hbs``);
await click(GENERAL.selectByAttr('namespace'));
await click('[data-option-index="1"]');
await click(GENERAL.selectByAttr('mount'));
await click('[data-option-index]');
await fillIn(GENERAL.inputByAttr('resourcePath'), 'test-path');
await click(GENERAL.button('clear'));
const nsSelect = find(GENERAL.selectByAttr('namespace'));
assert.strictEqual(nsSelect.textContent.trim(), 'root', 'namespace was reset');
const mountSelect = find(GENERAL.selectByAttr('mount'));
assert.strictEqual(mountSelect.textContent.trim(), '', 'mount is cleared');
assert.dom(GENERAL.inputByAttr('resourcePath')).hasValue('', 'resource path is cleared');
});
test('it performs read operation successfully in root namespace', async function (assert) {
await render(hbs``);
await click(GENERAL.selectByAttr('mount'));
await click('[data-option-index]');
await fillIn(GENERAL.inputByAttr('resourcePath'), 'my-path');
await click(GENERAL.button('read'));
// Open modal
assert.dom('[data-test-read-secrets]').exists('renders read modal');
assert.dom(GENERAL.infoRowLabel('secret_key')).exists('renders secret data');
// Close modal
await click(GENERAL.button('close'));
assert.dom('[data-test-read-secrets]').doesNotExist('read modal closed');
});
test('it performs read operation successfully for child namespace while in root context', async function (assert) {
await render(hbs``);
await click(GENERAL.selectByAttr('namespace'));
await click('[data-option-index="1"]');
await click(GENERAL.selectByAttr('mount'));
await click('[data-option-index]');
await fillIn(GENERAL.inputByAttr('resourcePath'), 'my-path');
await click(GENERAL.button('read'));
// Open modal
assert.dom('[data-test-read-secrets]').exists('renders read modal');
assert.dom(GENERAL.infoRowLabel('secret_key')).exists('renders secret data');
// Close modal
await click(GENERAL.button('close'));
assert.dom('[data-test-read-secrets]').doesNotExist('read modal closed');
});
test('it performs recover operation successfully in root namespace', async function (assert) {
await render(hbs``);
await click(GENERAL.selectByAttr('mount'));
await click('[data-option-index]');
await fillIn(GENERAL.inputByAttr('resourcePath'), 'recovered-secret');
await click(GENERAL.button('recover'));
assert.dom(GENERAL.inlineAlert).containsText('Success', 'shows success message');
assert.dom(GENERAL.inlineAlert).containsText('recovered-secret', 'shows the recovered path');
});
test('it performs recover operation successfully for child namespace while in root context', async function (assert) {
await render(hbs``);
await click(GENERAL.selectByAttr('namespace'));
await click('[data-option-index="1"]');
await click(GENERAL.selectByAttr('mount'));
await click('[data-option-index]');
await fillIn(GENERAL.inputByAttr('resourcePath'), 'recovered-secret');
await click(GENERAL.button('recover'));
assert.dom(GENERAL.inlineAlert).containsText('Success', 'shows success message');
assert.dom(GENERAL.inlineAlert).containsText('recovered-secret', 'shows the recovered path');
});
test('it displays error alert when read operation fails', async function (assert) {
await render(hbs``);
await fillIn(GENERAL.inputByAttr('resourcePath'), 'nonexistent-secret');
await click(GENERAL.selectByAttr('mount'));
await click('[data-option-index]');
await click(GENERAL.button('read'));
assert.dom(GENERAL.inlineAlert).containsText('Error', 'shows error alert');
});
test('it toggles JSON view in read modal', async function (assert) {
await render(hbs``);
await fillIn(GENERAL.inputByAttr('resourcePath'), 'test-secret');
await click(GENERAL.selectByAttr('mount'));
await click('[data-option-index]');
await click(GENERAL.button('read'));
assert.dom('[data-test-read-secrets]').exists('read modal opens');
await click(GENERAL.toggleInput('snapshot-read-secrets'));
assert.dom('.hds-code-block').exists('renders JSON view');
});
});