diff --git a/shell/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts b/shell/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts new file mode 100644 index 00000000000..8b569fe86a2 --- /dev/null +++ b/shell/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts @@ -0,0 +1,145 @@ +import { shallowMount } from '@vue/test-utils'; +import Auth from '@shell/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue'; +import LabeledSelect from '@shell/components/form/LabeledSelect.vue'; +import SimpleSecretSelector from '@shell/components/form/SimpleSecretSelector'; + +describe('component: Auth.vue', () => { + const defaultProps = { + mode: 'edit', + value: {}, + namespace: 'test-namespace' + }; + + it('should render correctly with initial props', () => { + const wrapper = shallowMount(Auth, { + props: defaultProps, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + expect(wrapper.find('h3').text()).toBe('%monitoringReceiver.auth.label%'); + expect(wrapper.findComponent(LabeledSelect).exists()).toBe(true); + }); + + it('should initialize with basic auth type if present', () => { + const wrapper = shallowMount(Auth, { + props: { ...defaultProps, value: { basicAuth: { username: { name: 'test' } } } }, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + expect(wrapper.vm.authType).toBe('basicAuth'); + expect(wrapper.findAllComponents(SimpleSecretSelector)).toHaveLength(2); + }); + + it('should initialize with bearer token auth type if present', () => { + const wrapper = shallowMount(Auth, { + props: { ...defaultProps, value: { bearerTokenSecret: { name: 'test' } } }, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + expect(wrapper.vm.authType).toBe('bearerTokenSecret'); + expect(wrapper.findAllComponents(SimpleSecretSelector)).toHaveLength(1); + }); + + it('should switch to basic auth and clear other types', async() => { + const value = { bearerTokenSecret: { name: 'b', key: 'k' } }; + const wrapper = shallowMount(Auth, { + props: { ...defaultProps, value }, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + await wrapper.findComponent(LabeledSelect).vm.$emit('update:value', 'basicAuth'); + expect(wrapper.vm.authType).toBe('basicAuth'); + expect(wrapper.props('value').bearerTokenSecret).toBeUndefined(); + expect(wrapper.props('value').basicAuth).toBeDefined(); + }); + + it('should switch to bearer token and clear other types', async() => { + const value = { basicAuth: { username: { name: 'u' } } }; + const wrapper = shallowMount(Auth, { + props: { ...defaultProps, value }, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + await wrapper.findComponent(LabeledSelect).vm.$emit('update:value', 'bearerTokenSecret'); + expect(wrapper.vm.authType).toBe('bearerTokenSecret'); + expect(wrapper.props('value').basicAuth).toBeUndefined(); + expect(wrapper.props('value').bearerTokenSecret).toBeDefined(); + }); + + it('should switch to none and clear other types', async() => { + const value = { basicAuth: { username: { name: 'u' } } }; + const wrapper = shallowMount(Auth, { + props: { ...defaultProps, value }, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + await wrapper.findComponent(LabeledSelect).vm.$emit('update:value', 'none'); + expect(wrapper.vm.authType).toBe('none'); + expect(wrapper.props('value').basicAuth).toBeUndefined(); + }); + + it('should update basic auth username and password', async() => { + const wrapper = shallowMount(Auth, { + props: { ...defaultProps, value: { basicAuth: { username: { name: 'test' } } } }, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + await wrapper.vm.$nextTick(); + + const selectors = wrapper.findAllComponents(SimpleSecretSelector); + + expect(selectors).toHaveLength(2); + + const usernameSelector = selectors[0]; + const passwordSelector = selectors[1]; + + await usernameSelector.vm.$emit('updateSecretName', 'user-secret'); + await usernameSelector.vm.$emit('updateSecretKey', 'user-key'); + await passwordSelector.vm.$emit('updateSecretName', 'pass-secret'); + await passwordSelector.vm.$emit('updateSecretKey', 'pass-key'); + + const basicAuth = wrapper.props('value').basicAuth; + + expect(basicAuth.username.name).toBe('user-secret'); + expect(basicAuth.username.key).toBe('user-key'); + expect(basicAuth.password.name).toBe('pass-secret'); + expect(basicAuth.password.key).toBe('pass-key'); + }); + + it('should update bearer token secret', async() => { + const wrapper = shallowMount(Auth, { + props: { ...defaultProps, value: { bearerTokenSecret: { name: 'test' } } }, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + await wrapper.vm.$nextTick(); + + const selector = wrapper.findComponent(SimpleSecretSelector); + + expect(selector.exists()).toBe(true); + + await selector.vm.$emit('updateSecretName', 'bearer-name'); + await selector.vm.$emit('updateSecretKey', 'bearer-key'); + + const bearerToken = wrapper.props('value').bearerTokenSecret; + + expect(bearerToken.name).toBe('bearer-name'); + expect(bearerToken.key).toBe('bearer-key'); + }); + + it('should render in view mode', () => { + const wrapper = shallowMount(Auth, { + props: { + mode: 'view', value: { basicAuth: {} }, namespace: 'ns' + }, + global: { mocks: { $fetchState: { pending: false, error: null } } } + }); + + expect(wrapper.findComponent(LabeledSelect).attributes('disabled')).toBe('true'); + const selectors = wrapper.findAllComponents(SimpleSecretSelector); + + selectors.forEach((selector) => { + expect(selector.props('disabled')).toBe(true); + }); + }); +}); diff --git a/shell/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts b/shell/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts new file mode 100644 index 00000000000..cfa09d0bcdd --- /dev/null +++ b/shell/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts @@ -0,0 +1,226 @@ + +import Tls from '@shell/edit/monitoring.coreos.com.alertmanagerconfig/tls.vue'; +import { shallowMount } from '@vue/test-utils'; +import { _EDIT, _VIEW } from '@shell/config/query-params'; +import SimpleSecretSelector from '@shell/components/form/SimpleSecretSelector.vue'; +import { LabeledInput } from '@components/Form/LabeledInput'; +import { Banner } from '@components/Banner'; + +describe('component: Tls', () => { + const mockStore = { + getters: { + 'i18n/t': (key: string) => key, + 'i18n/exists': () => true, + }, + }; + + const requiredProps = { + value: { tlsConfig: {} }, + namespace: 'test-namespace', + }; + + it('should render all child components', () => { + const wrapper = shallowMount(Tls, { + props: { ...requiredProps, mode: _EDIT }, + global: { mocks: mockStore }, + }); + + const secretSelectors = wrapper.findAllComponents(SimpleSecretSelector); + const labeledInput = wrapper.findComponent(LabeledInput); + + expect(secretSelectors).toHaveLength(3); + expect(labeledInput.exists()).toBe(true); + }); + + it('should show a banner if namespace is not provided', () => { + const wrapper = shallowMount(Tls, { + props: { + ...requiredProps, + namespace: undefined, + mode: _EDIT, + }, + global: { mocks: mockStore }, + }); + const banner = wrapper.findComponent(Banner); + + expect(banner.exists()).toBe(true); + expect(banner.props().color).toBe('error'); + expect(banner.vm.$slots.default()[0].children).toBe('%alertmanagerConfigReceiver.namespaceWarning%'); + }); + + it('should not show a banner if namespace is provided', () => { + const wrapper = shallowMount(Tls, { + props: { ...requiredProps, mode: _EDIT }, + global: { mocks: mockStore }, + }); + const banner = wrapper.findComponent(Banner); + + expect(banner.exists()).toBe(false); + }); + + it('should disable inputs when in view mode', () => { + const wrapper = shallowMount(Tls, { + props: { ...requiredProps, mode: _VIEW }, + global: { mocks: mockStore }, + }); + + const secretSelectors = wrapper.findAllComponents(SimpleSecretSelector); + const labeledInput = wrapper.findComponent(LabeledInput); + + secretSelectors.forEach((s) => expect(s.props().disabled).toBe(true)); + expect(labeledInput.props().mode).toBe(_VIEW); + }); + + it('should initialize tlsConfig if not present', () => { + const value = {}; + const wrapper = shallowMount(Tls, { + props: { + ...requiredProps, + value, + mode: _EDIT, + }, + global: { mocks: mockStore }, + }); + + expect(wrapper.props().value.tlsConfig).toBeDefined(); + }); + + it('should correctly initialize data from props', () => { + const value = { + tlsConfig: { + ca: { + secret: { + key: 'ca-key', + name: 'ca-name' + } + }, + cert: { + secret: { + key: 'cert-key', + name: 'cert-name' + } + }, + keySecret: { + key: 'key-key', + name: 'key-name' + } + } + }; + + const wrapper = shallowMount(Tls, { + props: { + ...requiredProps, + value, + mode: _EDIT, + }, + global: { mocks: mockStore }, + }); + + const data = wrapper.vm.$data; + + expect(data.initialCaSecretKey).toBe('ca-key'); + expect(data.initialCaSecretName).toBe('ca-name'); + expect(data.initialClientCertSecretKey).toBe('cert-key'); + expect(data.initialClientCertSecretName).toBe('cert-name'); + expect(data.initialClientKeySecretKey).toBe('key-key'); + expect(data.initialClientKeySecretName).toBe('key-name'); + }); + + describe('event handling', () => { + it.each([ + ['ca', 0, 'ca', 'updateCaSecretName', 'updateCaSecretKey'], + ['cert', 1, 'cert', 'updateClientCertSecretName', 'updateClientCertSecretKey'], + ['key', 2, 'keySecret', 'updateClientKeySecretName', 'updateClientKeySecretKey'], + ])('should handle %p secret selector events', async(secretType, index, tlsConfigKey, nameHandler, keyHandler) => { + const value = { tlsConfig: {} }; + const wrapper = shallowMount(Tls, { + props: { + ...requiredProps, value, mode: _EDIT + }, + global: { mocks: mockStore }, + }); + + const secretSelector = wrapper.findAllComponents(SimpleSecretSelector)[index]; + const name = `${ secretType }-secret-name`; + const key = `${ secretType }-secret-key`; + + await secretSelector.vm.$emit('updateSecretName', name); + await wrapper.vm.$nextTick(); + + const nameValue = tlsConfigKey === 'keySecret' ? value.tlsConfig[tlsConfigKey].name : value.tlsConfig[tlsConfigKey].secret.name; + + expect(nameValue).toBe(name); + + await secretSelector.vm.$emit('updateSecretKey', key); + await wrapper.vm.$nextTick(); + + const keyValue = tlsConfigKey === 'keySecret' ? value.tlsConfig[tlsConfigKey].key : value.tlsConfig[tlsConfigKey].secret.key; + + expect(keyValue).toBe(key); + }); + }); + + describe('secret updates', () => { + it.each([ + ['Ca', 'updateCaSecretName', 'ca', 'name'], + ['Ca', 'updateCaSecretKey', 'ca', 'key'], + ['ClientCert', 'updateClientCertSecretName', 'cert', 'name'], + ['ClientCert', 'updateClientCertSecretKey', 'cert', 'key'], + ['ClientKey', 'updateClientKeySecretName', 'keySecret', 'name'], + ['ClientKey', 'updateClientKeySecretKey', 'keySecret', 'key'], + ])('should update %p secret %p', (secret, handler, obj, field) => { + const value = { tlsConfig: {} }; + const wrapper = shallowMount(Tls, { + props: { + ...requiredProps, + value, + mode: _EDIT + }, + global: { mocks: mockStore }, + }); + + const data = 'test-data'; + + (wrapper.vm as any)[handler](data); + + const dataValue = obj === 'keySecret' ? value.tlsConfig[obj][field] : value.tlsConfig[obj].secret[field]; + + expect(dataValue).toBe(data); + }); + + it.each([ + ['Ca', 'updateCaSecretName', 'ca'], + ['ClientCert', 'updateClientCertSecretName', 'cert'], + ])('should remove %p secret', (secret, handler, obj) => { + const value = { tlsConfig: { [obj]: { secret: { name: 'test-name', key: 'test-key' } } } }; + const wrapper = shallowMount(Tls, { + props: { + ...requiredProps, + value, + mode: _EDIT + }, + global: { mocks: mockStore }, + }); + + (wrapper.vm as any)[handler]('__[[NONE]]__'); + + expect(value.tlsConfig[obj]).toStrictEqual({}); + }); + + it('should remove "ClientKey" secret', () => { + const value = { tlsConfig: { keySecret: { name: 'test-name', key: 'test-key' } } }; + const wrapper = shallowMount(Tls, { + props: { + ...requiredProps, + value, + mode: _EDIT + }, + global: { mocks: mockStore }, + }); + + (wrapper.vm as any).updateClientKeySecretName('__[[NONE]]__'); + + expect(value.tlsConfig.keySecret).toStrictEqual({}); + }); + }); +}); diff --git a/shell/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue b/shell/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue index c906a30d029..0bf8467c082 100644 --- a/shell/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +++ b/shell/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue @@ -1,12 +1,15 @@