Skip to content

Commit 7dfefe6

Browse files
committed
test: implement tests for manifestResolver
1 parent 5284763 commit 7dfefe6

File tree

3 files changed

+143
-20
lines changed

3 files changed

+143
-20
lines changed

packages/core/src/amazonq/lsp/util.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import { LanguageServerSetup, LanguageServerSetupStage, telemetry } from '../../shared/telemetry'
7+
import { tryFunctions } from '../../shared/utilities/tsUtils'
78

89
/**
910
* Runs the designated stage within a telemetry span and optionally uses the getMetadata extractor to record metadata from the result of the stage.
@@ -15,14 +16,41 @@ import { LanguageServerSetup, LanguageServerSetupStage, telemetry } from '../../
1516
export async function lspSetupStage<T>(
1617
stageName: LanguageServerSetupStage,
1718
runStage: () => Promise<T>,
18-
getMetadata?: (result: T) => Partial<LanguageServerSetup>
19+
getMetadata?: MetadataExtracter<T>
1920
) {
2021
return await telemetry.languageServer_setup.run(async (span) => {
21-
const result = await runStage()
2222
span.record({ languageServerSetupStage: stageName })
23+
const result = await runStage()
2324
if (getMetadata) {
2425
span.record(getMetadata(result))
2526
}
2627
return result
2728
})
2829
}
30+
31+
export async function tryResolvers<Result>(
32+
stageName: LanguageServerSetupStage,
33+
resolvers: StageResolver<Result>[],
34+
getMetadata: MetadataExtracter<Result>
35+
) {
36+
const fs = resolvers.map((resolver) => async () => {
37+
return await lspSetupStage(
38+
stageName,
39+
async () => {
40+
telemetry.record(resolver.telemetryMetadata)
41+
const result = await resolver.resolve()
42+
return result
43+
},
44+
getMetadata
45+
)
46+
})
47+
48+
return await tryFunctions(fs)
49+
}
50+
51+
export interface StageResolver<Result> {
52+
resolve: () => Promise<Result>
53+
telemetryMetadata: Partial<LanguageServerSetup>
54+
}
55+
56+
type MetadataExtracter<R> = (r: R) => Partial<LanguageServerSetup>

packages/core/src/shared/lsp/manifestResolver.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import { RetryableResourceFetcher } from '../resourcefetcher/httpResourceFetcher
99
import { Timeout } from '../utilities/timeoutUtils'
1010
import globals from '../extensionGlobals'
1111
import { Manifest } from './types'
12-
import { lspSetupStage } from '../../amazonq/lsp/util'
13-
import { tryFunctions } from '../utilities/tsUtils'
12+
import { StageResolver, tryResolvers } from '../../amazonq/lsp/util'
1413

1514
const logger = getLogger('lsp')
1615

@@ -34,22 +33,23 @@ export class ManifestResolver {
3433
* Fetches the latest manifest, falling back to local cache on failure
3534
*/
3635
async resolve(): Promise<Manifest> {
37-
const resolvers = [async () => await this.fetchRemoteManifest(), async () => await this.getLocalManifest()].map(
38-
(resolver) => async () => await resolveManifestWith(resolver)
39-
)
40-
return await tryFunctions(resolvers)
41-
42-
async function resolveManifestWith(resolver: () => Promise<Manifest>) {
43-
return await lspSetupStage(
44-
'getManifest',
45-
async () => await resolver(),
46-
(result) => {
47-
return {
48-
manifestSchemaVersion: result.manifestSchemaVersion,
49-
manifestLocation: result.location,
50-
}
51-
}
52-
)
36+
const resolvers: StageResolver<Manifest>[] = [
37+
{
38+
resolve: async () => await this.fetchRemoteManifest(),
39+
telemetryMetadata: { id: this.lsName, manifestLocation: 'remote' },
40+
},
41+
{
42+
resolve: async () => await this.getLocalManifest(),
43+
telemetryMetadata: { id: this.lsName, manifestLocation: 'cache' },
44+
},
45+
]
46+
47+
return await tryResolvers('getManifest', resolvers, extractMetadata)
48+
49+
function extractMetadata(r: Manifest) {
50+
return {
51+
manifestSchemaVersion: r.manifestSchemaVersion,
52+
}
5353
}
5454
}
5555

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import sinon from 'sinon'
8+
import { ManifestResolver } from '../../../shared'
9+
import { assertTelemetry } from '../../testUtil'
10+
11+
describe('manifestResolver', function () {
12+
let remoteStub: sinon.SinonStub
13+
let localStub: sinon.SinonStub
14+
15+
before(function () {
16+
remoteStub = sinon.stub(ManifestResolver.prototype, 'fetchRemoteManifest' as any)
17+
localStub = sinon.stub(ManifestResolver.prototype, 'getLocalManifest' as any)
18+
})
19+
20+
after(function () {
21+
sinon.restore()
22+
})
23+
24+
it('attempts to fetch from remote first', async function () {
25+
remoteStub.resolves({
26+
manifestSchemaVersion: '1.0.0',
27+
artifactId: 'artifact-id',
28+
artifactDescription: 'artifact-description',
29+
isManifestDeprecated: false,
30+
versions: [],
31+
location: 'remote',
32+
})
33+
34+
const r = await new ManifestResolver('remote-manifest.com', 'myLS').resolve()
35+
assert.strictEqual(r.location, 'remote')
36+
assertTelemetry('languageServer_setup', {
37+
manifestLocation: 'remote',
38+
manifestSchemaVersion: '1.0.0',
39+
languageServerSetupStage: 'getManifest',
40+
id: 'myLS',
41+
result: 'Succeeded',
42+
})
43+
})
44+
45+
it('uses local cache when remote fails', async function () {
46+
remoteStub.rejects(new Error('failed to fetch'))
47+
localStub.resolves({
48+
manifestSchemaVersion: '1.0.0',
49+
artifactId: 'artifact-id',
50+
artifactDescription: 'artifact-description',
51+
isManifestDeprecated: false,
52+
versions: [],
53+
location: 'cache',
54+
})
55+
56+
const r = await new ManifestResolver('remote-manifest.com', 'myLS').resolve()
57+
assert.strictEqual(r.location, 'cache')
58+
assertTelemetry('languageServer_setup', [
59+
{
60+
manifestLocation: 'remote',
61+
languageServerSetupStage: 'getManifest',
62+
id: 'myLS',
63+
result: 'Failed',
64+
},
65+
{
66+
manifestLocation: 'cache',
67+
manifestSchemaVersion: '1.0.0',
68+
languageServerSetupStage: 'getManifest',
69+
id: 'myLS',
70+
result: 'Succeeded',
71+
},
72+
])
73+
})
74+
75+
it('fails if both local and remote fail', async function () {
76+
remoteStub.rejects(new Error('failed to fetch'))
77+
localStub.rejects(new Error('failed to fetch'))
78+
79+
await assert.rejects(new ManifestResolver('remote-manifest.com', 'myLS').resolve(), /failed to fetch/)
80+
assertTelemetry('languageServer_setup', [
81+
{
82+
manifestLocation: 'remote',
83+
languageServerSetupStage: 'getManifest',
84+
id: 'myLS',
85+
result: 'Failed',
86+
},
87+
{
88+
manifestLocation: 'cache',
89+
languageServerSetupStage: 'getManifest',
90+
id: 'myLS',
91+
result: 'Failed',
92+
},
93+
])
94+
})
95+
})

0 commit comments

Comments
 (0)