fix: test stabilization for windows
This commit is contained in:
@@ -960,7 +960,8 @@ export class MediaEngine extends EventEmitter {
|
||||
const dbMedia = await db.select().from(media).where(eq(media.id, id)).get();
|
||||
if (!dbMedia?.filePath) return null;
|
||||
const dataDir = this.getDataDir();
|
||||
return path.relative(dataDir, dbMedia.filePath);
|
||||
const relativePath = path.relative(dataDir, dbMedia.filePath);
|
||||
return relativePath.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
async rebuildDatabaseFromFiles(): Promise<void> {
|
||||
|
||||
@@ -29,6 +29,7 @@ import { MediaEngine, MediaData } from '../../src/main/engine/MediaEngine';
|
||||
const mockMedia = new Map<string, any>();
|
||||
const mockPostMedia = new Map<string, any>();
|
||||
const mockFiles = new Map<string, Buffer | string>();
|
||||
const normalizePath = (value: string): string => value.replace(/\\/g, '/');
|
||||
|
||||
// Track database operations for testing
|
||||
let mediaDeleteCalled = false;
|
||||
@@ -126,7 +127,8 @@ vi.mock('../../src/main/database', () => ({
|
||||
// Mock fs/promises
|
||||
vi.mock('fs/promises', () => ({
|
||||
readFile: vi.fn(async (path: string) => {
|
||||
const content = mockFiles.get(path);
|
||||
const normalizedPath = normalizePath(path);
|
||||
const content = mockFiles.get(normalizedPath);
|
||||
if (!content) {
|
||||
const error = new Error(`ENOENT: no such file or directory, open '${path}'`);
|
||||
(error as any).code = 'ENOENT';
|
||||
@@ -135,29 +137,29 @@ vi.mock('fs/promises', () => ({
|
||||
return content;
|
||||
}),
|
||||
writeFile: vi.fn(async (path: string, content: Buffer | string) => {
|
||||
mockFiles.set(path, content);
|
||||
mockFiles.set(normalizePath(path), content);
|
||||
}),
|
||||
unlink: vi.fn(async (path: string) => {
|
||||
mockFiles.delete(path);
|
||||
mockFiles.delete(normalizePath(path));
|
||||
}),
|
||||
mkdir: vi.fn(async () => {}),
|
||||
readdir: vi.fn(async () => []),
|
||||
stat: vi.fn(async (path: string) => ({
|
||||
isFile: () => mockFiles.has(path),
|
||||
isDirectory: () => !mockFiles.has(path),
|
||||
size: mockFiles.get(path)?.length || 0,
|
||||
isFile: () => mockFiles.has(normalizePath(path)),
|
||||
isDirectory: () => !mockFiles.has(normalizePath(path)),
|
||||
size: mockFiles.get(normalizePath(path))?.length || 0,
|
||||
})),
|
||||
access: vi.fn(async (path: string) => {
|
||||
if (!mockFiles.has(path)) {
|
||||
if (!mockFiles.has(normalizePath(path))) {
|
||||
const error = new Error(`ENOENT`);
|
||||
(error as any).code = 'ENOENT';
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
copyFile: vi.fn(async (src: string, dest: string) => {
|
||||
const content = mockFiles.get(src);
|
||||
const content = mockFiles.get(normalizePath(src));
|
||||
if (content) {
|
||||
mockFiles.set(dest, content);
|
||||
mockFiles.set(normalizePath(dest), content);
|
||||
}
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import * as path from 'path';
|
||||
|
||||
// Mock data stores
|
||||
const mockFiles = new Map<string, string>();
|
||||
@@ -749,17 +750,20 @@ describe('MetaEngine', () => {
|
||||
});
|
||||
|
||||
it('should use custom dataDir when provided in setProjectContext', () => {
|
||||
metaEngine.setProjectContext('project-with-custom-dir', '/custom/data/path');
|
||||
const customDataDir = path.join('custom', 'data', 'path');
|
||||
metaEngine.setProjectContext('project-with-custom-dir', customDataDir);
|
||||
|
||||
const metaDir = metaEngine.getMetaDir();
|
||||
expect(metaDir).toContain('/custom/data/path');
|
||||
expect(normalizePath(metaDir)).toContain(normalizePath(customDataDir));
|
||||
});
|
||||
|
||||
it('should sync dataPath from database to project.json if different', async () => {
|
||||
const metaDir = metaEngine.getMetaDir();
|
||||
const oldPath = path.join('old', 'path', 'from', 'file');
|
||||
const newPath = path.join('new', 'path', 'from', 'database');
|
||||
mockFiles.set(normalizePath(`${metaDir}/project.json`), JSON.stringify({
|
||||
name: 'Project',
|
||||
dataPath: '/old/path/from/file',
|
||||
dataPath: oldPath,
|
||||
}));
|
||||
|
||||
// Database has the currently selected (authoritative) path
|
||||
@@ -767,7 +771,7 @@ describe('MetaEngine', () => {
|
||||
id: 'test-project',
|
||||
name: 'Project',
|
||||
description: null,
|
||||
dataPath: '/new/path/from/database',
|
||||
dataPath: newPath,
|
||||
slug: 'project',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
@@ -779,7 +783,7 @@ describe('MetaEngine', () => {
|
||||
const savedProjectJson = mockFiles.get(normalizePath(`${metaDir}/project.json`));
|
||||
expect(savedProjectJson).toBeDefined();
|
||||
const parsed = JSON.parse(savedProjectJson!);
|
||||
expect(parsed.dataPath).toBe('/new/path/from/database');
|
||||
expect(normalizePath(parsed.dataPath)).toBe(normalizePath(newPath));
|
||||
expect(mockLocalDb.update).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import { ProjectEngine, ProjectData } from '../../src/main/engine/ProjectEngine';
|
||||
import { resetMockCounters } from '../utils/factories';
|
||||
|
||||
const normalizePath = (value: string): string => value.replace(/\\/g, '/');
|
||||
|
||||
// Create mock data stores
|
||||
const mockProjects = new Map<string, any>();
|
||||
|
||||
@@ -550,7 +553,7 @@ describe('ProjectEngine', () => {
|
||||
|
||||
describe('Custom dataPath', () => {
|
||||
it('should create project with custom dataPath', async () => {
|
||||
const customPath = '/Users/test/Documents/MyBlog';
|
||||
const customPath = path.join('Users', 'test', 'Documents', 'MyBlog');
|
||||
const project = await projectEngine.createProject({
|
||||
name: 'Custom Path Project',
|
||||
dataPath: customPath,
|
||||
@@ -561,7 +564,8 @@ describe('ProjectEngine', () => {
|
||||
|
||||
it('should create meta and thumbnails directories in custom dataPath', async () => {
|
||||
const fs = await import('fs/promises');
|
||||
const customPath = '/Users/test/Documents/MyBlog';
|
||||
const customPath = path.join('Users', 'test', 'Documents', 'MyBlog');
|
||||
const normalizedCustomPath = normalizePath(customPath);
|
||||
|
||||
await projectEngine.createProject({
|
||||
name: 'Custom Dirs Project',
|
||||
@@ -569,17 +573,18 @@ describe('ProjectEngine', () => {
|
||||
});
|
||||
|
||||
const mkdirCalls = vi.mocked(fs.mkdir).mock.calls;
|
||||
const createdPaths = mkdirCalls.map(call => call[0]);
|
||||
const createdPaths = mkdirCalls.map(call => normalizePath(String(call[0])));
|
||||
|
||||
// Should create meta/ and thumbnails/ in custom dataPath
|
||||
expect(createdPaths).toContainEqual(expect.stringContaining(customPath));
|
||||
expect(createdPaths.some(p => String(p).includes(customPath) && String(p).includes('meta'))).toBe(true);
|
||||
expect(createdPaths.some(p => String(p).includes(customPath) && String(p).includes('thumbnails'))).toBe(true);
|
||||
expect(createdPaths).toContainEqual(expect.stringContaining(normalizedCustomPath));
|
||||
expect(createdPaths.some(p => p.includes(normalizedCustomPath) && p.includes('meta'))).toBe(true);
|
||||
expect(createdPaths.some(p => p.includes(normalizedCustomPath) && p.includes('thumbnails'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should create posts and media directories in custom dataPath', async () => {
|
||||
const fs = await import('fs/promises');
|
||||
const customPath = '/Users/test/Documents/MyBlog';
|
||||
const customPath = path.join('Users', 'test', 'Documents', 'MyBlog');
|
||||
const normalizedCustomPath = normalizePath(customPath);
|
||||
|
||||
await projectEngine.createProject({
|
||||
name: 'Custom Data Project',
|
||||
@@ -587,11 +592,11 @@ describe('ProjectEngine', () => {
|
||||
});
|
||||
|
||||
const mkdirCalls = vi.mocked(fs.mkdir).mock.calls;
|
||||
const createdPaths = mkdirCalls.map(call => call[0]);
|
||||
const createdPaths = mkdirCalls.map(call => normalizePath(String(call[0])));
|
||||
|
||||
// Should create posts/ and media/ in custom dataPath
|
||||
expect(createdPaths.some(p => String(p).includes(customPath) && String(p).includes('posts'))).toBe(true);
|
||||
expect(createdPaths.some(p => String(p).includes(customPath) && String(p).includes('media'))).toBe(true);
|
||||
expect(createdPaths.some(p => p.includes(normalizedCustomPath) && p.includes('posts'))).toBe(true);
|
||||
expect(createdPaths.some(p => p.includes(normalizedCustomPath) && p.includes('media'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should create meta and thumbnails in internal storage when no dataPath', async () => {
|
||||
@@ -611,7 +616,7 @@ describe('ProjectEngine', () => {
|
||||
|
||||
it('should use getDataDir with custom dataPath', () => {
|
||||
const projectId = 'test-id';
|
||||
const customPath = '/Users/test/MyBlog';
|
||||
const customPath = path.join('Users', 'test', 'MyBlog');
|
||||
|
||||
const dataDir = projectEngine.getDataDir(projectId, customPath);
|
||||
|
||||
@@ -826,7 +831,7 @@ describe('ProjectEngine', () => {
|
||||
name: 'My Project',
|
||||
slug: 'my-project',
|
||||
description: 'A test project',
|
||||
dataPath: '/custom/path',
|
||||
dataPath: path.join('custom', 'path'),
|
||||
createdAt: new Date('2024-01-01'),
|
||||
updatedAt: new Date('2024-06-01'),
|
||||
isActive: true,
|
||||
@@ -848,7 +853,7 @@ describe('ProjectEngine', () => {
|
||||
expect(result?.name).toBe('My Project');
|
||||
expect(result?.slug).toBe('my-project');
|
||||
expect(result?.description).toBe('A test project');
|
||||
expect(result?.dataPath).toBe('/custom/path');
|
||||
expect(result?.dataPath).toBe(path.join('custom', 'path'));
|
||||
expect(result?.isActive).toBe(true);
|
||||
});
|
||||
|
||||
@@ -1181,7 +1186,7 @@ describe('ProjectEngine', () => {
|
||||
id: 'resolved-project',
|
||||
name: 'Resolved Project',
|
||||
slug: 'resolved',
|
||||
dataPath: '/custom/data/path',
|
||||
dataPath: path.join('custom', 'data', 'path'),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
isActive: false,
|
||||
@@ -1198,8 +1203,9 @@ describe('ProjectEngine', () => {
|
||||
|
||||
const paths = await projectEngine.getProjectPathsResolved('resolved-project');
|
||||
|
||||
expect(paths.posts).toContain('/custom/data/path');
|
||||
expect(paths.media).toContain('/custom/data/path');
|
||||
const normalizedBasePath = normalizePath(projectWithPath.dataPath);
|
||||
expect(normalizePath(paths.posts)).toContain(normalizedBasePath);
|
||||
expect(normalizePath(paths.media)).toContain(normalizedBasePath);
|
||||
});
|
||||
|
||||
it('should use internal path when project has no dataPath', async () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { visit } from 'unist-util-visit';
|
||||
import { normalizeMilkdownMarkdown } from '../../../src/renderer/utils/markdownEscape';
|
||||
|
||||
const wxrRefDir = path.join(__dirname, '../../assets/wxr-ref');
|
||||
const normalizeLineEndingsToLf = (value: string): string => value.replace(/\r\n/g, '\n');
|
||||
|
||||
const remarkTightListsPlugin: Plugin<[Record<string, unknown>], Root> = () => {
|
||||
return (tree: Root) => {
|
||||
@@ -42,13 +43,14 @@ describe('Milkdown markdown round trip', () => {
|
||||
const files = fs.readdirSync(wxrRefDir).filter((file) => file.endsWith('.md'));
|
||||
|
||||
for (const file of files) {
|
||||
const raw = fs.readFileSync(path.join(wxrRefDir, file), 'utf-8');
|
||||
const raw = normalizeLineEndingsToLf(fs.readFileSync(path.join(wxrRefDir, file), 'utf-8'));
|
||||
const { content } = matter(raw);
|
||||
const normalizedContent = normalizeLineEndingsToLf(content);
|
||||
|
||||
const editor = await Editor.make()
|
||||
.config((ctx) => {
|
||||
ctx.set(rootCtx, root);
|
||||
ctx.set(defaultValueCtx, content);
|
||||
ctx.set(defaultValueCtx, normalizedContent);
|
||||
ctx.set(remarkStringifyOptionsCtx, {
|
||||
bullet: '-',
|
||||
listItemIndent: 'one',
|
||||
@@ -59,16 +61,16 @@ describe('Milkdown markdown round trip', () => {
|
||||
.use(gfm)
|
||||
.create();
|
||||
|
||||
const serialized = editor.action((ctx) => {
|
||||
const serialized = normalizeLineEndingsToLf(editor.action((ctx) => {
|
||||
const parser = ctx.get(parserCtx);
|
||||
const serializer = ctx.get(serializerCtx);
|
||||
const doc = parser(content);
|
||||
const doc = parser(normalizedContent);
|
||||
return normalizeMilkdownMarkdown(serializer(doc));
|
||||
});
|
||||
}));
|
||||
|
||||
await editor.destroy();
|
||||
|
||||
expect(serialized, `round trip mismatch for ${file}`).toBe(content);
|
||||
expect(serialized, `round trip mismatch for ${file}`).toBe(normalizedContent);
|
||||
}
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user