test(phase-02): add Nyquist validation tests for plan 06 gaps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-06-01 15:17:25 +02:00
co-authored by Claude Sonnet 4.6
parent da526cb727
commit 0505beb0a4
3 changed files with 279 additions and 0 deletions
@@ -0,0 +1,81 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
// Must mock before importing router (which imports useAuthStore)
vi.mock('../../stores/auth.js', () => ({
useAuthStore: vi.fn(),
}))
// Mock all lazily-imported view components so the router can be instantiated
vi.mock('../../views/auth/LoginView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/auth/RegisterView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/auth/PasswordResetView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/auth/NewPasswordView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/AdminView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/SharedView.vue', () => ({ default: { template: '<div />' } }))
// Heavy view components imported statically — stub them too
vi.mock('../../views/FileManagerView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/TopicsView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/DocumentView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/SettingsView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/CloudFolderView.vue', () => ({ default: { template: '<div />' } }))
vi.mock('../../views/CloudStorageView.vue', () => ({ default: { template: '<div />' } }))
import { useAuthStore } from '../../stores/auth.js'
import router from '../index.js'
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
})
describe('router — auth route meta (SEC-07, AUTH-01)', () => {
const authPaths = ['/login', '/register', '/password-reset', '/password-reset/confirm']
it.each(authPaths)(
'route %s has meta.public = true so guard skips auth check',
(path) => {
const route = router.getRoutes().find(r => r.path === path)
expect(route, `route "${path}" should exist`).toBeDefined()
expect(route.meta.public).toBe(true)
}
)
it.each(authPaths)(
'route %s has meta.layout = "auth" so sidebar is never rendered',
(path) => {
const route = router.getRoutes().find(r => r.path === path)
expect(route, `route "${path}" should exist`).toBeDefined()
expect(route.meta.layout).toBe('auth')
}
)
})
describe('router — admin guard (SEC-07)', () => {
it('redirects a non-admin user away from /admin to /', async () => {
// Arrange: authenticated but role = 'viewer'
useAuthStore.mockReturnValue({
accessToken: 'fake-token',
user: { id: '1', role: 'viewer' },
refresh: vi.fn().mockResolvedValue(undefined),
})
// Act: attempt to navigate to /admin
const result = await router.push('/admin')
// The guard returns { path: '/' }, so the router resolves to '/'
expect(router.currentRoute.value.path).toBe('/')
})
it('allows an admin user to reach /admin', async () => {
// Arrange: authenticated as admin
useAuthStore.mockReturnValue({
accessToken: 'fake-token',
user: { id: '1', role: 'admin' },
refresh: vi.fn().mockResolvedValue(undefined),
})
await router.push('/admin')
expect(router.currentRoute.value.path).toBe('/admin')
})
})