Files
renovate/lib/config/migrations/migrations-service.spec.ts
Sebastian Poxhofer 2dbfd85506 refactor(linters): enable node/no-sync and migrate to async calls (#44693)
* ci(linter): add renovate/no-sync-io rule

Flags calls to a fixed list of Node.js sync I/O APIs (readFileSync,
writeFileSync, mkdirSync, rmSync, readdirSync, statSync, existsSync,
copyFileSync, execSync, spawnSync, execFileSync) in tools/** and
**/*.spec.ts only; production lib code is not targeted.

Set to "warn" instead of "error": the repo has 40 pre-existing
violations, exceeding the agreed fix threshold of 30, so violations
are left in place for follow-up rather than mass-fixed here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(linter): enforce renovate/no-sync-io at error

Promote the rule from warn to error now that the codebase has been
migrated off synchronous I/O in tools/ and spec files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(tools): migrate synchronous fs calls to async equivalents

Replace *Sync fs calls with their async/promise-based counterparts in
tools/ scripts (generate-imports, check runner, utils, cwdchanged hook)
now that renovate/no-sync-io is enforced at error there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: migrate synchronous fs calls in specs to async equivalents

Replace *Sync fs calls (readdirSync, writeFileSync, existsSync, etc.)
with their async/promise-based counterparts across affected spec
files, making the enclosing it()/module-scope initializers async as
needed, to satisfy renovate/no-sync-io now enforced at error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(linter): replace custom no-sync-io rule with built-in node/no-sync

The oxc built-in covers a superset of the custom fixed-name list.
Migrate the additionally flagged sites: async glob in test-shards,
async execa in prepare-deps, promisified gzip/generateKeyPair in
rpm/hex specs, and rename the misleading execSync mock alias
(it mocks the async rawExec).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 14:28:26 +00:00

118 lines
3.6 KiB
TypeScript

import { readdir } from 'node:fs/promises';
import upath from 'upath';
import type { RenovateConfig } from '../types.ts';
import { AbstractMigration } from './base/abstract-migration.ts';
import { MigrationsService } from './migrations-service.ts';
import type { Migration } from './types.ts';
describe('config/migrations/migrations-service', () => {
it('should remove deprecated properties', () => {
for (const property of MigrationsService.removedProperties) {
const originalConfig: RenovateConfig = {
[property]: 'test',
};
const migratedConfig = MigrationsService.run(originalConfig);
expect(
MigrationsService.isMigrated(originalConfig, migratedConfig),
).toBeTrue();
expect(migratedConfig).toEqual({});
}
});
it('should rename renamed properties', () => {
for (const [
oldPropertyName,
newPropertyName,
] of MigrationsService.renamedProperties.entries()) {
const originalConfig: RenovateConfig = {
[oldPropertyName]: 'test',
};
const migratedConfig = MigrationsService.run(originalConfig);
expect(
MigrationsService.isMigrated(originalConfig, migratedConfig),
).toBeTrue();
expect(migratedConfig).toEqual({
[newPropertyName]: 'test',
});
}
});
it('should save original order of properties', () => {
const originalConfig = {
exposeEnv: true,
versionScheme: 'test',
excludedPackageNames: ['test'],
};
const migratedConfig = MigrationsService.run(originalConfig);
const mappedProperties = Object.keys(originalConfig).map((property) =>
MigrationsService.renamedProperties.get(property),
);
expect(
MigrationsService.isMigrated(originalConfig, migratedConfig),
).toBeTrue();
expect(mappedProperties).toEqual(Object.keys(migratedConfig));
});
it('should allow custom migrations by regexp', () => {
let isMigrationDone = false;
const originalConfig: RenovateConfig = {
// @ts-expect-error -- testing
fooBar: 'one',
};
class CustomMigration extends AbstractMigration {
override readonly deprecated = true;
override readonly propertyName = /^foo/;
override run(): void {
isMigrationDone = true;
}
}
class CustomMigrationsService extends MigrationsService {
public static override getMigrations(
original: RenovateConfig,
migrated: RenovateConfig,
): readonly Migration[] {
return [new CustomMigration(original, migrated)];
}
}
const migratedConfig = CustomMigrationsService.run(originalConfig);
expect(migratedConfig).toEqual({});
expect(isMigrationDone).toBeTrue();
});
it('there should be a single migration per property name', () => {
const migrations = MigrationsService.getMigrations({}, {});
const set = new Set<string | RegExp>();
const duplicateProperties: (string | RegExp)[] = [];
for (const { propertyName } of migrations) {
if (set.has(propertyName)) {
duplicateProperties.push(propertyName);
continue;
}
set.add(propertyName);
}
expect(duplicateProperties).toBeEmptyArray();
});
it('includes all defined migration classes in MigrationsService.customMigrations', async () => {
const allDefinedMigrationClasses: string[] = (
await readdir(upath.join(import.meta.dirname, 'custom'), {
withFileTypes: true,
})
)
.map((file) => file.name)
.filter((name) => !name.includes('spec.ts'));
expect(MigrationsService.customMigrations).toHaveLength(
allDefinedMigrationClasses.length,
);
});
});