/** * Server Certificate live check. * * A PC pins the certificate the site presents, which a proxy or IIS binding can * make different from the file this page manages. The page must show the * presented pin, warn plainly when it differs, and let the admin recheck. */ import { describe, it, expect, vi, beforeEach } from 'vitest' import { mount, flushPromises } from '@vue/test-utils' vi.mock('@/api', () => ({ default: { get: vi.fn(), post: vi.fn() } })) import api from '@/api' import ServerCertSettings from './ServerCertSettings.vue' const FILEPIN = 'a'.repeat(64) const OTHERPIN = 'b'.repeat(64) function certificate() { return { data: { status: 'success', data: { subject_cn: 'shopdb.example.test', issuer: 'shopdb.example.test', not_before: null, not_after: null, thumbprint: 'AA:BB', spkisha256: FILEPIN, sha1thumbprint: 'CC', san_list: [], expires_in_days: 300, is_self_signed: true } } } } async function render(live) { api.get.mockImplementation((path) => Promise.resolve( path === '/endpoints/settings/server-cert/live' ? { data: { status: 'success', data: live } } : certificate())) const wrapper = mount(ServerCertSettings) await flushPromises() return wrapper } describe('ServerCertSettings live check', () => { beforeEach(() => { api.get.mockReset() }) it('shows the presented pin in green when it matches the file', async () => { const wrapper = await render({ configured: true, url: 'https://shopdb.example.test', presentedspkisha256: FILEPIN, presentedsha1thumbprint: 'CC', matchesfile: true, error: null }) const check = wrapper.find('.live-check') expect(check.find('.live-match').text()).toContain(FILEPIN) expect(check.text()).toContain('Matches the pin of the certificate file above') expect(check.find('.live-warning').exists()).toBe(false) }) it('warns in red with both pins when the site presents another certificate', async () => { const wrapper = await render({ configured: true, url: 'https://shopdb.example.test', presentedspkisha256: OTHERPIN, presentedsha1thumbprint: 'DD', matchesfile: false, error: null }) const warning = wrapper.find('.live-warning') expect(warning.exists()).toBe(true) expect(warning.text()).toContain( 'PCs will be pinned to the certificate they actually receive; use the presented pin') expect(warning.text()).toContain(FILEPIN) expect(warning.text()).toContain(OTHERPIN) expect(wrapper.find('.live-mismatch').exists()).toBe(true) }) it('says when no site address is configured', async () => { const wrapper = await render({ configured: false }) expect(wrapper.find('.live-unconfigured').exists()).toBe(true) }) it('shows the check error', async () => { const wrapper = await render({ configured: true, url: 'https://shopdb.example.test', presentedspkisha256: null, presentedsha1thumbprint: null, matchesfile: false, error: 'Could not read the certificate' }) expect(wrapper.find('.live-error').text()).toContain('Could not read the certificate') }) it('rechecks on demand', async () => { const wrapper = await render({ configured: false }) const before = api.get.mock.calls.filter( ([path]) => path === '/endpoints/settings/server-cert/live').length await wrapper.find('.live-recheck').trigger('click') await flushPromises() const after = api.get.mock.calls.filter( ([path]) => path === '/endpoints/settings/server-cert/live').length expect(after).toBe(before + 1) }) })