/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: BUSL-1.1 */ import sinon from 'sinon'; import { module, test } from 'qunit'; import { setupRenderingTest } from 'vault/tests/helpers'; import { click, fillIn, render } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; import { setupMirage } from 'ember-cli-mirage/test-support'; module('Integration | Component | shamir/dr-token-flow', function (hooks) { setupRenderingTest(hooks); setupMirage(hooks); test('begin to middle flow works', async function (assert) { assert.expect(15); this.server.get('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { assert.ok('Check endpoint is queried on init'); return {}; }); this.server.post('/sys/replication/dr/secondary/generate-operation-token/attempt', function (_, req) { const requestBody = JSON.parse(req.requestBody); assert.ok('Starts the token generation'); assert.deepEqual(requestBody, { attempt: true }); return { started: true, nonce: 'nonce-1234', progress: 0, required: 3, encoded_token: '', otp: 'otp-9876', otp_length: 24, complete: false, }; }); this.server.post('/sys/replication/dr/secondary/generate-operation-token/update', function (_, req) { const requestBody = JSON.parse(req.requestBody); assert.ok('Makes request at the /update path'); assert.deepEqual(requestBody, { key: 'some-key', nonce: 'nonce-1234' }); return { started: true, nonce: 'nonce-1234', progress: 1, required: 3, encoded_token: '', otp: '', otp_length: 24, complete: false, }; }); await render(hbs``); assert.dom('[data-test-dr-token-flow-step="begin"]').exists('First step shows'); assert.dom('[data-test-use-pgp-key-cta]').hasText('Provide PGP Key'); assert.dom('[data-test-generate-token-cta]').hasText('Generate operation token'); await click('[data-test-generate-token-cta]'); assert.dom('[data-test-dr-token-flow-step="shamir"]').exists('Shows shamir step after start'); assert .dom('.shamir-progress') .hasText('0/3 keys provided', 'progress shows reflecting checkStatus response with defaults'); assert.dom('[data-test-otp-info]').exists('OTP info banner shows'); assert.dom('[data-test-otp]').hasText('otp-9876', 'Shows OTP in copy banner'); // Fill in shamir key and submit await fillIn('[data-test-shamir-key-input]', 'some-key'); await click('[data-test-shamir-submit]'); assert.dom('.shamir-progress').hasText('1/3 keys provided', 'progress shows reflecting attempt response'); assert .dom('[data-test-otp-info]') .exists('OTP info still banner shows even when attempt response does not include it'); assert .dom('[data-test-otp]') .hasText('otp-9876', 'Still shows OTP in copy banner when attempt response does not include it'); }); test('middle to finish flow works', async function (assert) { assert.expect(9); this.server.get('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { assert.ok('Check endpoint is queried on init'); return { started: true, nonce: 'nonce-1234', progress: 2, required: 3, encoded_token: '', otp: '', otp_length: 24, complete: false, }; }); this.server.post('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { assert.notOk('attempt endpoint should not be queried'); }); this.server.post('/sys/replication/dr/secondary/generate-operation-token/update', function (_, req) { const requestBody = JSON.parse(req.requestBody); assert.ok('Makes request at the /update path'); assert.deepEqual(requestBody, { key: 'some-key', nonce: 'nonce-1234' }); return { started: true, nonce: 'nonce-1234', progress: 3, required: 3, encoded_token: 'encoded-token-here', otp: '', otp_length: 24, complete: true, }; }); await render(hbs``); assert.dom('[data-test-dr-token-flow-step="shamir"]').exists('Shows shamir step on load'); assert .dom('.shamir-progress') .hasText('2/3 keys provided', 'progress shows reflecting checkStatus response'); assert.dom('[data-test-otp-info]').doesNotExist('OTP info banner not shown'); assert.dom('[data-test-otp]').doesNotExist('otp-9876', 'OTP copy banner not shown'); await fillIn('[data-test-shamir-key-input]', 'some-key'); await click('[data-test-shamir-submit]'); assert .dom('[data-test-dr-token-flow-step="show-token"]') .exists('updates to show encoded token on complete'); assert .dom('[data-test-shamir-encoded-token]') .hasText('encoded-token-here', 'shows encoded token from /update response'); }); test('it works correctly when pgp key chosen', async function (assert) { assert.expect(3); this.server.get('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { return {}; }); this.server.post( '/sys/replication/dr/secondary/generate-operation-token/attempt', function (schema, req) { const body = JSON.parse(req.requestBody); assert.deepEqual(body, { pgp_key: 'some-key-here' }, 'correct payload'); return { started: true, progress: 1, required: 3, complete: false, }; } ); await render(hbs``); await click('[data-test-use-pgp-key-cta]'); assert.dom('[data-test-choose-pgp-key-form="begin"]').exists('PGP form shows'); await click('[data-test-text-toggle]'); await fillIn('[data-test-pgp-file-textarea]', 'some-key-here'); await click('[data-test-use-pgp-key-button]'); await click('[data-test-confirm-pgp-key-submit]'); assert.dom('[data-test-dr-token-flow-step="shamir"]').exists('Renders shamir step after PGP key chosen'); }); test('it cancels correctly when generation not started', async function (assert) { assert.expect(2); const cancelSpy = sinon.spy(); this.set('onCancel', cancelSpy); this.server.get('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { return {}; }); this.server.delete('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { assert.notOk('delete endpoint should not be queried'); return {}; }); await render( hbs`` ); assert.dom('[data-test-shamir-modal-cancel-button]').hasText('Cancel', 'Close button has correct copy'); await click('[data-test-shamir-modal-cancel-button]'); assert.ok(cancelSpy.calledOnce, 'cancel spy called on click'); }); test('it cancels correctly when generation has started but not finished', async function (assert) { assert.expect(3); const cancelSpy = sinon.spy(); this.set('onCancel', cancelSpy); this.server.get('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { return { started: true, progress: 1, required: 3, complete: false, }; }); this.server.delete('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { assert.ok('delete endpoint is queried'); return {}; }); await render( hbs`` ); assert.dom('[data-test-shamir-modal-cancel-button]').hasText('Cancel', 'Close button has correct copy'); await click('[data-test-shamir-modal-cancel-button]'); assert.ok(cancelSpy.calledOnce, 'cancel spy called on click'); }); test('it closes correctly when generation is completed', async function (assert) { assert.expect(2); const cancelSpy = sinon.spy(); this.set('onCancel', cancelSpy); this.server.get('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { return { started: true, progress: 3, required: 3, complete: true, encoded_token: 'foobar', }; }); this.server.delete('/sys/replication/dr/secondary/generate-operation-token/attempt', function () { assert.notOk('delete endpoint should not be queried'); return {}; }); await render( hbs`` ); assert.dom('[data-test-shamir-modal-cancel-button]').hasText('Close', 'Close button has correct copy'); await click('[data-test-shamir-modal-cancel-button]'); assert.ok(cancelSpy.calledOnce, 'cancel spy called on click'); }); });