Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ build/
.gradle/
.kotlin/
.idea/
.planning/
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.avsystem.justworks.core.gen

import com.avsystem.justworks.core.parser.ParseResult
import com.avsystem.justworks.core.parser.SpecParser
import java.io.File
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.fail

/**
* Integration test that validates the full generation pipeline against real-world
* OpenAPI specs. This ensures that enum generation, ApiClientBase, and client code
* work correctly with production-grade specs.
*/
class IntegrationTest {
private val modelPackage = "com.example.model"
private val apiPackage = "com.example.api"

companion object {
private val SPEC_FIXTURES = listOf(
"/fixtures/platform-api.json",
"/fixtures/analytics-api.json",
)
}

private fun parseSpec(resourcePath: String): ParseResult.Success {
val specUrl = javaClass.getResource(resourcePath)
?: fail("Spec fixture not found: $resourcePath")
val specFile = File(specUrl.toURI())
return when (val result = SpecParser.parse(specFile)) {
is ParseResult.Success -> result
is ParseResult.Failure -> fail("Failed to parse $resourcePath: ${result.errors}")
}
}

@Test
fun `real-world specs generate compilable enum code without class body conflicts`() {
for (fixture in SPEC_FIXTURES) {
val spec = parseSpec(fixture).apiSpec
if (spec.enums.isEmpty()) continue

val generator = ModelGenerator(modelPackage)
val files = generator.generate(spec)
assertTrue(files.isNotEmpty(), "$fixture: ModelGenerator should produce output files")

val enumSources = files
.map { it.toString() }
.filter { it.contains("enum class") }

assertTrue(enumSources.isNotEmpty(), "$fixture: Should generate at least one enum class")

for (source in enumSources) {
assertFalse(
source.contains(Regex("""[A-Z_]+\(\) \{""")),
"$fixture: Enum constants should not have anonymous class body",
)
assertFalse(
source.contains(Regex("""\b[A-Z][A-Z_0-9]+ \{""")),
"$fixture: Enum constants should not have class body",
)
assertTrue(
source.contains("@SerialName"),
"$fixture: Enum source should contain @SerialName annotations",
)
assertTrue(
source.contains("@Serializable"),
"$fixture: Enum source should contain @Serializable annotation",
)
}
}
}

@Test
fun `real-world specs generate ApiClientBase when endpoints exist`() {
for (fixture in SPEC_FIXTURES) {
val spec = parseSpec(fixture).apiSpec
if (spec.endpoints.isEmpty()) continue

val apiClientBaseFile = ApiClientBaseGenerator.generate()
assertNotNull(apiClientBaseFile, "$fixture: ApiClientBaseGenerator should produce output")

val source = apiClientBaseFile.toString()
assertTrue(
source.contains("ApiClientBase"),
"$fixture: Generated output should contain ApiClientBase class",
)
assertTrue(
source.contains("abstract class ApiClientBase"),
"$fixture: ApiClientBase should be an abstract class",
)
}
}

@Test
fun `real-world specs full pipeline generates client code without exceptions`() {
for (fixture in SPEC_FIXTURES) {
val spec = parseSpec(fixture).apiSpec

val modelGenerator = ModelGenerator(modelPackage)
val modelFiles = modelGenerator.generate(spec)
assertTrue(modelFiles.isNotEmpty(), "$fixture: ModelGenerator should produce files")

if (spec.endpoints.isNotEmpty()) {
val clientGenerator = ClientGenerator(apiPackage, modelPackage)
val clientFiles = clientGenerator.generate(spec)
assertTrue(
clientFiles.isNotEmpty(),
"$fixture: ClientGenerator should produce files for a spec with endpoints",
)
}

val apiClientBaseFile = ApiClientBaseGenerator.generate()
assertNotNull(apiClientBaseFile, "$fixture: ApiClientBaseGenerator should produce output")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.avsystem.justworks.core.model.TypeRef
import com.squareup.kotlinpoet.KModifier
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

Expand Down Expand Up @@ -269,6 +270,27 @@ class ModelGeneratorTest {
assertEquals(listOf("AVAILABLE", "PENDING", "SOLD"), names)
}

@Test
fun `enum constants do not produce anonymous class body`() {
val files = generator.generate(spec(enums = listOf(statusEnum)))
val source = files.first().toString()
// Assert no class body braces on enum constants
assertFalse(
source.contains(Regex("""[A-Z_]+\(\) \{""")),
"Enum constants should not have anonymous class body: $source",
)
assertFalse(
source.contains(Regex("""[A-Z_]+ \{""")),
"Enum constants should not have class body: $source",
)
// Assert @SerialName present
assertTrue(source.contains("""@SerialName("available")"""), "Missing @SerialName for available: $source")
assertTrue(source.contains("""@SerialName("pending")"""), "Missing @SerialName for pending: $source")
assertTrue(source.contains("""@SerialName("sold")"""), "Missing @SerialName for sold: $source")
// Assert @Serializable on enum class
assertTrue(source.contains("@Serializable"), "Missing @Serializable on enum class: $source")
}

@Test
fun `integer enum values have SerialName with numeric string`() {
val intEnum =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ class TypeMappingTest {
assertEquals("com.example.model.Pet_Address", result.toString())
}

// -- SCHM-07: Instant mapping verification --

@Test
fun `Instant maps to kotlin-time package`() {
assertEquals("kotlin.time", INSTANT.packageName)
assertEquals("Instant", INSTANT.simpleName)
}

// -- Unknown --

@Test
Expand Down
Loading
Loading