|
| 1 | +package com.metaobjects.generator.spring; |
| 2 | + |
| 3 | +import com.metaobjects.loader.MetaDataLoader; |
| 4 | +import com.metaobjects.registry.SharedRegistryTestBase; |
| 5 | +import org.junit.Rule; |
| 6 | +import org.junit.Test; |
| 7 | +import org.junit.rules.TemporaryFolder; |
| 8 | + |
| 9 | +import java.io.IOException; |
| 10 | +import java.nio.file.Files; |
| 11 | +import java.nio.file.Path; |
| 12 | +import java.util.HashMap; |
| 13 | +import java.util.Map; |
| 14 | + |
| 15 | +import static org.junit.Assert.assertFalse; |
| 16 | +import static org.junit.Assert.assertTrue; |
| 17 | + |
| 18 | +/** |
| 19 | + * FR-017 / typed-enum coherence guard: a TPH {@code @discriminator} that is declared |
| 20 | + * {@code field.enum} MUST be typed in the generated DTO as the value-constrained enum (the |
| 21 | + * cross-port typed-enum contract — TS string-literal union, Python {@code Literal}, C#/Kotlin/Java |
| 22 | + * enum), NEVER as a raw {@code String}. |
| 23 | + * |
| 24 | + * <p>This is otherwise only exercised end-to-end by the docker-gated api-contract TPH corpus, where |
| 25 | + * a regression to a {@code String}-typed discriminator would slip past the HTTP/JSON-seeding ports |
| 26 | + * (it deserializes fine) and only break the direct-constructing Java lane. This fast unit-level |
| 27 | + * assertion pins the static type so the discriminator can't silently regress to {@code String}. |
| 28 | + * Loads the SAME shared fixture the integration lane uses |
| 29 | + * ({@code fixtures/api-contract-conformance/tph/meta.json}: TPH base {@code Auth} with a |
| 30 | + * {@code field.enum type} discriminator over {@code Bridge|Copay|PriorAuth}).</p> |
| 31 | + */ |
| 32 | +public class TphDiscriminatorEnumConformanceTest extends SharedRegistryTestBase { |
| 33 | + |
| 34 | + @Rule |
| 35 | + public TemporaryFolder tmp = new TemporaryFolder(); |
| 36 | + |
| 37 | + @Test |
| 38 | + public void tphEnumDiscriminatorIsTypedAsTheEnumNotString() throws Exception { |
| 39 | + Path repoRoot = Path.of(System.getProperty("user.dir")).resolve("../../..").normalize(); |
| 40 | + Path fixture = repoRoot.resolve("fixtures/api-contract-conformance/tph/meta.json"); |
| 41 | + assertTrue("shared TPH fixture must exist at " + fixture, Files.exists(fixture)); |
| 42 | + MetaDataLoader loader = SpringTestFixtures.loadFixture( |
| 43 | + tmp.newFolder().toPath(), "tphDiscriminatorEnum", Files.readString(fixture)); |
| 44 | + |
| 45 | + Path out = tmp.newFolder().toPath(); |
| 46 | + Map<String, String> args = new HashMap<>(); |
| 47 | + args.put("outputDir", out.toString()); |
| 48 | + SpringDtoGenerator gen = new SpringDtoGenerator(); |
| 49 | + gen.setArgs(args); |
| 50 | + gen.execute(loader); |
| 51 | + |
| 52 | + // The TPH base's union DTO carries the discriminator. |
| 53 | + Path dto = out.resolve("acme/auth/AuthDto.java"); |
| 54 | + assertTrue("AuthDto.java must be emitted", Files.exists(dto)); |
| 55 | + String src = Files.readString(dto); |
| 56 | + |
| 57 | + // The discriminator component is the value-constrained enum, not a bare String. |
| 58 | + assertTrue("the @discriminator field.enum must be typed as the generated enum; saw:\n" + src, |
| 59 | + src.contains("AuthType type")); |
| 60 | + assertTrue("the discriminator enum must be materialized with its @values; saw:\n" + src, |
| 61 | + src.contains("public enum AuthType { Bridge, Copay, PriorAuth }")); |
| 62 | + assertFalse("the @discriminator must NOT regress to a raw String component; saw:\n" + src, |
| 63 | + src.contains("String type")); |
| 64 | + } |
| 65 | +} |
0 commit comments