Skip to content

Commit

Permalink
chore(tests): extract and refactor query complexity suite
Browse files Browse the repository at this point in the history
  • Loading branch information
MichalLytek committed Feb 3, 2023
1 parent 090215f commit bc5aa9d
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 68 deletions.
148 changes: 148 additions & 0 deletions tests/functional/query-complexity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// tslint:disable:member-ordering
import "reflect-metadata";
import {
GraphQLSchema,
parse,
TypeInfo,
ValidationContext,
visit,
visitWithTypeInfo,
GraphQLError,
} from "graphql";
import { fieldExtensionsEstimator, getComplexity, simpleEstimator } from "graphql-query-complexity";

import {
ObjectType,
Field,
Resolver,
Query,
Arg,
buildSchema,
Subscription,
ClassType,
} from "../../src";
import { getMetadataStorage } from "../../src/metadata/getMetadataStorage";
import { getSchemaInfo } from "../helpers/getSchemaInfo";

// helpers
function calculateComplexityPoints(query: string, schema: GraphQLSchema) {
const complexityPoints = getComplexity({
query: parse(query),
schema,
estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
});
return complexityPoints;
}

describe("Query complexity", () => {
describe("Queries", () => {
let schema: GraphQLSchema;

beforeAll(async () => {
getMetadataStorage().clear();

@ObjectType()
class SampleObject {
@Field({ complexity: 10 })
complexResolverMethod: number;
}

@Resolver(of => SampleObject)
class SampleResolver {
@Query()
sampleQuery(): SampleObject {
const obj = new SampleObject();
return obj;
}
}

schema = await buildSchema({
resolvers: [SampleResolver],
validate: false,
});
});

it("should build the schema without errors", () => {
expect(schema).toBeDefined();
});

it("should properly calculate complexity points for a query with complex field resolver", () => {
const query = /* graphql */ `
query {
sampleQuery {
complexResolverMethod
}
}
`;
const points = calculateComplexityPoints(query, schema);

expect(points).toEqual(11);
});
});

describe("Subscriptions", () => {
let schema: GraphQLSchema;
let validationErrors: GraphQLError[];

beforeEach(() => {
validationErrors = [];
});

beforeAll(async () => {
getMetadataStorage().clear();

@ObjectType()
class SampleObject {
@Field()
normalField: string;
}

function createResolver(name: string, objectType: ClassType) {
@Resolver(of => objectType, { isAbstract: true })
class BaseResolver {
protected name = "baseName";

@Query({ name: `${name}Query` })
baseQuery(@Arg("arg") arg: boolean): boolean {
return true;
}

@Subscription({ topics: "baseTopic", name: `${name}Subscription` })
baseSubscription(@Arg("arg") arg: boolean): boolean {
return true;
}
}

return BaseResolver;
}

@Resolver()
class ChildResolver extends createResolver("prefix", SampleObject) {
@Subscription({ topics: "childTopic", complexity: 4 })
childSubscription(): boolean {
return true;
}
}

const schemaInfo = await getSchemaInfo({
resolvers: [ChildResolver],
});

schema = schemaInfo.schema;
});

it("should build schema correctly", async () => {
expect(schema).toBeDefined();
});

it("should properly calculate subscription complexity", () => {
const query = `subscription {
childSubscription
}`;

const points = calculateComplexityPoints(query, schema);

expect(points).toEqual(4);
});
});
});
68 changes: 0 additions & 68 deletions tests/functional/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,10 @@ import {
GraphQLSchema,
graphql,
TypeKind,
parse,
TypeInfo,
ValidationContext,
visit,
visitWithTypeInfo,
IntrospectionInputObjectType,
GraphQLError,
} from "graphql";
import * as path from "path";
import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity";
import ComplexityVisitor from "graphql-query-complexity/dist/cjs/QueryComplexity";

import {
ObjectType,
Expand Down Expand Up @@ -1222,27 +1215,6 @@ describe("Resolvers", () => {
};
}

// helpers
function generateAndVisitComplexMethod(maximumComplexity: number) {
const query = /* graphql */ `
query {
sampleQuery {
complexResolverMethod
}
}
`;
const ast = parse(query);
const typeInfo = new TypeInfo(schema);
const context = new ValidationContext(schema, ast, typeInfo, err =>
validationErrors.push(err),
);
const visitor = new ComplexityVisitor(context, {
maximumComplexity,
estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
});
visit(ast, visitWithTypeInfo(typeInfo, visitor));
}

let mutationInputValue: any;
beforeEach(() => {
queryRoot = undefined;
Expand Down Expand Up @@ -1643,27 +1615,6 @@ describe("Resolvers", () => {
expect(fieldResolverMethodResult).toBeLessThanOrEqual(1);
});

it("should fail when a query exceeds the max allowed complexity", () => {
generateAndVisitComplexMethod(5);
expect(validationErrors.length).toEqual(1);
expect(validationErrors[0].message).toEqual(
"The query exceeds the maximum complexity of 5. Actual complexity is 11",
);
});

it("should succeed when a query does not exceed the max allowed complexity", () => {
generateAndVisitComplexMethod(12);
expect(validationErrors.length).toEqual(0);
});

it("Complexity of a field should be overridden by complexity of a field resolver", () => {
generateAndVisitComplexMethod(9);
expect(validationErrors.length).toEqual(1);
expect(validationErrors[0].message).toEqual(
"The query exceeds the maximum complexity of 9. Actual complexity is 11",
);
});

it("should return value from field resolver arg", async () => {
const value = 21.37;
const query = `query {
Expand Down Expand Up @@ -2407,25 +2358,6 @@ describe("Resolvers", () => {
expect(subscriptionNames).toContain("overriddenSubscription");
expect(prefixSubscription.args).toHaveLength(1);
});
it("should fail when a subscription exceeds the max allowed complexity", () => {
const query = `subscription {
childSubscription
}`;
const ast = parse(query);
const typeInfo = new TypeInfo(schema);
const context = new ValidationContext(schema, ast, typeInfo, err =>
validationErrors.push(err),
);
const visitor = new ComplexityVisitor(context, {
maximumComplexity: 2,
estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
});
visit(ast, visitWithTypeInfo(typeInfo, visitor));
expect(validationErrors.length).toEqual(1);
expect(validationErrors[0].message).toEqual(
"The query exceeds the maximum complexity of 2. Actual complexity is 4",
);
});

it("should generate proper object fields in schema", async () => {
const sampleObjectType = schemaIntrospection.types.find(
Expand Down

0 comments on commit bc5aa9d

Please sign in to comment.