Skip to content

Commit

Permalink
E2E tests (#6779)
Browse files Browse the repository at this point in the history
* e2e setup

* prepare and setup

* cjs

* should start mesh

* improved utils

* available port and stuff

* use tsx cjs loader

* test compose and waitforexit reject

* fix compose

* e2e test ci

* chore(dependencies): updated changesets for modified dependencies

* sync getavailport

* e2e node matrix

* changeset

* serve script

* tenv and stuff

* tenv args and serve helper

* tenv only serve and compose

* esm config in cjs project

* simplify and use 0.0.0.0

* more details

* extend proc and less listners

* stderr for logs, stdout for outputs

* stable tenv std

* compose to stdout

* unnecessary serve script

* more wait for serve

* stable stderr

* compose to target

* simpler match for stability

* actually check stuff

* update snapshots

* snapshot file

* unnecessary port

* use 0.0.0.0

* unnecessary comment

* allow nodejs modules in e2e

* better args

* refactor and begin with type merging batching example

* increase timeout

* open example

* clarify

* link

* lol

* lol

* even more lol

* test plans

* WIP spawn detached and kill whole process group

* improve child process handling and use node with tsx for subgraphs

* stop reachability wait after exit

* append new line when logging to stderr

* nobuild e2e

* utils mkdir independant of fs

* type merging batching planning tests run concurrently

* execution tests

* check for aborted on retry

* touches

* fix(fusion/query-planner): skip the resolver if it has required variables that the parent subgraph doesn't have

* update snaps

* unnecessary assers

* listen to stderr

* subgraphs -> services

* service can be in <name>/index.ts

* thrift-calculator

* sqlite-chinook

* lockfile

* serveoptions

* ensure compose creates file

* tenv serve.execute

* just fusiongraph

* rest transport explicit type export

* openapi-javascript-wiki

* simpler doc

* tenv composition target is temp

* use target's absolute path if detected

* unused import

* increase reachability wait timeout even more

* try less workeser

* await available port making sure the server closes

* maxConcurr

* use __project

* wait for reachability longer

* disposable

* waitforexit is internal

* tenv containers and waiting adjustments

* mysql-employees

* mysql-employees no dates

* neo4j-example

* detectopenhandles

* args type leak from cjs-project

* auto-type-merging

* timeout e2e tests because of open handles

* batching-resolver

* federation-example

* unnecessary delay

* federation example servers

* soap-demo

* openapi-subscriptions

* no example queries

* revert prettierpath

* tfetch not necessary

* unnecessary deferstream plugin

* openapi v3 petstore

* mysql-rfam

* batching resolver simpler api service

* lockfile

* json-schema-subscriptions

* fusiongraph is not necessary

* json-schema-reddit

* neo4j uses serve and pubsub

* make sure the pubsub is destroyed

* mysql-rfam pubsub

* lockfile

* use ardatan/graphql-tools#6055

* chore(dependencies): updated changesets for modified dependencies

* specify endpoint for petstore

* auto-type-merging use container petstore

* use stable release of utils

* chore(dependencies): updated changesets for modified dependencies

* Fix federation example

* no skipping

* no dates in snaps

* Changeset

* update snap relating to a720512

* json-schema-reddit example

* use stable release of utils

* chore(dependencies): updated changesets for modified dependencies

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Arda TANRIKULU <[email protected]>
  • Loading branch information
3 people authored Apr 16, 2024
1 parent e196a82 commit 6399add
Show file tree
Hide file tree
Showing 296 changed files with 8,132 additions and 64,617 deletions.
6 changes: 6 additions & 0 deletions .changeset/@graphql-mesh_compose-cli-6779-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@graphql-mesh/compose-cli": patch
---
dependencies updates:
- Added dependency [`tsx@^4.7.1` ↗︎](https://www.npmjs.com/package/tsx/v/4.7.1) (to `dependencies`)
- Removed dependency [`ts-node@^10.9.2` ↗︎](https://www.npmjs.com/package/ts-node/v/10.9.2) (from `dependencies`)
5 changes: 5 additions & 0 deletions .changeset/@graphql-mesh_fusion-runtime-6779-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/fusion-runtime": patch
---
dependencies updates:
- Updated dependency [`@graphql-tools/utils@^10.1.3` ↗︎](https://www.npmjs.com/package/@graphql-tools/utils/v/10.1.3) (from `^10.1.0`, in `dependencies`)
6 changes: 6 additions & 0 deletions .changeset/@graphql-mesh_serve-cli-6779-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@graphql-mesh/serve-cli": patch
---
dependencies updates:
- Added dependency [`tsx@^4.7.1` ↗︎](https://www.npmjs.com/package/tsx/v/4.7.1) (to `dependencies`)
- Removed dependency [`ts-node@^10.9.2` ↗︎](https://www.npmjs.com/package/ts-node/v/10.9.2) (from `dependencies`)
5 changes: 5 additions & 0 deletions .changeset/@graphql-mesh_serve-runtime-6779-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/serve-runtime": patch
---
dependencies updates:
- Updated dependency [`@graphql-tools/utils@^10.1.3` ↗︎](https://www.npmjs.com/package/@graphql-tools/utils/v/10.1.3) (from `^10.0.8`, in `dependencies`)
5 changes: 5 additions & 0 deletions .changeset/gold-donkeys-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/utils": patch
---

fs operations types don't depend on Node
5 changes: 5 additions & 0 deletions .changeset/kind-pears-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/serve-cli": patch
---

Destroy pubsub on process kill signals
6 changes: 6 additions & 0 deletions .changeset/light-fishes-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@graphql-mesh/compose-cli": minor
"@graphql-mesh/serve-cli": minor
---

use tsx node loader instead of ts-node
5 changes: 5 additions & 0 deletions .changeset/moody-bugs-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/transport-rest": patch
---

Explicitly specify type for type exports
23 changes: 23 additions & 0 deletions .changeset/red-socks-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@graphql-mesh/fusion-execution": patch
---

Skip the resolver if it has required variables that the parent subgraph doesn't have

```graphql
type Foo
# There is only Foo_A_id, but no Foo_B_id or Foo_C_id
@variable(name: "Foo_A_id", select: "id", subgraph: "A")
# This resolver should be skipped
@resolver(operation: "query FooFromB($Foo_B_id: ID!) { foo(id: $Foo_B_id) }", subgraph: "B")
# This resolver should be used
@resolver(operation: "query FooFromB($Foo_A_id: ID!) { foo(id: $Foo_A_id) }", subgraph: "B")
# This resolver should be skipped
@resolver(operation: "query FooFromC($Foo_C_id: ID!) { foo(id: $Foo_C_id) }", subgraph: "C")
# This resolver should be used
@resolver(operation: "query FooFromC($Foo_A_id: ID!) { foo(id: $Foo_A_id) }", subgraph: "C") {
id: ID! @source(subgraph: "A")
bar: String! @source(subgraph: "B")
baz: String! @source(subgraph: "C")
}
```
6 changes: 6 additions & 0 deletions .changeset/rotten-ducks-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@graphql-mesh/fusion-federation": patch
---

- Use the subgraph names given by the user instead of uppercased ones generated by Rover CLI
- If a field that is a key in a different supergraph but is an external field, still consider it as a part of that subgraph for Fusion query planning
5 changes: 5 additions & 0 deletions .changeset/rude-planes-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/compose-cli": patch
---

fix writing to json target
5 changes: 5 additions & 0 deletions .changeset/slow-hairs-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/compose-cli": minor
---

write composed schema to stdout by default when no target is specified
5 changes: 5 additions & 0 deletions .changeset/spicy-pans-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/compose-cli": patch
---

Use target's absolute path if detected
5 changes: 5 additions & 0 deletions .changeset/twelve-melons-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-mesh/utils": minor
---

logs go to stderr, stdout is for program result or output
6 changes: 6 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
},
{
"files": ["e2e/**/*"],
"rules": {
"import/no-nodejs-modules": "off"
}
}
],
"ignorePatterns": [
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,22 @@ jobs:
run: yarn build
- name: Node.js integrity check
run: yarn bob check

e2e:
name: e2e / node ${{matrix.node-version}}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, 20, 21]
steps:
- name: checkout
uses: actions/checkout@v4
- uses: the-guild-org/shared-config/setup@main
name: setup env
with:
nodeVersion: ${{matrix.node-version}}
packageManagerVersion: modern
- name: test
timeout-minutes: 3
run: yarn test:e2e --detectOpenHandles
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ dist/
.bob/
.yarn
supergraph-invalid.graphql
__generated__/
270 changes: 270 additions & 0 deletions e2e/auto-type-merging/__snapshots__/auto-type-merging.test.ts.snap

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions e2e/auto-type-merging/auto-type-merging.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Container, createTenv } from '@e2e/tenv';

const { compose, service, serve, container } = createTenv(__dirname);

let petstore!: Container;
beforeAll(async () => {
petstore = await container({
name: 'petstore',
image: 'swaggerapi/petstore3:1.0.7',
containerPort: 8080,
healthcheck: ['CMD-SHELL', 'wget --spider http://0.0.0.0:8080'],
});
});

it('should compose the appropriate schema', async () => {
const { result } = await compose({
services: [petstore, await service('vaccination')],
maskServicePorts: true,
});
expect(result).toMatchSnapshot();
});

it.concurrent.each([
{
name: 'GetPet',
query: /* GraphQL */ `
query GetPet {
getPetById(petId: 1) {
__typename
id
name
vaccinated
}
}
`,
},
])('should execute $name', async ({ query }) => {
const { target } = await compose({
target: 'graphql',
services: [petstore, await service('vaccination')],
});
const { execute } = await serve({ fusiongraph: target });
await expect(execute({ query })).resolves.toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Args } from '@e2e/args';
import {
camelCase,
createFilterTransform,
Expand All @@ -6,23 +7,19 @@ import {
defineConfig as defineComposeConfig,
loadGraphQLHTTPSubgraph,
} from '@graphql-mesh/compose-cli';
/**
* The configuration to serve the supergraph
*/

import { defineConfig as defineServeConfig } from '@graphql-mesh/serve-cli';
import { useDeferStream } from '@graphql-yoga/plugin-defer-stream';
import { loadOpenAPISubgraph } from '@omnigraph/openapi';

/**
* The configuration to build a supergraph
*/
const args = Args(process.argv);

export const composeConfig = defineComposeConfig({
target: args.get('target'),
subgraphs: [
{
sourceHandler: loadOpenAPISubgraph('petstore', {
source: 'https://petstore.swagger.io/v2/swagger.json',
source: `http://0.0.0.0:${args.getServicePort('petstore')}/api/v3/openapi.json`,
// endpoint must be manually specified because the openapi.json spec doesn't contain one
endpoint: `http://0.0.0.0:${args.getServicePort('petstore')}/api/v3`,
}),
transforms: [
createFilterTransform({
Expand All @@ -33,7 +30,7 @@ export const composeConfig = defineComposeConfig({
},
{
sourceHandler: loadGraphQLHTTPSubgraph('vaccination', {
endpoint: 'http://localhost:4001/graphql',
endpoint: `http://0.0.0.0:${args.getServicePort('vaccination')}/graphql`,
}),
transforms: [
createPrefixTransform({
Expand All @@ -50,7 +47,8 @@ export const composeConfig = defineComposeConfig({
});

export const serveConfig = defineServeConfig({
fusiongraph: './fusiongraph.graphql',
port: args.getPort(),
fusiongraph: args.get('fusiongraph'),
graphiql: {
defaultQuery: /* GraphQL */ `
query Test {
Expand All @@ -63,5 +61,4 @@ export const serveConfig = defineServeConfig({
}
`,
},
plugins: () => [useDeferStream()],
});
11 changes: 11 additions & 0 deletions e2e/auto-type-merging/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@e2e/auto-type-merging",
"private": true,
"devDependencies": {
"@graphql-mesh/compose-cli": "workspace:*",
"@graphql-mesh/serve-cli": "workspace:*",
"@omnigraph/openapi": "workspace:*",
"graphql": "^16.8.1",
"graphql-yoga": "^5.1.1"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createServer } from 'http';
import { createSchema, createYoga } from 'graphql-yoga';
import { Args } from '@e2e/args';

export const vaccinationApi = createYoga({
export const yoga = createYoga({
schema: createSchema({
typeDefs: /* GraphQL */ `
scalar BigInt
Expand All @@ -16,14 +18,19 @@ export const vaccinationApi = createYoga({
`,
resolvers: {
Query: {
pet_by_id: async (root, args, context, info) => {
await new Promise(resolve => setTimeout(resolve, 1000));
pet_by_id: async (_root, args, _context, _info) => {
return {
id: args.id,
vaccinated: Math.random() > 0.5,
vaccinated: false,
};
},
},
},
}),
});

const port = Args(process.argv).getServicePort('vaccination', true);

createServer(yoga).listen(port, () => {
console.log(`Vaccination service listening on http://localhost:${port}`);
});
74 changes: 74 additions & 0 deletions e2e/batching-resolver/__snapshots__/batching-resolver.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should compose the appropriate schema 1`] = `
"schema @transport(subgraph: "API", kind: "rest", location: "http://0.0.0.0:<api_port>") {
query: Query
mutation: Mutation
}
directive @httpOperation(subgraph: String, path: String, operationSpecificHeaders: ObjMap, httpMethod: HTTPMethod, isBinary: Boolean, requestBaseBody: ObjMap, queryParamArgMap: ObjMap, queryStringOptionsByParam: ObjMap, jsonApiFields: Boolean) on FIELD_DEFINITION
directive @transport(subgraph: String, kind: String, location: String, headers: ObjMap, queryStringOptions: ObjMap, queryParams: ObjMap) on OBJECT
type Query {
dummy: String @resolver(subgraph: "API", operation: "query dummy { dummy }") @source(subgraph: "API", name: "dummy", type: "String")
user(id: Float!): User @resolver(subgraph: "API", kind: BATCH, operation: """
mutation UserBatch($id: [Float!]!) {
usersByIds(input: { ids: $id }) {
results
}
}
""")
}
type Mutation {
usersByIds(input: UsersByIdRequest_Input): UsersByIdResponse @httpOperation(subgraph: "API", path: "/users_by_ids", operationSpecificHeaders: "\\"{\\\\\\"Content-Type\\\\\\":\\\\\\"application/json\\\\\\",\\\\\\"accept\\\\\\":\\\\\\"application/json\\\\\\"}\\"", httpMethod: POST) @resolver(subgraph: "API", operation: "mutation mutationusersByIds($input: UsersByIdRequest_Input) { usersByIds(input: $input) }") @source(subgraph: "API", name: "usersByIds", type: "UsersByIdResponse")
}
type UsersByIdResponse @source(subgraph: "API", name: "UsersByIdResponse") {
results: [User]! @source(subgraph: "API", name: "results", type: "[User]!")
}
type User @source(subgraph: "API", name: "User") {
id: Float! @source(subgraph: "API", name: "id", type: "Float!")
name: String! @source(subgraph: "API", name: "name", type: "String!")
}
input UsersByIdRequest_Input @source(subgraph: "API", name: "UsersByIdRequest_Input") {
ids: [Float]! @source(subgraph: "API", name: "ids", type: "[Float]!")
}
scalar ObjMap @source(subgraph: "API", name: "ObjMap") @source(subgraph: "API", name: "ObjMap")
enum HTTPMethod @source(subgraph: "API", name: "HTTPMethod") {
GET @source(subgraph: "API", name: "GET")
HEAD @source(subgraph: "API", name: "HEAD")
POST @source(subgraph: "API", name: "POST")
PUT @source(subgraph: "API", name: "PUT")
DELETE @source(subgraph: "API", name: "DELETE")
CONNECT @source(subgraph: "API", name: "CONNECT")
OPTIONS @source(subgraph: "API", name: "OPTIONS")
TRACE @source(subgraph: "API", name: "TRACE")
PATCH @source(subgraph: "API", name: "PATCH")
}
"
`;
exports[`should execute User 1`] = `
{
"data": {
"jane": [
{
"id": 2,
"name": "Jane Doe",
},
],
"john": [
{
"id": 1,
"name": "John Doe",
},
],
},
}
`;
Loading

0 comments on commit 6399add

Please sign in to comment.