Skip to content

Commit db089f7

Browse files
committed
remove old files, rename new files in their place
1 parent aa1c81f commit db089f7

File tree

12 files changed

+367
-898
lines changed

12 files changed

+367
-898
lines changed
Lines changed: 101 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,111 @@
1-
import { graphql, GraphQLSchema } from 'graphql';
2-
import { convertFixtureToQuery } from '../utils/convert-fixture-to-query.js';
1+
import { visit, DocumentNode, Kind } from "graphql";
2+
import { TypeInfo, visitWithTypeInfo, coerceInputValue } from "graphql";
3+
import {
4+
isInputType,
5+
isListType,
6+
isNullableType,
7+
GraphQLSchema,
8+
getNullableType,
9+
isAbstractType,
10+
getNamedType,
11+
} from "graphql";
12+
import { inlineNamedFragmentSpreads } from "../utils/inline-named-fragment-spreads.js";
313

4-
/**
5-
* Interface for validation result
6-
*/
7-
export interface ValidationResult {
14+
export interface ValidateFixtureInputResult {
815
valid: boolean;
916
errors: string[];
10-
data: any;
11-
query: string | null;
1217
}
1318

14-
/**
15-
* Validate input fixture data using the original schema with Query root
16-
*
17-
* Since input fixture data represents the result of running an input query
18-
* against the schema, we can validate it by generating a query from the fixture
19-
* structure and executing it against the original schema directly.
20-
*
21-
* This approach is simpler than building reduced schemas because:
22-
* 1. The original schema already has a Query root type
23-
* 2. No need to convert input types to output types
24-
* 3. Uses the full schema context for validation
25-
* 4. Leverages existing resolvers if any
26-
*
27-
* @param {Object} inputFixtureData - The input fixture data to validate
28-
* @param {GraphQLSchema} originalSchema - The original GraphQL schema with Query root
29-
* @returns {Promise<Object>} Validation result with structure:
30-
* - valid: boolean - Whether the fixture data is valid
31-
* - errors: string[] - Array of error messages (empty if valid)
32-
* - data: Object|null - The resulting data from query execution
33-
* - query: string|null - The GraphQL query generated from fixture structure
34-
*/
19+
export function validateFixtureInput(
20+
queryAST: DocumentNode,
21+
schema: GraphQLSchema,
22+
value: any
23+
): ValidateFixtureInputResult {
24+
const inlineFragmentSpreadsAst = inlineNamedFragmentSpreads(queryAST);
25+
const typeInfo = new TypeInfo(schema);
26+
const valueStack: any[] = [[value]];
27+
const errors: string[] = [];
28+
visit(
29+
inlineFragmentSpreadsAst,
30+
visitWithTypeInfo(typeInfo, {
31+
Field: {
32+
enter(node) {
33+
const currentValues = valueStack[valueStack.length - 1];
34+
const nestedValues = [];
3535

36-
export async function validateFixtureInput(
37-
inputFixtureData: Record<string, any>,
38-
originalSchema: GraphQLSchema
39-
): Promise<ValidationResult> {
40-
try {
41-
// Step 1: Convert fixture data structure to a GraphQL query
42-
// The query directly matches the Input type structure (no field wrapper needed)
43-
const query = convertFixtureToQuery(inputFixtureData, '');
44-
45-
// Step 2: Execute the query against the original schema
46-
// The fixture data becomes the root value that resolvers will traverse
47-
const result = await graphql({
48-
schema: originalSchema,
49-
source: query,
50-
rootValue: inputFixtureData
51-
});
36+
const responseKey = node.alias?.value || node.name.value;
5237

53-
if (result.errors && result.errors.length > 0) {
54-
return {
55-
valid: false,
56-
errors: result.errors.map(err => err.message),
57-
data: result.data || null,
58-
query
59-
};
60-
}
38+
const fieldDefinition = typeInfo.getFieldDef();
39+
const fieldType = fieldDefinition?.type;
6140

62-
// If we successfully executed the query and got data back,
63-
// it means the fixture structure matches what the schema expects
64-
return {
65-
valid: true,
66-
errors: [],
67-
data: result.data,
68-
query
69-
};
41+
for (const currentValue of currentValues) {
42+
const valueForResponseKey = currentValue[responseKey];
7043

71-
} catch (error) {
72-
const errorMessage = error instanceof Error ? error.message : String(error);
73-
return {
74-
valid: false,
75-
errors: [`Input fixture validation failed: ${errorMessage}`],
76-
data: null,
77-
query: null
78-
};
79-
}
80-
}
44+
if (valueForResponseKey === undefined) {
45+
errors.push(`Missing expected fixture data for ${responseKey}`);
46+
continue;
47+
} else if (isInputType(fieldType)) {
48+
coerceInputValue(
49+
valueForResponseKey,
50+
fieldType,
51+
(path, _invalidValue, error) => {
52+
errors.push(`${error.message} At "${path.join(".")}"`);
53+
}
54+
);
55+
} else if (
56+
isNullableType(fieldType) &&
57+
valueForResponseKey === null
58+
) {
59+
continue;
60+
} else if (isListType(getNullableType(fieldType))) {
61+
if (Array.isArray(valueForResponseKey)) {
62+
nestedValues.push(...valueForResponseKey);
63+
} else {
64+
errors.push(
65+
`Expected array for ${responseKey}, but got ${typeof valueForResponseKey}`
66+
);
67+
}
68+
} else {
69+
if (typeof valueForResponseKey === "object") {
70+
nestedValues.push(valueForResponseKey);
71+
} else {
72+
errors.push(
73+
`Expected object for ${responseKey}, but got ${typeof valueForResponseKey}`
74+
);
75+
}
76+
}
77+
}
78+
79+
valueStack.push(nestedValues);
80+
},
81+
leave() {
82+
valueStack.pop();
83+
},
84+
},
85+
SelectionSet: {
86+
enter(node) {
87+
if (isAbstractType(getNamedType(typeInfo.getType()))) {
88+
const hasTypename = node.selections.some(
89+
(selection) =>
90+
selection.kind == Kind.FIELD &&
91+
selection.name.value == "__typename"
92+
);
8193

94+
const fragmentSpreadCount = node.selections.filter(
95+
(selection) =>
96+
selection.kind == Kind.FRAGMENT_SPREAD ||
97+
selection.kind == Kind.INLINE_FRAGMENT
98+
).length;
99+
100+
if (!hasTypename && fragmentSpreadCount > 1) {
101+
errors.push(
102+
`Missing __typename field for abstract type ${getNamedType(typeInfo.getType())?.name}`
103+
);
104+
}
105+
}
106+
},
107+
},
108+
})
109+
);
110+
return { valid: errors.length === 0, errors };
111+
}

src/methods/validate-fixture.ts

Lines changed: 0 additions & 111 deletions
This file was deleted.

src/methods/validate-input-query-fixture-match.ts

Lines changed: 0 additions & 88 deletions
This file was deleted.

0 commit comments

Comments
 (0)