- Added new dependencies: `adler2`, `crc32fast`, `flate2`, `miniz_oxide`, and `libredox`. - Updated existing dependencies: `tokio-rustls` to version 0.26.4 and `filetime` to version 0.2.27. - Removed the `backup.rs` file as it is no longer needed. - Introduced tests for configuration and credential management. - Enhanced the `identity` module to generate W3C compliant DID documents. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
228 lines
7.6 KiB
TypeScript
228 lines
7.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { setActivePinia, createPinia } from 'pinia'
|
|
|
|
// Mock the app store
|
|
const mockPackages: Record<string, { state: string }> = {}
|
|
vi.mock('@/stores/app', () => ({
|
|
useAppStore: () => ({
|
|
packages: mockPackages,
|
|
}),
|
|
}))
|
|
|
|
// Mock the goals data with a controlled set
|
|
vi.mock('@/data/goals', () => ({
|
|
GOALS: [
|
|
{
|
|
id: 'accept-payments',
|
|
title: 'Accept Payments',
|
|
subtitle: 'Receive Bitcoin and Lightning payments',
|
|
icon: 'payments',
|
|
category: 'payments',
|
|
requiredApps: ['bitcoin-knots', 'lnd'],
|
|
steps: [
|
|
{ id: 'install-bitcoin', title: 'Install Bitcoin', description: '', appId: 'bitcoin-knots', action: 'install', isAutomatic: true },
|
|
{ id: 'install-lnd', title: 'Install LND', description: '', appId: 'lnd', action: 'install', isAutomatic: true },
|
|
{ id: 'open-channel', title: 'Open Channel', description: '', action: 'configure', isAutomatic: false },
|
|
],
|
|
estimatedTime: '~30 min',
|
|
difficulty: 'beginner',
|
|
},
|
|
{
|
|
id: 'create-identity',
|
|
title: 'Create Identity',
|
|
subtitle: 'Sovereign digital identity',
|
|
icon: 'identity',
|
|
category: 'identity',
|
|
requiredApps: [],
|
|
steps: [
|
|
{ id: 'generate-did', title: 'Generate DID', description: '', action: 'verify', isAutomatic: true },
|
|
{ id: 'setup-nostr', title: 'Setup Nostr', description: '', action: 'configure', isAutomatic: false },
|
|
],
|
|
estimatedTime: '~5 min',
|
|
difficulty: 'beginner',
|
|
},
|
|
{
|
|
id: 'store-photos',
|
|
title: 'Store Photos',
|
|
subtitle: 'Private photo backup',
|
|
icon: 'photos',
|
|
category: 'storage',
|
|
requiredApps: ['immich'],
|
|
steps: [
|
|
{ id: 'install-immich', title: 'Install Immich', description: '', appId: 'immich', action: 'install', isAutomatic: true },
|
|
{ id: 'configure-immich', title: 'Configure', description: '', action: 'configure', isAutomatic: false },
|
|
],
|
|
estimatedTime: '~15 min',
|
|
difficulty: 'beginner',
|
|
},
|
|
],
|
|
}))
|
|
|
|
import { useGoalStore } from '../goals'
|
|
|
|
describe('useGoalStore', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia())
|
|
vi.clearAllMocks()
|
|
localStorage.clear()
|
|
// Clear mock packages
|
|
Object.keys(mockPackages).forEach((k) => delete mockPackages[k])
|
|
})
|
|
|
|
it('starts with empty progress', () => {
|
|
const store = useGoalStore()
|
|
expect(store.progress).toEqual({})
|
|
})
|
|
|
|
it('loads progress from localStorage', () => {
|
|
const savedProgress = {
|
|
'accept-payments': {
|
|
goalId: 'accept-payments',
|
|
status: 'in-progress',
|
|
currentStepIndex: 1,
|
|
completedSteps: ['install-bitcoin'],
|
|
startedAt: 1000,
|
|
},
|
|
}
|
|
localStorage.setItem('archipelago-goal-progress', JSON.stringify(savedProgress))
|
|
|
|
const store = useGoalStore()
|
|
expect(store.progress['accept-payments']).toBeDefined()
|
|
expect(store.progress['accept-payments']!.completedSteps).toContain('install-bitcoin')
|
|
})
|
|
|
|
it('handles corrupt localStorage data', () => {
|
|
localStorage.setItem('archipelago-goal-progress', 'not-valid-json{{{')
|
|
|
|
const store = useGoalStore()
|
|
expect(store.progress).toEqual({})
|
|
})
|
|
|
|
it('startGoal creates progress entry and saves', () => {
|
|
const store = useGoalStore()
|
|
|
|
store.startGoal('accept-payments')
|
|
|
|
expect(store.progress['accept-payments']).toBeDefined()
|
|
expect(store.progress['accept-payments']!.status).toBe('in-progress')
|
|
expect(store.progress['accept-payments']!.currentStepIndex).toBe(0)
|
|
expect(store.progress['accept-payments']!.completedSteps).toEqual([])
|
|
expect(localStorage.getItem('archipelago-goal-progress')).not.toBeNull()
|
|
})
|
|
|
|
it('completeStep adds step to completedSteps', () => {
|
|
const store = useGoalStore()
|
|
store.startGoal('accept-payments')
|
|
|
|
store.completeStep('accept-payments', 'install-bitcoin')
|
|
|
|
expect(store.progress['accept-payments']!.completedSteps).toContain('install-bitcoin')
|
|
})
|
|
|
|
it('completeStep does not duplicate step IDs', () => {
|
|
const store = useGoalStore()
|
|
store.startGoal('accept-payments')
|
|
|
|
store.completeStep('accept-payments', 'install-bitcoin')
|
|
store.completeStep('accept-payments', 'install-bitcoin')
|
|
|
|
expect(store.progress['accept-payments']!.completedSteps.filter((s) => s === 'install-bitcoin')).toHaveLength(1)
|
|
})
|
|
|
|
it('completeStep marks goal completed when all steps done', () => {
|
|
const store = useGoalStore()
|
|
store.startGoal('accept-payments')
|
|
|
|
store.completeStep('accept-payments', 'install-bitcoin')
|
|
store.completeStep('accept-payments', 'install-lnd')
|
|
store.completeStep('accept-payments', 'open-channel')
|
|
|
|
expect(store.progress['accept-payments']!.status).toBe('completed')
|
|
})
|
|
|
|
it('completeStep is a no-op when goal not started', () => {
|
|
const store = useGoalStore()
|
|
|
|
store.completeStep('accept-payments', 'install-bitcoin')
|
|
|
|
expect(store.progress['accept-payments']).toBeUndefined()
|
|
})
|
|
|
|
it('resetGoal removes progress entry', () => {
|
|
const store = useGoalStore()
|
|
store.startGoal('accept-payments')
|
|
expect(store.progress['accept-payments']).toBeDefined()
|
|
|
|
store.resetGoal('accept-payments')
|
|
|
|
expect(store.progress['accept-payments']).toBeUndefined()
|
|
})
|
|
|
|
describe('getGoalStatus', () => {
|
|
it('returns not-started for unknown goal', () => {
|
|
const store = useGoalStore()
|
|
expect(store.getGoalStatus('nonexistent')).toBe('not-started')
|
|
})
|
|
|
|
it('returns not-started when no apps installed and no progress', () => {
|
|
const store = useGoalStore()
|
|
expect(store.getGoalStatus('accept-payments')).toBe('not-started')
|
|
})
|
|
|
|
it('returns completed when all required apps are running', () => {
|
|
mockPackages['bitcoin-knots'] = { state: 'running' }
|
|
mockPackages['lnd'] = { state: 'running' }
|
|
|
|
const store = useGoalStore()
|
|
expect(store.getGoalStatus('accept-payments')).toBe('completed')
|
|
})
|
|
|
|
it('returns in-progress when some required apps are installed', () => {
|
|
mockPackages['bitcoin-knots'] = { state: 'running' }
|
|
|
|
const store = useGoalStore()
|
|
expect(store.getGoalStatus('accept-payments')).toBe('in-progress')
|
|
})
|
|
|
|
it('uses manual progress for goals without required apps', () => {
|
|
const store = useGoalStore()
|
|
|
|
// create-identity has no required apps
|
|
expect(store.getGoalStatus('create-identity')).toBe('not-started')
|
|
|
|
store.startGoal('create-identity')
|
|
expect(store.getGoalStatus('create-identity')).toBe('in-progress')
|
|
})
|
|
|
|
it('recognizes app aliases (immich-server matches immich)', () => {
|
|
mockPackages['immich-server'] = { state: 'running' }
|
|
|
|
const store = useGoalStore()
|
|
expect(store.getGoalStatus('store-photos')).toBe('completed')
|
|
})
|
|
|
|
it('auto-syncs install steps from actual package state', () => {
|
|
mockPackages['bitcoin-knots'] = { state: 'stopped' }
|
|
|
|
const store = useGoalStore()
|
|
store.getGoalStatus('accept-payments')
|
|
|
|
// Should have auto-created progress and marked install-bitcoin as completed
|
|
expect(store.progress['accept-payments']).toBeDefined()
|
|
expect(store.progress['accept-payments']!.completedSteps).toContain('install-bitcoin')
|
|
})
|
|
})
|
|
|
|
it('goalStatuses computes status for all goals', () => {
|
|
mockPackages['bitcoin-knots'] = { state: 'running' }
|
|
mockPackages['lnd'] = { state: 'running' }
|
|
|
|
const store = useGoalStore()
|
|
const statuses = store.goalStatuses
|
|
|
|
expect(statuses['accept-payments']).toBe('completed')
|
|
expect(statuses['create-identity']).toBe('not-started')
|
|
expect(statuses['store-photos']).toBe('not-started')
|
|
})
|
|
})
|