mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-21 08:16:42 +02:00
chore: archive inactive projects to apps-archived/
Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b97149ac12
commit
61d181fbc2
3148 changed files with 437 additions and 46640 deletions
165
apps-archived/uload/apps/web/src/tests/factories/index.ts
Normal file
165
apps-archived/uload/apps/web/src/tests/factories/index.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import type { User, Tag, Folder, Link, Click } from '$lib/pocketbase';
|
||||
|
||||
let idCounter = 0;
|
||||
|
||||
function generateId(): string {
|
||||
return `test_${Date.now()}_${++idCounter}`;
|
||||
}
|
||||
|
||||
export function createTestUser(overrides: Partial<User> = {}): User {
|
||||
return {
|
||||
id: generateId(),
|
||||
email: 'test@example.com',
|
||||
username: 'testuser',
|
||||
name: 'Test User',
|
||||
avatar: '',
|
||||
bio: 'Test bio',
|
||||
location: 'Test Location',
|
||||
website: 'https://example.com',
|
||||
github: 'testuser',
|
||||
twitter: 'testuser',
|
||||
linkedin: 'testuser',
|
||||
instagram: 'testuser',
|
||||
publicProfile: true,
|
||||
showClickStats: true,
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function createTestTag(overrides: Partial<Tag> = {}): Tag {
|
||||
return {
|
||||
id: generateId(),
|
||||
user_id: 'test_user_123',
|
||||
name: 'Test Tag',
|
||||
slug: 'test-tag',
|
||||
color: '#3B82F6',
|
||||
icon: '🏷️',
|
||||
is_public: false,
|
||||
usage_count: 0,
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function createTestFolder(overrides: Partial<Folder> = {}): Folder {
|
||||
return {
|
||||
id: generateId(),
|
||||
user_id: 'test_user_123',
|
||||
name: 'test-folder',
|
||||
display_name: 'Test Folder',
|
||||
description: 'Test folder description',
|
||||
icon: '📁',
|
||||
color: '#3B82F6',
|
||||
is_public: true,
|
||||
order: 0,
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function createTestLink(overrides: Partial<Link> = {}): Link {
|
||||
return {
|
||||
id: generateId(),
|
||||
user_id: 'test_user_123',
|
||||
original_url: 'https://example.com',
|
||||
short_code: 'abc123',
|
||||
title: 'Test Link',
|
||||
description: 'Test link description',
|
||||
is_active: true,
|
||||
expires_at: undefined,
|
||||
password: undefined,
|
||||
max_clicks: undefined,
|
||||
use_username: false,
|
||||
folder_id: undefined,
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function createTestClick(overrides: Partial<Click> = {}): Click {
|
||||
return {
|
||||
id: generateId(),
|
||||
link_id: 'test_link_123',
|
||||
ip_address: '127.0.0.1',
|
||||
user_agent: 'Mozilla/5.0 Test Browser',
|
||||
referer: 'https://google.com',
|
||||
country: 'US',
|
||||
device_type: 'desktop',
|
||||
browser: 'Chrome',
|
||||
clicked_at: new Date().toISOString(),
|
||||
created: new Date().toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function createBatchTestTags(count: number, userId: string): Tag[] {
|
||||
const tags: Tag[] = [];
|
||||
const colors = ['#3B82F6', '#EF4444', '#10B981', '#F59E0B', '#8B5CF6'];
|
||||
const icons = ['🏷️', '📌', '⭐', '💡', '🔥'];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
tags.push(
|
||||
createTestTag({
|
||||
name: `Tag ${i + 1}`,
|
||||
slug: `tag-${i + 1}`,
|
||||
user_id: userId,
|
||||
color: colors[i % colors.length],
|
||||
icon: icons[i % icons.length],
|
||||
usage_count: Math.floor(Math.random() * 10),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
export function createBatchTestFolders(count: number, userId: string): Folder[] {
|
||||
const folders: Folder[] = [];
|
||||
const colors = ['#3B82F6', '#EF4444', '#10B981', '#F59E0B', '#8B5CF6'];
|
||||
const icons = ['📁', '📂', '🗂️', '📚', '💼'];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
folders.push(
|
||||
createTestFolder({
|
||||
name: `folder-${i + 1}`,
|
||||
display_name: `Folder ${i + 1}`,
|
||||
user_id: userId,
|
||||
color: colors[i % colors.length],
|
||||
icon: icons[i % icons.length],
|
||||
order: i,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return folders;
|
||||
}
|
||||
|
||||
export function createAuthError(message: string = 'Invalid credentials') {
|
||||
return {
|
||||
response: {
|
||||
data: {
|
||||
message,
|
||||
code: 401,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createValidationError(field: string, message: string) {
|
||||
return {
|
||||
response: {
|
||||
data: {
|
||||
data: {
|
||||
[field]: {
|
||||
message,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
107
apps-archived/uload/apps/web/src/tests/mocks/pocketbase.ts
Normal file
107
apps-archived/uload/apps/web/src/tests/mocks/pocketbase.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import { vi } from 'vitest';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
export interface MockCollection {
|
||||
create: Mock;
|
||||
update: Mock;
|
||||
delete: Mock;
|
||||
getList: Mock;
|
||||
getOne: Mock;
|
||||
getFirstListItem: Mock;
|
||||
authWithPassword: Mock;
|
||||
}
|
||||
|
||||
export interface MockPocketBase {
|
||||
collection: Mock<[string], MockCollection>;
|
||||
authStore: {
|
||||
isValid: boolean;
|
||||
token: string;
|
||||
model: any;
|
||||
clear: Mock;
|
||||
};
|
||||
baseUrl: string;
|
||||
}
|
||||
|
||||
export function createMockPocketBase(): MockPocketBase {
|
||||
return {
|
||||
collection: vi.fn((name: string) => ({
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
getList: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
items: [],
|
||||
totalItems: 0,
|
||||
totalPages: 0,
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
})
|
||||
),
|
||||
getOne: vi.fn(),
|
||||
getFirstListItem: vi.fn(() => Promise.reject(new Error('No items found'))),
|
||||
authWithPassword: vi.fn(),
|
||||
})),
|
||||
authStore: {
|
||||
isValid: false,
|
||||
token: '',
|
||||
model: null,
|
||||
clear: vi.fn(),
|
||||
},
|
||||
baseUrl: 'http://localhost:8090',
|
||||
};
|
||||
}
|
||||
|
||||
export function mockSuccessfulAuth(pb: MockPocketBase, user: any) {
|
||||
const collection = pb.collection('users') as MockCollection;
|
||||
collection.authWithPassword.mockResolvedValue({
|
||||
token: 'mock-token',
|
||||
record: user,
|
||||
});
|
||||
pb.authStore.isValid = true;
|
||||
pb.authStore.token = 'mock-token';
|
||||
pb.authStore.model = user;
|
||||
}
|
||||
|
||||
export function mockFailedAuth(pb: MockPocketBase, error: string = 'Invalid credentials') {
|
||||
const collection = pb.collection('users') as MockCollection;
|
||||
collection.authWithPassword.mockRejectedValue(new Error(error));
|
||||
}
|
||||
|
||||
export function mockCreateSuccess(pb: MockPocketBase, collectionName: string, data: any) {
|
||||
const collection = pb.collection(collectionName) as MockCollection;
|
||||
collection.create.mockResolvedValue({
|
||||
...data,
|
||||
id: 'mock-id-' + Date.now(),
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
export function mockCreateError(pb: MockPocketBase, collectionName: string, error: any) {
|
||||
const collection = pb.collection(collectionName) as MockCollection;
|
||||
collection.create.mockRejectedValue(error);
|
||||
}
|
||||
|
||||
export function mockGetListSuccess(pb: MockPocketBase, collectionName: string, items: any[]) {
|
||||
const collection = pb.collection(collectionName) as MockCollection;
|
||||
collection.getList.mockResolvedValue({
|
||||
items,
|
||||
totalItems: items.length,
|
||||
totalPages: 1,
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
});
|
||||
}
|
||||
|
||||
export function mockUpdateSuccess(pb: MockPocketBase, collectionName: string, updatedData: any) {
|
||||
const collection = pb.collection(collectionName) as MockCollection;
|
||||
collection.update.mockResolvedValue({
|
||||
...updatedData,
|
||||
updated: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
export function mockDeleteSuccess(pb: MockPocketBase, collectionName: string) {
|
||||
const collection = pb.collection(collectionName) as MockCollection;
|
||||
collection.delete.mockResolvedValue(true);
|
||||
}
|
||||
240
apps-archived/uload/apps/web/src/tests/setup.ts
Normal file
240
apps-archived/uload/apps/web/src/tests/setup.ts
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
// Global test setup für uLoad
|
||||
import { vi } from 'vitest';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
// Check if we're in browser environment
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
|
||||
|
||||
// Mock SvelteKit modules
|
||||
vi.mock('$app/environment', () => ({
|
||||
browser: false,
|
||||
dev: true,
|
||||
building: false,
|
||||
version: '1.0.0',
|
||||
}));
|
||||
|
||||
vi.mock('$app/stores', () => ({
|
||||
page: {
|
||||
subscribe: vi.fn(() => () => {}),
|
||||
},
|
||||
updated: {
|
||||
subscribe: vi.fn(() => () => {}),
|
||||
},
|
||||
navigating: {
|
||||
subscribe: vi.fn(() => () => {}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('$app/navigation', () => ({
|
||||
goto: vi.fn(),
|
||||
invalidate: vi.fn(),
|
||||
invalidateAll: vi.fn(),
|
||||
preloadData: vi.fn(),
|
||||
pushState: vi.fn(),
|
||||
replaceState: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock PocketBase für Tests
|
||||
vi.mock('$lib/pocketbase', async () => {
|
||||
const actual = await vi.importActual('$lib/pocketbase');
|
||||
|
||||
// Mock PocketBase-Instanz
|
||||
const mockPb = {
|
||||
authStore: {
|
||||
model: null,
|
||||
token: '',
|
||||
isValid: false,
|
||||
save: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
onChange: vi.fn(() => () => {}),
|
||||
},
|
||||
collection: vi.fn(() => ({
|
||||
create: vi.fn(),
|
||||
getList: vi.fn(),
|
||||
getOne: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
authWithPassword: vi.fn(),
|
||||
authRefresh: vi.fn(),
|
||||
requestPasswordReset: vi.fn(),
|
||||
confirmPasswordReset: vi.fn(),
|
||||
requestVerification: vi.fn(),
|
||||
confirmVerification: vi.fn(),
|
||||
})),
|
||||
send: vi.fn(),
|
||||
};
|
||||
|
||||
return {
|
||||
...actual,
|
||||
pb: mockPb,
|
||||
};
|
||||
});
|
||||
|
||||
// Mock Toast Service
|
||||
vi.mock('$lib/services/toast', () => ({
|
||||
toast: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warning: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock Theme Store
|
||||
vi.mock('$lib/theme.svelte', () => ({
|
||||
themeMode: { value: 'light' },
|
||||
themePreset: { value: 'minimal' },
|
||||
applyTheme: vi.fn(),
|
||||
initializeTheme: vi.fn(),
|
||||
}));
|
||||
|
||||
// Global test utilities
|
||||
export const createMockEvent = (data: any = {}) => ({
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: new URL('http://localhost:5173'),
|
||||
formData: async () => new FormData(),
|
||||
json: async () => data,
|
||||
...data.request,
|
||||
},
|
||||
locals: {
|
||||
pb: {
|
||||
authStore: { model: null, isValid: false },
|
||||
collection: vi.fn(() => ({
|
||||
create: vi.fn(),
|
||||
getList: vi.fn(),
|
||||
getOne: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
})),
|
||||
},
|
||||
...data.locals,
|
||||
},
|
||||
cookies: {
|
||||
get: vi.fn(),
|
||||
set: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
},
|
||||
...data,
|
||||
});
|
||||
|
||||
// Mock User für Tests
|
||||
export const createMockUser = (overrides = {}) => ({
|
||||
id: 'test-user-id',
|
||||
email: 'test@example.com',
|
||||
username: 'testuser',
|
||||
name: 'Test User',
|
||||
verified: true,
|
||||
created: '2024-01-01T00:00:00Z',
|
||||
updated: '2024-01-01T00:00:00Z',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
// Mock Link für Tests
|
||||
export const createMockLink = (overrides = {}) => ({
|
||||
id: 'test-link-id',
|
||||
user_id: 'test-user-id',
|
||||
original_url: 'https://example.com',
|
||||
short_code: 'test123',
|
||||
title: 'Test Link',
|
||||
is_active: true,
|
||||
click_count: 0,
|
||||
created: '2024-01-01T00:00:00Z',
|
||||
updated: '2024-01-01T00:00:00Z',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
// Mock Analytics für Tests
|
||||
export const createMockAnalytics = (overrides = {}) => ({
|
||||
id: 'test-analytics-id',
|
||||
link_id: 'test-link-id',
|
||||
ip_address: '127.0.0.1',
|
||||
user_agent: 'Mozilla/5.0',
|
||||
country: 'Germany',
|
||||
device: 'desktop',
|
||||
created: '2024-01-01T00:00:00Z',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
// Test Environment Setup
|
||||
beforeEach(() => {
|
||||
// Reset all mocks before each test
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Reset DOM (only in browser)
|
||||
if (isBrowser && typeof document !== 'undefined') {
|
||||
document.body.innerHTML = '';
|
||||
}
|
||||
|
||||
// Reset localStorage/sessionStorage (only in browser)
|
||||
if (isBrowser && typeof localStorage !== 'undefined') {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
}
|
||||
});
|
||||
|
||||
// Global error handler für Tests (only in Node)
|
||||
if (isNode) {
|
||||
process.on('unhandledRejection', (error) => {
|
||||
console.error('Unhandled Promise Rejection in test:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Extend expect with custom matchers
|
||||
import { expect } from 'vitest';
|
||||
|
||||
expect.extend({
|
||||
toBeValidEmail(received: string) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const pass = emailRegex.test(received);
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected "${received}" not to be a valid email`
|
||||
: `Expected "${received}" to be a valid email`,
|
||||
};
|
||||
},
|
||||
|
||||
toBeValidUsername(received: string) {
|
||||
const usernameRegex = /^[a-z0-9_-]+$/;
|
||||
const pass = usernameRegex.test(received) && received.length >= 3;
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected "${received}" not to be a valid username`
|
||||
: `Expected "${received}" to be a valid username (3+ chars, lowercase, numbers, - and _ only)`,
|
||||
};
|
||||
},
|
||||
|
||||
toBeValidShortCode(received: string) {
|
||||
const codeRegex = /^[a-zA-Z0-9_-]+$/;
|
||||
const pass = codeRegex.test(received) && received.length >= 3;
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected "${received}" not to be a valid short code`
|
||||
: `Expected "${received}" to be a valid short code`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// Type declarations für custom matchers
|
||||
declare module 'vitest' {
|
||||
interface Assertion<T = any> {
|
||||
toBeValidEmail(): T;
|
||||
toBeValidUsername(): T;
|
||||
toBeValidShortCode(): T;
|
||||
}
|
||||
interface AsymmetricMatchersContaining {
|
||||
toBeValidEmail(): any;
|
||||
toBeValidUsername(): any;
|
||||
toBeValidShortCode(): any;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue