fix: test hardening

This commit is contained in:
2026-02-15 22:07:36 +01:00
parent 8b938f4241
commit 70fc714df5
3 changed files with 47 additions and 1 deletions

View File

@@ -192,7 +192,10 @@ export class TagEngine extends EventEmitter {
tagName: string,
transform: (postTags: string[]) => string[]
): 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;
return {

View File

@@ -62,6 +62,7 @@ function createSelectChain(mockData: any[] = []) {
let insertedValues: any[] = [];
let updateCalls: any[] = [];
let deleteCalled = false;
let deleteCallCount = 0;
let selectMockData: any[] = [];
function createDrizzleMock() {
@@ -87,6 +88,7 @@ function createDrizzleMock() {
delete: vi.fn(() => ({
where: vi.fn(() => {
deleteCalled = true;
deleteCallCount++;
return Promise.resolve();
}),
})),
@@ -126,6 +128,7 @@ describe('PostMediaEngine', () => {
insertedValues = [];
updateCalls = [];
deleteCalled = false;
deleteCallCount = 0;
selectMockData = [];
resetMockCounters();
@@ -236,6 +239,7 @@ describe('PostMediaEngine', () => {
await engine.unlinkMediaFromPost(postId, mediaId);
expect(deleteCalled).toBe(true);
expect(deleteCallCount).toBe(1);
});
it('should update media sidecar to remove postId', async () => {
@@ -407,6 +411,7 @@ describe('PostMediaEngine', () => {
expect(result.unlinked).toHaveLength(3);
// deleteCalled flag is set to true when any delete is called
expect(deleteCalled).toBe(true);
expect(deleteCallCount).toBe(3);
});
it('should emit mediaBatchUnlinked event once at the end', async () => {
@@ -473,6 +478,7 @@ describe('PostMediaEngine', () => {
const result = await engine.unlinkManyFromPost(postId, mediaIds);
expect(result.unlinked).toEqual(['media-1', 'media-2']);
expect(deleteCallCount).toBe(2);
expect(mockUpdateMedia).toHaveBeenCalledTimes(2);
});
});

View File

@@ -302,6 +302,24 @@ describe('TagEngine', () => {
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', () => {
@@ -403,6 +421,25 @@ describe('TagEngine', () => {
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', () => {