fix: test hardening
This commit is contained in:
@@ -192,7 +192,10 @@ export class TagEngine extends EventEmitter {
|
|||||||
tagName: string,
|
tagName: string,
|
||||||
transform: (postTags: string[]) => string[]
|
transform: (postTags: string[]) => string[]
|
||||||
): Promise<{ total: number; process: (onEachUpdated: (updated: number, total: number) => void) => Promise<number> }> {
|
): Promise<{ total: number; process: (onEachUpdated: (updated: number, total: number) => void) => Promise<number> }> {
|
||||||
const postsToUpdate = await this.queryPostsContainingTag(tagName);
|
const rawPostsToUpdate = await this.queryPostsContainingTag(tagName);
|
||||||
|
const postsToUpdate = Array.from(
|
||||||
|
new Map(rawPostsToUpdate.map((row) => [row.postId, row])).values()
|
||||||
|
);
|
||||||
const total = postsToUpdate.length;
|
const total = postsToUpdate.length;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ function createSelectChain(mockData: any[] = []) {
|
|||||||
let insertedValues: any[] = [];
|
let insertedValues: any[] = [];
|
||||||
let updateCalls: any[] = [];
|
let updateCalls: any[] = [];
|
||||||
let deleteCalled = false;
|
let deleteCalled = false;
|
||||||
|
let deleteCallCount = 0;
|
||||||
let selectMockData: any[] = [];
|
let selectMockData: any[] = [];
|
||||||
|
|
||||||
function createDrizzleMock() {
|
function createDrizzleMock() {
|
||||||
@@ -87,6 +88,7 @@ function createDrizzleMock() {
|
|||||||
delete: vi.fn(() => ({
|
delete: vi.fn(() => ({
|
||||||
where: vi.fn(() => {
|
where: vi.fn(() => {
|
||||||
deleteCalled = true;
|
deleteCalled = true;
|
||||||
|
deleteCallCount++;
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}),
|
}),
|
||||||
})),
|
})),
|
||||||
@@ -126,6 +128,7 @@ describe('PostMediaEngine', () => {
|
|||||||
insertedValues = [];
|
insertedValues = [];
|
||||||
updateCalls = [];
|
updateCalls = [];
|
||||||
deleteCalled = false;
|
deleteCalled = false;
|
||||||
|
deleteCallCount = 0;
|
||||||
selectMockData = [];
|
selectMockData = [];
|
||||||
resetMockCounters();
|
resetMockCounters();
|
||||||
|
|
||||||
@@ -236,6 +239,7 @@ describe('PostMediaEngine', () => {
|
|||||||
await engine.unlinkMediaFromPost(postId, mediaId);
|
await engine.unlinkMediaFromPost(postId, mediaId);
|
||||||
|
|
||||||
expect(deleteCalled).toBe(true);
|
expect(deleteCalled).toBe(true);
|
||||||
|
expect(deleteCallCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update media sidecar to remove postId', async () => {
|
it('should update media sidecar to remove postId', async () => {
|
||||||
@@ -407,6 +411,7 @@ describe('PostMediaEngine', () => {
|
|||||||
expect(result.unlinked).toHaveLength(3);
|
expect(result.unlinked).toHaveLength(3);
|
||||||
// deleteCalled flag is set to true when any delete is called
|
// deleteCalled flag is set to true when any delete is called
|
||||||
expect(deleteCalled).toBe(true);
|
expect(deleteCalled).toBe(true);
|
||||||
|
expect(deleteCallCount).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit mediaBatchUnlinked event once at the end', async () => {
|
it('should emit mediaBatchUnlinked event once at the end', async () => {
|
||||||
@@ -473,6 +478,7 @@ describe('PostMediaEngine', () => {
|
|||||||
const result = await engine.unlinkManyFromPost(postId, mediaIds);
|
const result = await engine.unlinkManyFromPost(postId, mediaIds);
|
||||||
|
|
||||||
expect(result.unlinked).toEqual(['media-1', 'media-2']);
|
expect(result.unlinked).toEqual(['media-1', 'media-2']);
|
||||||
|
expect(deleteCallCount).toBe(2);
|
||||||
expect(mockUpdateMedia).toHaveBeenCalledTimes(2);
|
expect(mockUpdateMedia).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -302,6 +302,24 @@ describe('TagEngine', () => {
|
|||||||
|
|
||||||
await expect(tagEngine.deleteTag('non-existent')).rejects.toThrow('Tag not found');
|
await expect(tagEngine.deleteTag('non-existent')).rejects.toThrow('Tag not found');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should only update each post once when query returns duplicate rows', async () => {
|
||||||
|
mockSelectDataQueue = [
|
||||||
|
[{ id: 'tag-1', name: 'react', color: null, projectId: 'default', createdAt: new Date(), updatedAt: new Date() }],
|
||||||
|
];
|
||||||
|
|
||||||
|
mockLocalClient.execute.mockResolvedValueOnce({
|
||||||
|
rows: [
|
||||||
|
{ id: 'post-1', tags: '["react", "typescript"]' },
|
||||||
|
{ id: 'post-1', tags: '["react", "typescript"]' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await tagEngine.deleteTag('tag-1');
|
||||||
|
|
||||||
|
expect(result.postsUpdated).toBe(1);
|
||||||
|
expect(mockPostEngine.syncPublishedPostFile).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('mergeTags', () => {
|
describe('mergeTags', () => {
|
||||||
@@ -403,6 +421,25 @@ describe('TagEngine', () => {
|
|||||||
newName: 'new-name',
|
newName: 'new-name',
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should only update each post once when query returns duplicate rows', async () => {
|
||||||
|
mockSelectDataQueue = [
|
||||||
|
[{ id: 'tag-1', name: 'old-name', projectId: 'default', createdAt: new Date(), updatedAt: new Date() }],
|
||||||
|
[], // no duplicate target tag
|
||||||
|
];
|
||||||
|
|
||||||
|
mockLocalClient.execute.mockResolvedValueOnce({
|
||||||
|
rows: [
|
||||||
|
{ id: 'post-1', tags: '["old-name"]' },
|
||||||
|
{ id: 'post-1', tags: '["old-name"]' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await tagEngine.renameTag('tag-1', 'new-name');
|
||||||
|
|
||||||
|
expect(result.postsUpdated).toBe(1);
|
||||||
|
expect(mockPostEngine.syncPublishedPostFile).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getTag', () => {
|
describe('getTag', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user