backfill-screenshots.test.js
1 /** 2 * Unit Tests for Backfill Screenshots Utility 3 */ 4 5 import { describe, it, mock, beforeEach } from 'node:test'; 6 import assert from 'node:assert'; 7 import { 8 findSitesWithMissingScreenshots, 9 updateSiteScreenshots, 10 } from '../../src/utils/backfill-screenshots.js'; 11 12 describe('Backfill Screenshots Module', () => { 13 let mockDb; 14 15 beforeEach(() => { 16 // Reset mock database before each test 17 mockDb = { 18 prepare: mock.fn(), 19 }; 20 }); 21 22 describe('findSitesWithMissingScreenshots', () => { 23 it('should find sites with missing desktop screenshots', () => { 24 const mockSites = [ 25 { 26 id: 1, 27 domain: 'example.com', 28 landing_page_url: 'https://example.com', 29 keyword: 'plumber sydney', 30 missing_desktop_above: 1, 31 missing_desktop_below: 0, 32 missing_mobile_above: 0, 33 missing_desktop_above_uncropped: 0, 34 missing_desktop_below_uncropped: 0, 35 missing_mobile_above_uncropped: 0, 36 }, 37 ]; 38 39 const mockStmt = { 40 all: mock.fn(() => mockSites), 41 }; 42 43 mockDb.prepare.mock.mockImplementation(() => mockStmt); 44 45 const result = findSitesWithMissingScreenshots(mockDb, 100); 46 47 assert.strictEqual(result, mockSites); 48 assert.strictEqual(mockDb.prepare.mock.callCount(), 1); 49 assert.strictEqual(mockStmt.all.mock.callCount(), 1); 50 assert.deepStrictEqual(mockStmt.all.mock.calls[0].arguments, [100]); 51 }); 52 53 it('should find sites with missing mobile screenshots', () => { 54 const mockSites = [ 55 { 56 id: 2, 57 domain: 'test.com', 58 landing_page_url: 'https://test.com', 59 keyword: 'electrician melbourne', 60 missing_desktop_above: 0, 61 missing_desktop_below: 0, 62 missing_mobile_above: 1, 63 missing_desktop_above_uncropped: 0, 64 missing_desktop_below_uncropped: 0, 65 missing_mobile_above_uncropped: 0, 66 }, 67 ]; 68 69 const mockStmt = { 70 all: mock.fn(() => mockSites), 71 }; 72 73 mockDb.prepare.mock.mockImplementation(() => mockStmt); 74 75 const result = findSitesWithMissingScreenshots(mockDb, 50); 76 77 assert.strictEqual(result.length, 1); 78 assert.strictEqual(result[0].missing_mobile_above, 1); 79 assert.strictEqual(mockStmt.all.mock.calls[0].arguments[0], 50); 80 }); 81 82 it('should find sites with missing uncropped screenshots', () => { 83 const mockSites = [ 84 { 85 id: 3, 86 domain: 'uncropped-missing.com', 87 landing_page_url: 'https://uncropped-missing.com', 88 keyword: 'roofing perth', 89 missing_desktop_above: 0, 90 missing_desktop_below: 0, 91 missing_mobile_above: 0, 92 missing_desktop_above_uncropped: 1, 93 missing_desktop_below_uncropped: 1, 94 missing_mobile_above_uncropped: 1, 95 }, 96 ]; 97 98 const mockStmt = { 99 all: mock.fn(() => mockSites), 100 }; 101 102 mockDb.prepare.mock.mockImplementation(() => mockStmt); 103 104 const result = findSitesWithMissingScreenshots(mockDb, 100); 105 106 assert.strictEqual(result.length, 1); 107 assert.strictEqual(result[0].missing_desktop_above_uncropped, 1); 108 assert.strictEqual(result[0].missing_desktop_below_uncropped, 1); 109 assert.strictEqual(result[0].missing_mobile_above_uncropped, 1); 110 }); 111 112 it('should return empty array when no sites have missing screenshots', () => { 113 const mockStmt = { 114 all: mock.fn(() => []), 115 }; 116 117 mockDb.prepare.mock.mockImplementation(() => mockStmt); 118 119 const result = findSitesWithMissingScreenshots(mockDb, 100); 120 121 assert.strictEqual(result.length, 0); 122 }); 123 124 it('should respect the limit parameter', () => { 125 const mockStmt = { 126 all: mock.fn(() => []), 127 }; 128 129 mockDb.prepare.mock.mockImplementation(() => mockStmt); 130 131 findSitesWithMissingScreenshots(mockDb, 25); 132 133 assert.strictEqual(mockStmt.all.mock.calls[0].arguments[0], 25); 134 }); 135 }); 136 137 describe('updateSiteScreenshots', () => { 138 it('should update all screenshot fields', () => { 139 const mockStmt = { 140 run: mock.fn(), 141 }; 142 143 mockDb.prepare.mock.mockImplementation(() => mockStmt); 144 145 const screenshots = { 146 desktop_above: Buffer.from('desktop_above_data'), 147 desktop_below: Buffer.from('desktop_below_data'), 148 mobile_above: Buffer.from('mobile_above_data'), 149 }; 150 151 const screenshotsUncropped = { 152 desktop_above: Buffer.from('desktop_above_uncropped_data'), 153 desktop_below: Buffer.from('desktop_below_uncropped_data'), 154 mobile_above: Buffer.from('mobile_above_uncropped_data'), 155 }; 156 157 updateSiteScreenshots(mockDb, 1, screenshots, screenshotsUncropped); 158 159 assert.strictEqual(mockDb.prepare.mock.callCount(), 1); 160 assert.strictEqual(mockStmt.run.mock.callCount(), 1); 161 162 const runArgs = mockStmt.run.mock.calls[0].arguments; 163 assert.strictEqual(runArgs[0], screenshots.desktop_above); 164 assert.strictEqual(runArgs[1], screenshots.desktop_below); 165 assert.strictEqual(runArgs[2], screenshots.mobile_above); 166 assert.strictEqual(runArgs[3], screenshotsUncropped.desktop_above); 167 assert.strictEqual(runArgs[4], screenshotsUncropped.desktop_below); 168 assert.strictEqual(runArgs[5], screenshotsUncropped.mobile_above); 169 assert.strictEqual(runArgs[6], 1); // site ID 170 }); 171 172 it('should handle null screenshots gracefully', () => { 173 const mockStmt = { 174 run: mock.fn(), 175 }; 176 177 mockDb.prepare.mock.mockImplementation(() => mockStmt); 178 179 const screenshots = { 180 desktop_above: null, 181 desktop_below: Buffer.from('desktop_below_data'), 182 mobile_above: null, 183 }; 184 185 const screenshotsUncropped = { 186 desktop_above: null, 187 desktop_below: Buffer.from('desktop_below_uncropped_data'), 188 mobile_above: null, 189 }; 190 191 updateSiteScreenshots(mockDb, 5, screenshots, screenshotsUncropped); 192 193 const runArgs = mockStmt.run.mock.calls[0].arguments; 194 assert.strictEqual(runArgs[0], null); 195 assert.strictEqual(runArgs[1], screenshots.desktop_below); 196 assert.strictEqual(runArgs[2], null); 197 assert.strictEqual(runArgs[3], null); 198 assert.strictEqual(runArgs[4], screenshotsUncropped.desktop_below); 199 assert.strictEqual(runArgs[5], null); 200 assert.strictEqual(runArgs[6], 5); 201 }); 202 203 it('should update the correct site by ID', () => { 204 const mockStmt = { 205 run: mock.fn(), 206 }; 207 208 mockDb.prepare.mock.mockImplementation(() => mockStmt); 209 210 const screenshots = { 211 desktop_above: Buffer.from('data'), 212 desktop_below: Buffer.from('data'), 213 mobile_above: Buffer.from('data'), 214 }; 215 216 const screenshotsUncropped = { 217 desktop_above: Buffer.from('data'), 218 desktop_below: Buffer.from('data'), 219 mobile_above: Buffer.from('data'), 220 }; 221 222 updateSiteScreenshots(mockDb, 42, screenshots, screenshotsUncropped); 223 224 const runArgs = mockStmt.run.mock.calls[0].arguments; 225 assert.strictEqual(runArgs[6], 42); 226 }); 227 }); 228 });