diff --git a/plugins/catalog-backend/src/database/operations/stitcher/syncSearchRows.test.ts b/plugins/catalog-backend/src/database/operations/stitcher/syncSearchRows.test.ts index 49e67be736..0c7ac6f95a 100644 --- a/plugins/catalog-backend/src/database/operations/stitcher/syncSearchRows.test.ts +++ b/plugins/catalog-backend/src/database/operations/stitcher/syncSearchRows.test.ts @@ -224,6 +224,26 @@ describe.each(databases.eachSupportedId())('syncSearchRows, %p', databaseId => { expect(rows.map(r => r.value).sort()).toEqual(['java', 'python', 'rust']); }); + it('silently rejects a direct duplicate insert via the UNIQUE constraint', async () => { + await syncSearchRows(knex, 'e1', [row('a', 'x'), row('b', 'y')]); + + // Simulate a concurrent process inserting a duplicate directly — + // the UNIQUE constraint on (entity_id, key, value) should reject it + // silently. ON CONFLICT without a column list covers any unique + // constraint on the table, which is safe since (entity_id, key, value) + // is the only one. + await knex('search') + .insert({ entity_id: 'e1', key: 'a', value: 'x', original_value: 'x' }) + .onConflict() + .ignore(); + + const rows = await getSearchRows(); + expect(rows).toHaveLength(2); + expect(rows.find(r => r.key === 'a')).toEqual( + expect.objectContaining({ key: 'a', value: 'x' }), + ); + }); + it('simulates the typical steady-state case with one changed row', async () => { // Build a realistic-ish set of search rows const initial = [ diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 7abb3ca8a3..427a9c8ce0 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -2031,7 +2031,7 @@ describe('DefaultEntitiesCatalog', () => { original_value: 'B Test Entity', }, ]) - .onConflict(['entity_id', 'key', 'value']) + .onConflict() .ignore(); const catalog = new DefaultEntitiesCatalog({ @@ -2422,7 +2422,7 @@ describe('DefaultEntitiesCatalog', () => { original_value: 'one', }, ]) - .onConflict(['entity_id', 'key', 'value']) + .onConflict() .ignore(); const catalog = new DefaultEntitiesCatalog({