mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-25 23:11:31 +00:00
refactor(terraform-provider): extract URL constants and rename query methods (#42317)
* refactor(terraform-provider): extract URL constants and rename query methods * refactor(terraform-provider): consolidate repetitive test cases * refactor(terraform-provider): improve parameterized test readability
This commit is contained in:
@@ -18,64 +18,77 @@ const terraformProviderDatasource = new TerraformProviderDatasource();
|
||||
const primaryUrl = terraformProviderDatasource.defaultRegistryUrls[0];
|
||||
const secondaryUrl = terraformProviderDatasource.defaultRegistryUrls[1];
|
||||
|
||||
type MockVariant = 'empty' | '404' | 'error';
|
||||
|
||||
function mockDefaultRegistryLookup(variant: MockVariant): void {
|
||||
const primaryScope = httpMock
|
||||
.scope(primaryUrl)
|
||||
.get('/v1/providers/hashicorp/azurerm');
|
||||
const primarySd = httpMock
|
||||
.scope(primaryUrl)
|
||||
.get('/.well-known/terraform.json');
|
||||
const secondaryScope = httpMock
|
||||
.scope(secondaryUrl)
|
||||
.get('/terraform-provider-azurerm/index.json');
|
||||
|
||||
if (variant === 'empty') {
|
||||
primaryScope.reply(200, {});
|
||||
primarySd.reply(200, serviceDiscoveryResult);
|
||||
secondaryScope.reply(200, {});
|
||||
return;
|
||||
}
|
||||
|
||||
if (variant === '404') {
|
||||
primaryScope.reply(404);
|
||||
primarySd.reply(200, serviceDiscoveryResult);
|
||||
secondaryScope.reply(404);
|
||||
return;
|
||||
}
|
||||
|
||||
primaryScope.replyWithError('');
|
||||
primarySd.reply(200, serviceDiscoveryResult);
|
||||
secondaryScope.replyWithError('');
|
||||
}
|
||||
|
||||
function mockThirdPartyRegistryLookup(variant: MockVariant): void {
|
||||
const scope = httpMock
|
||||
.scope('https://registry.company.com')
|
||||
.get('/v1/providers/hashicorp/azurerm/versions');
|
||||
|
||||
if (variant === 'empty') {
|
||||
scope.reply(200, {});
|
||||
} else if (variant === '404') {
|
||||
scope.reply(404);
|
||||
} else {
|
||||
scope.replyWithError('');
|
||||
}
|
||||
|
||||
httpMock
|
||||
.scope('https://registry.company.com')
|
||||
.get('/.well-known/terraform.json')
|
||||
.reply(200, serviceDiscoveryResult);
|
||||
}
|
||||
|
||||
describe('modules/datasource/terraform-provider/index', () => {
|
||||
describe('getReleases', () => {
|
||||
it('returns null for empty result', async () => {
|
||||
httpMock
|
||||
.scope(primaryUrl)
|
||||
.get('/v1/providers/hashicorp/azurerm')
|
||||
.reply(200, {})
|
||||
.get('/.well-known/terraform.json')
|
||||
.reply(200, serviceDiscoveryResult);
|
||||
httpMock
|
||||
.scope(secondaryUrl)
|
||||
.get('/terraform-provider-azurerm/index.json')
|
||||
.reply(200, {});
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
it.each`
|
||||
description | variant
|
||||
${'empty results'} | ${'empty'}
|
||||
${'404 responses'} | ${'404'}
|
||||
${'unknown errors'} | ${'error'}
|
||||
`(
|
||||
'returns null when both default registries return $description',
|
||||
async ({ variant }) => {
|
||||
mockDefaultRegistryLookup(variant);
|
||||
|
||||
it('returns null for 404', async () => {
|
||||
httpMock
|
||||
.scope(primaryUrl)
|
||||
.get('/v1/providers/hashicorp/azurerm')
|
||||
.reply(404)
|
||||
.get('/.well-known/terraform.json')
|
||||
.reply(200, serviceDiscoveryResult);
|
||||
httpMock
|
||||
.scope(secondaryUrl)
|
||||
.get('/terraform-provider-azurerm/index.json')
|
||||
.reply(404);
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for unknown error', async () => {
|
||||
httpMock
|
||||
.scope(primaryUrl)
|
||||
.get('/v1/providers/hashicorp/azurerm')
|
||||
.replyWithError('')
|
||||
.get('/.well-known/terraform.json')
|
||||
.reply(200, serviceDiscoveryResult);
|
||||
httpMock
|
||||
.scope(secondaryUrl)
|
||||
.get('/terraform-provider-azurerm/index.json')
|
||||
.replyWithError('');
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
}),
|
||||
).toBeNull();
|
||||
},
|
||||
);
|
||||
|
||||
it('processes real data', async () => {
|
||||
httpMock
|
||||
@@ -105,53 +118,49 @@ describe('modules/datasource/terraform-provider/index', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns null for empty result from third party', async () => {
|
||||
it('does not set releaseTimestamp when latest version is not in releases', async () => {
|
||||
httpMock
|
||||
.scope('https://registry.company.com')
|
||||
.get('/v1/providers/hashicorp/azurerm/versions')
|
||||
.reply(200, {})
|
||||
.scope(primaryUrl)
|
||||
.get('/v1/providers/hashicorp/azurerm')
|
||||
.reply(200, {
|
||||
source: 'https://github.com/hashicorp/terraform-provider-azurerm',
|
||||
versions: ['2.52.0'],
|
||||
version: '9.9.9',
|
||||
published_at: '2019-11-26T08:22:56Z',
|
||||
})
|
||||
.get('/.well-known/terraform.json')
|
||||
.reply(200, serviceDiscoveryResult);
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
registryUrls: ['https://registry.company.com'],
|
||||
}),
|
||||
).toBeNull();
|
||||
const res = await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
});
|
||||
expect(res).toEqual({
|
||||
homepage: 'https://registry.terraform.io/providers/hashicorp/azurerm',
|
||||
registryUrl: 'https://registry.terraform.io',
|
||||
releases: [{ version: '2.52.0' }],
|
||||
sourceUrl: 'https://github.com/hashicorp/terraform-provider-azurerm',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns null for 404 from third party', async () => {
|
||||
httpMock
|
||||
.scope('https://registry.company.com')
|
||||
.get('/v1/providers/hashicorp/azurerm/versions')
|
||||
.reply(404)
|
||||
.get('/.well-known/terraform.json')
|
||||
.reply(200, serviceDiscoveryResult);
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
registryUrls: ['https://registry.company.com'],
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
it.each`
|
||||
description | variant
|
||||
${'empty results'} | ${'empty'}
|
||||
${'404 responses'} | ${'404'}
|
||||
${'unknown errors'} | ${'error'}
|
||||
`(
|
||||
'returns null when a third-party registry returns $description',
|
||||
async ({ variant }) => {
|
||||
mockThirdPartyRegistryLookup(variant);
|
||||
|
||||
it('returns null for unknown error from third party', async () => {
|
||||
httpMock
|
||||
.scope('https://registry.company.com')
|
||||
.get('/v1/providers/hashicorp/azurerm/versions')
|
||||
.replyWithError('')
|
||||
.get('/.well-known/terraform.json')
|
||||
.reply(200, serviceDiscoveryResult);
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
registryUrls: ['https://registry.company.com'],
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
registryUrls: ['https://registry.company.com'],
|
||||
}),
|
||||
).toBeNull();
|
||||
},
|
||||
);
|
||||
|
||||
it('processes real data from third party', async () => {
|
||||
httpMock
|
||||
@@ -216,41 +225,6 @@ describe('modules/datasource/terraform-provider/index', () => {
|
||||
'https://github.com/terraform-providers/terraform-provider-google-beta',
|
||||
});
|
||||
});
|
||||
|
||||
it('simulate failing secondary release source', async () => {
|
||||
httpMock
|
||||
.scope(primaryUrl)
|
||||
.get('/v1/providers/hashicorp/datadog')
|
||||
.reply(404, {
|
||||
errors: ['Not Found'],
|
||||
})
|
||||
.get('/.well-known/terraform.json')
|
||||
.reply(200, serviceDiscoveryResult);
|
||||
httpMock
|
||||
.scope(secondaryUrl)
|
||||
.get('/terraform-provider-datadog/index.json')
|
||||
.reply(404);
|
||||
|
||||
const res = await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'datadog',
|
||||
});
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for error in service discovery', async () => {
|
||||
httpMock.scope(primaryUrl).get('/.well-known/terraform.json').reply(404);
|
||||
httpMock
|
||||
.scope(secondaryUrl)
|
||||
.get('/terraform-provider-azurerm/index.json')
|
||||
.replyWithError('');
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: TerraformProviderDatasource.id,
|
||||
packageName: 'azurerm',
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBuilds', () => {
|
||||
|
||||
@@ -24,9 +24,12 @@ import type {
|
||||
export class TerraformProviderDatasource extends TerraformDatasource {
|
||||
static override readonly id = 'terraform-provider';
|
||||
|
||||
static readonly hashicorpReleaseUrl = 'https://releases.hashicorp.com';
|
||||
static readonly terraformRegistryUrl = 'https://registry.terraform.io';
|
||||
|
||||
static readonly defaultRegistryUrls = [
|
||||
'https://registry.terraform.io',
|
||||
'https://releases.hashicorp.com',
|
||||
TerraformProviderDatasource.terraformRegistryUrl,
|
||||
TerraformProviderDatasource.hashicorpReleaseUrl,
|
||||
];
|
||||
|
||||
static repositoryRegex = regEx(/^hashicorp\/(?<packageName>\S+)$/);
|
||||
@@ -61,28 +64,24 @@ export class TerraformProviderDatasource extends TerraformDatasource {
|
||||
`terraform-provider.getDependencies() packageName: ${packageName}`,
|
||||
);
|
||||
|
||||
if (registryUrl === this.defaultRegistryUrls[1]) {
|
||||
return await this.queryReleaseBackend(packageName, registryUrl);
|
||||
}
|
||||
const repository = TerraformProviderDatasource.getRepository({
|
||||
packageName,
|
||||
});
|
||||
const serviceDiscovery =
|
||||
await this.getTerraformServiceDiscoveryResult(registryUrl);
|
||||
|
||||
if (registryUrl === this.defaultRegistryUrls[0]) {
|
||||
return await this.queryRegistryExtendedApi(
|
||||
if (registryUrl === TerraformProviderDatasource.terraformRegistryUrl) {
|
||||
const repository = TerraformProviderDatasource.getRepository({
|
||||
packageName,
|
||||
});
|
||||
const serviceDiscovery =
|
||||
await this.getTerraformServiceDiscoveryResult(registryUrl);
|
||||
return await this.queryTerraformRegistry(
|
||||
serviceDiscovery,
|
||||
registryUrl,
|
||||
repository,
|
||||
);
|
||||
}
|
||||
if (registryUrl === TerraformProviderDatasource.hashicorpReleaseUrl) {
|
||||
return await this.queryReleaseBackend(packageName, registryUrl);
|
||||
}
|
||||
|
||||
return await this.queryRegistryVersions(
|
||||
serviceDiscovery,
|
||||
registryUrl,
|
||||
repository,
|
||||
);
|
||||
// Fall back to the standard Provider Registry Protocol for other registries.
|
||||
return await this.queryProviderRegistry(registryUrl, packageName);
|
||||
}
|
||||
|
||||
getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {
|
||||
@@ -103,11 +102,11 @@ export class TerraformProviderDatasource extends TerraformDatasource {
|
||||
}
|
||||
|
||||
/**
|
||||
* this uses the api that terraform registry has in addition to the base api
|
||||
* this endpoint provides more information, such as release date
|
||||
* this api is undocumented.
|
||||
* Query the Terraform Registry using the undocumented extended provider API.
|
||||
* This provides more information than the base Provider Registry Protocol,
|
||||
* such as the release date for the latest version and the source URL.
|
||||
*/
|
||||
private async queryRegistryExtendedApi(
|
||||
private async queryTerraformRegistry(
|
||||
serviceDiscovery: ServiceDiscoveryResult,
|
||||
registryUrl: string,
|
||||
repository: string,
|
||||
@@ -141,14 +140,19 @@ export class TerraformProviderDatasource extends TerraformDatasource {
|
||||
}
|
||||
|
||||
/**
|
||||
* this version uses the Provider Registry Protocol that all registries are required to implement
|
||||
* Query a registry using the Provider Registry Protocol that all registries
|
||||
* are required to implement.
|
||||
* https://www.terraform.io/internals/provider-registry-protocol
|
||||
*/
|
||||
private async queryRegistryVersions(
|
||||
serviceDiscovery: ServiceDiscoveryResult,
|
||||
private async queryProviderRegistry(
|
||||
registryUrl: string,
|
||||
repository: string,
|
||||
packageName: string,
|
||||
): Promise<ReleaseResult> {
|
||||
const repository = TerraformProviderDatasource.getRepository({
|
||||
packageName,
|
||||
});
|
||||
const serviceDiscovery: ServiceDiscoveryResult =
|
||||
await this.getTerraformServiceDiscoveryResult(registryUrl);
|
||||
const backendURL = createSDBackendURL(
|
||||
registryUrl,
|
||||
'providers.v1',
|
||||
@@ -200,7 +204,7 @@ export class TerraformProviderDatasource extends TerraformDatasource {
|
||||
repository: string,
|
||||
version: string,
|
||||
): Promise<TerraformBuild[] | null> {
|
||||
if (registryURL === TerraformProviderDatasource.defaultRegistryUrls[1]) {
|
||||
if (registryURL === TerraformProviderDatasource.hashicorpReleaseUrl) {
|
||||
// check if registryURL === secondary backend
|
||||
const repositoryRegexResult =
|
||||
TerraformProviderDatasource.repositoryRegex.exec(repository)?.groups;
|
||||
@@ -356,7 +360,7 @@ export class TerraformProviderDatasource extends TerraformDatasource {
|
||||
): Promise<VersionDetailResponse> {
|
||||
return (
|
||||
await this.http.getJsonUnchecked<VersionDetailResponse>(
|
||||
`${TerraformProviderDatasource.defaultRegistryUrls[1]}/${backendLookUpName}/${version}/index.json`,
|
||||
`${TerraformProviderDatasource.hashicorpReleaseUrl}/${backendLookUpName}/${version}/index.json`,
|
||||
)
|
||||
).body;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user