@@ -21,11 +21,18 @@ import { createMockVariantAnalysisRepoTask } from '../../factories/remote-querie
2121import { CodeQLCliServer } from '../../../cli' ;
2222import { storagePath } from '../global.helper' ;
2323import { VariantAnalysisResultsManager } from '../../../remote-queries/variant-analysis-results-manager' ;
24- import { VariantAnalysis } from '../../../remote-queries/shared/variant-analysis' ;
24+ import {
25+ VariantAnalysis ,
26+ VariantAnalysisScannedRepositoryDownloadStatus
27+ } from '../../../remote-queries/shared/variant-analysis' ;
2528import { createMockVariantAnalysis } from '../../factories/remote-queries/shared/variant-analysis' ;
29+ import { QueryStatus } from '../../../query-status' ;
2630
27- describe ( 'Variant Analysis Manager' , async function ( ) {
31+ describe . only ( 'Variant Analysis Manager' , async function ( ) {
2832 let sandbox : sinon . SinonSandbox ;
33+ let pathExistsStub : sinon . SinonStub ;
34+ let readJsonStub : sinon . SinonStub ;
35+ let outputJsonStub : sinon . SinonStub ;
2936 let cli : CodeQLCliServer ;
3037 let cancellationTokenSource : CancellationTokenSource ;
3138 let variantAnalysisManager : VariantAnalysisManager ;
@@ -41,6 +48,9 @@ describe('Variant Analysis Manager', async function() {
4148 sandbox . stub ( config , 'isVariantAnalysisLiveResultsEnabled' ) . returns ( false ) ;
4249 sandbox . stub ( fs , 'mkdirSync' ) ;
4350 sandbox . stub ( fs , 'writeFile' ) ;
51+ pathExistsStub = sandbox . stub ( fs , 'pathExists' ) . callThrough ( ) ;
52+ readJsonStub = sandbox . stub ( fs , 'readJson' ) . callThrough ( ) ;
53+ outputJsonStub = sandbox . stub ( fs , 'outputJson' ) ;
4454
4555 cancellationTokenSource = new CancellationTokenSource ( ) ;
4656
@@ -61,6 +71,74 @@ describe('Variant Analysis Manager', async function() {
6171 sandbox . restore ( ) ;
6272 } ) ;
6373
74+ describe ( 'rehydrateVariantAnalysis' , ( ) => {
75+ const variantAnalysis = createMockVariantAnalysis ( ) ;
76+
77+ describe ( 'when the directory does not exist' , ( ) => {
78+ beforeEach ( ( ) => {
79+ pathExistsStub . withArgs ( path . join ( storagePath , variantAnalysis . id . toString ( ) ) ) . resolves ( false ) ;
80+ } ) ;
81+
82+ it ( 'should fire the removed event if the file does not exist' , async ( ) => {
83+ const stub = sandbox . stub ( ) ;
84+ variantAnalysisManager . onVariantAnalysisRemoved ( stub ) ;
85+
86+ await variantAnalysisManager . rehydrateVariantAnalysis ( variantAnalysis , QueryStatus . InProgress ) ;
87+
88+ expect ( stub ) . to . have . been . calledOnce ;
89+ sinon . assert . calledWith ( pathExistsStub , path . join ( storagePath , variantAnalysis . id . toString ( ) ) ) ;
90+ } ) ;
91+ } ) ;
92+
93+ describe ( 'when the directory exists' , ( ) => {
94+ beforeEach ( ( ) => {
95+ pathExistsStub . withArgs ( path . join ( storagePath , variantAnalysis . id . toString ( ) ) ) . resolves ( true ) ;
96+ } ) ;
97+
98+ it ( 'should store the variant analysis' , async ( ) => {
99+ await variantAnalysisManager . rehydrateVariantAnalysis ( variantAnalysis , QueryStatus . InProgress ) ;
100+
101+ expect ( await variantAnalysisManager . getVariantAnalysis ( variantAnalysis . id ) ) . to . deep . equal ( variantAnalysis ) ;
102+ } ) ;
103+
104+ it ( 'should not error if the repo states file does not exist' , async ( ) => {
105+ readJsonStub . withArgs ( path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) ) . rejects ( new Error ( 'File does not exist' ) ) ;
106+
107+ await variantAnalysisManager . rehydrateVariantAnalysis ( variantAnalysis , QueryStatus . InProgress ) ;
108+
109+ sinon . assert . calledWith ( readJsonStub , path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) ) ;
110+ expect ( await variantAnalysisManager . getRepoStates ( variantAnalysis . id ) ) . to . deep . equal ( [ ] ) ;
111+ } ) ;
112+
113+ it ( 'should read in the repo states if it exists' , async ( ) => {
114+ readJsonStub . withArgs ( path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) ) . resolves ( {
115+ [ scannedRepos [ 0 ] . repository . id ] : {
116+ repositoryId : scannedRepos [ 0 ] . repository . id ,
117+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Succeeded ,
118+ } ,
119+ [ scannedRepos [ 1 ] . repository . id ] : {
120+ repositoryId : scannedRepos [ 1 ] . repository . id ,
121+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . InProgress ,
122+ } ,
123+ } ) ;
124+
125+ await variantAnalysisManager . rehydrateVariantAnalysis ( variantAnalysis , QueryStatus . InProgress ) ;
126+
127+ sinon . assert . calledWith ( readJsonStub , path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) ) ;
128+ expect ( await variantAnalysisManager . getRepoStates ( variantAnalysis . id ) ) . to . have . same . deep . members ( [
129+ {
130+ repositoryId : scannedRepos [ 0 ] . repository . id ,
131+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Succeeded ,
132+ } ,
133+ {
134+ repositoryId : scannedRepos [ 1 ] . repository . id ,
135+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . InProgress ,
136+ } ,
137+ ] ) ;
138+ } ) ;
139+ } ) ;
140+ } ) ;
141+
64142 describe ( 'when credentials are invalid' , async ( ) => {
65143 beforeEach ( async ( ) => { sandbox . stub ( Credentials , 'initialize' ) . resolves ( undefined ) ; } ) ;
66144
@@ -155,6 +233,171 @@ describe('Variant Analysis Manager', async function() {
155233
156234 expect ( getVariantAnalysisRepoResultStub . calledOnce ) . to . be . true ;
157235 } ) ;
236+
237+ it ( 'should skip the download if the repository has already been downloaded' , async ( ) => {
238+ // First, do a download so it is downloaded. This avoids having to mock the repo states.
239+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
240+ scannedRepos [ 0 ] ,
241+ variantAnalysis ,
242+ cancellationTokenSource . token
243+ ) ;
244+
245+ getVariantAnalysisRepoStub . resetHistory ( ) ;
246+
247+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
248+ scannedRepos [ 0 ] ,
249+ variantAnalysis ,
250+ cancellationTokenSource . token
251+ ) ;
252+
253+ expect ( getVariantAnalysisRepoStub . notCalled ) . to . be . true ;
254+ } ) ;
255+
256+ it ( 'should write the repo state when the download is successful' , async ( ) => {
257+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
258+ scannedRepos [ 0 ] ,
259+ variantAnalysis ,
260+ cancellationTokenSource . token
261+ ) ;
262+
263+ sinon . assert . calledWith ( outputJsonStub , path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) , {
264+ [ scannedRepos [ 0 ] . repository . id ] : {
265+ repositoryId : scannedRepos [ 0 ] . repository . id ,
266+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Succeeded ,
267+ } ,
268+ } ) ;
269+ } ) ;
270+
271+ it ( 'should not write the repo state when the download fails' , async ( ) => {
272+ getVariantAnalysisRepoResultStub . rejects ( new Error ( 'Failed to download' ) ) ;
273+
274+ try {
275+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
276+ scannedRepos [ 0 ] ,
277+ variantAnalysis ,
278+ cancellationTokenSource . token
279+ ) ;
280+ fail ( 'Expected an error to be thrown' ) ;
281+ } catch ( e : any ) {
282+ // we can ignore this error, we expect this
283+ }
284+
285+ sinon . assert . notCalled ( outputJsonStub ) ;
286+ } ) ;
287+
288+ it ( 'should have a failed repo state when the repo task API fails' , async ( ) => {
289+ getVariantAnalysisRepoStub . onFirstCall ( ) . rejects ( new Error ( 'Failed to download' ) ) ;
290+
291+ try {
292+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
293+ scannedRepos [ 0 ] ,
294+ variantAnalysis ,
295+ cancellationTokenSource . token
296+ ) ;
297+ fail ( 'Expected an error to be thrown' ) ;
298+ } catch ( e ) {
299+ // we can ignore this error, we expect this
300+ }
301+
302+ sinon . assert . notCalled ( outputJsonStub ) ;
303+
304+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
305+ scannedRepos [ 1 ] ,
306+ variantAnalysis ,
307+ cancellationTokenSource . token
308+ ) ;
309+
310+ sinon . assert . calledWith ( outputJsonStub , path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) , {
311+ [ scannedRepos [ 0 ] . repository . id ] : {
312+ repositoryId : scannedRepos [ 0 ] . repository . id ,
313+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Failed ,
314+ } ,
315+ [ scannedRepos [ 1 ] . repository . id ] : {
316+ repositoryId : scannedRepos [ 1 ] . repository . id ,
317+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Succeeded ,
318+ } ,
319+ } ) ;
320+ } ) ;
321+
322+ it ( 'should have a failed repo state when the download fails' , async ( ) => {
323+ getVariantAnalysisRepoResultStub . onFirstCall ( ) . rejects ( new Error ( 'Failed to download' ) ) ;
324+
325+ try {
326+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
327+ scannedRepos [ 0 ] ,
328+ variantAnalysis ,
329+ cancellationTokenSource . token
330+ ) ;
331+ fail ( 'Expected an error to be thrown' ) ;
332+ } catch ( e ) {
333+ // we can ignore this error, we expect this
334+ }
335+
336+ sinon . assert . notCalled ( outputJsonStub ) ;
337+
338+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
339+ scannedRepos [ 1 ] ,
340+ variantAnalysis ,
341+ cancellationTokenSource . token
342+ ) ;
343+
344+ sinon . assert . calledWith ( outputJsonStub , path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) , {
345+ [ scannedRepos [ 0 ] . repository . id ] : {
346+ repositoryId : scannedRepos [ 0 ] . repository . id ,
347+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Failed ,
348+ } ,
349+ [ scannedRepos [ 1 ] . repository . id ] : {
350+ repositoryId : scannedRepos [ 1 ] . repository . id ,
351+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Succeeded ,
352+ } ,
353+ } ) ;
354+ } ) ;
355+
356+ it ( 'should update the repo state correctly' , async ( ) => {
357+ // To set some initial repo states, we need to mock the correct methods so that the repo states are read in.
358+ // The actual tests for these are in rehydrateVariantAnalysis, so we can just mock them here and test that
359+ // the methods are called.
360+
361+ pathExistsStub . withArgs ( path . join ( storagePath , variantAnalysis . id . toString ( ) ) ) . resolves ( true ) ;
362+ // This will read in the correct repo states
363+ readJsonStub . withArgs ( path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) ) . resolves ( {
364+ [ scannedRepos [ 1 ] . repository . id ] : {
365+ repositoryId : scannedRepos [ 1 ] . repository . id ,
366+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Succeeded ,
367+ } ,
368+ [ scannedRepos [ 2 ] . repository . id ] : {
369+ repositoryId : scannedRepos [ 2 ] . repository . id ,
370+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . InProgress ,
371+ } ,
372+ } ) ;
373+
374+ await variantAnalysisManager . rehydrateVariantAnalysis ( {
375+ ...createMockVariantAnalysis ( ) ,
376+ id : variantAnalysis . id ,
377+ } , QueryStatus . InProgress ) ;
378+ sinon . assert . calledWith ( readJsonStub , path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) ) ;
379+
380+ await variantAnalysisManager . autoDownloadVariantAnalysisResult (
381+ scannedRepos [ 0 ] ,
382+ variantAnalysis ,
383+ cancellationTokenSource . token
384+ ) ;
385+
386+ sinon . assert . calledWith ( outputJsonStub , path . join ( storagePath , variantAnalysis . id . toString ( ) , 'repo_states.json' ) , {
387+ [ scannedRepos [ 1 ] . repository . id ] : {
388+ repositoryId : scannedRepos [ 1 ] . repository . id ,
389+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Succeeded ,
390+ } ,
391+ [ scannedRepos [ 2 ] . repository . id ] : {
392+ repositoryId : scannedRepos [ 2 ] . repository . id ,
393+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . InProgress ,
394+ } ,
395+ [ scannedRepos [ 0 ] . repository . id ] : {
396+ repositoryId : scannedRepos [ 0 ] . repository . id ,
397+ downloadStatus : VariantAnalysisScannedRepositoryDownloadStatus . Succeeded ,
398+ }
399+ } ) ;
400+ } ) ;
158401 } ) ;
159402
160403 describe ( 'enqueueDownload' , async ( ) => {
0 commit comments