Skip to content

Commit 51cc763

Browse files
refactor(vue3): migrate CFSSL root certificate settings to script setup ts
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent 1944352 commit 51cc763

2 files changed

Lines changed: 462 additions & 169 deletions

File tree

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 LibreSign contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { beforeEach, describe, expect, it, vi } from 'vitest'
7+
import { flushPromises, mount } from '@vue/test-utils'
8+
9+
import RootCertificateCfssl from '../../../views/Settings/RootCertificateCfssl.vue'
10+
11+
const axiosGetMock = vi.fn()
12+
const axiosPostMock = vi.fn()
13+
const showErrorMock = vi.fn()
14+
const loadStateMock = vi.fn()
15+
const subscribeMock = vi.fn()
16+
const unsubscribeMock = vi.fn()
17+
const checkSetupMock = vi.fn()
18+
const cfsslBinariesOkMock = vi.fn()
19+
20+
vi.mock('@nextcloud/axios', () => ({
21+
default: {
22+
get: (...args: unknown[]) => axiosGetMock(...args),
23+
post: (...args: unknown[]) => axiosPostMock(...args),
24+
},
25+
}))
26+
27+
vi.mock('@nextcloud/dialogs', () => ({
28+
showError: (...args: unknown[]) => showErrorMock(...args),
29+
}))
30+
31+
vi.mock('@nextcloud/event-bus', () => ({
32+
subscribe: (...args: unknown[]) => subscribeMock(...args),
33+
unsubscribe: (...args: unknown[]) => unsubscribeMock(...args),
34+
}))
35+
36+
vi.mock('@nextcloud/initial-state', () => ({
37+
loadState: (...args: unknown[]) => loadStateMock(...args),
38+
}))
39+
40+
vi.mock('@nextcloud/router', () => ({
41+
generateOcsUrl: vi.fn((path: string) => path),
42+
}))
43+
44+
vi.mock('@nextcloud/l10n', () => ({
45+
t: vi.fn((_app: string, text: string, vars?: Record<string, string>) => {
46+
if (!vars) {
47+
return text
48+
}
49+
return text.replace(/{(\w+)}/g, (_match, key) => String(vars[key]))
50+
}),
51+
translate: vi.fn((_app: string, text: string) => text),
52+
translatePlural: vi.fn((_app: string, singular: string, plural: string, count: number) => (count === 1 ? singular : plural)),
53+
n: vi.fn((_app: string, singular: string, plural: string, count: number) => (count === 1 ? singular : plural)),
54+
getLanguage: vi.fn(() => 'en'),
55+
getLocale: vi.fn(() => 'en'),
56+
isRTL: vi.fn(() => false),
57+
}))
58+
59+
vi.mock('../../../helpers/certification', () => ({
60+
selectCustonOption: vi.fn(() => ({
61+
unwrap: () => ({ label: 'Country' }),
62+
})),
63+
}))
64+
65+
vi.mock('../../../logger.js', () => ({
66+
default: {
67+
debug: vi.fn(),
68+
},
69+
}))
70+
71+
vi.mock('../../../store/configureCheck.js', () => ({
72+
useConfigureCheckStore: vi.fn(() => ({
73+
items: [{ resource: 'cfssl-configure', status: 'success' }],
74+
isConfigureOk: vi.fn(() => true),
75+
cfsslBinariesOk: (...args: unknown[]) => cfsslBinariesOkMock(...args),
76+
checkSetup: (...args: unknown[]) => checkSetupMock(...args),
77+
})),
78+
}))
79+
80+
describe('RootCertificateCfssl.vue', () => {
81+
beforeEach(() => {
82+
vi.clearAllMocks()
83+
cfsslBinariesOkMock.mockReturnValue(true)
84+
loadStateMock.mockImplementation((_app: string, _key: string, fallback: unknown) => fallback)
85+
axiosGetMock.mockResolvedValue({
86+
data: {
87+
ocs: {
88+
data: {
89+
generated: false,
90+
rootCert: {
91+
commonName: '',
92+
names: [],
93+
},
94+
cfsslUri: '',
95+
configPath: '',
96+
},
97+
},
98+
},
99+
})
100+
axiosPostMock.mockResolvedValue({
101+
data: {
102+
ocs: {
103+
data: {
104+
data: {
105+
generated: true,
106+
rootCert: {
107+
commonName: 'LibreSign Root',
108+
names: [{ id: 'C', value: 'BR' }],
109+
},
110+
cfsslUri: 'https://cfssl.example.test',
111+
configPath: '/tmp/cfssl.json',
112+
},
113+
},
114+
},
115+
},
116+
})
117+
})
118+
119+
function createWrapper() {
120+
return mount(RootCertificateCfssl, {
121+
global: {
122+
stubs: {
123+
NcSettingsSection: { template: '<section><slot /></section>' },
124+
NcDialog: { template: '<div><slot /><slot name="actions" /></div>' },
125+
NcButton: { template: '<button @click="$emit(\'click\')"><slot /></button>' },
126+
NcTextField: { template: '<input />' },
127+
NcCheckboxRadioSwitch: { template: '<div><slot /></div>' },
128+
CertificateCustonOptions: { template: '<div />' },
129+
CertificatePolicy: { template: '<div />' },
130+
},
131+
},
132+
})
133+
}
134+
135+
it('loads the CFSSL engine state and root certificate on mount', async () => {
136+
loadStateMock.mockImplementation((_app: string, key: string, fallback: unknown) => {
137+
if (key === 'certificate_engine') return 'cfssl'
138+
return fallback
139+
})
140+
141+
const wrapper = createWrapper()
142+
await flushPromises()
143+
144+
expect(wrapper.vm.isThisEngine).toBe(true)
145+
expect(cfsslBinariesOkMock).toHaveBeenCalled()
146+
expect(axiosGetMock).toHaveBeenCalledWith('/apps/libresign/api/v1/admin/certificate')
147+
expect(wrapper.vm.description).toBe('To generate new signatures, you must first generate the root certificate.')
148+
})
149+
150+
it('requires a valid certificate policy only when the toggle is enabled', async () => {
151+
const wrapper = createWrapper()
152+
await flushPromises()
153+
154+
wrapper.vm.formDisabled = false
155+
wrapper.vm.toggleCertificatePolicy = false
156+
expect(wrapper.vm.canSave).toBe(true)
157+
158+
wrapper.vm.toggleCertificatePolicy = true
159+
wrapper.vm.certificatePolicyValid = false
160+
expect(wrapper.vm.canSave).toBe(false)
161+
162+
wrapper.vm.certificatePolicyValid = true
163+
expect(wrapper.vm.canSave).toBe(true)
164+
})
165+
166+
it('resets the form when clearing a generated certificate', async () => {
167+
const wrapper = createWrapper()
168+
await flushPromises()
169+
170+
wrapper.vm.certificate = {
171+
rootCert: {
172+
commonName: 'LibreSign Root',
173+
names: [{ id: 'C', value: 'BR' }],
174+
},
175+
cfsslUri: 'https://cfssl.example.test',
176+
configPath: '/tmp/cfssl.json',
177+
}
178+
wrapper.vm.customData = true
179+
wrapper.vm.formDisabled = true
180+
wrapper.vm.modal = true
181+
182+
wrapper.vm.clearAndShowForm()
183+
184+
expect(wrapper.vm.certificate.rootCert.commonName).toBe('')
185+
expect(wrapper.vm.certificate.rootCert.names).toEqual([])
186+
expect(wrapper.vm.certificate.cfsslUri).toBe('')
187+
expect(wrapper.vm.certificate.configPath).toBe('')
188+
expect(wrapper.vm.customData).toBe(false)
189+
expect(wrapper.vm.formDisabled).toBe(false)
190+
expect(wrapper.vm.modal).toBe(false)
191+
})
192+
193+
it('updates visibility and reloads data when the certificate engine changes', async () => {
194+
const wrapper = createWrapper()
195+
await flushPromises()
196+
axiosGetMock.mockClear()
197+
198+
wrapper.vm.changeEngine('openssl')
199+
await flushPromises()
200+
expect(wrapper.vm.isThisEngine).toBe(false)
201+
expect(axiosGetMock).not.toHaveBeenCalled()
202+
203+
wrapper.vm.changeEngine('cfssl')
204+
await flushPromises()
205+
206+
expect(wrapper.vm.isThisEngine).toBe(true)
207+
expect(axiosGetMock).toHaveBeenCalledWith('/apps/libresign/api/v1/admin/certificate')
208+
})
209+
210+
it('generates the certificate and refreshes setup checks on success', async () => {
211+
const wrapper = createWrapper()
212+
await flushPromises()
213+
wrapper.vm.certificate.rootCert.commonName = 'LibreSign Root'
214+
wrapper.vm.customData = true
215+
wrapper.vm.certificate.cfsslUri = 'https://cfssl.example.test'
216+
217+
await wrapper.vm.generateCertificate()
218+
await flushPromises()
219+
220+
expect(axiosPostMock).toHaveBeenCalledWith(
221+
'/apps/libresign/api/v1/admin/certificate/cfssl',
222+
expect.objectContaining({
223+
rootCert: expect.objectContaining({ commonName: 'LibreSign Root' }),
224+
cfsslUri: 'https://cfssl.example.test',
225+
}),
226+
)
227+
expect(wrapper.vm.certificate.generated).toBe(true)
228+
expect(wrapper.vm.submitLabel).toBe('Generated certificate!')
229+
expect(checkSetupMock).toHaveBeenCalledTimes(1)
230+
})
231+
232+
it('shows a user-facing error when generation fails', async () => {
233+
axiosPostMock.mockRejectedValue({
234+
response: {
235+
data: {
236+
ocs: {
237+
data: {
238+
message: 'CFSSL error',
239+
},
240+
},
241+
},
242+
},
243+
})
244+
245+
const wrapper = createWrapper()
246+
await flushPromises()
247+
248+
await wrapper.vm.generateCertificate()
249+
await flushPromises()
250+
251+
expect(showErrorMock).toHaveBeenCalledWith('Could not generate certificate.\nCFSSL error')
252+
expect(wrapper.vm.submitLabel).toBe('Generate root certificate')
253+
})
254+
})

0 commit comments

Comments
 (0)