From 80cf7772a3dc2c5fcc624ed090c0002a8a4b091b Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Sat, 21 May 2022 21:11:50 -0400 Subject: [PATCH 1/9] added more tests to the buildTypeWeights function to check for more schema cases --- test/analysis/buildTypeWeights.test.ts | 345 ++++++++++++++++++++++++- 1 file changed, 336 insertions(+), 9 deletions(-) diff --git a/test/analysis/buildTypeWeights.test.ts b/test/analysis/buildTypeWeights.test.ts index 1463668..aaf1063 100644 --- a/test/analysis/buildTypeWeights.test.ts +++ b/test/analysis/buildTypeWeights.test.ts @@ -28,11 +28,14 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { test('multiple types', () => { schema = buildSchema(` + type Query { + user: User, + movie: Movie, + } type User { name: String email: String } - type Movie { name: String director: String @@ -40,6 +43,10 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { `); expect(buildTypeWeightsFromSchema(schema)).toEqual({ + Query: { + weight: 1, + fields: {}, + }, User: { weight: 1, fields: { @@ -63,12 +70,10 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { user: User movie: Movie } - type User { name: String - email: String + film: Movie } - type Movie { name: String director: User @@ -121,14 +126,183 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { }); }); + test('types with arguments', () => { + schema = buildSchema(` + type Query { + character(id: ID!): Character + } + type Character { + id: ID! + name: String! + }`); + expect(buildTypeWeightsFromSchema(schema)).toEqual({ + Query: { + weight: 1, + fields: {}, + }, + Character: { + weight: 1, + fields: { + id: 0, + name: 0, + }, + }, + }); + }); + + test('enum types', () => { + schema = buildSchema(` + type Query { + hero(episode: Episode): Character + } + type Character { + id: ID! + name: String! + } + enum Episode { + NEWHOPE + EMPIRE + JEDI + }`); + expect(buildTypeWeightsFromSchema(schema)).toEqual({ + Query: { + weight: 1, + fields: {}, + }, + Character: { + weight: 1, + fields: { + id: 0, + name: 0, + }, + }, + Episode: { + weight: 0, + fields: {}, + }, + }); + }); + + // ? varibale weight + test('fields returning lists of objects', () => { + schema = buildSchema(` + type Query { + reviews(episode: Episode!, first: Int): [Review] + } + type Review { + episode: Episode + stars: Int! + commentary: String + } + enum Episode { + NEWHOPE + EMPIRE + JEDI + }`); + expect(buildTypeWeightsFromSchema(schema)).toEqual({ + Query: { + weight: 1, + fields: {}, + }, + Review: { + weight: 1, // ? weight is the argument passed as 'first'. it's variable... + fields: { + id: 0, + name: 0, + }, + }, + Episode: { + weight: 0, + fields: {}, + }, + }); + }); + + test('interface types', () => { + schema = buildSchema(` + interface Character { + id: ID! + name: String! + friends: [Character] + } + + type Human implements Character { + id: ID! + name: String! + homePlanet: String + friends: [Character] + } + + type Droid implements Character { + id: ID! + name: String! + friends: [Character] + primaryFunction: String + }`); + expect(buildTypeWeightsFromSchema(schema)).toEqual({ + character: { + weight: 1, + fields: { + id: 0, + name: 0, + }, + }, + human: { + weight: 1, + fields: { + id: 0, + name: 0, + homePlanet: 0, + }, + }, + droid: { + weight: 1, + fields: { + id: 0, + name: 0, + primaryFunction: 0, + }, + }, + Episode: { + weight: 0, + fields: {}, + }, + }); + }); + + test('union tyes', () => { + schema = buildSchema(` + union SearchResult = Human | Droid + type Human{ + homePlanet: String + } + type Droid { + primaryFunction: String + }`); + expect(buildTypeWeightsFromSchema(schema)).toEqual({ + SearchResult: { + weight: 1, + fields: {}, + }, + human: { + weight: 1, + fields: { + homePlanet: 0, + }, + }, + droid: { + weight: 1, + fields: { + primaryFunction: 0, + }, + }, + }); + }); + // TODO: Tests should be written to acount for the additional scenarios possible in a schema // Mutation type + // Input types (a part of mutations?) // Subscription type - // List type - // Enem types - // Interface - // Unions - // Input types }); describe('changes "type weight object" type weights with user configuration of...', () => { @@ -251,3 +425,156 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { }); }); }); + +/** + * 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 + } + + enum Episode { + NEWHOPE + EMPIRE + JEDI + } + + interface Character { + id: ID! + name: String! + friends: [Character] + appearsIn: [Episode]! + } + + type Human implements Character { + id: ID! + name: String! + homePlanet: String + friends: [Character] + appearsIn: [Episode]! + } + + type Droid implements Character { + id: ID! + name: String! + friends: [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 + } + type Topic { + relatedTopics(first: Int): [Topic] + name: String + } + * + * 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 +*/ +const typeWeights: TypeWeightObject = { + query: { + // object type + weight: 1, + fields: {}, + }, + episode: { + // enum + weight: 0, + fields: {}, + }, + 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, + fields: { + num: 0, + id: 0, + float: 0, + bool: 0, + string: 0, + }, + }, + test: { + weight: 1, + fields: { + name: 0, + }, + }, +}; From 0ff813b160fa67a67bbcb754c91c4b7c1909a7b8 Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Sat, 21 May 2022 21:13:02 -0400 Subject: [PATCH 2/9] Removed tempalte schema code at the bottom of the file --- test/analysis/buildTypeWeights.test.ts | 153 ------------------------- 1 file changed, 153 deletions(-) diff --git a/test/analysis/buildTypeWeights.test.ts b/test/analysis/buildTypeWeights.test.ts index aaf1063..81d2bc8 100644 --- a/test/analysis/buildTypeWeights.test.ts +++ b/test/analysis/buildTypeWeights.test.ts @@ -425,156 +425,3 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { }); }); }); - -/** - * 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 - } - - enum Episode { - NEWHOPE - EMPIRE - JEDI - } - - interface Character { - id: ID! - name: String! - friends: [Character] - appearsIn: [Episode]! - } - - type Human implements Character { - id: ID! - name: String! - homePlanet: String - friends: [Character] - appearsIn: [Episode]! - } - - type Droid implements Character { - id: ID! - name: String! - friends: [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 - } - type Topic { - relatedTopics(first: Int): [Topic] - name: String - } - * - * 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 -*/ -const typeWeights: TypeWeightObject = { - query: { - // object type - weight: 1, - fields: {}, - }, - episode: { - // enum - weight: 0, - fields: {}, - }, - 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, - fields: { - num: 0, - id: 0, - float: 0, - bool: 0, - string: 0, - }, - }, - test: { - weight: 1, - fields: { - name: 0, - }, - }, -}; From 2fa2ad367ae9aff523c1ba47203f321c0e483159 Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Sun, 22 May 2022 07:20:47 -0400 Subject: [PATCH 3/9] wrote out all af the possible graphql types and how they relate to the typeWeightCOnfig object --- src/analysis/buildTypeWeights.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analysis/buildTypeWeights.ts b/src/analysis/buildTypeWeights.ts index 5b8beb3..3f24b74 100644 --- a/src/analysis/buildTypeWeights.ts +++ b/src/analysis/buildTypeWeights.ts @@ -15,10 +15,11 @@ import { GraphQLSchema } from 'graphql/type/schema'; function buildTypeWeightsFromSchema( schema: GraphQLSchema, typeWeightsConfig: TypeWeightConfig = { - mutation: 10, - object: 1, - scalar: 0, - connection: 2, + mutation: 10, // mutation + object: 1, // itnterfaces, unions, objects, query + scalar: 0, // enums, scalars + connection: 2, // pagination stuff + // ? subscription } ): TypeWeightObject { throw Error(`getTypeWeightsFromSchema is not implemented.`); From 2ab8c5cba818cba191576d9f6a920f65f10d4580 Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Mon, 23 May 2022 10:06:16 -0400 Subject: [PATCH 4/9] expanded on... something. Power went out and its 2 days later now. --- test/analysis/buildTypeWeights.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/analysis/buildTypeWeights.test.ts b/test/analysis/buildTypeWeights.test.ts index 81d2bc8..e96cce1 100644 --- a/test/analysis/buildTypeWeights.test.ts +++ b/test/analysis/buildTypeWeights.test.ts @@ -311,13 +311,13 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { beforeEach(() => { schema = buildSchema(` type Query { - user: User - movie: Movie + user(id: ID!): User + movie(id: ID!): Movie } type User { name: String - email: String + film: Movie } type Movie { @@ -337,7 +337,6 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { weight: 1, fields: { name: 0, - email: 0, }, }, Movie: { @@ -376,7 +375,6 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { }); expectedOutput.user.fields.name = 2; - expectedOutput.user.fields.email = 2; expectedOutput.movie.fields.name = 2; expect(typeWeightObject).toEqual({ expectedOutput }); From eb233565294656808cc5f02ba53cbc7388fb4678 Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Mon, 23 May 2022 11:03:01 -0400 Subject: [PATCH 5/9] added a test to check for error if schema is invalid --- test/analysis/buildTypeWeights.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/analysis/buildTypeWeights.test.ts b/test/analysis/buildTypeWeights.test.ts index e96cce1..19a12df 100644 --- a/test/analysis/buildTypeWeights.test.ts +++ b/test/analysis/buildTypeWeights.test.ts @@ -421,5 +421,8 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { 'negative' ); }); + + // TODO: throw validation error if schema is invalid + test('schema is invalid', () => {}); }); }); From 259da24e69b5fa280c9649a32972c6fd0b2aa002 Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Fri, 27 May 2022 08:28:29 -0400 Subject: [PATCH 6/9] added readonly properites to the typeWeightObject types --- src/@types/buildTypeWeights.d.ts | 8 ++++---- test/analysis/buildTypeWeights.test.ts | 14 +++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/@types/buildTypeWeights.d.ts b/src/@types/buildTypeWeights.d.ts index 8591cce..b160338 100644 --- a/src/@types/buildTypeWeights.d.ts +++ b/src/@types/buildTypeWeights.d.ts @@ -1,14 +1,14 @@ interface Fields { - [index: string]: number; + readonly [index: string]: number; } interface Type { - weight: number; - fields: Fields; + readonly weight: number; + readonly fields: Fields; } interface TypeWeightObject { - [index: string]: Type; + readonly [index: string]: Type; } interface TypeWeightConfig { diff --git a/test/analysis/buildTypeWeights.test.ts b/test/analysis/buildTypeWeights.test.ts index 19a12df..9377ddb 100644 --- a/test/analysis/buildTypeWeights.test.ts +++ b/test/analysis/buildTypeWeights.test.ts @@ -2,6 +2,18 @@ import { buildSchema } from 'graphql'; import { GraphQLSchema } from 'graphql/type/schema'; import buildTypeWeightsFromSchema from '../../src/analysis/buildTypeWeights'; +// these types allow the tests to overwite properties on the typeWeightObject +interface TestFields { + [index: string]: number; +} +interface TestType { + weight: number; + fields: TestFields; +} +interface TestTypeWeightObject { + [index: string]: TestType; +} + xdescribe('Test buildTypeWeightsFromSchema function', () => { let schema: GraphQLSchema; @@ -306,7 +318,7 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { }); describe('changes "type weight object" type weights with user configuration of...', () => { - let expectedOutput: TypeWeightObject; + let expectedOutput: TestTypeWeightObject; beforeEach(() => { schema = buildSchema(` From 4911724de3c565128e3cf79f33d470bc5465ec5d Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Fri, 27 May 2022 08:44:57 -0400 Subject: [PATCH 7/9] split the test for lists into two, one for lists with arguments and the other lists of unknown size. Also added the function into the query field for lists wit arguments --- src/@types/buildTypeWeights.d.ts | 2 +- test/analysis/buildTypeWeights.test.ts | 49 ++++++++++++++++++-------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/@types/buildTypeWeights.d.ts b/src/@types/buildTypeWeights.d.ts index b160338..cb1a9fe 100644 --- a/src/@types/buildTypeWeights.d.ts +++ b/src/@types/buildTypeWeights.d.ts @@ -1,5 +1,5 @@ interface Fields { - readonly [index: string]: number; + readonly [index: string]: number | ((arg: number, type: Type) => number); } interface Type { diff --git a/test/analysis/buildTypeWeights.test.ts b/test/analysis/buildTypeWeights.test.ts index 9377ddb..1f703e3 100644 --- a/test/analysis/buildTypeWeights.test.ts +++ b/test/analysis/buildTypeWeights.test.ts @@ -195,8 +195,7 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { }); }); - // ? varibale weight - test('fields returning lists of objects', () => { + test('fields returning lists of objects of determinate size', () => { schema = buildSchema(` type Query { reviews(episode: Episode!, first: Int): [Review] @@ -214,13 +213,16 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { expect(buildTypeWeightsFromSchema(schema)).toEqual({ Query: { weight: 1, - fields: {}, + fields: { + // ? we could maybe use a closure with the type already included to make this function more easily called + reviews: (arg: number, type: Type) => arg * type.weight, + }, }, Review: { - weight: 1, // ? weight is the argument passed as 'first'. it's variable... + weight: 1, fields: { - id: 0, - name: 0, + stars: 0, + commentary: 0, }, }, Episode: { @@ -230,36 +232,55 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { }); }); + // TODO: need to figure out how to handle this situation. Skip for now. + // The field friends returns a list of an unknown number of objects. + xtest('fields returning lists of objects of indetermitae size', () => { + schema = buildSchema(` + type Human { + id: ID! + name: String! + homePlanet: String + friends: [Human] + } + `); + expect(buildTypeWeightsFromSchema(schema)).toEqual({ + Human: { + weight: 1, + fields: { + // ? we could maybe use a closure with the type already included to make this function more easily called + friends: (arg: number, type: Type) => arg * type.weight, + }, + }, + }); + }); + test('interface types', () => { schema = buildSchema(` interface Character { id: ID! - name: String! - friends: [Character] + name: String! } type Human implements Character { id: ID! name: String! homePlanet: String - friends: [Character] } type Droid implements Character { id: ID! - name: String! - friends: [Character] + name: String! primaryFunction: String }`); expect(buildTypeWeightsFromSchema(schema)).toEqual({ - character: { + Character: { weight: 1, fields: { id: 0, name: 0, }, }, - human: { + Human: { weight: 1, fields: { id: 0, @@ -267,7 +288,7 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { homePlanet: 0, }, }, - droid: { + Droid: { weight: 1, fields: { id: 0, From 7202a25d17191a811aba2b67777ed6349b230935 Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Fri, 27 May 2022 18:17:38 -0400 Subject: [PATCH 8/9] addressing changes from PR review --- test/analysis/buildTypeWeights.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/analysis/buildTypeWeights.test.ts b/test/analysis/buildTypeWeights.test.ts index 1f703e3..5d8bc1a 100644 --- a/test/analysis/buildTypeWeights.test.ts +++ b/test/analysis/buildTypeWeights.test.ts @@ -214,8 +214,10 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { Query: { weight: 1, fields: { - // ? we could maybe use a closure with the type already included to make this function more easily called + // FIXME: check the best solution during implementation and update the tests here. + // we could maybe use a closure with the type already included to make this function more easily called reviews: (arg: number, type: Type) => arg * type.weight, + // code from PR review -> reviews: (type) => args[multiplierName] * typeWeightObject[type].weight }, }, Review: { @@ -247,7 +249,8 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { Human: { weight: 1, fields: { - // ? we could maybe use a closure with the type already included to make this function more easily called + // FIXME: check the best solution during implementation and update the tests here. + // we could maybe use a closure with the type already included to make this function more easily called friends: (arg: number, type: Type) => arg * type.weight, }, }, From 211fc2973286741d4616888aad20c5f85017188e Mon Sep 17 00:00:00 2001 From: "[Evan McNeely]" Date: Fri, 27 May 2022 19:03:47 -0400 Subject: [PATCH 9/9] removed some notes on implementation details. Not relevent in tests --- test/analysis/buildTypeWeights.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/analysis/buildTypeWeights.test.ts b/test/analysis/buildTypeWeights.test.ts index 5d8bc1a..5ded759 100644 --- a/test/analysis/buildTypeWeights.test.ts +++ b/test/analysis/buildTypeWeights.test.ts @@ -215,7 +215,6 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { weight: 1, fields: { // FIXME: check the best solution during implementation and update the tests here. - // we could maybe use a closure with the type already included to make this function more easily called reviews: (arg: number, type: Type) => arg * type.weight, // code from PR review -> reviews: (type) => args[multiplierName] * typeWeightObject[type].weight }, @@ -250,7 +249,6 @@ xdescribe('Test buildTypeWeightsFromSchema function', () => { weight: 1, fields: { // FIXME: check the best solution during implementation and update the tests here. - // we could maybe use a closure with the type already included to make this function more easily called friends: (arg: number, type: Type) => arg * type.weight, }, },