Skip to content

Commit 8aa1548

Browse files
committed
Merge branch 'master' into master-v2
# Conflicts: # core/src/test/kotlin/org/neo4j/graphql/TranslatorExceptionTests.kt # core/src/test/kotlin/org/neo4j/graphql/utils/AsciiDocTestSuite.kt # core/src/test/kotlin/org/neo4j/graphql/utils/CypherTestSuite.kt # core/src/test/kotlin/org/neo4j/graphql/utils/GraphQLSchemaTestSuite.kt
2 parents 12e96fc + 4e5fca7 commit 8aa1548

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+662
-512
lines changed

core/src/test/kotlin/org/neo4j/graphql/utils/AsciiDocTestSuite.kt

Lines changed: 164 additions & 265 deletions
Large diffs are not rendered by default.

core/src/test/kotlin/org/neo4j/graphql/utils/CypherTestSuite.kt

Lines changed: 127 additions & 131 deletions
Large diffs are not rendered by default.

core/src/test/kotlin/org/neo4j/graphql/utils/GraphQLSchemaTestSuite.kt

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.neo4j.graphql.utils
22

3+
import demo.org.neo4j.graphql.utils.asciidoc.ast.CodeBlock
4+
import demo.org.neo4j.graphql.utils.asciidoc.ast.Section
35
import graphql.language.InterfaceTypeDefinition
46
import graphql.language.UnionTypeDefinition
57
import graphql.schema.GraphQLScalarType
@@ -9,6 +11,7 @@ import graphql.schema.diff.SchemaDiffSet
911
import graphql.schema.diff.reporting.CapturingReporter
1012
import graphql.schema.idl.*
1113
import org.assertj.core.api.Assertions.assertThat
14+
import org.bouncycastle.asn1.x500.style.RFC4519Style.title
1215
import org.junit.jupiter.api.Assertions
1316
import org.junit.jupiter.api.Assumptions
1417
import org.junit.jupiter.api.DynamicNode
@@ -19,48 +22,61 @@ import org.neo4j.graphql.SchemaConfig
1922
import org.opentest4j.AssertionFailedError
2023
import java.util.*
2124

22-
class GraphQLSchemaTestSuite(fileName: String) : AsciiDocTestSuite(fileName, TEST_CASE_MARKERS) {
25+
class GraphQLSchemaTestSuite(fileName: String) : AsciiDocTestSuite<GraphQLSchemaTestSuite.TestCase>(
26+
fileName,
27+
listOf(
28+
matcher("graphql", exactly = true, setter = TestCase::augmentedSchema),
29+
)
30+
) {
2331

24-
override fun testFactory(
25-
title: String,
26-
globalBlocks: Map<String, List<ParsedBlock>>,
27-
codeBlocks: Map<String, List<ParsedBlock>>,
28-
ignore: Boolean
29-
): List<DynamicNode> {
30-
val targetSchemaBlock = codeBlocks[GRAPHQL_MARKER]?.first()
32+
data class TestCase(
33+
var schema: CodeBlock,
34+
var schemaConfig: CodeBlock?,
35+
var augmentedSchema: CodeBlock? = null,
36+
)
37+
38+
override fun createTestCase(section: Section): TestCase? {
39+
val schema = findSetupCodeBlocks(section, "graphql", mapOf("schema" to "true")).firstOrNull() ?: return null
40+
val schemaConfig = findSetupCodeBlocks(section, "json", mapOf("schema-config" to "true")).firstOrNull()
41+
return TestCase(schema, schemaConfig)
42+
}
43+
44+
override fun createTests(testCase: TestCase, section: Section, ignoreReason: String?): List<DynamicNode> {
45+
val targetSchemaBlock = testCase.augmentedSchema
3146
targetSchemaBlock?.let {
3247
try {
33-
it.reformattedCode = SCHEMA_PRINTER.print(createMockSchema(it.code()))
48+
it.reformattedContent = SCHEMA_PRINTER.print(createMockSchema(it.content))
3449
} catch (ignore: Exception) {
3550
}
3651
}
52+
if (targetSchemaBlock == null) {
53+
return emptyList()
54+
}
3755
val compareSchemaTest = DynamicTest.dynamicTest("compare schema", targetSchemaBlock?.uri) {
38-
val configBlock = codeBlocks[SCHEMA_CONFIG_MARKER]?.first()
39-
val config = configBlock?.code()?.let { MAPPER.readValue(it, SchemaConfig::class.java) } ?: SchemaConfig()
56+
val configBlock = testCase.schemaConfig
57+
val config = configBlock?.content?.let { MAPPER.readValue(it, SchemaConfig::class.java) } ?: SchemaConfig()
4058

41-
val targetSchema = targetSchemaBlock?.code()
42-
?: throw IllegalStateException("missing graphql for $title")
59+
val targetSchema = targetSchemaBlock.content
4360

4461
var augmentedSchema: GraphQLSchema? = null
4562
var expectedSchema: GraphQLSchema? = null
4663
try {
47-
val schema = globalBlocks[SCHEMA_MARKER]?.first()?.code()
48-
?: throw IllegalStateException("Schema should be defined")
64+
val schema = testCase.schema.content
4965
augmentedSchema = SchemaBuilder.buildSchema(schema, config, addLibraryDirectivesToSchema = false)
5066
expectedSchema = createMockSchema(targetSchema)
5167

5268
diff(expectedSchema, augmentedSchema)
5369
diff(augmentedSchema, expectedSchema)
54-
targetSchemaBlock.adjustedCode = SCHEMA_PRINTER.print(augmentedSchema)
70+
targetSchemaBlock.generatedContent = SCHEMA_PRINTER.print(augmentedSchema)
5571
} catch (e: Throwable) {
56-
if (ignore) {
57-
Assumptions.assumeFalse(true, e.message)
72+
if (ignoreReason != null) {
73+
Assumptions.assumeFalse(true) { "$ignoreReason ${e.message}" }
5874
} else {
5975
if (augmentedSchema == null) {
6076
Assertions.fail<Throwable>(e)
6177
}
6278
val actualSchema = SCHEMA_PRINTER.print(augmentedSchema)
63-
targetSchemaBlock.adjustedCode = actualSchema
79+
targetSchemaBlock.generatedContent = actualSchema
6480
throw AssertionFailedError("augmented schema differs for '$title'",
6581
expectedSchema?.let { SCHEMA_PRINTER.print(it) } ?: targetSchema,
6682
actualSchema,
@@ -72,6 +88,7 @@ class GraphQLSchemaTestSuite(fileName: String) : AsciiDocTestSuite(fileName, TES
7288
return Collections.singletonList(compareSchemaTest)
7389
}
7490

91+
7592
private fun createMockSchema(targetSchema: String): GraphQLSchema {
7693
val schemaParser = SchemaParser()
7794

@@ -97,8 +114,6 @@ class GraphQLSchemaTestSuite(fileName: String) : AsciiDocTestSuite(fileName, TES
97114
}
98115

99116
companion object {
100-
private const val GRAPHQL_MARKER = "[source,graphql]"
101-
private val TEST_CASE_MARKERS: List<String> = listOf(SCHEMA_CONFIG_MARKER, GRAPHQL_MARKER)
102117

103118
private val SCHEMA_PRINTER = SchemaPrinter(
104119
SchemaPrinter.Options.defaultOptions()
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package demo.org.neo4j.graphql.utils.asciidoc
2+
3+
import demo.org.neo4j.graphql.utils.asciidoc.ast.*
4+
import java.io.File
5+
import java.net.URI
6+
import java.util.regex.Pattern
7+
import javax.ws.rs.core.UriBuilder
8+
9+
class AsciiDocParser(
10+
fileName: String
11+
) {
12+
13+
private val file = File(AsciiDocParser::class.java.getResource("/$fileName")?.toURI()!!)
14+
private val srcLocation = File("src/test/resources/", fileName).toURI()
15+
16+
private var root = Document(srcLocation)
17+
private var currentSection: Section = root
18+
private var currentDepth: Int = 0
19+
20+
21+
fun parse(): Document {
22+
val lines = file.readLines()
23+
var title: String?
24+
25+
var insideCodeblock = false
26+
var offset = 0
27+
28+
val fileContent = StringBuilder()
29+
30+
root = Document(srcLocation)
31+
currentSection = root
32+
currentDepth = 0
33+
var caption: String? = null
34+
35+
var currentCodeBlock: CodeBlock? = null
36+
var content = StringBuilder()
37+
38+
39+
loop@ for ((lineNr, line) in lines.withIndex()) {
40+
fileContent.append(line).append('\n')
41+
42+
if (line.startsWith("#") || line.startsWith("//")) {
43+
offset += line.length + 1
44+
continue
45+
}
46+
47+
val headlineMatcher = HEADLINE_PATTERN.matcher(line)
48+
49+
when {
50+
51+
headlineMatcher.matches() -> {
52+
addBlock(content)
53+
val depth = headlineMatcher.group(1).length
54+
title = headlineMatcher.group(2)
55+
val uri = UriBuilder.fromUri(srcLocation).queryParam("line", lineNr + 1).build()
56+
startSection(title, uri, depth)
57+
}
58+
59+
line.startsWith(".") && !insideCodeblock -> {
60+
caption = line.substring(1).trim()
61+
}
62+
63+
line.startsWith("[source,") -> {
64+
addBlock(content)
65+
val uri = UriBuilder.fromUri(srcLocation).queryParam("line", lineNr + 1).build()
66+
67+
val parts = line.substring(8, line.indexOf("]")).trim().split(",")
68+
val language = parts[0]
69+
val attributes = parts.slice(1..<parts.size).map {
70+
val attributeParts = it.split("=")
71+
attributeParts[0] to attributeParts.getOrNull(1)
72+
}.toMap()
73+
74+
currentCodeBlock = CodeBlock(uri, language, currentSection, attributes).also {
75+
it.caption = caption
76+
currentSection.blocks.add(it)
77+
}
78+
caption = null
79+
}
80+
81+
line == "'''" -> {
82+
addBlock(content)
83+
currentSection.blocks.add(ThematicBreak())
84+
}
85+
86+
line == "----" -> {
87+
insideCodeblock = !insideCodeblock
88+
if (insideCodeblock) {
89+
currentCodeBlock?.start = offset + line.length + 1
90+
content = StringBuilder()
91+
} else if (currentCodeBlock != null) {
92+
currentCodeBlock.end = offset
93+
currentCodeBlock.content = content.toString().trim()
94+
currentCodeBlock = null
95+
content = StringBuilder()
96+
}
97+
}
98+
99+
else -> {
100+
content.append(line).append("\n")
101+
}
102+
}
103+
offset += line.length + 1 // +1 b/c of newline
104+
}
105+
addBlock(content)
106+
root.content = fileContent.toString()
107+
return root
108+
}
109+
110+
private fun addBlock(content: StringBuilder) {
111+
val str = content.toString()
112+
if (str.trim().isNotEmpty()) {
113+
currentSection.let { it.blocks.add(Block(it, str)) }
114+
}
115+
content.clear()
116+
}
117+
118+
private fun startSection(
119+
title: String, uri: URI, depth: Int
120+
) {
121+
122+
val parent = when {
123+
depth > currentDepth -> currentSection
124+
depth == currentDepth -> currentSection.parent
125+
else -> {
126+
var parent = currentSection.parent
127+
for (i in 0 until currentDepth - depth) {
128+
parent = parent?.parent
129+
}
130+
parent
131+
}
132+
} ?: error("cannot create sub-level on null")
133+
currentSection = Section(title, uri, parent)
134+
.also { parent.blocks.add(it) }
135+
136+
currentDepth = depth
137+
}
138+
139+
companion object {
140+
private val HEADLINE_PATTERN: Pattern = Pattern.compile("^(=+) (.*)$")
141+
}
142+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package demo.org.neo4j.graphql.utils.asciidoc.ast
2+
3+
class Block(
4+
parent: StructuralNode,
5+
val content: String
6+
) : StructuralNode(parent) {
7+
8+
override fun toString(): String {
9+
return "Block(content='$content')"
10+
}
11+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package demo.org.neo4j.graphql.utils.asciidoc.ast
2+
3+
import java.net.URI
4+
5+
class CodeBlock(
6+
val uri: URI,
7+
val language: String,
8+
override val parent: Section,
9+
val attributes: Map<String, String?>
10+
) : StructuralNode(parent) {
11+
12+
var caption: String? = null
13+
14+
var start: Int? = null
15+
var end: Int? = null
16+
17+
lateinit var content: String
18+
19+
/**
20+
* The content that was generated but diffs to the current content
21+
*/
22+
var generatedContent: String? = null
23+
24+
/**
25+
* The original content reformatted
26+
*/
27+
var reformattedContent: String? = null
28+
29+
var semanticEqual = false
30+
31+
/**
32+
* update only if other (tandem) is also updated
33+
*/
34+
var tandemUpdate: CodeBlock? = null
35+
36+
val marker: String
37+
get() = "[source,$language${attributes.map { ",${it.key}${it.value?.let { "=${it}" } ?: ""}" }.joinToString()}]"
38+
39+
override fun toString(): String {
40+
return "CodeBlock(language='$language', attributes=$attributes)"
41+
}
42+
43+
fun matches(language: String, filter: Map<String, String?> = emptyMap(), exactly: Boolean = false) =
44+
this.language == language && filter.all { (k, v) -> attributes[k] == v } && (!exactly || attributes.size == filter.size)
45+
46+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package demo.org.neo4j.graphql.utils.asciidoc.ast
2+
3+
import java.net.URI
4+
5+
class Document(
6+
uri: URI,
7+
) : Section(uri.path.substringAfterLast('/'), uri, null) {
8+
9+
lateinit var content: String
10+
11+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package demo.org.neo4j.graphql.utils.asciidoc.ast
2+
3+
import java.net.URI
4+
5+
open class Section(
6+
val title: String,
7+
val uri: URI,
8+
override val parent: Section?,
9+
) : StructuralNode(parent) {
10+
11+
override fun toString(): String {
12+
return "Section(title='$title')"
13+
}
14+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package demo.org.neo4j.graphql.utils.asciidoc.ast
2+
3+
sealed class StructuralNode(
4+
open val parent: StructuralNode?
5+
) {
6+
val blocks = mutableListOf<StructuralNode>()
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package demo.org.neo4j.graphql.utils.asciidoc.ast
2+
3+
class ThematicBreak: StructuralNode(null)

0 commit comments

Comments
 (0)