Move remote queries specific code out of run-remote-query
This moves some of the code that is specific to remote queries out of the `run-remote-query.ts` file and instead places it in separate files that only deal with remote queries, rather than also dealing with variant analyses.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import * as os from 'os';
|
||||
import { Credentials } from '../authentication';
|
||||
import { RepositorySelection } from './repository-selection';
|
||||
import { Repository } from './shared/repository';
|
||||
import { RemoteQueriesResponse } from './gh-api/remote-queries';
|
||||
import * as ghApiClient from './gh-api/gh-api-client';
|
||||
import { showAndLogErrorMessage, showAndLogInformationMessage } from '../helpers';
|
||||
import { getErrorMessage } from '../pure/helpers-pure';
|
||||
import { pluralize } from '../pure/word';
|
||||
|
||||
export async function runRemoteQueriesApiRequest(
|
||||
credentials: Credentials,
|
||||
ref: string,
|
||||
language: string,
|
||||
repoSelection: RepositorySelection,
|
||||
controllerRepo: Repository,
|
||||
queryPackBase64: string,
|
||||
): Promise<void | RemoteQueriesResponse> {
|
||||
try {
|
||||
const response = await ghApiClient.submitRemoteQueries(credentials, {
|
||||
ref,
|
||||
language,
|
||||
repositories: repoSelection.repositories,
|
||||
repositoryLists: repoSelection.repositoryLists,
|
||||
repositoryOwners: repoSelection.owners,
|
||||
queryPack: queryPackBase64,
|
||||
controllerRepoId: controllerRepo.id,
|
||||
});
|
||||
const { popupMessage, logMessage } = parseResponse(controllerRepo, response);
|
||||
void showAndLogInformationMessage(popupMessage, { fullMessage: logMessage });
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
if (error.status === 404) {
|
||||
void showAndLogErrorMessage(`Controller repository was not found. Please make sure it's a valid repo name.${eol}`);
|
||||
} else {
|
||||
void showAndLogErrorMessage(getErrorMessage(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const eol = os.EOL;
|
||||
const eol2 = os.EOL + os.EOL;
|
||||
|
||||
// exported for testing only
|
||||
export function parseResponse(controllerRepo: Repository, response: RemoteQueriesResponse) {
|
||||
const repositoriesQueried = response.repositories_queried;
|
||||
const repositoryCount = repositoriesQueried.length;
|
||||
|
||||
const popupMessage = `Successfully scheduled runs on ${pluralize(repositoryCount, 'repository', 'repositories')}. [Click here to see the progress](https://github.com/${controllerRepo.fullName}/actions/runs/${response.workflow_run_id}).`
|
||||
+ (response.errors ? `${eol2}Some repositories could not be scheduled. See extension log for details.` : '');
|
||||
|
||||
let logMessage = `Successfully scheduled runs on ${pluralize(repositoryCount, 'repository', 'repositories')}. See https://github.com/${controllerRepo.fullName}/actions/runs/${response.workflow_run_id}.`;
|
||||
logMessage += `${eol2}Repositories queried:${eol}${repositoriesQueried.join(', ')}`;
|
||||
if (response.errors) {
|
||||
const { invalid_repositories, repositories_without_database, private_repositories, cutoff_repositories, cutoff_repositories_count } = response.errors;
|
||||
logMessage += `${eol2}Some repositories could not be scheduled.`;
|
||||
if (invalid_repositories?.length) {
|
||||
logMessage += `${eol2}${pluralize(invalid_repositories.length, 'repository', 'repositories')} invalid and could not be found:${eol}${invalid_repositories.join(', ')}`;
|
||||
}
|
||||
if (repositories_without_database?.length) {
|
||||
logMessage += `${eol2}${pluralize(repositories_without_database.length, 'repository', 'repositories')} did not have a CodeQL database available:${eol}${repositories_without_database.join(', ')}`;
|
||||
logMessage += `${eol}For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.`;
|
||||
}
|
||||
if (private_repositories?.length) {
|
||||
logMessage += `${eol2}${pluralize(private_repositories.length, 'repository', 'repositories')} not public:${eol}${private_repositories.join(', ')}`;
|
||||
logMessage += `${eol}When using a public controller repository, only public repositories can be queried.`;
|
||||
}
|
||||
if (cutoff_repositories_count) {
|
||||
logMessage += `${eol2}${pluralize(cutoff_repositories_count, 'repository', 'repositories')} over the limit for a single request`;
|
||||
if (cutoff_repositories) {
|
||||
logMessage += `:${eol}${cutoff_repositories.join(', ')}`;
|
||||
if (cutoff_repositories_count !== cutoff_repositories.length) {
|
||||
const moreRepositories = cutoff_repositories_count - cutoff_repositories.length;
|
||||
logMessage += `${eol}...${eol}And another ${pluralize(moreRepositories, 'repository', 'repositories')}.`;
|
||||
}
|
||||
} else {
|
||||
logMessage += '.';
|
||||
}
|
||||
logMessage += `${eol}Repositories were selected based on how recently they had been updated.`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
popupMessage,
|
||||
logMessage
|
||||
};
|
||||
}
|
||||
@@ -10,12 +10,10 @@ import { ProgressCallback } from '../commandRunner';
|
||||
import { createTimestampFile, showAndLogErrorMessage, showAndLogInformationMessage, showInformationMessageWithAction } from '../helpers';
|
||||
import { Logger } from '../logging';
|
||||
import {
|
||||
buildRemoteQueryEntity,
|
||||
prepareRemoteQueryRun,
|
||||
runRemoteQueriesApiRequest,
|
||||
} from './run-remote-query';
|
||||
import { RemoteQueriesView } from './remote-queries-view';
|
||||
import { RemoteQuery } from './remote-query';
|
||||
import { buildRemoteQueryEntity, RemoteQuery } from './remote-query';
|
||||
import { RemoteQueriesMonitor } from './remote-queries-monitor';
|
||||
import { getRemoteQueryIndex, getRepositoriesMetadata, RepositoriesMetadata } from './gh-api/gh-actions-api-client';
|
||||
import { RemoteQueryResultIndex } from './remote-query-result-index';
|
||||
@@ -26,6 +24,7 @@ import { assertNever } from '../pure/helpers-pure';
|
||||
import { QueryStatus } from '../query-status';
|
||||
import { DisposableObject } from '../pure/disposable-object';
|
||||
import { AnalysisResults } from './shared/analysis-result';
|
||||
import { runRemoteQueriesApiRequest } from './remote-queries-api';
|
||||
|
||||
const autoDownloadMaxSize = 300 * 1024;
|
||||
const autoDownloadMaxCount = 100;
|
||||
|
||||
@@ -1,12 +1,44 @@
|
||||
import { Repository } from './repository';
|
||||
import * as fs from 'fs-extra';
|
||||
import { Repository as RemoteRepository } from './repository';
|
||||
import { QueryMetadata } from '../pure/interface-types';
|
||||
import { getQueryName } from './run-remote-query';
|
||||
import { Repository } from './shared/repository';
|
||||
|
||||
export interface RemoteQuery {
|
||||
queryName: string;
|
||||
queryFilePath: string;
|
||||
queryText: string;
|
||||
language: string;
|
||||
controllerRepository: Repository;
|
||||
controllerRepository: RemoteRepository;
|
||||
executionStartTime: number; // Use number here since it needs to be serialized and desserialized.
|
||||
actionsWorkflowRunId: number;
|
||||
repositoryCount: number;
|
||||
}
|
||||
|
||||
export async function buildRemoteQueryEntity(
|
||||
queryFilePath: string,
|
||||
queryMetadata: QueryMetadata | undefined,
|
||||
controllerRepo: Repository,
|
||||
queryStartTime: number,
|
||||
workflowRunId: number,
|
||||
language: string,
|
||||
repositoryCount: number
|
||||
): Promise<RemoteQuery> {
|
||||
const queryName = getQueryName(queryMetadata, queryFilePath);
|
||||
const queryText = await fs.readFile(queryFilePath, 'utf8');
|
||||
const [owner, name] = controllerRepo.fullName.split('/');
|
||||
|
||||
return {
|
||||
queryName,
|
||||
queryFilePath,
|
||||
queryText,
|
||||
language,
|
||||
controllerRepository: {
|
||||
owner,
|
||||
name,
|
||||
},
|
||||
executionStartTime: queryStartTime,
|
||||
actionsWorkflowRunId: workflowRunId,
|
||||
repositoryCount,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,14 +2,11 @@ import { CancellationToken, Uri, window } from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as os from 'os';
|
||||
import * as tmp from 'tmp-promise';
|
||||
import {
|
||||
askForLanguage,
|
||||
findLanguage,
|
||||
getOnDiskWorkspaceFolders,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogInformationMessage,
|
||||
tryGetQueryMetadata,
|
||||
tmpDir,
|
||||
} from '../helpers';
|
||||
@@ -19,13 +16,10 @@ import { logger } from '../logging';
|
||||
import { getActionBranch, getRemoteControllerRepo, setRemoteControllerRepo } from '../config';
|
||||
import { ProgressCallback, UserCancellationException } from '../commandRunner';
|
||||
import { RequestError } from '@octokit/types/dist-types';
|
||||
import { RemoteQuery } from './remote-query';
|
||||
import { QueryMetadata } from '../pure/interface-types';
|
||||
import { getErrorMessage, REPO_REGEX } from '../pure/helpers-pure';
|
||||
import { pluralize } from '../pure/word';
|
||||
import { REPO_REGEX } from '../pure/helpers-pure';
|
||||
import * as ghApiClient from './gh-api/gh-api-client';
|
||||
import { RemoteQueriesResponse } from './gh-api/remote-queries';
|
||||
import { getRepositorySelection, isValidSelection, RepositorySelection } from './repository-selection';
|
||||
import { getRepositorySelection, isValidSelection } from './repository-selection';
|
||||
import { Repository } from './shared/repository';
|
||||
|
||||
export interface QlPack {
|
||||
@@ -257,84 +251,6 @@ export async function prepareRemoteQueryRun(
|
||||
};
|
||||
}
|
||||
|
||||
export async function runRemoteQueriesApiRequest(
|
||||
credentials: Credentials,
|
||||
ref: string,
|
||||
language: string,
|
||||
repoSelection: RepositorySelection,
|
||||
controllerRepo: Repository,
|
||||
queryPackBase64: string,
|
||||
): Promise<void | RemoteQueriesResponse> {
|
||||
try {
|
||||
const response = await ghApiClient.submitRemoteQueries(credentials, {
|
||||
ref,
|
||||
language,
|
||||
repositories: repoSelection.repositories,
|
||||
repositoryLists: repoSelection.repositoryLists,
|
||||
repositoryOwners: repoSelection.owners,
|
||||
queryPack: queryPackBase64,
|
||||
controllerRepoId: controllerRepo.id,
|
||||
});
|
||||
const { popupMessage, logMessage } = parseResponse(controllerRepo, response);
|
||||
void showAndLogInformationMessage(popupMessage, { fullMessage: logMessage });
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
if (error.status === 404) {
|
||||
void showAndLogErrorMessage(`Controller repository was not found. Please make sure it's a valid repo name.${eol}`);
|
||||
} else {
|
||||
void showAndLogErrorMessage(getErrorMessage(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const eol = os.EOL;
|
||||
const eol2 = os.EOL + os.EOL;
|
||||
|
||||
// exported for testing only
|
||||
export function parseResponse(controllerRepo: Repository, response: RemoteQueriesResponse) {
|
||||
const repositoriesQueried = response.repositories_queried;
|
||||
const repositoryCount = repositoriesQueried.length;
|
||||
|
||||
const popupMessage = `Successfully scheduled runs on ${pluralize(repositoryCount, 'repository', 'repositories')}. [Click here to see the progress](https://github.com/${controllerRepo.fullName}/actions/runs/${response.workflow_run_id}).`
|
||||
+ (response.errors ? `${eol2}Some repositories could not be scheduled. See extension log for details.` : '');
|
||||
|
||||
let logMessage = `Successfully scheduled runs on ${pluralize(repositoryCount, 'repository', 'repositories')}. See https://github.com/${controllerRepo.fullName}/actions/runs/${response.workflow_run_id}.`;
|
||||
logMessage += `${eol2}Repositories queried:${eol}${repositoriesQueried.join(', ')}`;
|
||||
if (response.errors) {
|
||||
const { invalid_repositories, repositories_without_database, private_repositories, cutoff_repositories, cutoff_repositories_count } = response.errors;
|
||||
logMessage += `${eol2}Some repositories could not be scheduled.`;
|
||||
if (invalid_repositories?.length) {
|
||||
logMessage += `${eol2}${pluralize(invalid_repositories.length, 'repository', 'repositories')} invalid and could not be found:${eol}${invalid_repositories.join(', ')}`;
|
||||
}
|
||||
if (repositories_without_database?.length) {
|
||||
logMessage += `${eol2}${pluralize(repositories_without_database.length, 'repository', 'repositories')} did not have a CodeQL database available:${eol}${repositories_without_database.join(', ')}`;
|
||||
logMessage += `${eol}For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.`;
|
||||
}
|
||||
if (private_repositories?.length) {
|
||||
logMessage += `${eol2}${pluralize(private_repositories.length, 'repository', 'repositories')} not public:${eol}${private_repositories.join(', ')}`;
|
||||
logMessage += `${eol}When using a public controller repository, only public repositories can be queried.`;
|
||||
}
|
||||
if (cutoff_repositories_count) {
|
||||
logMessage += `${eol2}${pluralize(cutoff_repositories_count, 'repository', 'repositories')} over the limit for a single request`;
|
||||
if (cutoff_repositories) {
|
||||
logMessage += `:${eol}${cutoff_repositories.join(', ')}`;
|
||||
if (cutoff_repositories_count !== cutoff_repositories.length) {
|
||||
const moreRepositories = cutoff_repositories_count - cutoff_repositories.length;
|
||||
logMessage += `${eol}...${eol}And another ${pluralize(moreRepositories, 'repository', 'repositories')}.`;
|
||||
}
|
||||
} else {
|
||||
logMessage += '.';
|
||||
}
|
||||
logMessage += `${eol}Repositories were selected based on how recently they had been updated.`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
popupMessage,
|
||||
logMessage
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the default suite of the query pack. This is used to ensure
|
||||
* only the specified query is run.
|
||||
@@ -359,34 +275,6 @@ async function ensureNameAndSuite(queryPackDir: string, packRelativePath: string
|
||||
await fs.writeFile(packPath, yaml.dump(qlpack));
|
||||
}
|
||||
|
||||
export async function buildRemoteQueryEntity(
|
||||
queryFilePath: string,
|
||||
queryMetadata: QueryMetadata | undefined,
|
||||
controllerRepo: Repository,
|
||||
queryStartTime: number,
|
||||
workflowRunId: number,
|
||||
language: string,
|
||||
repositoryCount: number
|
||||
): Promise<RemoteQuery> {
|
||||
const queryName = getQueryName(queryMetadata, queryFilePath);
|
||||
const queryText = await fs.readFile(queryFilePath, 'utf8');
|
||||
const [owner, name] = controllerRepo.fullName.split('/');
|
||||
|
||||
return {
|
||||
queryName,
|
||||
queryFilePath,
|
||||
queryText,
|
||||
language,
|
||||
controllerRepository: {
|
||||
owner,
|
||||
name,
|
||||
},
|
||||
executionStartTime: queryStartTime,
|
||||
actionsWorkflowRunId: workflowRunId,
|
||||
repositoryCount,
|
||||
};
|
||||
}
|
||||
|
||||
export function getQueryName(queryMetadata: QueryMetadata | undefined, queryFilePath: string): string {
|
||||
// The query name is either the name as specified in the query metadata, or the file name.
|
||||
return queryMetadata?.name ?? path.basename(queryFilePath);
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
import { expect } from 'chai';
|
||||
import * as os from 'os';
|
||||
import { parseResponse } from '../../../remote-queries/remote-queries-api';
|
||||
import { Repository } from '../../../remote-queries/shared/repository';
|
||||
|
||||
describe('parseResponse', () => {
|
||||
const controllerRepository: Repository = {
|
||||
id: 123,
|
||||
fullName: 'org/name',
|
||||
private: true
|
||||
};
|
||||
|
||||
it('should parse a successful response', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal('Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).');
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d'].join(os.EOL),
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with invalid repos', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
invalid_repositories: ['e/f', 'g/h'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories invalid and could not be found:',
|
||||
'e/f, g/h'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with repos w/o databases', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
repositories_without_database: ['e/f', 'g/h'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories did not have a CodeQL database available:',
|
||||
'e/f, g/h',
|
||||
'For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with private repos', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
private_repositories: ['e/f', 'g/h'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories not public:',
|
||||
'e/f, g/h',
|
||||
'When using a public controller repository, only public repositories can be queried.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with cutoff repos and cutoff repos count', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
cutoff_repositories: ['e/f', 'g/h'],
|
||||
cutoff_repositories_count: 2,
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories over the limit for a single request:',
|
||||
'e/f, g/h',
|
||||
'Repositories were selected based on how recently they had been updated.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with cutoff repos count but not cutoff repos', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
cutoff_repositories_count: 2,
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories over the limit for a single request.',
|
||||
'Repositories were selected based on how recently they had been updated.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with invalid repos and repos w/o databases', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
invalid_repositories: ['e/f', 'g/h'],
|
||||
repositories_without_database: ['i/j', 'k/l'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories invalid and could not be found:',
|
||||
'e/f, g/h',
|
||||
'',
|
||||
'2 repositories did not have a CodeQL database available:',
|
||||
'i/j, k/l',
|
||||
'For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with one repo of each category, and not pluralize "repositories"', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b'],
|
||||
errors: {
|
||||
private_repositories: ['e/f'],
|
||||
cutoff_repositories: ['i/j'],
|
||||
cutoff_repositories_count: 1,
|
||||
invalid_repositories: ['m/n'],
|
||||
repositories_without_database: ['q/r'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 1 repository. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
[
|
||||
'Successfully scheduled runs on 1 repository. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'1 repository invalid and could not be found:',
|
||||
'm/n',
|
||||
'',
|
||||
'1 repository did not have a CodeQL database available:',
|
||||
'q/r',
|
||||
'For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.',
|
||||
'',
|
||||
'1 repository not public:',
|
||||
'e/f',
|
||||
'When using a public controller repository, only public repositories can be queried.',
|
||||
'',
|
||||
'1 repository over the limit for a single request:',
|
||||
'i/j',
|
||||
'Repositories were selected based on how recently they had been updated.',
|
||||
].join(os.EOL)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,245 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import * as os from 'os';
|
||||
import { parseResponse } from '../../../remote-queries/run-remote-query';
|
||||
import { Repository } from '../../../remote-queries/shared/repository';
|
||||
|
||||
describe('run-remote-query', () => {
|
||||
describe('parseResponse', () => {
|
||||
const controllerRepository: Repository = {
|
||||
id: 123,
|
||||
fullName: 'org/name',
|
||||
private: true
|
||||
};
|
||||
|
||||
it('should parse a successful response', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal('Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).');
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d'].join(os.EOL),
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with invalid repos', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
invalid_repositories: ['e/f', 'g/h'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories invalid and could not be found:',
|
||||
'e/f, g/h'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with repos w/o databases', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
repositories_without_database: ['e/f', 'g/h'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories did not have a CodeQL database available:',
|
||||
'e/f, g/h',
|
||||
'For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with private repos', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
private_repositories: ['e/f', 'g/h'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories not public:',
|
||||
'e/f, g/h',
|
||||
'When using a public controller repository, only public repositories can be queried.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with cutoff repos and cutoff repos count', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
cutoff_repositories: ['e/f', 'g/h'],
|
||||
cutoff_repositories_count: 2,
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories over the limit for a single request:',
|
||||
'e/f, g/h',
|
||||
'Repositories were selected based on how recently they had been updated.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with cutoff repos count but not cutoff repos', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
cutoff_repositories_count: 2,
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories over the limit for a single request.',
|
||||
'Repositories were selected based on how recently they had been updated.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with invalid repos and repos w/o databases', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b', 'c/d'],
|
||||
errors: {
|
||||
invalid_repositories: ['e/f', 'g/h'],
|
||||
repositories_without_database: ['i/j', 'k/l'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
['Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b, c/d',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'2 repositories invalid and could not be found:',
|
||||
'e/f, g/h',
|
||||
'',
|
||||
'2 repositories did not have a CodeQL database available:',
|
||||
'i/j, k/l',
|
||||
'For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.'].join(os.EOL)
|
||||
);
|
||||
});
|
||||
|
||||
it('should parse a response with one repo of each category, and not pluralize "repositories"', () => {
|
||||
const result = parseResponse(controllerRepository, {
|
||||
workflow_run_id: 123,
|
||||
repositories_queried: ['a/b'],
|
||||
errors: {
|
||||
private_repositories: ['e/f'],
|
||||
cutoff_repositories: ['i/j'],
|
||||
cutoff_repositories_count: 1,
|
||||
invalid_repositories: ['m/n'],
|
||||
repositories_without_database: ['q/r'],
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
['Successfully scheduled runs on 1 repository. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
|
||||
'',
|
||||
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
[
|
||||
'Successfully scheduled runs on 1 repository. See https://github.com/org/name/actions/runs/123.',
|
||||
'',
|
||||
'Repositories queried:',
|
||||
'a/b',
|
||||
'',
|
||||
'Some repositories could not be scheduled.',
|
||||
'',
|
||||
'1 repository invalid and could not be found:',
|
||||
'm/n',
|
||||
'',
|
||||
'1 repository did not have a CodeQL database available:',
|
||||
'q/r',
|
||||
'For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.',
|
||||
'',
|
||||
'1 repository not public:',
|
||||
'e/f',
|
||||
'When using a public controller repository, only public repositories can be queried.',
|
||||
'',
|
||||
'1 repository over the limit for a single request:',
|
||||
'i/j',
|
||||
'Repositories were selected based on how recently they had been updated.',
|
||||
].join(os.EOL)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user