Skip to content

Spec conformance: Section 2 — Language #60

@craigsmitham

Description

@craigsmitham

Scope

Section 2 — Language. Covers source text, documents, operations, selection sets, fields, arguments, aliases, fragments, values, variables, type references, and directives. Subsections TBD during design.

Context

This is part of the phased spec conformance effort started in #57. Phase 1 (Section 5 — Validation) is complete and established conventions documented in test/GraphZen.SpecConformance.Tests/CLAUDE.md. Phase 1 patterns were designed around validation rules — this section covers parsing and language structure, which will likely need different infrastructure.

Stage 1 — Design

  • Read spec Section 2, extract all subsections and normative statements
  • Review graphql-js test coverage for language/parsing (~/Code/graphql/graphql-js/src/language/)
  • Determine test infrastructure needs (can we reuse SpecValidation? do we need a parsing harness?)
  • Determine class/folder structure and naming
  • Determine how implementation gaps should be expressed
  • Update test/GraphZen.SpecConformance.Tests/CLAUDE.md with patterns and infrastructure decisions for this section
  • Update this issue with the design below

Subsections

30 manifest entries, 30 conformance classes, 10 folders. Section numbers verified against ~/Code/graphql/graphql-spec/spec/Section 2 -- Language.md by counting ##/### headings.

Parent sections 2.1 (Source Text) and 2.10 (Input Values) are skipped — their children have individual entries, following Phase 1 precedent (e.g., 5.3 "Fields" had no entry, only 5.3.1/5.3.2/5.3.3). Section 2.9 (Fragments) gets its own entry because it defines grammar productions (FragmentSpread, FragmentDefinition, FragmentName) distinct from its children.

Section Heading Folder Class
2.1.1 White Space SourceText/ WhiteSpaceConformanceTests
2.1.2 Line Terminators SourceText/ LineTerminatorsConformanceTests
2.1.3 Comments SourceText/ CommentsConformanceTests
2.1.4 Insignificant Commas SourceText/ InsignificantCommasConformanceTests
2.1.5 Lexical Tokens SourceText/ LexicalTokensConformanceTests
2.1.6 Ignored Tokens SourceText/ IgnoredTokensConformanceTests
2.1.7 Punctuators SourceText/ PunctuatorsConformanceTests
2.1.8 Names SourceText/ NamesConformanceTests
2.2 Descriptions Documents/ DescriptionsConformanceTests
2.3 Document Documents/ DocumentConformanceTests
2.4 Operations Operations/ OperationsConformanceTests
2.5 Selection Sets Operations/ SelectionSetsConformanceTests
2.6 Fields Fields/ FieldsConformanceTests
2.7 Arguments Fields/ ArgumentsConformanceTests
2.8 Field Alias Fields/ FieldAliasConformanceTests
2.9 Fragments Fragments/ FragmentsConformanceTests
2.9.1 Type Conditions Fragments/ TypeConditionsConformanceTests
2.9.2 Inline Fragments Fragments/ InlineFragmentsConformanceTests
2.10.1 Int Value Values/ IntValueConformanceTests
2.10.2 Float Value Values/ FloatValueConformanceTests
2.10.3 Boolean Value Values/ BooleanValueConformanceTests
2.10.4 String Value Values/ StringValueConformanceTests
2.10.5 Null Value Values/ NullValueConformanceTests
2.10.6 Enum Value Values/ EnumValueConformanceTests
2.10.7 List Value Values/ ListValueConformanceTests
2.10.8 Input Object Values Values/ InputObjectValuesConformanceTests
2.11 Variables Variables/ VariablesConformanceTests
2.12 Type References TypeReferences/ TypeReferencesConformanceTests
2.13 Directives Directives/ DirectivesConformanceTests
2.14 Schema Coordinates SchemaCoordinates/ SchemaCoordinatesConformanceTests

Infrastructure

SpecParsing harness (Infrastructure/SpecParsing.cs)

New static class parallel to SpecValidation, tailored for parsing conformance. Key difference: Section 5 tests bind to a named validation rule; Section 2 tests exercise the parser directly.

public record ExpectedSyntaxError(string Message, int Line, int Column);

public class SyntaxErrorResult
{
    private readonly GraphQLServerError _error;

    public SyntaxErrorResult(GraphQLServerError error) => _error = error;

    public void ToEqual(ExpectedSyntaxError expected)
    {
        Assert.Equal(expected.Message, _error.Message);
        Assert.NotNull(_error.Locations);
        var location = _error.Locations![0];
        Assert.Equal(expected.Line, location.Line);
        Assert.Equal(expected.Column, location.Column);
    }

    public SyntaxErrorResult WithMessageContaining(string substring)
    {
        Assert.Contains(substring, _error.Message);
        return this;
    }

    public SyntaxErrorResult AtLocation(int line, int column)
    {
        Assert.NotNull(_error.Locations);
        var location = _error.Locations![0];
        Assert.Equal(line, location.Line);
        Assert.Equal(column, location.Column);
        return this;
    }
}

public static class SpecParsing
{
    public static DocumentSyntax ExpectValidSyntax(string source) =>
        Parser.ParseDocument(source);

    public static SyntaxErrorResult ExpectSyntaxError(string source)
    {
        var ex = Assert.Throws<GraphQLException>(() => Parser.ParseDocument(source));
        return new SyntaxErrorResult(ex.GraphQLError);
    }

    public static ValueSyntax ExpectValidValue(string source) =>
        Parser.ParseValue(source);

    public static TypeSyntax ExpectValidType(string source) =>
        Parser.ParseType(source);
}
Method Use when
ExpectValidSyntax(source) Source should parse as a valid document; returns DocumentSyntax for further assertions
ExpectSyntaxError(source) Source should fail to parse — chain with .ToEqual(...) or .WithMessageContaining(...).AtLocation(...)
ExpectValidValue(source) Source should parse as a valid value literal; returns ValueSyntax
ExpectValidType(source) Source should parse as a valid type reference; returns TypeSyntax

Error assertions: .ToEqual(new("message", Line: N, Column: M)) for exact match. .WithMessageContaining("substring").AtLocation(Line: N, Column: M) when message text is implementation-dependent (Superpower artifacts). The spec does not mandate error message wording.

Manifest (Infrastructure/SpecCoverageManifest.cs)

New LanguageSections property alongside existing ValidationSections:

public static IReadOnlyList<string> LanguageSections { get; } =
[
    "2.1.1", "2.1.2", "2.1.3", "2.1.4", "2.1.5", "2.1.6", "2.1.7", "2.1.8",
    "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8",
    "2.9", "2.9.1", "2.9.2",
    "2.10.1", "2.10.2", "2.10.3", "2.10.4", "2.10.5", "2.10.6", "2.10.7", "2.10.8",
    "2.11", "2.12", "2.13", "2.14",
];

Coverage test (Infrastructure/LanguageCoverageTests.cs)

Same reflection-based pattern as ValidationCoverageTests — verifies every LanguageSections entry has a corresponding [SpecSection] class.

graphql-js reference files

graphql-js test file Section 2 subsections covered
lexer-test.ts (23 tests) 2.1.1–2.1.8 (lexical grammar)
parser-test.ts (55 tests) 2.3–2.9, 2.11–2.13 (executable document syntax)
blockString-test.ts (3 tests) 2.10.4 (block string semantics)
printString-test.ts (8 tests) 2.10.4 (string escape sequences)

Open Questions (Resolved)

  1. Error message format. GraphZen uses Superpower error messages which differ from graphql-js. The spec doesn't mandate message text. Resolution: Assert error location precisely. Use ToEqual for semantically important messages; use WithMessageContaining + AtLocation for implementation-dependent wording. SyntaxErrorResult supports both patterns.

  2. Schema Coordinates (2.14). Self-contained grammar with its own lexer (no whitespace allowed). GraphZen has no ParseSchemaCoordinate method. Resolution: Create the conformance class with all tests skipped. Each test describes expected behavior. When ParseSchemaCoordinate is implemented, tests are ready.

  3. SpecSectionAttribute second parameter. Resolution: Continue Phase 1 convention — second parameter is the spec heading (e.g., [SpecSection("2.10.4", "String Value")]).

Stage 2 — Implementation

  • Add SpecParsing harness, ExpectedSyntaxError, and SyntaxErrorResult to Infrastructure/
  • Add LanguageSections to SpecCoverageManifest
  • Add LanguageCoverageTests
  • Add manifest entries (30 sections)
  • Create conformance classes (30 classes across 10 folders)
  • PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions