diff --git a/src/backlinks/handler.js b/src/backlinks/handler.js index 4a5dba24..3b825cc3 100644 --- a/src/backlinks/handler.js +++ b/src/backlinks/handler.js @@ -100,18 +100,24 @@ export default async function auditBrokenBacklinks(message, context) { const filteredBacklinks = result?.backlinks?.filter( (backlink) => !excludedURLs?.includes(backlink.url_to), ); - const brokenBacklinks = await filterOutValidBacklinks(filteredBacklinks, log); + let brokenBacklinks = await filterOutValidBacklinks(filteredBacklinks, log); + try { + const topPages = await dataAccess.getTopPagesForSite(siteId, 'ahrefs', 'global'); + const keywords = topPages.map( + (page) => ({ + url: page.getURL(), + keyword: page.getTopKeyword(), + traffic: page.getTraffic(), + }), + ); + brokenBacklinks = enhanceBacklinksWithFixes(brokenBacklinks, keywords, log); + } catch (e) { + log.error(`Enhancing backlinks with fixes for siteId ${siteId} failed with error: ${e.message}`, e); + } - const topPages = await dataAccess.getTopPagesForSite(siteId, 'ahrefs', 'global'); - const keywords = topPages.map( - (page) => ( - { url: page.getURL(), keyword: page.getTopKeyword(), traffic: page.getTraffic() } - ), - ); - const enhancedBacklinks = enhanceBacklinksWithFixes(brokenBacklinks, keywords, log); auditResult = { finalUrl: auditContext.finalUrl, - brokenBacklinks: enhancedBacklinks, + brokenBacklinks, fullAuditRef, }; } catch (e) { diff --git a/test/audits/backlinks.test.js b/test/audits/backlinks.test.js index 29f792ba..2c09f8d1 100644 --- a/test/audits/backlinks.test.js +++ b/test/audits/backlinks.test.js @@ -362,6 +362,56 @@ describe('Backlinks Tests', function () { .calledWith(context.env.AUDIT_RESULTS_QUEUE_URL, expectedMessage); }); + it('should detect broken backlinks and save the proper audit result, even if the suggested fix fails', async () => { + mockDataAccess.getSiteByID = sinon.stub().withArgs('site1').resolves(site); + mockDataAccess.getTopPagesForSite.resolves([createSiteTopPage({ + siteId: site.getId(), + url: `${site.getBaseURL()}/foo.html`, + traffic: 1000, + source: 'ahrefs', + geo: 'global', + importedAt: new Date('2024-06-18').toISOString(), + topKeyword: 'c++', + })]); + const brokenBacklink = { + backlinks: [ + { + title: 'backlink that has a faulty path', + url_from: 'https://from.com/from-1', + url_to: 'https://foo.com/c++', + domain_traffic: 4000, + }], + }; + mockDataAccess.getConfiguration = sinon.stub().resolves(configuration); + nock(site.getBaseURL()) + .get(/.*/) + .reply(200); + + nock('https://ahrefs.com') + .get(/.*/) + .reply(200, brokenBacklink); + + const expectedMessage = { + type: message.type, + url: site.getBaseURL(), + auditContext: { + finalUrl: 'bar.foo.com', + }, + auditResult: { + finalUrl: 'bar.foo.com', + brokenBacklinks: brokenBacklink.backlinks, + fullAuditRef: 'https://ahrefs.com/site-explorer/broken-backlinks?select=title%2Curl_from%2Curl_to%2Ctraffic_domain&limit=50&mode=prefix&order_by=domain_rating_source%3Adesc%2Ctraffic_domain%3Adesc&target=bar.foo.com&output=json&where=%7B%22and%22%3A%5B%7B%22field%22%3A%22is_dofollow%22%2C%22is%22%3A%5B%22eq%22%2C1%5D%7D%2C%7B%22field%22%3A%22is_content%22%2C%22is%22%3A%5B%22eq%22%2C1%5D%7D%2C%7B%22field%22%3A%22domain_rating_source%22%2C%22is%22%3A%5B%22gte%22%2C29.5%5D%7D%2C%7B%22field%22%3A%22traffic_domain%22%2C%22is%22%3A%5B%22gte%22%2C500%5D%7D%2C%7B%22field%22%3A%22links_external%22%2C%22is%22%3A%5B%22lte%22%2C300%5D%7D%5D%7D', + }, + }; + const response = await auditBrokenBacklinks(message, context); + + expect(response.status).to.equal(204); + expect(mockDataAccess.addAudit).to.have.been.calledOnce; + expect(context.sqs.sendMessage).to.have.been.calledOnce; + expect(context.sqs.sendMessage).to.have.been + .calledWith(context.env.AUDIT_RESULTS_QUEUE_URL, expectedMessage); + }); + it('should successfully perform an audit to detect broken backlinks and set finalUrl, for baseUrl redirecting to www domain', async () => { mockDataAccess.getSiteByID = sinon.stub().withArgs('site2').resolves(site2); mockDataAccess.getTopPagesForSite.resolves([]);