diff --git a/context-tests/src/test/kotlin/io/spine/tools/validation/AssumedRequiredIdSpec.kt b/context-tests/src/test/kotlin/io/spine/tools/validation/AssumedRequiredIdSpec.kt new file mode 100644 index 0000000000..478865aa3b --- /dev/null +++ b/context-tests/src/test/kotlin/io/spine/tools/validation/AssumedRequiredIdSpec.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.tools.validation + +import com.google.protobuf.Descriptors.Descriptor +import io.kotest.matchers.string.shouldNotContain +import io.spine.logging.testing.ConsoleTap +import io.spine.logging.testing.tapConsole +import io.spine.testing.compiler.PipelineSetup +import io.spine.testing.compiler.acceptingOnly +import io.spine.testing.compiler.pipelineParams +import io.spine.testing.compiler.withRequestFile +import io.spine.testing.compiler.withSettingsDir +import io.spine.tools.code.SourceSetName +import io.spine.tools.compiler.params.WorkingDirectory +import io.spine.tools.compiler.protobuf.field +import io.spine.tools.validation.given.EntityWithInt32Id +import java.nio.file.Path +import kotlin.io.path.createDirectories +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +@DisplayName("Assumed `(required)` entity IDs should") +internal class AssumedRequiredIdSpec { + + @TempDir + lateinit var workingDir: Path + + @Test + fun `not emit a warning for an int32 field`() { + val descriptor = EntityWithInt32Id.getDescriptor() + val output = compile(descriptor) + val field = descriptor.field("id") + + output shouldNotContain field.fullName + output shouldNotContain "should not be declared as `(required)`" + output shouldNotContain "assumed to be required" + } + + private fun compile(descriptor: Descriptor): String { + val wd = WorkingDirectory(workingDir) + val outputDir = workingDir.resolve("output") + outputDir.createDirectories() + val params = pipelineParams { + withRequestFile(wd.requestDirectory.file(SourceSetName("testFixtures"))) + withSettingsDir(wd.settingsDirectory.path) + } + val setup = PipelineSetup.byResources( + params, + plugins = listOf(object : ValidationPlugin() {}), + outputRoot = outputDir, + descriptorFilter = acceptingOnly(descriptor) + ) {} + val pipeline = setup.createPipeline() + return tapConsole { + pipeline() + } + } + + companion object { + + @JvmStatic + @BeforeAll + fun installConsoleTap() { + ConsoleTap.install() + } + } +} diff --git a/context-tests/src/testFixtures/proto/spine/validation/assumed_required_id_spec.proto b/context-tests/src/testFixtures/proto/spine/validation/assumed_required_id_spec.proto new file mode 100644 index 0000000000..c574431880 --- /dev/null +++ b/context-tests/src/testFixtures/proto/spine/validation/assumed_required_id_spec.proto @@ -0,0 +1,42 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +syntax = "proto3"; + +package spine.validation.stubs; + +import "spine/options.proto"; + +option (type_url_prefix) = "type.spine.io"; +option java_package = "io.spine.tools.validation.given"; +option java_outer_classname = "AssumedRequiredIdSpecProto"; +option java_multiple_files = true; + +message EntityWithInt32Id { + option (entity).kind = ENTITY; + + int32 id = 1; +} diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/RequiredGenerator.kt b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/RequiredGenerator.kt index 61948ece2b..b96c14b8f7 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/RequiredGenerator.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/RequiredGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2026, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,7 @@ import io.spine.tools.validation.java.generate.SingleOptionCode import io.spine.tools.validation.java.generate.ValidateScope.parentName import io.spine.tools.validation.java.generate.ValidateScope.parentPath import io.spine.tools.validation.java.generate.ValidateScope.violations +import io.spine.tools.validation.option.required.RequiredFieldSupport.isSupported import io.spine.validation.ConstraintViolation import io.spine.tools.validation.option.IF_MISSING import io.spine.tools.validation.RequiredField @@ -71,6 +72,7 @@ internal class RequiredGenerator : OptionGeneratorWithConverter() { override fun codeFor(type: TypeName): List = allRequiredFields .filter { it.id.type == type } + .filter { it.subject.type.isSupported() } .map { GenerateRequired(it, converter).code() } } diff --git a/tests/validating/src/test/kotlin/io/spine/test/options/AssumedRequiredITest.kt b/tests/validating/src/test/kotlin/io/spine/test/options/AssumedRequiredITest.kt index 418344e4fd..015ad04c63 100644 --- a/tests/validating/src/test/kotlin/io/spine/test/options/AssumedRequiredITest.kt +++ b/tests/validating/src/test/kotlin/io/spine/test/options/AssumedRequiredITest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024, TeamDev. All rights reserved. + * Copyright 2026, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import io.spine.base.Identifier import io.spine.test.tools.validate.TaskId import io.spine.test.tools.validate.command.AssignTask import io.spine.test.tools.validate.command.CreateProject +import io.spine.test.tools.validate.entity.NumberStats import io.spine.test.tools.validate.entity.Project import io.spine.test.tools.validate.entity.Task import io.spine.tools.validation.assertions.assertInvalid @@ -85,5 +86,11 @@ internal class AssumedRequiredITest { val msg = Task.newBuilder() assertValid(msg) } + + @Test + fun `not requiring a primitive ID`() { + val msg = NumberStats.newBuilder() + assertValid(msg) + } } } diff --git a/tests/validating/src/testFixtures/proto/spine/test/tools/validate/entity.proto b/tests/validating/src/testFixtures/proto/spine/test/tools/validate/entity.proto index db2d77a63a..b868dc2ee5 100644 --- a/tests/validating/src/testFixtures/proto/spine/test/tools/validate/entity.proto +++ b/tests/validating/src/testFixtures/proto/spine/test/tools/validate/entity.proto @@ -1,11 +1,11 @@ /* - * Copyright 2024, TeamDev. All rights reserved. + * Copyright 2026, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Redistribution and use in source and/or binary forms, with or without * modification, must retain the above copyright notice and the following @@ -23,6 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + syntax = "proto3"; package spine.test.tools.validate; @@ -52,3 +53,10 @@ message Task { // Here the first field (which is assumed to be required) is explicitly set optional. string id = 1 [(required) = false]; } + +// An entity type with a primitive ID. +message NumberStats { + option (entity).kind = ENTITY; + + int32 id = 1; +}