@@ -21,6 +21,7 @@ import LoggingContext from '../../../src/modules/logging/LoggingContext.js';
2121import LoggingService from '../../../src/modules/logging/LoggingService.js' ;
2222import MigrationJobTask from '../../../src/modules/models/job/MigrationJobTask.js' ;
2323import MigrationJob from '../../../src/modules/models/job/MigrationJob.js' ;
24+ import ProcessedData from '../../../src/modules/models/job/ProcessedData.js' ;
2425import Script from '../../../src/modules/models/script/Script.js' ;
2526import type { LookupIdMapType } from '../../../src/modules/models/script/LookupIdMapType.js' ;
2627import ScriptMockField from '../../../src/modules/models/script/ScriptMockField.js' ;
@@ -2156,4 +2157,159 @@ describe('MigrationJobTask file target output', () => {
21562157 fs . rmSync ( tempDir , { recursive : true , force : true } ) ;
21572158 }
21582159 } ) ;
2160+
2161+ it ( 'tracks skipped reason counters for same-data and no-match records' , ( ) => {
2162+ const { task } = createTaskFixture ( ) ;
2163+ task . scriptObject . operation = OPERATION . Update ;
2164+ const processedData = new ProcessedData ( ) ;
2165+
2166+ const classifyForwardsRecord = (
2167+ task as unknown as {
2168+ _classifyForwardsRecord : (
2169+ sourceRecord : Record < string , unknown > ,
2170+ clonedRecord : Record < string , unknown > ,
2171+ processedData : ProcessedData ,
2172+ targetRecord : Record < string , unknown > | undefined ,
2173+ shouldCompare : boolean ,
2174+ notInsertableFields : string [ ] ,
2175+ notUpdateableFields : string [ ] ,
2176+ doNotDeleteIdFieldOnInsert : boolean
2177+ ) => void ;
2178+ }
2179+ ) . _classifyForwardsRecord . bind ( task ) ;
2180+
2181+ classifyForwardsRecord (
2182+ { Id : 'a00000000000001' } ,
2183+ { Name : 'Same' } ,
2184+ processedData ,
2185+ { Id : '001000000000001' } ,
2186+ false ,
2187+ [ ] ,
2188+ [ ] ,
2189+ false
2190+ ) ;
2191+
2192+ classifyForwardsRecord (
2193+ { Id : 'a00000000000002' } ,
2194+ { Name : 'NoTarget' } ,
2195+ processedData ,
2196+ undefined ,
2197+ true ,
2198+ [ ] ,
2199+ [ ] ,
2200+ false
2201+ ) ;
2202+
2203+ assert . equal ( processedData . skippedBecauseSameDataCount , 1 ) ;
2204+ assert . equal ( processedData . skippedBecauseNoMatchingTargetCount , 1 ) ;
2205+ assert . equal ( processedData . recordsToInsert . length , 0 ) ;
2206+ assert . equal ( processedData . recordsToUpdate . length , 0 ) ;
2207+ } ) ;
2208+
2209+ it ( 'logs skipped warning with split reason counters' , async ( ) => {
2210+ const originalLogger = Common . logger ;
2211+ const logger = createLoggingService ( ) ;
2212+ const logCalls : Array < { message : string ; tokens : string [ ] } > = [ ] ;
2213+ const diagnosticLines : string [ ] = [ ] ;
2214+ const originalLog = logger . log . bind ( logger ) as ( ...args : unknown [ ] ) => void ;
2215+ const originalVerboseFile = logger . verboseFile . bind ( logger ) as ( ...args : unknown [ ] ) => void ;
2216+ logger . log = ( ( message : string , ...tokens : string [ ] ) => {
2217+ logCalls . push ( { message, tokens } ) ;
2218+ originalLog ( message , ...tokens ) ;
2219+ } ) as typeof logger . log ;
2220+ logger . verboseFile = ( ( message : string , ...tokens : string [ ] ) => {
2221+ diagnosticLines . push ( typeof message === 'string' ? message : String ( message ) ) ;
2222+ originalVerboseFile ( message , ...tokens ) ;
2223+ } ) as typeof logger . verboseFile ;
2224+
2225+ Common . logger = logger ;
2226+ const { task } = createTaskFixture ( ) ;
2227+ task . job . script . logger = logger ;
2228+ task . scriptObject . operation = OPERATION . Upsert ;
2229+
2230+ const updateRecordsForPassAsync = (
2231+ task as unknown as {
2232+ _updateRecordsForPassAsync : (
2233+ updateMode : 'forwards' | 'backwards' ,
2234+ warnMissingParentsAsync : ( ( data : ProcessedData ) => Promise < void > ) | undefined ,
2235+ processPersonAccounts : boolean
2236+ ) => Promise < {
2237+ processedCount : number ;
2238+ nonProcessedCount : number ;
2239+ skippedBecauseSameDataCount : number ;
2240+ skippedBecauseNoMatchingTargetCount : number ;
2241+ processedData : ProcessedData ;
2242+ summary : { inserted : number ; updated : number ; deleted : number } ;
2243+ } > ;
2244+ }
2245+ ) . _updateRecordsForPassAsync ;
2246+ const isPersonAccountOrContact = (
2247+ task as unknown as {
2248+ _isPersonAccountOrContact : ( ) => boolean ;
2249+ }
2250+ ) . _isPersonAccountOrContact ;
2251+
2252+ (
2253+ task as unknown as {
2254+ _updateRecordsForPassAsync : (
2255+ updateMode : 'forwards' | 'backwards' ,
2256+ warnMissingParentsAsync : ( ( data : ProcessedData ) => Promise < void > ) | undefined ,
2257+ processPersonAccounts : boolean
2258+ ) => Promise < {
2259+ processedCount : number ;
2260+ nonProcessedCount : number ;
2261+ skippedBecauseSameDataCount : number ;
2262+ skippedBecauseNoMatchingTargetCount : number ;
2263+ processedData : ProcessedData ;
2264+ summary : { inserted : number ; updated : number ; deleted : number } ;
2265+ } > ;
2266+ _isPersonAccountOrContact : ( ) => boolean ;
2267+ }
2268+ ) . _updateRecordsForPassAsync = async ( ) => ( {
2269+ processedCount : 2 ,
2270+ nonProcessedCount : 5 ,
2271+ skippedBecauseSameDataCount : 2 ,
2272+ skippedBecauseNoMatchingTargetCount : 1 ,
2273+ processedData : new ProcessedData ( ) ,
2274+ summary : { inserted : 1 , updated : 1 , deleted : 0 } ,
2275+ } ) ;
2276+ (
2277+ task as unknown as {
2278+ _isPersonAccountOrContact : ( ) => boolean ;
2279+ }
2280+ ) . _isPersonAccountOrContact = ( ) => false ;
2281+
2282+ try {
2283+ const processed = await task . updateRecordsAsync ( 'forwards' ) ;
2284+ assert . equal ( processed , 2 ) ;
2285+ const skippedCall = logCalls . find ( ( item ) => item . message === 'skippedUpdatesWarning' ) ;
2286+ assert . ok ( skippedCall ) ;
2287+ assert . deepEqual ( skippedCall ?. tokens , [ 'Account' , '5' , '2' , '1' , '2' ] ) ;
2288+ assert . equal (
2289+ diagnosticLines . some (
2290+ ( line ) =>
2291+ line . includes ( '[diagnostic] update skipped summary:' ) &&
2292+ line . includes ( 'object=Account' ) &&
2293+ line . includes ( 'total=5' ) &&
2294+ line . includes ( 'sameData=2' ) &&
2295+ line . includes ( 'noMatchingTarget=1' ) &&
2296+ line . includes ( 'other=2' )
2297+ ) ,
2298+ true
2299+ ) ;
2300+ } finally {
2301+ (
2302+ task as unknown as {
2303+ _updateRecordsForPassAsync : typeof updateRecordsForPassAsync ;
2304+ _isPersonAccountOrContact : typeof isPersonAccountOrContact ;
2305+ }
2306+ ) . _updateRecordsForPassAsync = updateRecordsForPassAsync ;
2307+ (
2308+ task as unknown as {
2309+ _isPersonAccountOrContact : typeof isPersonAccountOrContact ;
2310+ }
2311+ ) . _isPersonAccountOrContact = isPersonAccountOrContact ;
2312+ Common . logger = originalLogger ;
2313+ }
2314+ } ) ;
21592315} ) ;
0 commit comments