import * as fs from 'fs' import { buildSchema, print, validate, DocumentNode, OperationDefinitionNode, DefinitionNode } from 'graphql' import { Configuration, generateRandomQuery } from '../src/index' import { GITHUB_PROVIDERS } from './github-providers' import * as dedent from 'dedent' // Globals const schemaDef = fs.readFileSync('./test/fixtures/schema.graphql').toString() const schema = buildSchema(schemaDef) const schemaDefGitHub = fs .readFileSync('./test/fixtures/github.graphql') .toString() const schemaGitHub = buildSchema(schemaDefGitHub) const schemaDefSimple = fs .readFileSync('./test/fixtures/simple.graphql') .toString() const schemaSimple = buildSchema(schemaDefSimple) function getOperationDefinition(doc: DocumentNode): OperationDefinitionNode { const opDefs: DefinitionNode[] = doc.definitions.filter((def) => { return def.kind === 'OperationDefinition' }) return opDefs[0] as OperationDefinitionNode } test(`Obtain random query from example schema`, () => { const config: Configuration = { breadthProbability: 0.1, depthProbability: 0.1, providePlaceholders: true } const { queryDocument, variableValues } = generateRandomQuery(schema, config) expect(queryDocument).toBeDefined() expect(print(queryDocument) === '').toEqual(false) const opDef = getOperationDefinition(queryDocument) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) const errors = validate(schema, queryDocument) expect(errors).toEqual([]) }) test(`Obtain complete query from example schema`, () => { const config: Configuration = { breadthProbability: 1, depthProbability: 1, maxDepth: 2, providePlaceholders: true } const { queryDocument, variableValues } = generateRandomQuery(schema, config) const opDef = getOperationDefinition(queryDocument) const errors = validate(schema, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument) === '').toEqual(false) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) expect(errors).toEqual([]) }) test(`Avoid picking field with only nested subfields when approaching max depth`, () => { const config: Configuration = { breadthProbability: 1, depthProbability: 1, maxDepth: 3, considerInterfaces: true, considerUnions: true, providePlaceholders: true } const { queryDocument, variableValues } = generateRandomQuery(schema, config) const opDef = getOperationDefinition(queryDocument) const errors = validate(schema, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument) === '').toEqual(false) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) expect(errors).toEqual([]) }) test(`Obtain random query from GitHub schema`, () => { const config: Configuration = { breadthProbability: 0.5, depthProbability: 0.5, maxDepth: 10, ignoreOptionalArguments: true, argumentsToConsider: ['first'], considerInterfaces: true, considerUnions: true, seed: 3, providePlaceholders: true } const { queryDocument, variableValues } = generateRandomQuery( schemaGitHub, config ) const opDef = getOperationDefinition(queryDocument) const errors = validate(schemaGitHub, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument).replace(/\s/g, '')).toEqual( `query RandomQuery($Query__codeOfConduct__key: String!) { codeOfConduct(key: $Query__codeOfConduct__key) { name url } }`.replace(/\s/g, '') ) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) expect(errors).toEqual([]) }) test(`Seeded query generation is deterministic`, () => { const config: Configuration = { breadthProbability: 0.5, depthProbability: 0.5, maxDepth: 10, ignoreOptionalArguments: true, argumentsToConsider: ['first'], considerInterfaces: true, considerUnions: true, seed: 3, providePlaceholders: true } const { queryDocument, variableValues } = generateRandomQuery( schemaGitHub, config ) const opDef = getOperationDefinition(queryDocument) const errors = validate(schemaGitHub, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument).replace(/\s/g, '')).toEqual( `query RandomQuery($Query__codeOfConduct__key: String!) { codeOfConduct(key: $Query__codeOfConduct__key) { name url } }`.replace(/\s/g, '') ) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) expect(errors).toEqual([]) }) test(`Missing provider leads to error`, () => { const config: Configuration = { breadthProbability: 0.5, depthProbability: 0.5, maxDepth: 10, ignoreOptionalArguments: true, argumentsToConsider: ['first'], considerInterfaces: true, considerUnions: true, seed: 3, providerMap: { blub__blib__blab: 'blob' } } /** * Target query: * * query RandomQuery($Query__codeOfConduct__key: String!) { codeOfConduct(key: $Query__codeOfConduct__key) { name url } } */ expect(() => generateRandomQuery(schemaGitHub, config)).toThrowError( `Missing provider for non-null variable "Query__codeOfConduct__key" of type "String!". Either add a provider (e.g., using a wildcard "*__*" or "*__*__*"), or set providePlaceholders configuration option to true.` ) }) test(`Null providers are allowed`, () => { const config: Configuration = { breadthProbability: 0.5, depthProbability: 0.5, maxDepth: 10, ignoreOptionalArguments: true, argumentsToConsider: ['first'], considerInterfaces: true, considerUnions: true, seed: 3, providerMap: { '*__*__*': null } } const { queryDocument, variableValues } = generateRandomQuery( schemaGitHub, config ) const opDef = getOperationDefinition(queryDocument) const errors = validate(schemaGitHub, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument).replace(/\s/g, '')).toEqual( `query RandomQuery($Query__codeOfConduct__key: String!) { codeOfConduct(key: $Query__codeOfConduct__key) { name url } }`.replace(/\s/g, '') ) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) expect(errors).toEqual([]) }) test(`Provide custom provider map for GitHub schema`, () => { const config: Configuration = { breadthProbability: 0.2, depthProbability: 0.3, maxDepth: 7, ignoreOptionalArguments: true, argumentsToConsider: ['first'], providerMap: GITHUB_PROVIDERS, considerInterfaces: true, considerUnions: true } const { queryDocument, variableValues } = generateRandomQuery( schemaGitHub, config ) const opDef = getOperationDefinition(queryDocument) const errors = validate(schemaGitHub, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument) === '').toEqual(false) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) expect(errors).toEqual([]) }) test(`Utilize type__field provider`, () => { const config: Configuration = { breadthProbability: 0.2, depthProbability: 0.3, maxDepth: 7, ignoreOptionalArguments: true, argumentsToConsider: ['first'], providerMap: GITHUB_PROVIDERS, considerInterfaces: true, considerUnions: true, seed: 0.2805754930509623 } const { queryDocument, variableValues } = generateRandomQuery( schemaGitHub, config ) const opDef = getOperationDefinition(queryDocument) const errors = validate(schemaGitHub, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument).replace(/\s/g, '')).toEqual( `query RandomQuery($Repository__releases__first: Int, $Project__columns__first: Int, $Repository__projects__first: Int, $RepositoryOwner__pinnedRepositories__first: Int, $PullRequest_ _assignees__first: Int, $User__publicKeys__first: Int, $User__commitComments__first: Int, $User__organization__login: String!, $User__pullRequests__first: Int, $User__starredRepositories__ first: Int, $User__watching__first: Int, $PullRequest__participants__first: Int, $Repository__deployKeys__first: Int, $Repository__assignableUsers__first: Int, $Repository__collaborators__ first: Int, $Repository__issueOrPullRequest__number: Int!, $PullRequest__comments__first: Int, $PullRequest__labels__first: Int, $Repository__pullRequest__number: Int!, $Query__repository_ _name: String!, $Query__repository__owner: String!) { repository(name: $Query__repository__name, owner: $Query__repository__owner) { releases(first: $Repository__releases__first) { totalCount } projects(first: $Repository__projects__first) { nodes { columns(first: $Project__columns__first) { pageInfo { hasNextPage } } updatedAt url } } pullRequest(number: $Repository__pullRequest__number) { headRepositoryOwner { pinnedRepositories(first: $RepositoryOwner__pinnedRepositories__first) { edges { cursor } } avatarUrl ... on User { bio companyHTML createdAt id isSiteAdmin location login updatedAt websiteUrl } } assignees(first: $PullRequest__assignees__first) { nodes { bioHTML isBountyHunter isCampusExpert } } headRef { repository { description diskUsage viewerHasStarred viewerSubscription } } mergeCommit { abbreviatedOid changedFiles deletions id treeResourcePath treeUrl url viewerSubscription } mergedBy { login ... on User { isCampusExpert viewerCanFollow } } participants(first: $PullRequest__participants__first) { nodes { publicKeys(first: $User__publicKeys__first) { pageInfo { hasNextPage } } commitComments(first: $User__commitComments__first) { totalCount } organization(login: $User__organization__login) { databaseId id location newTeamUrl resourcePath viewerCanAdminister } pullRequests(first: $User__pullRequests__first) { nodes { baseRefOid changedFiles closedAt databaseId headRefOid revertUrl state title updatedAt viewerCanApplySuggestion viewerCanReact viewerCanUpdate viewerCannotUpdateReasons viewerSubscription } edges { cursor } } starredRepositories(first: $User__starredRepositories__first) { pageInfo { endCursor } } watching(first: $User__watching__first) { nodes { hasIssuesEnabled hasWikiEnabled isFork projectsResourcePath updatedAt viewerCanCreateProjects viewerSubscription } } avatarUrl company createdAt email isBountyHunter isEmployee } pageInfo { hasPreviousPage } } potentialMergeCommit { authoredDate committedDate committedViaWeb deletions treeUrl } reactionGroups { createdAt } repository { deployKeys(first: $Repository__deployKeys__first) { edges { node { verified } } } assignableUsers(first: $Repository__assignableUsers__first) { nodes { company email isCampusExpert isDeveloperProgramMember isSiteAdmin resourcePath } } collaborators(first: $Repository__collaborators__first) { pageInfo { endCursor } nodes { bioHTML databaseId isHireable } totalCount } issueOrPullRequest(number: $Repository__issueOrPullRequest__number) { ... on Issue { bodyHTML id } } pullRequest(number: $Repository__pullRequest__number) { comments(first: $PullRequest__comments__first) { pageInfo { startCursor } } editor { avatarUrl ... on Bot { createdAt } } labels(first: $PullRequest__labels__first) { totalCount } milestone { url } activeLockReason body createdViaEmail headRefOid number permalink viewerCanReact viewerDidAuthor } descriptionHTML forkCount isArchived isMirror mergeCommitAllowed nameWithOwner projectsUrl rebaseMergeAllowed viewerCanAdminister viewerHasStarred } suggestedReviewers { reviewer { avatarUrl isBountyHunter isEmployee isSiteAdmin } } authorAssociation baseRefName baseRefOid bodyHTML bodyText changedFiles mergedAt publishedAt state viewerCanApplySuggestion viewerCanSubscribe viewerCannotUpdateReasons } createdAt isArchived isLocked mergeCommitAllowed nameWithOwner pushedAt viewerHasStarred } }`.replace(/\s/g, '') ) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) expect(errors).toEqual([]) expect(print(queryDocument).trim().split(`\n`).length).toBe(247) }) test(`Provided variables are passed to providers`, () => { const config: Configuration = { breadthProbability: 1, depthProbability: 1, providerMap: { '*__*__name': (providedVars) => { if (typeof providedVars['Query__repository__owner'] === 'string') { return 'Two' } return 'One' }, '*__*__owner': (providedVars) => { if (typeof providedVars['Query__repository__name'] === 'string') { return 'Two' } return 'One' } } } const { queryDocument, variableValues } = generateRandomQuery( schemaSimple, config ) const errors = validate(schemaSimple, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument).replace(/\s/g, '')).toEqual( `query RandomQuery($Query__repository__name: String!, $Query__repository__owner: String!) { name repository(name: $Query__repository__name, owner: $Query__repository__owner) }`.replace(/\s/g, '') ) expect(errors).toEqual([]) expect( variableValues['Query__repository__name'] != variableValues['Query__repository__owner'] ).toBeTruthy() }) test(`Counts are as expected`, () => { const config: Configuration = { breadthProbability: 0.5, depthProbability: 0.5, maxDepth: 10, ignoreOptionalArguments: true, argumentsToConsider: ['first'], considerInterfaces: true, considerUnions: true, seed: 5 } const { queryDocument, variableValues, typeCount, resolveCount, seed } = generateRandomQuery(schemaGitHub, config) const opDef = getOperationDefinition(queryDocument) const errors = validate(schemaGitHub, queryDocument) expect(queryDocument).toBeDefined() expect(print(queryDocument).replace(/\s/g, '')).toEqual( `query RandomQuery($Query__marketplaceListings__first: Int) { marketplaceListings(first: $Query__marketplaceListings__first) { edges { node { secondaryCategory { description howItWorks primaryListingCount resourcePath secondaryListingCount url } app { databaseId id logoBackgroundColor slug url } configurationUrl extendedDescription extendedDescriptionHTML fullDescriptionHTML hasApprovalBeenRequested howItWorks howItWorksHTML installedForViewer isApproved isDelisted isPaid logoUrl name normalizedShortDescription resourcePath slug statusUrl supportUrl url viewerCanDelist viewerCanEditCategories viewerCanEditPlans viewerCanReject viewerHasPurchasedForAllOrganizations } cursor } } }`.replace(/\s/g, '') ) expect(Object.keys(opDef.variableDefinitions).length).toEqual( Object.keys(variableValues).length ) expect(errors).toEqual([]) }) test('Use providePlaceholders option', () => { const schema = buildSchema(` scalar Custom type Query { field (user: String!, active: Boolean!, age: Int, worth: Float, id: ID, custom: Custom): String } `) const config = { seed: 1, providePlaceholders: true, ignoreOptionalArguments: false } const { queryDocument, variableValues } = generateRandomQuery(schema, config) const query = print(queryDocument) expect(print(queryDocument).replace(/\s/g, '')).toEqual( ` query RandomQuery( $Query__field__user: String!, $Query__field__active: Boolean!, $Query__field__age: Int, $Query__field__worth: Float, $Query__field__id: ID, $Query__field__custom: Custom) { field( user: $Query__field__user, active: $Query__field__active, age: $Query__field__age, worth: $Query__field__worth, id: $Query__field__id, custom: $Query__field__custom) }`.replace(/\s/g, '') ) const variables = JSON.stringify(variableValues, null, 2) expect(variables.trim()).toEqual( dedent(` { "Query__field__user": "PLACEHOLDER", "Query__field__active": true, "Query__field__age": 10, "Query__field__worth": 10, "Query__field__id": "PLACEHOLDER", "Query__field__custom": "PLACEHOLDER" } `).trim() ) })