diff --git a/src/controllers/suggestions.js b/src/controllers/suggestions.js index c01b18341..1d1133cb3 100644 --- a/src/controllers/suggestions.js +++ b/src/controllers/suggestions.js @@ -51,16 +51,21 @@ function SuggestionsController(ctx, sqs, env) { throw new Error('Data access required'); } - const AUTOFIX_UNGROUPED_OPPTY_TYPES = [ - 'broken-backlinks', - 'form-accessibility', - 'product-metatags', - 'security-permissions-redundant', + /** + * Opportunity types that use URL-based suggestion grouping for auto-fix. + * Suggestions are grouped by URL and sent as separate auto-fix messages per URL. + * Add here only when URL grouping is needed. + */ + const URL_GROUPED_OPPORTUNITY_TYPES = [ + 'alt-text', + 'broken-internal-links', + 'high-organic-low-ctr', + 'meta-tags', ]; const DEFAULT_PAGE_SIZE = 100; - const shouldGroupSuggestionsForAutofix = (type) => !AUTOFIX_UNGROUPED_OPPTY_TYPES.includes(type); + const shouldGroupSuggestionsForAutofix = (type) => URL_GROUPED_OPPORTUNITY_TYPES.includes(type); /** * Checks if a suggestion is a domain-wide auto generated suggestion diff --git a/test/controllers/suggestions.test.js b/test/controllers/suggestions.test.js index 2dfa65b4a..f8dd14357 100644 --- a/test/controllers/suggestions.test.js +++ b/test/controllers/suggestions.test.js @@ -2398,6 +2398,65 @@ describe('Suggestions Controller', () => { expect(mockSqs.sendMessage).to.have.been.calledOnce; }); + it('defaults to ungrouped behavior for unknown opportunity types', async () => { + // New opportunity types should default to ungrouped behavior + opportunity.getType = sandbox.stub().returns('some-new-opportunity-type'); + // Enable the auto-fix handler for this test + mockConfiguration.findLatest.resolves({ + isHandlerEnabledForSite: sandbox.stub().withArgs('some-new-opportunity-type-auto-fix', site).returns(true), + }); + + const newTypeSuggs = [ + { + id: SUGGESTION_IDS[0], + opportunityId: OPPORTUNITY_ID, + type: 'CONTENT_UPDATE', + rank: 1, + status: 'NEW', + data: { + url: 'https://www.example.com/page1', + content: 'Some content', + }, + updatedAt: new Date(), + }, + { + id: SUGGESTION_IDS[1], + opportunityId: OPPORTUNITY_ID, + type: 'CONTENT_UPDATE', + rank: 2, + status: 'NEW', + data: { + url: 'https://www.example.com/page2', + content: 'Other content', + }, + updatedAt: new Date(), + }, + ]; + + mockSuggestion.allByOpportunityId.resolves( + [mockSuggestionEntity(newTypeSuggs[0]), mockSuggestionEntity(newTypeSuggs[1])], + ); + mockSuggestion.bulkUpdateStatus.resolves([ + mockSuggestionEntity({ ...newTypeSuggs[0], status: 'IN_PROGRESS' }), + mockSuggestionEntity({ ...newTypeSuggs[1], status: 'IN_PROGRESS' }), + ]); + + const response = await suggestionsControllerWithMock.autofixSuggestions({ + params: { + siteId: SITE_ID, + opportunityId: OPPORTUNITY_ID, + }, + data: { suggestionIds: [SUGGESTION_IDS[0], SUGGESTION_IDS[1]] }, + ...context, + }); + + expect(response.status).to.equal(207); + const bulkPatchResponse = await response.json(); + expect(bulkPatchResponse.metadata).to.have.property('success', 2); + // Unknown types should NOT be grouped by URL, so single SQS call with all suggestions + expect(mockSqs.sendMessage).to.have.been.calledOnce; + }); + it('triggers autofixSuggestion with customData for non-grouped type', async () => { opportunity.getType = sandbox.stub().returns('product-metatags'); mockSuggestion.allByOpportunityId.resolves( @@ -2436,7 +2495,7 @@ describe('Suggestions Controller', () => { expect(sqsCallArgs[1].customData).to.deep.equal(customData); }); - it('triggers autofixSuggestion without customData for grouped type', async () => { + it('triggers autofixSuggestion without customData for non-grouped type', async () => { opportunity.getType = sandbox.stub().returns('form-accessibility'); mockSuggestion.allByOpportunityId.resolves( [mockSuggestionEntity(formAccessibilitySuggs[0])],