diff --git a/src/support/prompts-storage.js b/src/support/prompts-storage.js index bb700edf7e..85d6002f25 100644 --- a/src/support/prompts-storage.js +++ b/src/support/prompts-storage.js @@ -714,12 +714,16 @@ export async function upsertPrompts({ updated_by: updatedBy, }; - if (match && match.status !== 'active') { - // eslint-disable-next-line no-continue - continue; - } - if (match) { + // Restore-on-match: when an incoming prompt matches an existing row we + // update that row. This includes rows that were previously soft-deleted + // (status !== 'active') — the update sets status back to 'active' (via + // `row.status`, default 'active'), restoring the prompt. Previously a + // soft-deleted match was dropped entirely (no insert, no update, no + // restore), so re-importing an identical prompt silently no-oped while + // the API still returned 201 with created:0/skipped:N. Excluding the + // restore left brands with only soft-deleted rows unable to re-create + // identical prompts. toUpdate.push({ ...row, id: match.id }); processed.push({ ...row, prompt_id: promptId }); } else { diff --git a/test/support/prompts-storage.test.js b/test/support/prompts-storage.test.js index 9366239ed4..fccac87e94 100644 --- a/test/support/prompts-storage.test.js +++ b/test/support/prompts-storage.test.js @@ -886,12 +886,14 @@ describe('prompts-storage', () => { expect(result.updated).to.equal(1); }); - it('skips a deleted prompt matched by prompt_id without updating or inserting', async () => { + it('restores a soft-deleted prompt matched by prompt_id (update back to active, no insert)', async () => { const deletedRow = { id: 'row-uuid', prompt_id: 'del-1', text: 'Deleted text', regions: [], status: 'deleted', }; const existingData = { data: [deletedRow], error: null }; - const updateStub = sinon.stub().returns({ eq: () => thenable({ error: null }) }); + const updateEqStub = sinon.stub().returns(thenable({ error: null })); + const updateStub = sinon.stub().returns({ eq: updateEqStub }); + const insertSpy = sinon.stub().returns({ select: () => thenable({ data: [], error: null }) }); const client = { from: (table) => { if (table === 'prompts') { @@ -904,7 +906,7 @@ describe('prompts-storage', () => { }), }), }), - insert: () => ({ select: () => thenable({ data: [], error: null }) }), + insert: insertSpy, update: updateStub, }; } @@ -917,13 +919,19 @@ describe('prompts-storage', () => { prompts: [{ id: 'del-1', prompt: 'Deleted text', regions: [] }], postgrestClient: client, }); - expect(result.updated).to.equal(0); + // Match on the soft-deleted row now updates it instead of skipping. + expect(result.updated).to.equal(1); expect(result.created).to.equal(0); - expect(result.skipped).to.equal(1); - expect(updateStub.callCount).to.equal(0); + expect(result.skipped).to.equal(0); + expect(insertSpy.callCount).to.equal(0); + expect(updateStub.callCount).to.equal(1); + // The patch restores status to 'active'. + expect(updateStub.firstCall.args[0]).to.include({ status: 'active' }); + // ...targeting the existing soft-deleted row by its uuid. + expect(updateEqStub.calledWith('id', 'row-uuid')).to.equal(true); }); - it('skips a deleted prompt matched by text+regions without inserting', async () => { + it('restores a soft-deleted prompt matched by text+regions (update back to active, no insert)', async () => { const deletedRow = { id: 'row-uuid', prompt_id: 'del-2', text: 'Same text', regions: ['us'], status: 'deleted', }; @@ -931,6 +939,8 @@ describe('prompts-storage', () => { const insertSpy = sinon.stub().returns({ select: () => thenable({ data: [], error: null }), }); + const updateEqStub = sinon.stub().returns(thenable({ error: null })); + const updateStub = sinon.stub().returns({ eq: updateEqStub }); const client = { from: (table) => { if (table === 'prompts') { @@ -941,7 +951,7 @@ describe('prompts-storage', () => { }), }), insert: insertSpy, - update: () => ({ eq: () => thenable({ error: null }) }), + update: updateStub, }; } return makeChain({}); @@ -954,9 +964,13 @@ describe('prompts-storage', () => { prompts: [{ prompt: 'Same text', regions: ['us'] }], postgrestClient: client, }); - expect(result.skipped).to.equal(1); + expect(result.updated).to.equal(1); expect(result.created).to.equal(0); + expect(result.skipped).to.equal(0); expect(insertSpy.callCount).to.equal(0); + expect(updateStub.callCount).to.equal(1); + expect(updateStub.firstCall.args[0]).to.include({ status: 'active' }); + expect(updateEqStub.calledWith('id', 'row-uuid')).to.equal(true); }); it('throws on insert error', async () => {