|
| 1 | +/** |
| 2 | + * This class is meant as basic functionality/integration test to find obvious errors in the generated |
| 3 | + * resolvers, schema, or backend code. Basically, it's originally meant as something of a functionality + sanity check. |
| 4 | + * |
| 5 | + * What it does: |
| 6 | + * For each GraphQL type, a random object input object is created. Nested input objects are created to a configurable |
| 7 | + * depth. After level 3, only required fields are included in the generated inputs. For every created object, the |
| 8 | + * object is retrieved with all possible subfields down to a configurable level based on its ID. Then the all fields |
| 9 | + * are updated in the same way as an object is created. Finally, for all types, a list objects is retrieved with all |
| 10 | + * possible subfields down to a configurable level. A simple equality filter is added for the ID of the type. |
| 11 | + * |
| 12 | + * |
| 13 | + * What id does NOT do: |
| 14 | + * - Does not verify that the returned object matches the expected result. (TODO) |
| 15 | + * - Executes no queries over annotated edges (TODO) |
| 16 | + * - Executes no mutations to annotate edges (TODO). |
| 17 | + * - ... a lot of other things probably. |
| 18 | + */ |
| 19 | + |
| 20 | +const { InMemoryCache } = require('apollo-cache-inmemory') |
| 21 | +const { ApolloClient } = require('apollo-client'); |
| 22 | +const { HttpLink } = require('apollo-link-http'); |
| 23 | +const gql = require('graphql-tag'); |
| 24 | +const { introspectSchema } = require('graphql-tools'); |
| 25 | +const fetch = require('node-fetch'); |
| 26 | +const { isObjectType } = require('graphql'); |
| 27 | +const tools = require('./generation-tools.js'); |
| 28 | + |
| 29 | +/** |
| 30 | + * The transaction tests requires the database to be empty prior to testing. |
| 31 | + */ |
| 32 | +async function transactionTest() { |
| 33 | + // connect |
| 34 | + let uri = 'http://localhost:4000'; |
| 35 | + let {client, schema} = await connect(uri); |
| 36 | + |
| 37 | + // attempt to create objects but trigger an exception |
| 38 | + |
| 39 | + // verify that not |
| 40 | +} |
| 41 | + |
| 42 | +async function run() { |
| 43 | + // connect client to server |
| 44 | + let uri = 'http://localhost:4000'; |
| 45 | + let {client, schema} = await connect(uri); |
| 46 | + |
| 47 | + // iterate type schema |
| 48 | + for(let i in schema._typeMap){ |
| 49 | + let t = schema._typeMap[i]; |
| 50 | + if(!isObjectType(t) || i == 'Query' || i == 'Mutation' || i.startsWith('_') || i.includes('EdgeFrom')) continue; |
| 51 | + |
| 52 | + // mutations to create |
| 53 | + let inputToCreate = tools.makeInputToCreate(t, schema, 6, true); |
| 54 | + let createArg = tools.jsonToGraphQL(inputToCreate); |
| 55 | + let create = ` |
| 56 | + mutation { |
| 57 | + create${t.name}(data:${createArg}) { |
| 58 | + id |
| 59 | + } |
| 60 | + } |
| 61 | + `; |
| 62 | + console.log(`Mutations:\tcreate${t.name}`); |
| 63 | + const m1 = await client.mutate({ mutation: gql`${create}` }); |
| 64 | + if(m1.errors){ |
| 65 | + console.error(m1.errors); |
| 66 | + } |
| 67 | + |
| 68 | + // query to get by ID |
| 69 | + let subfields = tools.getSubFields(t, 3); |
| 70 | + let id = m1.data[`create${t.name}`].id; |
| 71 | + let n = `${t.name[0].toLowerCase() + t.name.substr(1)}`; |
| 72 | + let get = ` |
| 73 | + query { |
| 74 | + ${n}(id: "${id}") |
| 75 | + ${subfields} |
| 76 | + } |
| 77 | + `; |
| 78 | + console.log(`Query:\t\t${t.name[0].toLowerCase() + t.name.substr(1)} (ID=${id})`); |
| 79 | + const q1 = await client.query({ query: gql`${get}` }); |
| 80 | + if(q1.errors){ |
| 81 | + console.error(q1.errors); |
| 82 | + } |
| 83 | + |
| 84 | + // update fields, create a new object and write over the values |
| 85 | + let inputToUpdate = tools.makeInputToCreate(t, schema, 7, true); |
| 86 | + let updateArg = tools.jsonToGraphQL(inputToUpdate); |
| 87 | + let mutation2 = ` |
| 88 | + mutation { |
| 89 | + update${t.name}(id: "${id}", data:${updateArg}) { |
| 90 | + id |
| 91 | + } |
| 92 | + } |
| 93 | + `; |
| 94 | + console.log(`Mutation:\tupdate${t.name} (id=${id})`); |
| 95 | + const m2 = await client.mutate({mutation: gql`${mutation2}`}); |
| 96 | + if (m2.errors) { |
| 97 | + console.error(m2.errors); |
| 98 | + } |
| 99 | + |
| 100 | + // query list of type |
| 101 | + subfields = tools.getSubFields(t, 3); |
| 102 | + let getList = ` |
| 103 | + query { |
| 104 | + listOf${t.name}s(first: 7, after: "", filter: { id: { _neq: "" } }) { |
| 105 | + isEndOfWholeList |
| 106 | + totalCount |
| 107 | + content |
| 108 | + ${subfields} |
| 109 | + } |
| 110 | + } |
| 111 | + `; |
| 112 | + const q2 = await client.query({ query: gql`${getList}` }); |
| 113 | + let totalCount = q2.data[`listOf${t.name}s`].totalCount; |
| 114 | + console.log(`Query:\t\tlistOf${t.name}s ${totalCount}`); |
| 115 | + if(q2.errors){ |
| 116 | + console.error(q2.errors); |
| 117 | + } |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +async function connect(uri){ |
| 122 | + const httpLink = new HttpLink({ uri: uri, fetch }); |
| 123 | + const client = new ApolloClient({ link: httpLink, cache: new InMemoryCache() }); |
| 124 | + const schema = await introspectSchema(httpLink); // parse remote schema |
| 125 | + return { client: client, schema: schema }; |
| 126 | +} |
| 127 | + |
| 128 | +run().then(() => { |
| 129 | + console.log("Client tests passed."); |
| 130 | + let exitAfterClientTests = process.env.EXIT_AFTER_CLIENT_TESTS === 'true'; |
| 131 | + if(exitAfterClientTests) process.exit(0); |
| 132 | +}).catch(reason => { |
| 133 | + let exitAfterClientTests = process.env.EXIT_AFTER_CLIENT_TESTS === 'true'; |
| 134 | + // Not the nicest way to exit, but it works for testing. |
| 135 | + console.error(reason); |
| 136 | + console.error("Client tests did NOT pass."); |
| 137 | + if(exitAfterClientTests) process.exit(1); |
| 138 | +}); |
0 commit comments