From f1d30c9b065eaf01572788cd4aa7a7b3bdfa1591 Mon Sep 17 00:00:00 2001 From: Jong Date: Thu, 20 Nov 2025 14:53:21 +0900 Subject: [PATCH 01/10] add: WispClassName object to manage class names --- .../src/main/java/com/angrypodo/wisp/WispClassName.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 wisp-processor/src/main/java/com/angrypodo/wisp/WispClassName.kt diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/WispClassName.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/WispClassName.kt new file mode 100644 index 0000000..d5586bf --- /dev/null +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/WispClassName.kt @@ -0,0 +1,10 @@ +package com.angrypodo.wisp + +import com.squareup.kotlinpoet.ClassName + +internal object WispClassName { + private const val RUNTIME_PACKAGE = "com.angrypodo.wisp.runtime" + const val GENERATED_PACKAGE = "com.angrypodo.wisp.generated" + + val ROUTE_FACTORY = ClassName(RUNTIME_PACKAGE, "RouteFactory") +} From b4dd15963dca9b3556cb041acc3631c100995ac3 Mon Sep 17 00:00:00 2001 From: Jong Date: Thu, 20 Nov 2025 15:08:14 +0900 Subject: [PATCH 02/10] feat: WispRegistry object that holds a map of route paths to their corresponding route factory class names --- .../wisp/generator/WispRegistryGenerator.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 wisp-processor/src/main/java/com/angrypodo/wisp/generator/WispRegistryGenerator.kt diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/generator/WispRegistryGenerator.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/generator/WispRegistryGenerator.kt new file mode 100644 index 0000000..8f5d64d --- /dev/null +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/generator/WispRegistryGenerator.kt @@ -0,0 +1,45 @@ +package com.angrypodo.wisp.generator + +import com.angrypodo.wisp.WispClassName.GENERATED_PACKAGE +import com.angrypodo.wisp.WispClassName.ROUTE_FACTORY +import com.angrypodo.wisp.model.RouteInfo +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.MAP +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.STRING +import com.squareup.kotlinpoet.TypeSpec + +internal object WispRegistryGenerator { + private const val REGISTRY_NAME = "WispRegistry" + + fun generate(routes: List): FileSpec { + val mapType = MAP.parameterizedBy(STRING, ROUTE_FACTORY) + + val initializerBlock = CodeBlock.builder() + .add("mapOf(\n") + .indent() + + routes.forEach { route -> + initializerBlock.add("%S to %T,\n", route.wispPath, route.factoryClassName) + } + + initializerBlock.unindent().add(")") + + val factoriesProperty = PropertySpec.builder("factories", mapType) + .addModifiers(KModifier.INTERNAL) + .initializer(initializerBlock.build()) + .build() + + val registryObject = TypeSpec.objectBuilder(REGISTRY_NAME) + .addModifiers(KModifier.INTERNAL) + .addProperty(factoriesProperty) + .build() + + return FileSpec.builder(GENERATED_PACKAGE, REGISTRY_NAME) + .addType(registryObject) + .build() + } +} From 3309ab9aa8d610be7bf5c713109e0063570dfcd3 Mon Sep 17 00:00:00 2001 From: Jong Date: Thu, 20 Nov 2025 15:09:03 +0900 Subject: [PATCH 03/10] chore: uses ROUTE_FACTORY constant in RouteFactoryGenerator class --- .../com/angrypodo/wisp/generator/RouteFactoryGenerator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/generator/RouteFactoryGenerator.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/generator/RouteFactoryGenerator.kt index 45d803c..dfd4548 100644 --- a/wisp-processor/src/main/java/com/angrypodo/wisp/generator/RouteFactoryGenerator.kt +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/generator/RouteFactoryGenerator.kt @@ -1,5 +1,6 @@ package com.angrypodo.wisp.generator +import com.angrypodo.wisp.WispClassName.ROUTE_FACTORY import com.angrypodo.wisp.model.ClassRouteInfo import com.angrypodo.wisp.model.ObjectRouteInfo import com.angrypodo.wisp.model.ParameterInfo @@ -25,7 +26,6 @@ internal class RouteFactoryGenerator( private val logger: KSPLogger ) { - private val routeFactoryInterface = ClassName("com.angrypodo.wisp.runtime", "RouteFactory") private val missingParameterError = ClassName( "com.angrypodo.wisp.runtime", "WispError", @@ -47,7 +47,7 @@ internal class RouteFactoryGenerator( val factoryObject = TypeSpec.objectBuilder(routeInfo.factoryClassName) .addModifiers(KModifier.INTERNAL) - .addSuperinterface(routeFactoryInterface) + .addSuperinterface(ROUTE_FACTORY) .addFunction(createFun) .build() From 1d2227df931f42882ad753beb11632a2a9aba3ac Mon Sep 17 00:00:00 2001 From: Jong Date: Thu, 20 Nov 2025 15:30:34 +0900 Subject: [PATCH 04/10] feat: generate WispRegistry and validates routes --- .../java/com/angrypodo/wisp/WispProcessor.kt | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt index e1bfa48..ba1341a 100644 --- a/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt @@ -2,6 +2,7 @@ package com.angrypodo.wisp import com.angrypodo.wisp.annotations.Wisp import com.angrypodo.wisp.generator.RouteFactoryGenerator +import com.angrypodo.wisp.generator.WispRegistryGenerator import com.angrypodo.wisp.mapper.toRouteInfo import com.angrypodo.wisp.model.RouteInfo import com.google.devtools.ksp.processing.CodeGenerator @@ -11,6 +12,7 @@ import com.google.devtools.ksp.processing.Resolver import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSFile import com.google.devtools.ksp.validate import com.squareup.kotlinpoet.ksp.writeTo @@ -32,22 +34,51 @@ internal class WispProcessor( val (processableSymbols, deferredSymbols) = symbols.partition { it.validate() } - processableSymbols.forEach { processSymbol(it) } + val routesWithSymbols = processableSymbols.mapNotNull { routeClass -> + val routeInfo = processSymbol(routeClass) ?: return@mapNotNull null + routeInfo to routeClass + } + + if (!validateDuplicatePaths(routesWithSymbols)) return deferredSymbols + + if (routesWithSymbols.isNotEmpty()) { + val routeInfos = routesWithSymbols.map { it.first } + val sourceFiles = routesWithSymbols.mapNotNull { it.second.containingFile }.distinct() + + generateRouteRegistry(routeInfos, sourceFiles) + } return deferredSymbols } - private fun processSymbol(routeClass: KSClassDeclaration) { - if (!validateSerializable(routeClass)) { - return - } + private fun processSymbol(routeClass: KSClassDeclaration): RouteInfo? { + if (!validateSerializable(routeClass)) return null val routeInfo = routeClass.toRouteInfo() ?: run { logInvalidRouteError(routeClass) - return + return null } generateRouteFactory(routeClass, routeInfo) + + return routeInfo + } + + private fun validateDuplicatePaths(routesWithSymbols: List>): Boolean { + val duplicates = routesWithSymbols.groupBy { it.first.wispPath } + .filter { it.value.size > 1 } + + if (duplicates.isEmpty()) return true + + duplicates.forEach { (path, pairs) -> + pairs.forEach { (_, symbol) -> + logger.error( + "Wisp Error: The path '$path' is already used by another route.", + symbol + ) + } + } + return false } private fun validateSerializable(routeClass: KSClassDeclaration): Boolean { @@ -77,6 +108,15 @@ internal class WispProcessor( fileSpec.writeTo(codeGenerator, dependencies) } + private fun generateRouteRegistry( + routeInfos: List, + sourceFiles: List + ) { + val fileSpec = WispRegistryGenerator.generate(routeInfos) + val dependencies = Dependencies(true, *sourceFiles.toTypedArray()) + fileSpec.writeTo(codeGenerator, dependencies) + } + private fun KSClassDeclaration.hasSerializableAnnotation(): Boolean { return annotations.any { annotation -> val shortName = annotation.shortName.asString() From 4b9ce908083139920c0196bf97c992cd5ca34d4c Mon Sep 17 00:00:00 2001 From: Jong Date: Thu, 20 Nov 2025 15:37:46 +0900 Subject: [PATCH 05/10] add: RegistryGenerator tests --- .../wisp/generator/RegistryGeneratorTest.kt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 wisp-processor/src/test/java/com/angrypodo/wisp/generator/RegistryGeneratorTest.kt diff --git a/wisp-processor/src/test/java/com/angrypodo/wisp/generator/RegistryGeneratorTest.kt b/wisp-processor/src/test/java/com/angrypodo/wisp/generator/RegistryGeneratorTest.kt new file mode 100644 index 0000000..604bcdc --- /dev/null +++ b/wisp-processor/src/test/java/com/angrypodo/wisp/generator/RegistryGeneratorTest.kt @@ -0,0 +1,47 @@ +package com.angrypodo.wisp.generator + +import com.angrypodo.wisp.model.ClassRouteInfo +import com.angrypodo.wisp.model.ObjectRouteInfo +import com.squareup.kotlinpoet.ClassName +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +internal class RegistryGeneratorTest { + + @Test + @DisplayName("RouteInfo를 받아 WispRegistry 오브젝트와 맵을 생성한다") + fun `generate_registry_with_multiple_routes`() { + // Given: RouteInfo 데이터 2개 + val homeRoute = ObjectRouteInfo( + routeClassName = ClassName("com.example", "Home"), + factoryClassName = ClassName("com.example", "HomeRouteFactory"), + wispPath = "home" + ) + + val profileRoute = ClassRouteInfo( + routeClassName = ClassName("com.example", "Profile"), + factoryClassName = ClassName("com.example", "ProfileRouteFactory"), + wispPath = "profile/{id}", + parameters = emptyList() + ) + + val routes = listOf(homeRoute, profileRoute) + + // When: 코드 생성 실행 + val fileSpec = WispRegistryGenerator.generate(routes) + val generatedCode = fileSpec.toString() + + println(generatedCode) + + // Then: 생성된 WispRegistry 객체를 반환 + assertTrue(generatedCode.contains("object WispRegistry")) + assertTrue(generatedCode.contains("val factories: Map = mapOf(")) + + assertTrue(generatedCode.contains("import com.example.HomeRouteFactory")) + assertTrue(generatedCode.contains("\"home\" to HomeRouteFactory")) + + assertTrue(generatedCode.contains("import com.example.ProfileRouteFactory")) + assertTrue(generatedCode.contains("\"profile/{id}\" to ProfileRouteFactory")) + } +} From 7a7e464f721a727b0b365b9a77e5ada957dfa831 Mon Sep 17 00:00:00 2001 From: Jong Date: Fri, 21 Nov 2025 12:20:26 +0900 Subject: [PATCH 06/10] fix: lint format --- .../src/main/java/com/angrypodo/wisp/WispProcessor.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt index ba1341a..a7ad7f2 100644 --- a/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt @@ -64,7 +64,9 @@ internal class WispProcessor( return routeInfo } - private fun validateDuplicatePaths(routesWithSymbols: List>): Boolean { + private fun validateDuplicatePaths( + routesWithSymbols: List> + ): Boolean { val duplicates = routesWithSymbols.groupBy { it.first.wispPath } .filter { it.value.size > 1 } From 488f8bcbc6fe0fc6903549582df7e42453a45713 Mon Sep 17 00:00:00 2001 From: Jong Date: Sat, 22 Nov 2025 00:47:03 +0900 Subject: [PATCH 07/10] refactor: centralize error type definitions --- .../java/com/angrypodo/wisp/WispClassName.kt | 3 +++ .../wisp/generator/RouteFactoryGenerator.kt | 19 ++++--------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/WispClassName.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/WispClassName.kt index d5586bf..2d0ceae 100644 --- a/wisp-processor/src/main/java/com/angrypodo/wisp/WispClassName.kt +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/WispClassName.kt @@ -7,4 +7,7 @@ internal object WispClassName { const val GENERATED_PACKAGE = "com.angrypodo.wisp.generated" val ROUTE_FACTORY = ClassName(RUNTIME_PACKAGE, "RouteFactory") + + val MISSING_PARAMETER_ERROR = ClassName(RUNTIME_PACKAGE, "WispError", "MissingParameter") + val INVALID_PARAMETER_ERROR = ClassName(RUNTIME_PACKAGE, "WispError", "InvalidParameter") } diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/generator/RouteFactoryGenerator.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/generator/RouteFactoryGenerator.kt index dfd4548..2b717e6 100644 --- a/wisp-processor/src/main/java/com/angrypodo/wisp/generator/RouteFactoryGenerator.kt +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/generator/RouteFactoryGenerator.kt @@ -1,5 +1,7 @@ package com.angrypodo.wisp.generator +import com.angrypodo.wisp.WispClassName.INVALID_PARAMETER_ERROR +import com.angrypodo.wisp.WispClassName.MISSING_PARAMETER_ERROR import com.angrypodo.wisp.WispClassName.ROUTE_FACTORY import com.angrypodo.wisp.model.ClassRouteInfo import com.angrypodo.wisp.model.ObjectRouteInfo @@ -8,7 +10,6 @@ import com.angrypodo.wisp.model.RouteInfo import com.google.devtools.ksp.processing.KSPLogger import com.squareup.kotlinpoet.ANY import com.squareup.kotlinpoet.BOOLEAN -import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.DOUBLE import com.squareup.kotlinpoet.FLOAT @@ -25,18 +26,6 @@ import com.squareup.kotlinpoet.TypeSpec internal class RouteFactoryGenerator( private val logger: KSPLogger ) { - - private val missingParameterError = ClassName( - "com.angrypodo.wisp.runtime", - "WispError", - "MissingParameter" - ) - private val invalidParameterError = ClassName( - "com.angrypodo.wisp.runtime", - "WispError", - "InvalidParameter" - ) - fun generate(routeInfo: RouteInfo): FileSpec { val createFun = FunSpec.builder("create") .addModifiers(KModifier.OVERRIDE) @@ -87,8 +76,8 @@ internal class RouteFactoryGenerator( val nonNullableType = param.typeName.copy(nullable = false) val errorType = when (nonNullableType) { - STRING -> missingParameterError - else -> invalidParameterError + STRING -> MISSING_PARAMETER_ERROR + else -> INVALID_PARAMETER_ERROR } return CodeBlock.of( From c49a4932500f1730ae548e35311d995a470810a2 Mon Sep 17 00:00:00 2001 From: Jong Date: Sat, 22 Nov 2025 01:27:28 +0900 Subject: [PATCH 08/10] add: registry lookup function --- .../wisp/generator/WispRegistryGenerator.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/generator/WispRegistryGenerator.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/generator/WispRegistryGenerator.kt index 8f5d64d..d36979e 100644 --- a/wisp-processor/src/main/java/com/angrypodo/wisp/generator/WispRegistryGenerator.kt +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/generator/WispRegistryGenerator.kt @@ -5,6 +5,7 @@ import com.angrypodo.wisp.WispClassName.ROUTE_FACTORY import com.angrypodo.wisp.model.RouteInfo import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.MAP import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy @@ -14,6 +15,8 @@ import com.squareup.kotlinpoet.TypeSpec internal object WispRegistryGenerator { private const val REGISTRY_NAME = "WispRegistry" + private const val FACTORIES_PROPERTY_NAME = "factories" + private const val GET_FACTORY_FUN_NAME = "getRouteFactory" fun generate(routes: List): FileSpec { val mapType = MAP.parameterizedBy(STRING, ROUTE_FACTORY) @@ -28,14 +31,22 @@ internal object WispRegistryGenerator { initializerBlock.unindent().add(")") - val factoriesProperty = PropertySpec.builder("factories", mapType) - .addModifiers(KModifier.INTERNAL) + val factoriesProperty = PropertySpec.builder(FACTORIES_PROPERTY_NAME, mapType) + .addModifiers(KModifier.PRIVATE) .initializer(initializerBlock.build()) .build() + val getFactoryFun = FunSpec.builder(GET_FACTORY_FUN_NAME) + .addModifiers(KModifier.INTERNAL) + .addParameter("path", STRING) + .returns(ROUTE_FACTORY.copy(nullable = true)) + .addStatement("return %N[path]", factoriesProperty) + .build() + val registryObject = TypeSpec.objectBuilder(REGISTRY_NAME) .addModifiers(KModifier.INTERNAL) .addProperty(factoriesProperty) + .addFunction(getFactoryFun) .build() return FileSpec.builder(GENERATED_PACKAGE, REGISTRY_NAME) From efd128ea993adbcb141c18aa791923cca52fdbca Mon Sep 17 00:00:00 2001 From: Jong Date: Sat, 22 Nov 2025 22:48:13 +0900 Subject: [PATCH 09/10] refactor: move duplicate path validation logic to validator --- .../java/com/angrypodo/wisp/WispProcessor.kt | 31 ++++++------------- .../java/com/angrypodo/wisp/WispValidator.kt | 24 ++++++++++++-- .../com/angrypodo/wisp/WispValidatorTest.kt | 2 +- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt index a7ad7f2..0bc4cc5 100644 --- a/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/WispProcessor.kt @@ -1,5 +1,6 @@ package com.angrypodo.wisp +import com.angrypodo.wisp.WispValidator.validateDuplicatePaths import com.angrypodo.wisp.annotations.Wisp import com.angrypodo.wisp.generator.RouteFactoryGenerator import com.angrypodo.wisp.generator.WispRegistryGenerator @@ -39,12 +40,17 @@ internal class WispProcessor( routeInfo to routeClass } - if (!validateDuplicatePaths(routesWithSymbols)) return deferredSymbols + val routeInfos = routesWithSymbols.map { it.first } + + val duplicateValidationResult = validateDuplicatePaths(routeInfos) + + if (duplicateValidationResult is WispValidator.ValidationResult.Failure) { + duplicateValidationResult.errors.forEach { logger.error(it) } + return deferredSymbols + } if (routesWithSymbols.isNotEmpty()) { - val routeInfos = routesWithSymbols.map { it.first } val sourceFiles = routesWithSymbols.mapNotNull { it.second.containingFile }.distinct() - generateRouteRegistry(routeInfos, sourceFiles) } @@ -64,25 +70,6 @@ internal class WispProcessor( return routeInfo } - private fun validateDuplicatePaths( - routesWithSymbols: List> - ): Boolean { - val duplicates = routesWithSymbols.groupBy { it.first.wispPath } - .filter { it.value.size > 1 } - - if (duplicates.isEmpty()) return true - - duplicates.forEach { (path, pairs) -> - pairs.forEach { (_, symbol) -> - logger.error( - "Wisp Error: The path '$path' is already used by another route.", - symbol - ) - } - } - return false - } - private fun validateSerializable(routeClass: KSClassDeclaration): Boolean { if (routeClass.hasSerializableAnnotation()) return true val routeName = routeClass.qualifiedName?.asString() diff --git a/wisp-processor/src/main/java/com/angrypodo/wisp/WispValidator.kt b/wisp-processor/src/main/java/com/angrypodo/wisp/WispValidator.kt index 165fad5..a73417d 100644 --- a/wisp-processor/src/main/java/com/angrypodo/wisp/WispValidator.kt +++ b/wisp-processor/src/main/java/com/angrypodo/wisp/WispValidator.kt @@ -1,19 +1,37 @@ package com.angrypodo.wisp +import com.angrypodo.wisp.model.RouteInfo + internal object WispValidator { sealed interface ValidationResult { data object Success : ValidationResult - data class Failure(val message: String) : ValidationResult + data class Failure(val errors: List) : ValidationResult } fun validate(routeInfo: RouteClassInfo): ValidationResult { if (!routeInfo.isSerializable()) { return ValidationResult.Failure( - message = "Wisp Error: Route Class '${routeInfo.qualifiedName}' " + - "must be annotated with @Serializable." + listOf( + "Wisp Error: Route Class '${routeInfo.qualifiedName}' " + + "must be annotated with @Serializable." + ) ) } return ValidationResult.Success } + + fun validateDuplicatePaths(routes: List): ValidationResult { + val duplicates = routes.groupBy { it.wispPath } + .filter { it.value.size > 1 } + + if (duplicates.isEmpty()) return ValidationResult.Success + + val errorMessages = duplicates.map { (path, routeInfos) -> + val conflictingClasses = routeInfos.joinToString(", ") { it.routeClassName.simpleName } + "Wisp Error: The path '$path' is already used by multiple routes: [$conflictingClasses]" + } + + return ValidationResult.Failure(errorMessages) + } } diff --git a/wisp-processor/src/test/java/com/angrypodo/wisp/WispValidatorTest.kt b/wisp-processor/src/test/java/com/angrypodo/wisp/WispValidatorTest.kt index 98b6286..cf0b63d 100644 --- a/wisp-processor/src/test/java/com/angrypodo/wisp/WispValidatorTest.kt +++ b/wisp-processor/src/test/java/com/angrypodo/wisp/WispValidatorTest.kt @@ -26,7 +26,7 @@ class WispValidatorTest { assertTrue(result is WispValidator.ValidationResult.Failure) { "결과 타입은 Failure여야 합니다." } - assertEquals(expectedMessage, (result as WispValidator.ValidationResult.Failure).message) + assertEquals(expectedMessage, (result as WispValidator.ValidationResult.Failure).errors) } @Test From 384ba553c387878373cf6dbe3b18072dc4fb363a Mon Sep 17 00:00:00 2001 From: Jong Date: Sat, 22 Nov 2025 23:00:21 +0900 Subject: [PATCH 10/10] fix: lint format --- .../src/test/java/com/angrypodo/wisp/WispValidatorTest.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wisp-processor/src/test/java/com/angrypodo/wisp/WispValidatorTest.kt b/wisp-processor/src/test/java/com/angrypodo/wisp/WispValidatorTest.kt index cf0b63d..bcee6d1 100644 --- a/wisp-processor/src/test/java/com/angrypodo/wisp/WispValidatorTest.kt +++ b/wisp-processor/src/test/java/com/angrypodo/wisp/WispValidatorTest.kt @@ -26,7 +26,10 @@ class WispValidatorTest { assertTrue(result is WispValidator.ValidationResult.Failure) { "결과 타입은 Failure여야 합니다." } - assertEquals(expectedMessage, (result as WispValidator.ValidationResult.Failure).errors) + assertEquals( + listOf(expectedMessage), + (result as WispValidator.ValidationResult.Failure).errors + ) } @Test