-
-
Notifications
You must be signed in to change notification settings - Fork 2
Test suite for the type complexity analysis algorithm #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
f9d4246
created the function signature for getQueryComplexity and the framewo…
evanmcneely 6566b9e
refactored the type weight object to be readonly
evanmcneely 92a2569
renamed complexity analysis file structure to distinguish this feaure…
evanmcneely e059eab
commititng before power fail
evanmcneely 9bafb44
finished writing the bulk of the tests for getQueryTypeComplxity
evanmcneely 20b088d
Merge branch 'dev' of https://github.com/oslabs-beta/GraphQL-Gate int…
evanmcneely 3951486
updated the type weights object after rethinking how that function is…
evanmcneely 3282b49
added some annotations at the end of the day
evanmcneely 8f19ce2
Updated the error handling test
evanmcneely 2162d40
removed the topic type from the test schema and added scalars as a qu…
evanmcneely c435c93
merged the changes from buildTypeWeights tests
evanmcneely e5c194c
broke the lists test into two, one for listss with arguments and the …
evanmcneely edab88d
addressing changes requested in PR
evanmcneely 6243d26
Merge branch 'dev' into em/complexityTests
evanmcneely 760cdb5
skipping the tests to pass the Travis CI tests
evanmcneely 1910d65
Merge branch 'em/complexityTests' of https://github.com/oslabs-beta/G…
evanmcneely 3510641
corrected an error with one of the expected results from a test.
evanmcneely 3817cd4
update the expected typeWeightsObject in the comlpxity tests to inclu…
evanmcneely 4fd8bc4
Merge branch 'dev' into em/complexityTests
evanmcneely f0f9a01
lint fix
evanmcneely File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { parse } from 'graphql'; | ||
|
|
||
| /** | ||
| * This function should | ||
| * 1. validate the query using graphql methods | ||
| * 2. parse the query string using the graphql parse method | ||
| * 3. itreate through the query AST and | ||
| * - cross reference the type weight object to check type weight | ||
| * - total all the eweights of all types in the query | ||
| * 4. return the total as the query complexity | ||
| * | ||
| * TO DO: extend the functionality to work for mutations and subscriptions | ||
| * | ||
| * @param {string} queryString | ||
| * @param {TypeWeightObject} typeWeights | ||
| * @param {string} complexityOption | ||
| */ | ||
| function getQueryTypeComplexity(queryString: string, typeWeights: TypeWeightObject): number { | ||
| throw Error('getQueryComplexity is not implemented.'); | ||
| } | ||
|
|
||
| export default getQueryTypeComplexity; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,316 @@ | ||
| import getQueryTypeComplexity from '../../src/analysis/typeComplexityAnalysis'; | ||
|
|
||
| /** | ||
| * Here is the schema that creates the followning 'typeWeightsObject' used for the tests | ||
| * | ||
| type Query { | ||
| hero(episode: Episode): Character | ||
| reviews(episode: Episode!, first: Int): [Review] | ||
| search(text: String): [SearchResult] | ||
| character(id: ID!): Character | ||
| droid(id: ID!): Droid | ||
| human(id: ID!): Human | ||
| scalars: Scalars | ||
| } | ||
|
|
||
| enum Episode { | ||
| NEWHOPE | ||
| EMPIRE | ||
| JEDI | ||
| } | ||
|
|
||
| interface Character { | ||
| id: ID! | ||
| name: String! | ||
| friends(first: Int): [Character] | ||
| appearsIn: [Episode]! | ||
| } | ||
|
|
||
| type Human implements Character { | ||
| id: ID! | ||
| name: String! | ||
| homePlanet: String | ||
| friends(first: Int): [Character] | ||
| appearsIn: [Episode]! | ||
| } | ||
|
|
||
| type Droid implements Character { | ||
| id: ID! | ||
| name: String! | ||
| friends(first: Int): [Character] | ||
| primaryFunction: String | ||
| appearsIn: [Episode]! | ||
| } | ||
|
|
||
| type Review { | ||
| episode: Episode | ||
| stars: Int! | ||
| commentary: String | ||
| } | ||
|
|
||
| union SearchResult = Human | Droid | ||
|
|
||
| type Scalars { | ||
| num: Int, | ||
| id: ID, | ||
| float: Float, | ||
| bool: Boolean, | ||
| string: String | ||
| test: Test, | ||
| } | ||
|
|
||
| type Test { | ||
| name: String, | ||
| variable: Scalars | ||
| } | ||
|
|
||
| * | ||
| * TODO: extend this schema to include mutations, subscriptions and pagination | ||
| * | ||
| type Mutation { | ||
| createReview(episode: Episode, review: ReviewInput!): Review | ||
| } | ||
| type Subscription { | ||
| reviewAdded(episode: Episode): Review | ||
| } | ||
| type FriendsConnection { | ||
| totalCount: Int | ||
| edges: [FriendsEdge] | ||
| friends: [Character] | ||
| pageInfo: PageInfo! | ||
| } | ||
| type FriendsEdge { | ||
| cursor: ID! | ||
| node: Character | ||
| } | ||
| type PageInfo { | ||
| startCursor: ID | ||
| endCursor: ID | ||
| hasNextPage: Boolean! | ||
| } | ||
|
|
||
| add | ||
| friendsConnection(first: Int, after: ID): FriendsConnection! | ||
| to character, human and droid | ||
| */ | ||
|
|
||
| // this object is created by the schema above for use in all the tests below | ||
| const typeWeights: TypeWeightObject = { | ||
| query: { | ||
| // object type | ||
| weight: 1, | ||
| fields: { | ||
| // FIXME: update the function def that is supposed te be here to match implementation | ||
| // FIXME: add the function definition for the 'search' field which returns a list | ||
| reviews: (arg, type) => arg * type.weight, | ||
| }, | ||
| }, | ||
| episode: { | ||
| // enum | ||
| weight: 0, | ||
| fields: {}, | ||
| }, | ||
| character: { | ||
| // interface | ||
| weight: 1, | ||
| fields: { | ||
| id: 0, | ||
| name: 0, | ||
| // FIXME: add the function definition for the 'friends' field which returns a list | ||
| }, | ||
| }, | ||
| human: { | ||
| // implements an interface | ||
| weight: 1, | ||
| fields: { | ||
| id: 0, | ||
| name: 0, | ||
| homePlanet: 0, | ||
| }, | ||
| }, | ||
| droid: { | ||
| // implements an interface | ||
| weight: 1, | ||
| fields: { | ||
| id: 0, | ||
| name: 0, | ||
| }, | ||
| }, | ||
| review: { | ||
| weight: 1, | ||
| fields: { | ||
| stars: 0, | ||
| commentary: 0, | ||
| }, | ||
| }, | ||
| searchResult: { | ||
| // union type | ||
| weight: 1, | ||
| fields: {}, | ||
| }, | ||
| scalars: { | ||
| weight: 1, // object weight is 1, all scalar feilds have weight 0 | ||
| fields: { | ||
| num: 0, | ||
| id: 0, | ||
| float: 0, | ||
| bool: 0, | ||
| string: 0, | ||
| }, | ||
| }, | ||
| test: { | ||
| weight: 1, | ||
| fields: { | ||
| name: 0, | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| xdescribe('Test getQueryTypeComplexity function', () => { | ||
| let query = ''; | ||
| describe('Calculates the correct type complexity for queries', () => { | ||
| test('with one feild', () => { | ||
| query = `Query { scalars { num } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(2); // Query 1 + Scalars 1 | ||
| }); | ||
|
|
||
| test('with two or more fields', () => { | ||
| query = `Query { scalars { num } test { name } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(3); // Query 1 + scalars 1 + test 1 | ||
| }); | ||
|
|
||
| test('with one level of nested fields', () => { | ||
| query = `Query { scalars { num, test { name } } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(3); // Query 1 + scalars 1 + test 1 | ||
| }); | ||
|
|
||
| test('with multiple levels of nesting', () => { | ||
| query = `Query { scalars { num, test { name, scalars { id } } } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(4); // Query 1 + scalars 1 + test 1 + scalars 1 | ||
| }); | ||
|
|
||
| test('with aliases', () => { | ||
| query = `Query { foo: scalar { num } bar: scalar { id }}`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(3); // Query 1 + scalar 1 + scalar 1 | ||
| }); | ||
|
|
||
| test('with all scalar fields', () => { | ||
| query = `Query { scalars { id, num, float, bool, string } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(2); // Query 1 + scalar 1 | ||
| }); | ||
|
|
||
| test('with arguments and variables', () => { | ||
| query = `Query { hero(episode: EMPIRE) { id, name } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(2); // Query 1 + hero/character 1 | ||
| query = `Query { human(id: 1) { id, name, appearsIn } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(3); // Query 1 + human/character 1 + appearsIn/episode | ||
| // argument passed in as a variable | ||
| query = `Query { hero(episode: $ep) { id, name } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(2); // Query 1 + hero/character 1 | ||
| }); | ||
|
|
||
| test('with fragments', () => { | ||
| query = ` | ||
| Query { | ||
| leftComparison: hero(episode: EMPIRE) { | ||
| ...comparisonFields | ||
| } | ||
| rightComparison: hero(episode: JEDI) { | ||
| ...comparisonFields | ||
| } | ||
| } | ||
|
|
||
| fragment comparisonFields on Character { | ||
| name | ||
| appearsIn | ||
| } | ||
| }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(5); // Query 1 + 2*(character 1 + appearsIn/episode 1) | ||
| }); | ||
|
|
||
| test('with inline fragments', () => { | ||
| query = ` | ||
| Query { | ||
| hero(episode: EMPIRE) { | ||
| name | ||
| ... on Droid { | ||
| primaryFunction | ||
| } | ||
| ... on Human { | ||
| homeplanet | ||
| } | ||
| } | ||
| }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(2); // Query 1 + hero/character 1) | ||
| }); | ||
|
|
||
| /** | ||
| * FIXME: handle lists of unknown size. change the expected result Once we figure out the implementation. | ||
| */ | ||
| xtest('with lists of unknown size', () => { | ||
| query = ` | ||
| Query { | ||
| search(text: 'hi') { | ||
| id | ||
| name | ||
| } | ||
| }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(false); // ? | ||
| }); | ||
|
|
||
| test('with lists detrmined by arguments', () => { | ||
| query = `Query {reviews(episode: EMPIRE, first: 3) { stars, commentary } }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(4); // 1 Query + 3 reviews | ||
| }); | ||
|
|
||
| test('with nested lists', () => { | ||
| query = ` | ||
| query { | ||
| human(id: 1) { | ||
| name, | ||
| friends(first: 5) { | ||
| name, | ||
| friends(first: 3){ | ||
| name | ||
| } | ||
| } | ||
| } | ||
| }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(17); // 1 Query + 1 human/character + (5 friends/character X 3 friends/characters) | ||
| }); | ||
|
|
||
| test('accounting for __typename feild', () => { | ||
| query = ` | ||
| query { | ||
| search(text: "an", first: 4) { | ||
| __typename | ||
| ... on Human { | ||
| name | ||
| homePlanet | ||
| } | ||
| ... on Droid { | ||
| name | ||
| primaryFunction | ||
| } | ||
| } | ||
| }`; | ||
| expect(getQueryTypeComplexity(query, typeWeights)).toBe(5); // 1 Query + 4 search results | ||
| }); | ||
|
|
||
| // todo: directives @skip, @include and custom directives | ||
|
|
||
| // todo: expand on error handling | ||
| test('Throws an error if for a bad query', () => { | ||
| query = `Query { hello { hi } }`; // type doesn't exist | ||
| expect(() => getQueryTypeComplexity(query, typeWeights)).toThrow('Error'); | ||
| query = `Query { hero(episode: EMPIRE){ starship } }`; // field doesn't exist | ||
| expect(() => getQueryTypeComplexity(query, typeWeights)).toThrow('Error'); | ||
| query = `Query { hero(episode: EMPIRE) { id, name }`; // missing a closing bracket | ||
| expect(() => getQueryTypeComplexity(query, typeWeights)).toThrow('Error'); | ||
| }); | ||
| }); | ||
|
|
||
| xdescribe('Calculates the correct type complexity for mutations', () => {}); | ||
|
|
||
| xdescribe('Calculates the correct type complexity for subscriptions', () => {}); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.