Skip to content

Commit 14df9d0

Browse files
committed
chore(test): Add unit tests for more isolated testing
1 parent 9f0d630 commit 14df9d0

File tree

7 files changed

+728
-2
lines changed

7 files changed

+728
-2
lines changed

plugin/src/main/kotlin/io/github/androa/gradle/plugin/avro/GenerateAvroTask.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.github.androa.gradle.plugin.avro
33
import io.github.androa.gradle.plugin.avro.compiler.AvroCompiler
44
import io.github.androa.gradle.plugin.avro.compiler.CompilerOptions
55
import io.github.androa.gradle.plugin.avro.compiler.OptionalGettersType
6+
import io.github.androa.gradle.plugin.avro.idl.IdlTransformer
67
import org.apache.avro.compiler.specific.SpecificCompiler
78
import org.apache.avro.generic.GenericData
89
import org.gradle.api.DefaultTask

plugin/src/main/kotlin/io/github/androa/gradle/plugin/avro/IdlTransformer.kt renamed to plugin/src/main/kotlin/io/github/androa/gradle/plugin/avro/idl/IdlTransformer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.androa.gradle.plugin.avro
1+
package io.github.androa.gradle.plugin.avro.idl
22

33
import org.apache.avro.idl.IdlReader
44
import java.io.File
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package io.github.androa.gradle.plugin.avro
2+
3+
import org.gradle.api.Project
4+
import org.gradle.api.Task
5+
import org.gradle.api.tasks.SourceSetContainer
6+
import org.gradle.testfixtures.ProjectBuilder
7+
import org.junit.jupiter.api.Assertions.assertEquals
8+
import org.junit.jupiter.api.Assertions.assertFalse
9+
import org.junit.jupiter.api.Assertions.assertNotNull
10+
import org.junit.jupiter.api.Assertions.assertTrue
11+
import org.junit.jupiter.api.BeforeEach
12+
import org.junit.jupiter.api.Test
13+
import org.junit.jupiter.api.io.TempDir
14+
import java.nio.file.Path
15+
16+
class AvroPluginTest {
17+
@field:TempDir
18+
lateinit var tempDir: Path
19+
20+
private lateinit var project: Project
21+
22+
@BeforeEach
23+
fun setup() {
24+
project = ProjectBuilder.builder().withProjectDir(tempDir.toFile()).build()
25+
}
26+
27+
@Test
28+
fun `plugin registers generateAvro task`() {
29+
// Apply the plugin
30+
project.plugins.apply(AvroPlugin::class.java)
31+
32+
// Verify task is created
33+
val task = project.tasks.findByName("generateAvro")
34+
assertNotNull(task, "Plugin should register 'generateAvro' task")
35+
assertTrue(task is GenerateAvroTask, "Task should be a GenerateAvroTask")
36+
}
37+
38+
@Test
39+
fun `plugin creates extension with default values`() {
40+
// Apply the plugin
41+
project.plugins.apply(AvroPlugin::class.java)
42+
43+
// Get the extension
44+
val extension = project.extensions.findByName("generateAvro")
45+
assertNotNull(extension, "Plugin should create 'generateAvro' extension")
46+
assertTrue(extension is AvroExtension, "Extension should be an AvroExtension")
47+
48+
// Cast to AvroExtension
49+
val avroExtension = extension as AvroExtension
50+
51+
// Verify default values
52+
assertEquals("UTF-8", avroExtension.encoding.get())
53+
assertFalse(avroExtension.stringType.get())
54+
assertFalse(avroExtension.noSetters.get())
55+
assertFalse(avroExtension.addNullSafeAnnotations.get())
56+
assertFalse(avroExtension.addExtraOptionalGetters.get())
57+
assertFalse(avroExtension.useBigDecimal.get())
58+
}
59+
60+
@Test
61+
fun `plugin configures task from extension values`() {
62+
// Apply the plugin
63+
project.plugins.apply(AvroPlugin::class.java)
64+
65+
// Configure the extension
66+
val extension = project.extensions.getByType(AvroExtension::class.java)
67+
extension.stringType.set(true)
68+
extension.noSetters.set(true)
69+
70+
// Get the task
71+
val task = project.tasks.getByName("generateAvro") as GenerateAvroTask
72+
73+
// Verify task properties are populated from extension
74+
assertTrue(task.stringType.get(), "Task stringType should match extension value")
75+
assertTrue(task.noSetters.get(), "Task noSetters should match extension value")
76+
}
77+
78+
@Test
79+
fun `plugin adds task to task group`() {
80+
// Apply the plugin
81+
project.plugins.apply(AvroPlugin::class.java)
82+
83+
// Get the task
84+
val task = project.tasks.getByName("generateAvro")
85+
86+
// Verify the task exists and is configured correctly
87+
assertNotNull(task, "Task should exist")
88+
assertTrue(task is GenerateAvroTask, "Task should be a GenerateAvroTask")
89+
}
90+
91+
@Test
92+
fun `plugin adds generated source directory to source set`() {
93+
// Apply the java plugin
94+
project.plugins.apply("java")
95+
96+
// Apply the avro plugin
97+
project.plugins.apply(AvroPlugin::class.java)
98+
99+
// Get the task
100+
val task = project.tasks.getByName("generateAvro") as GenerateAvroTask
101+
102+
// Get source sets
103+
val sourceSets = project.extensions.getByType(SourceSetContainer::class.java)
104+
val mainSourceSet = sourceSets.getByName("main")
105+
106+
// Verify source set includes task output
107+
val javaSrcDirs = mainSourceSet.java.srcDirs
108+
assertTrue(
109+
javaSrcDirs.contains(task.outputDir.get().asFile),
110+
"Main source set should include Avro output directory",
111+
)
112+
}
113+
114+
@Test
115+
fun `plugin works without applying java plugin`() {
116+
// Apply only the avro plugin (without java plugin)
117+
project.plugins.apply(AvroPlugin::class.java)
118+
119+
// This should not throw an exception
120+
val task = project.tasks.getByName("generateAvro")
121+
assertNotNull(task, "Task should be created even without java plugin")
122+
}
123+
124+
/**
125+
* Helper method to get task dependency names
126+
*/
127+
private fun getTaskDependencyNames(task: Task): Set<String> {
128+
// This is a bit hacky but works for testing
129+
val method = task.javaClass.methods.find { it.name == "getDependsOn" }
130+
val dependsOn = method?.invoke(task) as? Set<*> ?: emptySet<Any>()
131+
132+
return dependsOn
133+
.mapNotNull {
134+
when (it) {
135+
is Task -> it.name
136+
is String -> it
137+
else -> null
138+
}
139+
}.toSet()
140+
}
141+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package io.github.androa.gradle.plugin.avro
2+
3+
import io.github.androa.gradle.plugin.avro.compiler.AvroCompiler
4+
import io.github.androa.gradle.plugin.avro.compiler.CompilerOptions
5+
import org.junit.jupiter.api.Assertions.assertEquals
6+
import org.junit.jupiter.api.Assertions.assertThrows
7+
import org.junit.jupiter.api.Assertions.assertTrue
8+
import org.junit.jupiter.api.Test
9+
import org.junit.jupiter.api.io.TempDir
10+
import java.nio.file.Path
11+
12+
class ErrorHandlingTest {
13+
@field:TempDir
14+
lateinit var tempDir: Path
15+
16+
@Test
17+
fun `compiler handles malformed schema files gracefully`() {
18+
// Setup
19+
val compiler = AvroCompiler()
20+
val outputDir = tempDir.resolve("output").toFile().apply { mkdirs() }
21+
22+
// Create a malformed schema file
23+
val malformedSchema = tempDir.resolve("malformed.avsc").toFile()
24+
malformedSchema.writeText(
25+
"""
26+
{
27+
"namespace": "com.example",
28+
"type": "record",
29+
"name": "Broken",
30+
"fields": [
31+
{ "name": "id", "type": "int" },
32+
{ "name": "invalid" } // Missing type
33+
]
34+
}
35+
""".trimIndent(),
36+
)
37+
38+
// Execute and verify exception is thrown with appropriate message
39+
val exception =
40+
assertThrows(Exception::class.java) {
41+
compiler.compileSchema(CompilerOptions(), setOf(malformedSchema), outputDir)
42+
}
43+
44+
// Verify error message contains helpful information
45+
assertTrue(
46+
exception.message?.contains("malformed") == true ||
47+
exception.message?.contains("schema") == true ||
48+
exception.message?.contains("parse") == true ||
49+
exception.message?.contains("missing") == true ||
50+
exception.message?.contains("type") == true,
51+
"Exception message should indicate schema parsing issue",
52+
)
53+
}
54+
55+
@Test
56+
fun `compiler handles non-existent input directory gracefully`() {
57+
// Setup
58+
val compiler = AvroCompiler()
59+
val nonExistentDir = tempDir.resolve("nonexistent").toFile()
60+
val outputDir = tempDir.resolve("output").toFile().apply { mkdirs() }
61+
62+
// Execute
63+
compiler.compileSchema(CompilerOptions(), setOf(nonExistentDir), outputDir)
64+
65+
// Verify - should not throw exception, and output dir should be empty
66+
val outputFiles = outputDir.listFiles() ?: emptyArray()
67+
assertEquals(0, outputFiles.size, "No output should be generated with empty input")
68+
}
69+
70+
@Test
71+
fun `task handles empty input directory`() {
72+
// Setup task
73+
val project =
74+
org.gradle.testfixtures.ProjectBuilder
75+
.builder()
76+
.withProjectDir(tempDir.toFile())
77+
.build()
78+
val task = project.tasks.create("generateAvro", GenerateAvroTask::class.java)
79+
80+
// Create an empty directory
81+
val emptyDir = tempDir.resolve("empty").toFile().apply { mkdirs() }
82+
val outputDir = tempDir.resolve("output").toFile().apply { mkdirs() }
83+
84+
// Configure task
85+
task.schemas.setFrom(emptyDir)
86+
task.outputDir.set(outputDir)
87+
task.intermediateDir.set(tempDir.resolve("intermediate").toFile())
88+
89+
// Execute - should not throw exception
90+
task.generate()
91+
92+
// Verify output dir exists but is empty
93+
assertTrue(outputDir.exists(), "Output directory should exist")
94+
}
95+
96+
@Test
97+
fun `generate task handles malformed avpr file gracefully`() {
98+
// Setup task
99+
val project =
100+
org.gradle.testfixtures.ProjectBuilder
101+
.builder()
102+
.withProjectDir(tempDir.toFile())
103+
.build()
104+
val task = project.tasks.create("generateAvro", GenerateAvroTask::class.java)
105+
106+
// Create a malformed protocol file
107+
val malformedProtocol = tempDir.resolve("malformed.avpr").toFile()
108+
malformedProtocol.writeText(
109+
"""
110+
{
111+
"protocol": "BrokenService",
112+
"namespace": "com.example",
113+
"types": [
114+
{
115+
"type": "record",
116+
"name": "Broken",
117+
"fields": [
118+
{ "name": "id", "type": "string" },
119+
{ "name": "data" } // Missing type
120+
]
121+
}
122+
]
123+
}
124+
""".trimIndent(),
125+
)
126+
127+
// Configure task
128+
task.schemas.setFrom(malformedProtocol)
129+
task.outputDir.set(tempDir.resolve("output").toFile())
130+
task.intermediateDir.set(tempDir.resolve("intermediate").toFile())
131+
132+
// Execute and verify
133+
val exception =
134+
assertThrows(Exception::class.java) {
135+
task.generate()
136+
}
137+
138+
// Verify error message is helpful
139+
assertTrue(
140+
exception.message?.contains("malformed") == true ||
141+
exception.message?.contains("protocol") == true ||
142+
exception.message?.contains("parse") == true ||
143+
exception.message?.contains("missing") == true ||
144+
exception.message?.contains("type") == true,
145+
"Exception message should indicate protocol parsing issue",
146+
)
147+
}
148+
}

0 commit comments

Comments
 (0)